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:
parent
e4694ca519
commit
22a10f0e4a
6 changed files with 162 additions and 157 deletions
|
@ -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 =
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue