Skip to content

Commit 22a10f0

Browse files
committed
refactor region manip. to remove redundancy, get closer to fn subtyping
also: remove "auto-mode-matching" for implemented interfaces, as it is complex and interacts poorly with classes cc #2263
1 parent e4694ca commit 22a10f0

File tree

6 files changed

+162
-157
lines changed

6 files changed

+162
-157
lines changed

src/rustc/middle/typeck/check.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ import astconv::{ast_conv, ast_ty_to_ty};
7070
import collect::{methods}; // ccx.to_ty()
7171
import method::{methods}; // methods for method::lookup
7272
import middle::ty::tys_in_fn_ty;
73-
import regionmanip::{replace_bound_regions_in_fn_ty,
74-
region_of, replace_bound_regions,
75-
collect_bound_regions_in_tys};
73+
import regionmanip::{replace_bound_regions_in_fn_ty, region_of};
7674
import rscope::*;
7775

7876
type fn_ctxt =

src/rustc/middle/typeck/check/regionmanip.rs

+96-106
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn replace_bound_regions_in_fn_ty(
2020
all_tys.map { |t| ty_to_str(tcx, t) }];
2121
let _i = indenter();
2222

23-
let isr = collect_bound_regions_in_tys(tcx, isr, all_tys) { |br|
23+
let isr = create_bound_region_mapping(tcx, isr, all_tys) { |br|
2424
#debug["br=%?", br];
2525
mapf(br)
2626
};
@@ -36,47 +36,105 @@ fn replace_bound_regions_in_fn_ty(
3636
ret {isr: isr,
3737
self_ty: t_self,
3838
fn_ty: alt check ty::get(t_fn).struct { ty::ty_fn(o) {o} }};
39-
}
4039

41-
// Takes `isr`, a mapping from in-scope region names ("isr"s) to their
42-
// corresponding regions (possibly produced by a call to
43-
// collect_bound_regions_in_tys; and `ty`, a type. Returns an updated
44-
// version of `ty`, in which bound regions in `ty` have been replaced
45-
// with the corresponding bindings in `isr`.
46-
fn replace_bound_regions(
47-
tcx: ty::ctxt,
48-
isr: isr_alist,
49-
ty: ty::t) -> ty::t {
50-
51-
ty::fold_regions(tcx, ty) { |r, in_fn|
52-
alt r {
53-
// As long as we are not within a fn() type, `&T` is mapped to the
54-
// free region anon_r. But within a fn type, it remains bound.
55-
ty::re_bound(ty::br_anon) if in_fn { r }
56-
57-
ty::re_bound(br) {
58-
alt isr.find(br) {
59-
// In most cases, all named, bound regions will be mapped to
60-
// some free region.
61-
some(fr) { fr }
62-
63-
// But in the case of a fn() type, there may be named regions
64-
// within that remain bound:
65-
none if in_fn { r }
66-
none {
67-
tcx.sess.bug(
68-
#fmt["Bound region not found in \
69-
in_scope_regions list: %s",
70-
region_to_str(tcx, r)]);
40+
41+
// Takes `isr`, a (possibly empty) mapping from in-scope region
42+
// names ("isr"s) to their corresponding regions; `tys`, a list of
43+
// types, and `to_r`, a closure that takes a bound_region and
44+
// returns a region. Returns an updated version of `isr`,
45+
// extended with the in-scope region names from all of the bound
46+
// regions appearing in the types in the `tys` list (if they're
47+
// not in `isr` already), with each of those in-scope region names
48+
// mapped to a region that's the result of applying `to_r` to
49+
// itself.
50+
fn create_bound_region_mapping(
51+
tcx: ty::ctxt,
52+
isr: isr_alist,
53+
tys: [ty::t],
54+
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
55+
56+
// Takes `isr` (described above), `to_r` (described above),
57+
// and `r`, a region. If `r` is anything other than a bound
58+
// region, or if it's a bound region that already appears in
59+
// `isr`, then we return `isr` unchanged. If `r` is a bound
60+
// region that doesn't already appear in `isr`, we return an
61+
// updated isr_alist that now contains a mapping from `r` to
62+
// the result of calling `to_r` on it.
63+
fn append_isr(isr: isr_alist,
64+
to_r: fn(ty::bound_region) -> ty::region,
65+
r: ty::region) -> isr_alist {
66+
alt r {
67+
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
68+
ty::re_var(_) {
69+
isr
70+
}
71+
ty::re_bound(br) {
72+
alt isr.find(br) {
73+
some(_) { isr }
74+
none { @cons((br, to_r(br)), isr) }
75+
}
7176
}
7277
}
73-
}
78+
}
79+
80+
// For each type `ty` in `tys`...
81+
tys.foldl(isr) { |isr, ty|
82+
let mut isr = isr;
83+
84+
// Using fold_regions is inefficient, because it
85+
// constructs new types, but it avoids code duplication in
86+
// terms of locating all the regions within the various
87+
// kinds of types. This had already caused me several
88+
// bugs so I decided to switch over.
89+
ty::fold_regions(tcx, ty) { |r, in_fn|
90+
if !in_fn { isr = append_isr(isr, to_r, r); }
91+
r
92+
};
93+
94+
isr
95+
}
96+
}
97+
98+
// Takes `isr`, a mapping from in-scope region names ("isr"s) to
99+
// their corresponding regions; and `ty`, a type. Returns an
100+
// updated version of `ty`, in which bound regions in `ty` have
101+
// been replaced with the corresponding bindings in `isr`.
102+
fn replace_bound_regions(
103+
tcx: ty::ctxt,
104+
isr: isr_alist,
105+
ty: ty::t) -> ty::t {
106+
107+
ty::fold_regions(tcx, ty) { |r, in_fn|
108+
alt r {
109+
// As long as we are not within a fn() type, `&T` is
110+
// mapped to the free region anon_r. But within a fn
111+
// type, it remains bound.
112+
ty::re_bound(ty::br_anon) if in_fn { r }
113+
114+
ty::re_bound(br) {
115+
alt isr.find(br) {
116+
// In most cases, all named, bound regions will be
117+
// mapped to some free region.
118+
some(fr) { fr }
119+
120+
// But in the case of a fn() type, there may be
121+
// named regions within that remain bound:
122+
none if in_fn { r }
123+
none {
124+
tcx.sess.bug(
125+
#fmt["Bound region not found in \
126+
in_scope_regions list: %s",
127+
region_to_str(tcx, r)]);
128+
}
129+
}
130+
}
74131

75-
// Free regions like these just stay the same:
76-
ty::re_static |
77-
ty::re_scope(_) |
78-
ty::re_free(_, _) |
79-
ty::re_var(_) { r }
132+
// Free regions like these just stay the same:
133+
ty::re_static |
134+
ty::re_scope(_) |
135+
ty::re_free(_, _) |
136+
ty::re_var(_) { r }
137+
}
80138
}
81139
}
82140
}
@@ -149,71 +207,3 @@ fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
149207
}
150208
}
151209
}
152-
153-
// Takes `isr`, a (possibly empty) mapping from in-scope region names ("isr"s)
154-
// to their corresponding regions; `tys`, a list of types, and `to_r`, a
155-
// closure that takes a bound_region and returns a region. Returns an updated
156-
// version of `isr`, extended with the in-scope region names from all of the
157-
// bound regions appearing in the types in the `tys` list (if they're not in
158-
// `isr` already), with each of those in-scope region names mapped to a region
159-
// that's the result of applying `to_r` to itself.
160-
161-
// "collect" is something of a misnomer -- we're not merely collecting
162-
// a list of the bound regions, but also doing the work of applying
163-
// `to_r` to them!
164-
fn collect_bound_regions_in_tys(
165-
tcx: ty::ctxt,
166-
isr: isr_alist,
167-
tys: [ty::t],
168-
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
169-
170-
// Takes `isr` (described above), `to_r` (described above), and `r`, a
171-
// region. If `r` is anything other than a bound region, or if it's a
172-
// bound region that already appears in `isr`, then we return `isr`
173-
// unchanged. If `r` is a bound region that doesn't already appear in
174-
// `isr`, we return an updated isr_alist that now contains a mapping from
175-
// `r` to the result of calling `to_r` on it.
176-
fn append_isr(isr: isr_alist,
177-
to_r: fn(ty::bound_region) -> ty::region,
178-
r: ty::region) -> isr_alist {
179-
alt r {
180-
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
181-
ty::re_var(_) {
182-
isr
183-
}
184-
ty::re_bound(br) {
185-
alt isr.find(br) {
186-
some(_) { isr }
187-
none { @cons((br, to_r(br)), isr) }
188-
}
189-
}
190-
}
191-
}
192-
193-
// For each region in `t`, apply the `append_isr` function to that
194-
// region, accumulating and returning the results in an isr_alist.
195-
fn fold_over_regions_in_type(
196-
tcx: ty::ctxt,
197-
isr: isr_alist,
198-
ty: ty::t,
199-
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
200-
201-
let mut isr = isr;
202-
203-
// Using fold_regions is inefficient, because it constructs new types,
204-
// but it avoids code duplication in terms of locating all the regions
205-
// within the various kinds of types. This had already caused me
206-
// several bugs so I decided to switch over.
207-
ty::fold_regions(tcx, ty) { |r, in_fn|
208-
if !in_fn { isr = append_isr(isr, to_r, r); }
209-
r
210-
};
211-
212-
ret isr;
213-
}
214-
215-
// For each type `t` in `tys`...
216-
tys.foldl(isr) { |isr, t|
217-
fold_over_regions_in_type(tcx, isr, t, to_r)
218-
}
219-
}

src/rustc/middle/typeck/collect.rs

+56-39
Original file line numberDiff line numberDiff line change
@@ -151,48 +151,74 @@ fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) {
151151
}
152152
}
153153

154-
fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
155-
impl_tps: uint, if_m: ty::method, substs: ty::substs,
156-
self_ty: ty::t) -> ty::t {
154+
#[doc = "
155+
Checks that a method from an impl/class conforms to the signature of
156+
the same method as declared in the iface.
157+
158+
# Parameters
159+
160+
- impl_m: the method in the impl
161+
- impl_tps: the type params declared on the impl itself (not the method!)
162+
- if_m: the method in the iface
163+
- if_substs: the substitutions used on the type of the iface
164+
- self_ty: the self type of the impl
165+
"]
166+
fn compare_impl_method(tcx: ty::ctxt, sp: span,
167+
impl_m: ty::method, impl_tps: uint,
168+
if_m: ty::method, if_substs: ty::substs,
169+
self_ty: ty::t) {
157170

158171
if impl_m.tps != if_m.tps {
159172
tcx.sess.span_err(sp, "method `" + if_m.ident +
160173
"` has an incompatible set of type parameters");
161-
ty::mk_fn(tcx, impl_m.fty)
162-
} else if vec::len(impl_m.fty.inputs) != vec::len(if_m.fty.inputs) {
174+
ret;
175+
}
176+
177+
if vec::len(impl_m.fty.inputs) != vec::len(if_m.fty.inputs) {
163178
tcx.sess.span_err(sp,#fmt["method `%s` has %u parameters \
164179
but the iface has %u",
165180
if_m.ident,
166181
vec::len(impl_m.fty.inputs),
167182
vec::len(if_m.fty.inputs)]);
168-
ty::mk_fn(tcx, impl_m.fty)
169-
} else {
170-
let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f|
171-
alt ty::get(f.ty).struct {
172-
ty::ty_param(*) | ty::ty_self
173-
if alt i.mode { ast::infer(_) { true } _ { false } } {
174-
{mode: ast::expl(ast::by_ref) with i}
175-
}
176-
_ { i }
177-
}
178-
});
179-
let impl_fty = ty::mk_fn(tcx, {inputs: auto_modes with impl_m.fty});
183+
ret;
184+
}
180185

181-
// Add dummy substs for the parameters of the impl method
186+
// Perform substitutions so that the iface/impl methods are expressed
187+
// in terms of the same set of type/region parameters:
188+
// - replace iface type parameters with those from `if_substs`
189+
// - replace method parameters on the iface with fresh, dummy parameters
190+
// that correspond to the parameters we will find on the impl
191+
// - replace self region with a fresh, dummy region
192+
let dummy_self_r = ty::re_free(0, ty::br_self);
193+
let impl_fty = {
194+
let impl_fty = ty::mk_fn(tcx, impl_m.fty);
195+
replace_bound_self(tcx, impl_fty, dummy_self_r)
196+
};
197+
let if_fty = {
198+
let dummy_tps = vec::from_fn((*if_m.tps).len()) { |i|
199+
// hack: we don't know the def id of the impl tp, but it
200+
// is not important for unification
201+
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
202+
};
182203
let substs = {
183-
self_r: substs.self_r,
204+
self_r: some(dummy_self_r),
184205
self_ty: some(self_ty),
185-
tps: substs.tps + vec::from_fn(vec::len(*if_m.tps), {|i|
186-
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
187-
})
206+
tps: if_substs.tps + dummy_tps
188207
};
189-
let mut if_fty = ty::mk_fn(tcx, if_m.fty);
190-
if_fty = ty::subst(tcx, substs, if_fty);
191-
require_same_types(
192-
tcx, sp, impl_fty, if_fty,
193-
{|| "method `" + if_m.ident +
194-
"` has an incompatible type"});
195-
ret impl_fty;
208+
let if_fty = ty::mk_fn(tcx, if_m.fty);
209+
ty::subst(tcx, substs, if_fty)
210+
};
211+
require_same_types(
212+
tcx, sp, impl_fty, if_fty,
213+
{|| "method `" + if_m.ident + "` has an incompatible type"});
214+
ret;
215+
216+
// Replaces bound references to the self region with `with_r`.
217+
fn replace_bound_self(tcx: ty::ctxt, ty: ty::t,
218+
with_r: ty::region) -> ty::t {
219+
ty::fold_regions(tcx, ty) { |r, _in_fn|
220+
if r == ty::re_bound(ty::br_self) {with_r} else {r}
221+
}
196222
}
197223
}
198224

@@ -219,18 +245,9 @@ fn check_methods_against_iface(ccx: @crate_ctxt,
219245
not match the iface method's \
220246
purity", m.ident]);
221247
}
222-
let mt = compare_impl_method(
248+
compare_impl_method(
223249
ccx.tcx, span, m, vec::len(tps),
224250
if_m, tpt.substs, selfty);
225-
let old = tcx.tcache.get(local_def(id));
226-
if old.ty != mt {
227-
tcx.tcache.insert(
228-
local_def(id),
229-
{bounds: old.bounds,
230-
rp: old.rp,
231-
ty: mt});
232-
write_ty_to_tcx(tcx, id, mt);
233-
}
234251
}
235252
none {
236253
tcx.sess.span_err(

src/rustc/middle/typeck/infer.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -361,29 +361,29 @@ iface st {
361361
}
362362

363363
impl of st for ty::t {
364-
fn sub(infcx: infer_ctxt, b: ty::t) -> ures {
364+
fn sub(infcx: infer_ctxt, &&b: ty::t) -> ures {
365365
sub(infcx).tys(self, b).to_ures()
366366
}
367367

368-
fn lub(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
368+
fn lub(infcx: infer_ctxt, &&b: ty::t) -> cres<ty::t> {
369369
lub(infcx).tys(self, b)
370370
}
371371

372-
fn glb(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> {
372+
fn glb(infcx: infer_ctxt, &&b: ty::t) -> cres<ty::t> {
373373
glb(infcx).tys(self, b)
374374
}
375375
}
376376

377377
impl of st for ty::region {
378-
fn sub(infcx: infer_ctxt, b: ty::region) -> ures {
378+
fn sub(infcx: infer_ctxt, &&b: ty::region) -> ures {
379379
sub(infcx).regions(self, b).chain {|_r| ok(()) }
380380
}
381381

382-
fn lub(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
382+
fn lub(infcx: infer_ctxt, &&b: ty::region) -> cres<ty::region> {
383383
lub(infcx).regions(self, b)
384384
}
385385

386-
fn glb(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> {
386+
fn glb(infcx: infer_ctxt, &&b: ty::region) -> cres<ty::region> {
387387
glb(infcx).regions(self, b)
388388
}
389389
}

0 commit comments

Comments
 (0)