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
This commit is contained in:
Niko Matsakis 2012-05-30 11:01:31 -07:00
parent e4694ca519
commit 22a10f0e4a
6 changed files with 162 additions and 157 deletions

View file

@ -70,9 +70,7 @@ import astconv::{ast_conv, ast_ty_to_ty};
import collect::{methods}; // ccx.to_ty() import collect::{methods}; // ccx.to_ty()
import method::{methods}; // methods for method::lookup import method::{methods}; // methods for method::lookup
import middle::ty::tys_in_fn_ty; import middle::ty::tys_in_fn_ty;
import regionmanip::{replace_bound_regions_in_fn_ty, import regionmanip::{replace_bound_regions_in_fn_ty, region_of};
region_of, replace_bound_regions,
collect_bound_regions_in_tys};
import rscope::*; import rscope::*;
type fn_ctxt = type fn_ctxt =

View file

@ -20,7 +20,7 @@ fn replace_bound_regions_in_fn_ty(
all_tys.map { |t| ty_to_str(tcx, t) }]; all_tys.map { |t| ty_to_str(tcx, t) }];
let _i = indenter(); let _i = indenter();
let isr = collect_bound_regions_in_tys(tcx, isr, all_tys) { |br| let isr = create_bound_region_mapping(tcx, isr, all_tys) { |br|
#debug["br=%?", br]; #debug["br=%?", br];
mapf(br) mapf(br)
}; };
@ -36,47 +36,105 @@ fn replace_bound_regions_in_fn_ty(
ret {isr: isr, ret {isr: isr,
self_ty: t_self, self_ty: t_self,
fn_ty: alt check ty::get(t_fn).struct { ty::ty_fn(o) {o} }}; fn_ty: alt check ty::get(t_fn).struct { ty::ty_fn(o) {o} }};
}
// Takes `isr`, a mapping from in-scope region names ("isr"s) to their
// corresponding regions (possibly produced by a call to
// collect_bound_regions_in_tys; and `ty`, a type. Returns an updated
// version of `ty`, in which bound regions in `ty` have been replaced
// with the corresponding bindings in `isr`.
fn replace_bound_regions(
tcx: ty::ctxt,
isr: isr_alist,
ty: ty::t) -> ty::t {
ty::fold_regions(tcx, ty) { |r, in_fn| // Takes `isr`, a (possibly empty) mapping from in-scope region
alt r { // names ("isr"s) to their corresponding regions; `tys`, a list of
// As long as we are not within a fn() type, `&T` is mapped to the // types, and `to_r`, a closure that takes a bound_region and
// free region anon_r. But within a fn type, it remains bound. // returns a region. Returns an updated version of `isr`,
ty::re_bound(ty::br_anon) if in_fn { r } // extended with the in-scope region names from all of the bound
// regions appearing in the types in the `tys` list (if they're
// not in `isr` already), with each of those in-scope region names
// mapped to a region that's the result of applying `to_r` to
// itself.
fn create_bound_region_mapping(
tcx: ty::ctxt,
isr: isr_alist,
tys: [ty::t],
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
ty::re_bound(br) { // Takes `isr` (described above), `to_r` (described above),
alt isr.find(br) { // and `r`, a region. If `r` is anything other than a bound
// In most cases, all named, bound regions will be mapped to // region, or if it's a bound region that already appears in
// some free region. // `isr`, then we return `isr` unchanged. If `r` is a bound
some(fr) { fr } // region that doesn't already appear in `isr`, we return an
// updated isr_alist that now contains a mapping from `r` to
// But in the case of a fn() type, there may be named regions // the result of calling `to_r` on it.
// within that remain bound: fn append_isr(isr: isr_alist,
none if in_fn { r } to_r: fn(ty::bound_region) -> ty::region,
none { r: ty::region) -> isr_alist {
tcx.sess.bug( alt r {
#fmt["Bound region not found in \ ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
in_scope_regions list: %s", ty::re_var(_) {
region_to_str(tcx, r)]); isr
}
ty::re_bound(br) {
alt isr.find(br) {
some(_) { isr }
none { @cons((br, to_r(br)), isr) }
}
} }
} }
} }
// Free regions like these just stay the same: // For each type `ty` in `tys`...
ty::re_static | tys.foldl(isr) { |isr, ty|
ty::re_scope(_) | let mut isr = isr;
ty::re_free(_, _) |
ty::re_var(_) { r } // Using fold_regions is inefficient, because it
// constructs new types, but it avoids code duplication in
// terms of locating all the regions within the various
// kinds of types. This had already caused me several
// bugs so I decided to switch over.
ty::fold_regions(tcx, ty) { |r, in_fn|
if !in_fn { isr = append_isr(isr, to_r, r); }
r
};
isr
}
}
// Takes `isr`, a mapping from in-scope region names ("isr"s) to
// their corresponding regions; and `ty`, a type. Returns an
// updated version of `ty`, in which bound regions in `ty` have
// been replaced with the corresponding bindings in `isr`.
fn replace_bound_regions(
tcx: ty::ctxt,
isr: isr_alist,
ty: ty::t) -> ty::t {
ty::fold_regions(tcx, ty) { |r, in_fn|
alt r {
// As long as we are not within a fn() type, `&T` is
// mapped to the free region anon_r. But within a fn
// type, it remains bound.
ty::re_bound(ty::br_anon) if in_fn { r }
ty::re_bound(br) {
alt isr.find(br) {
// In most cases, all named, bound regions will be
// mapped to some free region.
some(fr) { fr }
// But in the case of a fn() type, there may be
// named regions within that remain bound:
none if in_fn { r }
none {
tcx.sess.bug(
#fmt["Bound region not found in \
in_scope_regions list: %s",
region_to_str(tcx, r)]);
}
}
}
// Free regions like these just stay the same:
ty::re_static |
ty::re_scope(_) |
ty::re_free(_, _) |
ty::re_var(_) { r }
}
} }
} }
} }
@ -149,71 +207,3 @@ fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
} }
} }
} }
// Takes `isr`, a (possibly empty) mapping from in-scope region names ("isr"s)
// to their corresponding regions; `tys`, a list of types, and `to_r`, a
// closure that takes a bound_region and returns a region. Returns an updated
// version of `isr`, extended with the in-scope region names from all of the
// bound regions appearing in the types in the `tys` list (if they're not in
// `isr` already), with each of those in-scope region names mapped to a region
// that's the result of applying `to_r` to itself.
// "collect" is something of a misnomer -- we're not merely collecting
// a list of the bound regions, but also doing the work of applying
// `to_r` to them!
fn collect_bound_regions_in_tys(
tcx: ty::ctxt,
isr: isr_alist,
tys: [ty::t],
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
// Takes `isr` (described above), `to_r` (described above), and `r`, a
// region. If `r` is anything other than a bound region, or if it's a
// bound region that already appears in `isr`, then we return `isr`
// unchanged. If `r` is a bound region that doesn't already appear in
// `isr`, we return an updated isr_alist that now contains a mapping from
// `r` to the result of calling `to_r` on it.
fn append_isr(isr: isr_alist,
to_r: fn(ty::bound_region) -> ty::region,
r: ty::region) -> isr_alist {
alt r {
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) |
ty::re_var(_) {
isr
}
ty::re_bound(br) {
alt isr.find(br) {
some(_) { isr }
none { @cons((br, to_r(br)), isr) }
}
}
}
}
// For each region in `t`, apply the `append_isr` function to that
// region, accumulating and returning the results in an isr_alist.
fn fold_over_regions_in_type(
tcx: ty::ctxt,
isr: isr_alist,
ty: ty::t,
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
let mut isr = isr;
// Using fold_regions is inefficient, because it constructs new types,
// but it avoids code duplication in terms of locating all the regions
// within the various kinds of types. This had already caused me
// several bugs so I decided to switch over.
ty::fold_regions(tcx, ty) { |r, in_fn|
if !in_fn { isr = append_isr(isr, to_r, r); }
r
};
ret isr;
}
// For each type `t` in `tys`...
tys.foldl(isr) { |isr, t|
fold_over_regions_in_type(tcx, isr, t, to_r)
}
}

View file

@ -151,48 +151,74 @@ fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) {
} }
} }
fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, #[doc = "
impl_tps: uint, if_m: ty::method, substs: ty::substs, Checks that a method from an impl/class conforms to the signature of
self_ty: ty::t) -> ty::t { the same method as declared in the iface.
# Parameters
- impl_m: the method in the impl
- impl_tps: the type params declared on the impl itself (not the method!)
- if_m: the method in the iface
- if_substs: the substitutions used on the type of the iface
- self_ty: the self type of the impl
"]
fn compare_impl_method(tcx: ty::ctxt, sp: span,
impl_m: ty::method, impl_tps: uint,
if_m: ty::method, if_substs: ty::substs,
self_ty: ty::t) {
if impl_m.tps != if_m.tps { if impl_m.tps != if_m.tps {
tcx.sess.span_err(sp, "method `" + if_m.ident + tcx.sess.span_err(sp, "method `" + if_m.ident +
"` has an incompatible set of type parameters"); "` has an incompatible set of type parameters");
ty::mk_fn(tcx, impl_m.fty) ret;
} else if vec::len(impl_m.fty.inputs) != vec::len(if_m.fty.inputs) { }
if vec::len(impl_m.fty.inputs) != vec::len(if_m.fty.inputs) {
tcx.sess.span_err(sp,#fmt["method `%s` has %u parameters \ tcx.sess.span_err(sp,#fmt["method `%s` has %u parameters \
but the iface has %u", but the iface has %u",
if_m.ident, if_m.ident,
vec::len(impl_m.fty.inputs), vec::len(impl_m.fty.inputs),
vec::len(if_m.fty.inputs)]); vec::len(if_m.fty.inputs)]);
ty::mk_fn(tcx, impl_m.fty) ret;
} else { }
let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f|
alt ty::get(f.ty).struct {
ty::ty_param(*) | ty::ty_self
if alt i.mode { ast::infer(_) { true } _ { false } } {
{mode: ast::expl(ast::by_ref) with i}
}
_ { i }
}
});
let impl_fty = ty::mk_fn(tcx, {inputs: auto_modes with impl_m.fty});
// Add dummy substs for the parameters of the impl method // Perform substitutions so that the iface/impl methods are expressed
let substs = { // in terms of the same set of type/region parameters:
self_r: substs.self_r, // - replace iface type parameters with those from `if_substs`
self_ty: some(self_ty), // - replace method parameters on the iface with fresh, dummy parameters
tps: substs.tps + vec::from_fn(vec::len(*if_m.tps), {|i| // that correspond to the parameters we will find on the impl
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) // - replace self region with a fresh, dummy region
}) let dummy_self_r = ty::re_free(0, ty::br_self);
let impl_fty = {
let impl_fty = ty::mk_fn(tcx, impl_m.fty);
replace_bound_self(tcx, impl_fty, dummy_self_r)
};
let if_fty = {
let dummy_tps = vec::from_fn((*if_m.tps).len()) { |i|
// hack: we don't know the def id of the impl tp, but it
// is not important for unification
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
}; };
let mut if_fty = ty::mk_fn(tcx, if_m.fty); let substs = {
if_fty = ty::subst(tcx, substs, if_fty); self_r: some(dummy_self_r),
require_same_types( self_ty: some(self_ty),
tcx, sp, impl_fty, if_fty, tps: if_substs.tps + dummy_tps
{|| "method `" + if_m.ident + };
"` has an incompatible type"}); let if_fty = ty::mk_fn(tcx, if_m.fty);
ret impl_fty; ty::subst(tcx, substs, if_fty)
};
require_same_types(
tcx, sp, impl_fty, if_fty,
{|| "method `" + if_m.ident + "` has an incompatible type"});
ret;
// Replaces bound references to the self region with `with_r`.
fn replace_bound_self(tcx: ty::ctxt, ty: ty::t,
with_r: ty::region) -> ty::t {
ty::fold_regions(tcx, ty) { |r, _in_fn|
if r == ty::re_bound(ty::br_self) {with_r} else {r}
}
} }
} }
@ -219,18 +245,9 @@ fn check_methods_against_iface(ccx: @crate_ctxt,
not match the iface method's \ not match the iface method's \
purity", m.ident]); purity", m.ident]);
} }
let mt = compare_impl_method( compare_impl_method(
ccx.tcx, span, m, vec::len(tps), ccx.tcx, span, m, vec::len(tps),
if_m, tpt.substs, selfty); if_m, tpt.substs, selfty);
let old = tcx.tcache.get(local_def(id));
if old.ty != mt {
tcx.tcache.insert(
local_def(id),
{bounds: old.bounds,
rp: old.rp,
ty: mt});
write_ty_to_tcx(tcx, id, mt);
}
} }
none { none {
tcx.sess.span_err( tcx.sess.span_err(

View file

@ -361,29 +361,29 @@ iface st {
} }
impl of st for ty::t { impl of st for ty::t {
fn sub(infcx: infer_ctxt, b: ty::t) -> ures { fn sub(infcx: infer_ctxt, &&b: ty::t) -> ures {
sub(infcx).tys(self, b).to_ures() sub(infcx).tys(self, b).to_ures()
} }
fn lub(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> { fn lub(infcx: infer_ctxt, &&b: ty::t) -> cres<ty::t> {
lub(infcx).tys(self, b) lub(infcx).tys(self, b)
} }
fn glb(infcx: infer_ctxt, b: ty::t) -> cres<ty::t> { fn glb(infcx: infer_ctxt, &&b: ty::t) -> cres<ty::t> {
glb(infcx).tys(self, b) glb(infcx).tys(self, b)
} }
} }
impl of st for ty::region { impl of st for ty::region {
fn sub(infcx: infer_ctxt, b: ty::region) -> ures { fn sub(infcx: infer_ctxt, &&b: ty::region) -> ures {
sub(infcx).regions(self, b).chain {|_r| ok(()) } sub(infcx).regions(self, b).chain {|_r| ok(()) }
} }
fn lub(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> { fn lub(infcx: infer_ctxt, &&b: ty::region) -> cres<ty::region> {
lub(infcx).regions(self, b) lub(infcx).regions(self, b)
} }
fn glb(infcx: infer_ctxt, b: ty::region) -> cres<ty::region> { fn glb(infcx: infer_ctxt, &&b: ty::region) -> cres<ty::region> {
glb(infcx).regions(self, b) glb(infcx).regions(self, b)
} }
} }

View file

@ -1,9 +1,9 @@
iface add { iface add {
fn +(x: self) -> self; fn +(++x: self) -> self;
} }
impl of add for int { impl of add for int {
fn +(x: int) -> int { self + x } fn +(++x: int) -> int { self + x }
} }
fn do_add<A:add>(x: A, y: A) -> A { x + y } fn do_add<A:add>(x: A, y: A) -> A { x + y }

View file

@ -1,5 +1,5 @@
iface add { iface add {
fn +(x: self) -> self; fn +(++x: self) -> self;
} }
impl of add for int { impl of add for int {