pull method lookup / region manip into their own modules
This commit is contained in:
parent
564aa59a3f
commit
558be3a70f
4 changed files with 566 additions and 557 deletions
|
@ -38,12 +38,64 @@ can be broken down into several distinct phases:
|
|||
are written into the `tcx.node_types` table, which should *never* contain
|
||||
any reference to a type variable.
|
||||
|
||||
## Intermediate types
|
||||
|
||||
While type checking a function, the intermediate types for the
|
||||
expressions, blocks, and so forth contained within the function are
|
||||
stored in `fcx.node_types` and `fcx.node_type_substs`. These types
|
||||
may contain unresolved type variables. After type checking is
|
||||
complete, the functions in the writeback module are used to take the
|
||||
types from this table, resolve them, and then write them into their
|
||||
permanent home in the type context `ccx.tcx`.
|
||||
|
||||
This means that during inferencing you should use `fcx.write_ty()`
|
||||
and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
|
||||
nodes within the function.
|
||||
|
||||
The types of top-level items, which never contain unbound type
|
||||
variables, are stored directly into the `tcx` tables.
|
||||
|
||||
n.b.: A type variable is not the same thing as a type parameter. A
|
||||
type variable is rather an "instance" of a type parameter: that is,
|
||||
given a generic function `fn foo<T>(t: T)`: while checking the
|
||||
function `foo`, the type `ty_param(0)` refers to the type `T`, which
|
||||
is treated in abstract. When `foo()` is called, however, `T` will be
|
||||
substituted for a fresh type variable `ty_var(N)`. This variable will
|
||||
eventually be resolved to some concrete type (which might itself be
|
||||
type parameter).
|
||||
|
||||
*/
|
||||
|
||||
import astconv::{ast_conv, region_scope, empty_rscope, ast_ty_to_ty,
|
||||
in_anon_rscope};
|
||||
import collect::{methods}; // ccx.to_ty()
|
||||
import method::{methods}; // methods for method::lookup
|
||||
import regionmanip::{universally_quantify_regions_before_call,
|
||||
region_of, replace_bound_regions,
|
||||
collect_bound_regions_in_tys};
|
||||
|
||||
type fn_ctxt =
|
||||
// var_bindings, locals and next_var_id are shared
|
||||
// with any nested functions that capture the environment
|
||||
// (and with any functions whose environment is being captured).
|
||||
{self_ty: option<ty::t>,
|
||||
ret_ty: ty::t,
|
||||
// Used by loop bodies that return from the outer function
|
||||
indirect_ret_ty: option<ty::t>,
|
||||
purity: ast::purity,
|
||||
proto: ast::proto,
|
||||
infcx: infer::infer_ctxt,
|
||||
locals: hashmap<ast::node_id, ty_vid>,
|
||||
ty_var_counter: @mut uint,
|
||||
region_var_counter: @mut uint,
|
||||
|
||||
mut blocks: [ast::node_id], // stack of blocks in scope, may be empty
|
||||
in_scope_regions: isr_alist,
|
||||
|
||||
node_types: smallintmap::smallintmap<ty::t>,
|
||||
node_type_substs: hashmap<ast::node_id, ty::substs>,
|
||||
|
||||
ccx: @crate_ctxt};
|
||||
|
||||
// a list of mapping from in-scope-region-names ("isr") to the
|
||||
// corresponding ty::region
|
||||
|
@ -602,111 +654,6 @@ fn valid_range_bounds(ccx: @crate_ctxt, from: @ast::expr, to: @ast::expr)
|
|||
const_eval::compare_lit_exprs(ccx.tcx, from, to) <= 0
|
||||
}
|
||||
|
||||
// Helper for the other universally_quantify_*() routines. Extracts the bound
|
||||
// regions from bound_tys and then replaces those same regions with fresh
|
||||
// variables in `sty`, returning the resulting type.
|
||||
fn universally_quantify_from_sty(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
bound_tys: [ty::t],
|
||||
sty: ty::sty) -> ty::t {
|
||||
|
||||
#debug["universally_quantify_from_sty(bound_tys=%?)",
|
||||
bound_tys.map {|x| fcx.ty_to_str(x) }];
|
||||
indent {||
|
||||
let tcx = fcx.tcx();
|
||||
let isr = collect_bound_regions_in_tys(tcx, @nil, bound_tys) { |br|
|
||||
let rvar = fcx.next_region_var();
|
||||
#debug["Bound region %s maps to %s",
|
||||
bound_region_to_str(fcx.ccx.tcx, br),
|
||||
region_to_str(fcx.ccx.tcx, rvar)];
|
||||
rvar
|
||||
};
|
||||
let t_res = ty::fold_sty_to_ty(fcx.ccx.tcx, sty) { |t|
|
||||
replace_bound_regions(tcx, span, isr, t)
|
||||
};
|
||||
#debug["Result of universal quant. is %s", fcx.ty_to_str(t_res)];
|
||||
t_res
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces all region parameters in the given type with region variables.
|
||||
// Does not descend into fn types. This is used when deciding whether an impl
|
||||
// applies at a given call site. See also universally_quantify_before_call().
|
||||
fn universally_quantify_regions(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
ty: ty::t) -> ty::t {
|
||||
universally_quantify_from_sty(fcx, span, [ty], ty::get(ty).struct)
|
||||
}
|
||||
|
||||
// Expects a function type. Replaces all region parameters in the arguments
|
||||
// and return type with fresh region variables. This is used when typechecking
|
||||
// function calls, bind expressions, and method calls.
|
||||
fn universally_quantify_before_call(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
ty: ty::t) -> ty::t {
|
||||
|
||||
#debug["universally_quantify_before_call(ty=%s)",
|
||||
fcx.ty_to_str(ty)];
|
||||
|
||||
// This is subtle: we expect `ty` to be a function type, which normally
|
||||
// introduce a level of binding. In this case, we want to process the
|
||||
// types bound by the function but not by any nested functions.
|
||||
// Therefore, we match one level of structure.
|
||||
alt structure_of(fcx, span, ty) {
|
||||
sty @ ty::ty_fn(fty) {
|
||||
let all_tys = fty.inputs.map({|a| a.ty}) + [fty.output];
|
||||
universally_quantify_from_sty(fcx, span, all_tys, sty)
|
||||
}
|
||||
sty {
|
||||
#debug["not a fn ty: %?", sty];
|
||||
|
||||
// if not a function type, we're gonna' report an error
|
||||
// at some point, since the user is trying to call this thing
|
||||
ty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_bound_regions(
|
||||
tcx: ty::ctxt,
|
||||
span: span,
|
||||
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.span_bug(
|
||||
span,
|
||||
#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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool {
|
||||
check_expr(fcx, expr, some(expected))
|
||||
}
|
||||
|
@ -770,300 +717,6 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty {
|
|||
{substs: substs, ty: substd_ty}
|
||||
}
|
||||
|
||||
type self_subst = {selfty: ty::t,
|
||||
fcx: @fn_ctxt,
|
||||
sp: span};
|
||||
|
||||
enum lookup = {
|
||||
fcx: @fn_ctxt,
|
||||
expr: @ast::expr, // expr for a.b in a.b()
|
||||
node_id: ast::node_id, // node id of call (not always expr.id)
|
||||
m_name: ast::ident, // b in a.b(...)
|
||||
self_ty: ty::t, // type of a in a.b(...)
|
||||
supplied_tps: [ty::t], // Xs in a.b::<Xs>(...)
|
||||
include_private: bool
|
||||
};
|
||||
|
||||
impl methods for lookup {
|
||||
// Entrypoint:
|
||||
fn method() -> option<method_origin> {
|
||||
// First, see whether this is an interface-bounded parameter
|
||||
let pass1 = alt ty::get(self.self_ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
self.method_from_param(n, did)
|
||||
}
|
||||
ty::ty_iface(did, substs) {
|
||||
self.method_from_iface(did, substs)
|
||||
}
|
||||
ty::ty_class(did, substs) {
|
||||
self.method_from_class(did, substs)
|
||||
}
|
||||
_ {
|
||||
none
|
||||
}
|
||||
};
|
||||
|
||||
alt pass1 {
|
||||
some(r) { some(r) }
|
||||
none { self.method_from_scope() }
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
|
||||
|
||||
fn method_from_param(n: uint, did: ast::def_id) -> option<method_origin> {
|
||||
let tcx = self.tcx();
|
||||
let mut iface_bnd_idx = 0u; // count only iface bounds
|
||||
let bounds = tcx.ty_param_bounds.get(did.node);
|
||||
for vec::each(*bounds) {|bound|
|
||||
let (iid, bound_substs) = alt bound {
|
||||
ty::bound_copy | ty::bound_send { cont; /* ok */ }
|
||||
ty::bound_iface(bound_t) {
|
||||
alt check ty::get(bound_t).struct {
|
||||
ty::ty_iface(i, substs) { (i, substs) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ifce_methods = ty::iface_methods(tcx, iid);
|
||||
alt vec::position(*ifce_methods, {|m| m.ident == self.m_name}) {
|
||||
none {
|
||||
/* check next bound */
|
||||
iface_bnd_idx += 1u;
|
||||
}
|
||||
|
||||
some(pos) {
|
||||
// Replace any appearance of `self` with the type of the
|
||||
// generic parameter itself. Note that this is the only case
|
||||
// where this replacement is necessary: in all other cases, we
|
||||
// are either invoking a method directly from an impl or class
|
||||
// (where the self type is not permitted), or from a iface
|
||||
// type (in which case methods that refer to self are not
|
||||
// permitted).
|
||||
let substs = {self_ty: some(self.self_ty)
|
||||
with bound_substs};
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, ifce_methods[pos],
|
||||
method_param(iid, pos, n, iface_bnd_idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn method_from_iface(
|
||||
did: ast::def_id, iface_substs: ty::substs) -> option<method_origin> {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
for ms.eachi {|i, m|
|
||||
if m.ident != self.m_name { cont; }
|
||||
|
||||
let m_fty = ty::mk_fn(self.tcx(), m.fty);
|
||||
|
||||
if ty::type_has_self(m_fty) {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"can not call a method that contains a \
|
||||
self type through a boxed iface");
|
||||
}
|
||||
|
||||
if (*m.tps).len() > 0u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"can not call a generic method through a \
|
||||
boxed iface");
|
||||
}
|
||||
|
||||
// Note: although it is illegal to invoke a method that uses self
|
||||
// through a iface instance, we use a dummy subst here so that we
|
||||
// can soldier on with the compilation.
|
||||
let substs = {self_ty: some(self.self_ty)
|
||||
with iface_substs};
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, m, method_iface(did, i)));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn method_from_class(did: ast::def_id, class_substs: ty::substs)
|
||||
-> option<method_origin> {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
|
||||
for ms.each {|m|
|
||||
if m.ident != self.m_name { cont; }
|
||||
|
||||
if m.vis == ast::private && !self.include_private {
|
||||
self.tcx().sess.span_fatal(
|
||||
self.expr.span,
|
||||
"Call to private method not allowed outside \
|
||||
its defining class");
|
||||
}
|
||||
|
||||
// look up method named <name>.
|
||||
let m_declared = ty::lookup_class_method_by_name(
|
||||
self.tcx(), did, self.m_name, self.expr.span);
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
class_substs, m,
|
||||
method_static(m_declared)));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn ty_from_did(did: ast::def_id) -> ty::t {
|
||||
alt check ty::get(ty::lookup_item_type(self.tcx(), did).ty).struct {
|
||||
ty::ty_fn(fty) {
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
|
||||
}
|
||||
}
|
||||
/*
|
||||
if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) {
|
||||
// NDM iface/impl regions
|
||||
let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none);
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alt check ty::get(csearch::get_type(self.tcx(), did).ty).struct {
|
||||
ty::ty_fn(fty) {
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fn method_from_scope() -> option<method_origin> {
|
||||
let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
|
||||
|
||||
for list::each(impls_vecs) {|impls|
|
||||
let mut results = [];
|
||||
for vec::each(*impls) {|im|
|
||||
// Check whether this impl has a method with the right name.
|
||||
for im.methods.find({|m| m.ident == self.m_name}).each {|m|
|
||||
|
||||
// determine the `self` with fresh variables for
|
||||
// each parameter:
|
||||
let {substs: self_substs, ty: self_ty} =
|
||||
impl_self_ty(self.fcx, im.did);
|
||||
|
||||
// Here "self" refers to the callee side...
|
||||
let self_ty =
|
||||
universally_quantify_regions(
|
||||
self.fcx, self.expr.span, self_ty);
|
||||
|
||||
// ... and "ty" refers to the caller side.
|
||||
let ty =
|
||||
universally_quantify_regions(
|
||||
self.fcx, self.expr.span, self.self_ty);
|
||||
|
||||
// if we can assign the caller to the callee, that's a
|
||||
// potential match. Collect those in the vector.
|
||||
alt self.fcx.mk_subty(ty, self_ty) {
|
||||
result::err(_) { /* keep looking */ }
|
||||
result::ok(_) {
|
||||
results += [(self_substs, m.n_tps, m.did)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if results.len() >= 1u {
|
||||
if results.len() > 1u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"multiple applicable methods in scope");
|
||||
|
||||
// I would like to print out how each impl was imported,
|
||||
// but I cannot for the life of me figure out how to
|
||||
// annotate resolve to preserve this information.
|
||||
for results.eachi { |i, result|
|
||||
let (_, _, did) = result;
|
||||
let span = if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) { m.span }
|
||||
}
|
||||
} else {
|
||||
self.expr.span
|
||||
};
|
||||
self.tcx().sess.span_note(
|
||||
span,
|
||||
#fmt["candidate #%u is %s",
|
||||
(i+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
}
|
||||
|
||||
let (self_substs, n_tps, did) = results[0];
|
||||
let fty = self.ty_from_did(did);
|
||||
ret some(self.write_mty_from_fty(
|
||||
self_substs, n_tps, fty,
|
||||
method_static(did)));
|
||||
}
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn write_mty_from_m(self_substs: ty::substs,
|
||||
m: ty::method,
|
||||
origin: method_origin) -> method_origin {
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
// a bit hokey, but the method unbound has a bare protocol, whereas
|
||||
// a.b has a protocol like fn@() (perhaps eventually fn&()):
|
||||
let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});
|
||||
|
||||
ret self.write_mty_from_fty(self_substs, (*m.tps).len(),
|
||||
fty, origin);
|
||||
}
|
||||
|
||||
fn write_mty_from_fty(self_substs: ty::substs,
|
||||
n_tps_m: uint,
|
||||
fty: ty::t,
|
||||
origin: method_origin) -> method_origin {
|
||||
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
// Here I will use the "c_" prefix to refer to the method's
|
||||
// owner. You can read it as class, but it may also be an iface.
|
||||
|
||||
let n_tps_supplied = self.supplied_tps.len();
|
||||
let m_substs = {
|
||||
if n_tps_supplied == 0u {
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else if n_tps_m == 0u {
|
||||
tcx.sess.span_err(
|
||||
self.expr.span,
|
||||
"this method does not take type parameters");
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else if n_tps_supplied != n_tps_m {
|
||||
tcx.sess.span_err(
|
||||
self.expr.span,
|
||||
"incorrect number of type \
|
||||
parameters given for this method");
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else {
|
||||
self.supplied_tps
|
||||
}
|
||||
};
|
||||
|
||||
let all_substs = {tps: self_substs.tps + m_substs
|
||||
with self_substs};
|
||||
|
||||
self.fcx.write_ty_substs(self.node_id, fty, all_substs);
|
||||
|
||||
ret origin;
|
||||
}
|
||||
}
|
||||
|
||||
// Only for fields! Returns <none> for methods>
|
||||
// FIXME: privacy flags
|
||||
fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id,
|
||||
|
@ -1076,47 +729,6 @@ fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id,
|
|||
}
|
||||
}
|
||||
|
||||
/* Returns the region that &expr should be placed into. If expr is an
|
||||
* lvalue, this will be the region of the lvalue. Otherwise, if region is
|
||||
* an rvalue, the semantics are that the result is stored into a temporary
|
||||
* stack position and so the resulting region will be the enclosing block.
|
||||
*/
|
||||
fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
|
||||
fn borrow(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
|
||||
ty::encl_region(fcx.ccx.tcx, expr.id)
|
||||
}
|
||||
|
||||
fn deref(fcx: @fn_ctxt, base: @ast::expr) -> ty::region {
|
||||
let base_ty = fcx.expr_ty(base);
|
||||
let base_ty = structurally_resolved_type(fcx, base.span, base_ty);
|
||||
alt ty::get(base_ty).struct {
|
||||
ty::ty_rptr(region, _) { region }
|
||||
ty::ty_box(_) | ty::ty_uniq(_) { borrow(fcx, base) }
|
||||
_ { region_of(fcx, base) }
|
||||
}
|
||||
}
|
||||
|
||||
alt expr.node {
|
||||
ast::expr_path(path) {
|
||||
let defn = lookup_def(fcx, path.span, expr.id);
|
||||
alt defn {
|
||||
ast::def_local(local_id, _) |
|
||||
ast::def_upvar(local_id, _, _) {
|
||||
let local_scope = fcx.ccx.tcx.region_map.get(local_id);
|
||||
ty::re_scope(local_scope)
|
||||
}
|
||||
_ {
|
||||
ty::re_static
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::expr_field(base, _, _) { deref(fcx, base) }
|
||||
ast::expr_index(base, _) { deref(fcx, base) }
|
||||
ast::expr_unary(ast::deref, base) { deref(fcx, base) }
|
||||
_ { borrow(fcx, expr) }
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
expr: @ast::expr,
|
||||
expected: option<ty::t>,
|
||||
|
@ -1133,7 +745,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
|
||||
let mut bot = false;
|
||||
|
||||
let fty = universally_quantify_before_call(fcx, sp, fty);
|
||||
let fty = universally_quantify_regions_before_call(fcx, sp, fty);
|
||||
#debug["check_call_or_bind: after universal quant., fty=%s",
|
||||
fcx.ty_to_str(fty)];
|
||||
|
||||
|
@ -1291,13 +903,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
opname: str, args: [option<@ast::expr>])
|
||||
-> option<(ty::t, bool)> {
|
||||
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
||||
let lkup = lookup({fcx: fcx,
|
||||
expr: op_ex,
|
||||
node_id: callee_id,
|
||||
m_name: opname,
|
||||
self_ty: self_t,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: op_ex,
|
||||
node_id: callee_id,
|
||||
m_name: opname,
|
||||
self_ty: self_t,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
alt lkup.method() {
|
||||
some(origin) {
|
||||
let {fty: method_ty, bot: bot} = {
|
||||
|
@ -1977,13 +1589,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
}
|
||||
if !handled {
|
||||
let tps = vec::map(tys) { |ty| fcx.to_ty(ty) };
|
||||
let lkup = lookup({fcx: fcx,
|
||||
expr: expr,
|
||||
node_id: expr.id,
|
||||
m_name: field,
|
||||
self_ty: expr_t,
|
||||
supplied_tps: tps,
|
||||
include_private: self_ref(fcx, base.id)});
|
||||
let is_self_ref = self_ref(fcx, base.id);
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: expr,
|
||||
node_id: expr.id,
|
||||
m_name: field,
|
||||
self_ty: expr_t,
|
||||
supplied_tps: tps,
|
||||
include_private: is_self_ref});
|
||||
alt lkup.method() {
|
||||
some(origin) {
|
||||
fcx.ccx.method_map.insert(id, origin);
|
||||
|
@ -2032,13 +1645,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
|
||||
let p_ty = fcx.expr_ty(p);
|
||||
|
||||
let lkup = lookup({fcx: fcx,
|
||||
expr: p,
|
||||
node_id: alloc_id,
|
||||
m_name: "alloc",
|
||||
self_ty: p_ty,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: p,
|
||||
node_id: alloc_id,
|
||||
m_name: "alloc",
|
||||
self_ty: p_ty,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
alt lkup.method() {
|
||||
some(origin) {
|
||||
fcx.ccx.method_map.insert(alloc_id, origin);
|
||||
|
@ -2431,52 +2044,6 @@ fn check_constraints(fcx: @fn_ctxt, cs: [@ast::constr], args: [ast::arg]) {
|
|||
}
|
||||
}
|
||||
|
||||
type fn_ctxt =
|
||||
// var_bindings, locals and next_var_id are shared
|
||||
// with any nested functions that capture the environment
|
||||
// (and with any functions whose environment is being captured).
|
||||
{self_ty: option<ty::t>,
|
||||
ret_ty: ty::t,
|
||||
// Used by loop bodies that return from the outer function
|
||||
indirect_ret_ty: option<ty::t>,
|
||||
purity: ast::purity,
|
||||
proto: ast::proto,
|
||||
infcx: infer::infer_ctxt,
|
||||
locals: hashmap<ast::node_id, ty_vid>,
|
||||
ty_var_counter: @mut uint,
|
||||
region_var_counter: @mut uint,
|
||||
|
||||
mut blocks: [ast::node_id], // stack of blocks in scope, may be empty
|
||||
in_scope_regions: isr_alist,
|
||||
|
||||
// While type checking a function, the intermediate types for the
|
||||
// expressions, blocks, and so forth contained within the function are
|
||||
// stored in these tables. These types may contain unresolved type
|
||||
// variables. After type checking is complete, the functions in the
|
||||
// writeback module are used to take the types from this table, resolve
|
||||
// them, and then write them into their permanent home in the type
|
||||
// context `ccx.tcx`.
|
||||
//
|
||||
// This means that during inferencing you should use `fcx.write_ty()`
|
||||
// and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
|
||||
// nodes within the function.
|
||||
//
|
||||
// The types of top-level items, which never contain unbound type
|
||||
// variables, are stored directly into the `tcx` tables.
|
||||
//
|
||||
// n.b.: A type variable is not the same thing as a type parameter. A
|
||||
// type variable is rather an "instance" of a type parameter: that is,
|
||||
// given a generic function `fn foo<T>(t: T)`: while checking the
|
||||
// function `foo`, the type `ty_param(0)` refers to the type `T`, which
|
||||
// is treated in abstract. When `foo()` is called, however, `T` will be
|
||||
// substituted for a fresh type variable `ty_var(N)`. This variable will
|
||||
// eventually be resolved to some concrete type (which might itself be
|
||||
// type parameter).
|
||||
node_types: smallintmap::smallintmap<ty::t>,
|
||||
node_type_substs: hashmap<ast::node_id, ty::substs>,
|
||||
|
||||
ccx: @crate_ctxt};
|
||||
|
||||
// Determines whether the given node ID is a use of the def of
|
||||
// the self ID for the current method, if there is one
|
||||
// self IDs in an outer scope count. so that means that you can
|
||||
|
@ -2711,52 +2278,3 @@ fn check_bounds_are_used(ccx: @crate_ctxt,
|
|||
}
|
||||
}
|
||||
|
||||
type next_region_param_id = { mut id: uint };
|
||||
|
||||
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 {
|
||||
|
||||
tys.foldl(isr) { |isr, t|
|
||||
collect_bound_regions_in_ty(tcx, isr, t, to_r)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_bound_regions_in_ty(
|
||||
tcx: ty::ctxt,
|
||||
isr: isr_alist,
|
||||
ty: ty::t,
|
||||
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
294
src/rustc/middle/typeck/check/method.rs
Normal file
294
src/rustc/middle/typeck/check/method.rs
Normal file
|
@ -0,0 +1,294 @@
|
|||
/* Code to handle method lookups (which can be quite complex) */
|
||||
|
||||
import regionmanip::universally_quantify_regions;
|
||||
|
||||
enum lookup = {
|
||||
fcx: @fn_ctxt,
|
||||
expr: @ast::expr, // expr for a.b in a.b()
|
||||
node_id: ast::node_id, // node id of call (not always expr.id)
|
||||
m_name: ast::ident, // b in a.b(...)
|
||||
self_ty: ty::t, // type of a in a.b(...)
|
||||
supplied_tps: [ty::t], // Xs in a.b::<Xs>(...)
|
||||
include_private: bool
|
||||
};
|
||||
|
||||
impl methods for lookup {
|
||||
// Entrypoint:
|
||||
fn method() -> option<method_origin> {
|
||||
// First, see whether this is an interface-bounded parameter
|
||||
let pass1 = alt ty::get(self.self_ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
self.method_from_param(n, did)
|
||||
}
|
||||
ty::ty_iface(did, substs) {
|
||||
self.method_from_iface(did, substs)
|
||||
}
|
||||
ty::ty_class(did, substs) {
|
||||
self.method_from_class(did, substs)
|
||||
}
|
||||
_ {
|
||||
none
|
||||
}
|
||||
};
|
||||
|
||||
alt pass1 {
|
||||
some(r) { some(r) }
|
||||
none { self.method_from_scope() }
|
||||
}
|
||||
}
|
||||
|
||||
fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
|
||||
|
||||
fn method_from_param(n: uint, did: ast::def_id) -> option<method_origin> {
|
||||
let tcx = self.tcx();
|
||||
let mut iface_bnd_idx = 0u; // count only iface bounds
|
||||
let bounds = tcx.ty_param_bounds.get(did.node);
|
||||
for vec::each(*bounds) {|bound|
|
||||
let (iid, bound_substs) = alt bound {
|
||||
ty::bound_copy | ty::bound_send { cont; /* ok */ }
|
||||
ty::bound_iface(bound_t) {
|
||||
alt check ty::get(bound_t).struct {
|
||||
ty::ty_iface(i, substs) { (i, substs) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ifce_methods = ty::iface_methods(tcx, iid);
|
||||
alt vec::position(*ifce_methods, {|m| m.ident == self.m_name}) {
|
||||
none {
|
||||
/* check next bound */
|
||||
iface_bnd_idx += 1u;
|
||||
}
|
||||
|
||||
some(pos) {
|
||||
// Replace any appearance of `self` with the type of the
|
||||
// generic parameter itself. Note that this is the only case
|
||||
// where this replacement is necessary: in all other cases, we
|
||||
// are either invoking a method directly from an impl or class
|
||||
// (where the self type is not permitted), or from a iface
|
||||
// type (in which case methods that refer to self are not
|
||||
// permitted).
|
||||
let substs = {self_ty: some(self.self_ty)
|
||||
with bound_substs};
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, ifce_methods[pos],
|
||||
method_param(iid, pos, n, iface_bnd_idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn method_from_iface(
|
||||
did: ast::def_id, iface_substs: ty::substs) -> option<method_origin> {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
for ms.eachi {|i, m|
|
||||
if m.ident != self.m_name { cont; }
|
||||
|
||||
let m_fty = ty::mk_fn(self.tcx(), m.fty);
|
||||
|
||||
if ty::type_has_self(m_fty) {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"can not call a method that contains a \
|
||||
self type through a boxed iface");
|
||||
}
|
||||
|
||||
if (*m.tps).len() > 0u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"can not call a generic method through a \
|
||||
boxed iface");
|
||||
}
|
||||
|
||||
// Note: although it is illegal to invoke a method that uses self
|
||||
// through a iface instance, we use a dummy subst here so that we
|
||||
// can soldier on with the compilation.
|
||||
let substs = {self_ty: some(self.self_ty)
|
||||
with iface_substs};
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, m, method_iface(did, i)));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn method_from_class(did: ast::def_id, class_substs: ty::substs)
|
||||
-> option<method_origin> {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
|
||||
for ms.each {|m|
|
||||
if m.ident != self.m_name { cont; }
|
||||
|
||||
if m.vis == ast::private && !self.include_private {
|
||||
self.tcx().sess.span_fatal(
|
||||
self.expr.span,
|
||||
"Call to private method not allowed outside \
|
||||
its defining class");
|
||||
}
|
||||
|
||||
// look up method named <name>.
|
||||
let m_declared = ty::lookup_class_method_by_name(
|
||||
self.tcx(), did, self.m_name, self.expr.span);
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
class_substs, m,
|
||||
method_static(m_declared)));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn ty_from_did(did: ast::def_id) -> ty::t {
|
||||
alt check ty::get(ty::lookup_item_type(self.tcx(), did).ty).struct {
|
||||
ty::ty_fn(fty) {
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
|
||||
}
|
||||
}
|
||||
/*
|
||||
if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) {
|
||||
// NDM iface/impl regions
|
||||
let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none);
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alt check ty::get(csearch::get_type(self.tcx(), did).ty).struct {
|
||||
ty::ty_fn(fty) {
|
||||
ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
fn method_from_scope() -> option<method_origin> {
|
||||
let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
|
||||
|
||||
for list::each(impls_vecs) {|impls|
|
||||
let mut results = [];
|
||||
for vec::each(*impls) {|im|
|
||||
// Check whether this impl has a method with the right name.
|
||||
for im.methods.find({|m| m.ident == self.m_name}).each {|m|
|
||||
|
||||
// determine the `self` with fresh variables for
|
||||
// each parameter:
|
||||
let {substs: self_substs, ty: self_ty} =
|
||||
impl_self_ty(self.fcx, im.did);
|
||||
|
||||
// Here "self" refers to the callee side...
|
||||
let self_ty =
|
||||
universally_quantify_regions(
|
||||
self.fcx, self.expr.span, self_ty);
|
||||
|
||||
// ... and "ty" refers to the caller side.
|
||||
let ty =
|
||||
universally_quantify_regions(
|
||||
self.fcx, self.expr.span, self.self_ty);
|
||||
|
||||
// if we can assign the caller to the callee, that's a
|
||||
// potential match. Collect those in the vector.
|
||||
alt self.fcx.mk_subty(ty, self_ty) {
|
||||
result::err(_) { /* keep looking */ }
|
||||
result::ok(_) {
|
||||
results += [(self_substs, m.n_tps, m.did)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if results.len() >= 1u {
|
||||
if results.len() > 1u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"multiple applicable methods in scope");
|
||||
|
||||
// I would like to print out how each impl was imported,
|
||||
// but I cannot for the life of me figure out how to
|
||||
// annotate resolve to preserve this information.
|
||||
for results.eachi { |i, result|
|
||||
let (_, _, did) = result;
|
||||
let span = if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) { m.span }
|
||||
}
|
||||
} else {
|
||||
self.expr.span
|
||||
};
|
||||
self.tcx().sess.span_note(
|
||||
span,
|
||||
#fmt["candidate #%u is %s",
|
||||
(i+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
}
|
||||
|
||||
let (self_substs, n_tps, did) = results[0];
|
||||
let fty = self.ty_from_did(did);
|
||||
ret some(self.write_mty_from_fty(
|
||||
self_substs, n_tps, fty,
|
||||
method_static(did)));
|
||||
}
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn write_mty_from_m(self_substs: ty::substs,
|
||||
m: ty::method,
|
||||
origin: method_origin) -> method_origin {
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
// a bit hokey, but the method unbound has a bare protocol, whereas
|
||||
// a.b has a protocol like fn@() (perhaps eventually fn&()):
|
||||
let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});
|
||||
|
||||
ret self.write_mty_from_fty(self_substs, (*m.tps).len(),
|
||||
fty, origin);
|
||||
}
|
||||
|
||||
fn write_mty_from_fty(self_substs: ty::substs,
|
||||
n_tps_m: uint,
|
||||
fty: ty::t,
|
||||
origin: method_origin) -> method_origin {
|
||||
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
// Here I will use the "c_" prefix to refer to the method's
|
||||
// owner. You can read it as class, but it may also be an iface.
|
||||
|
||||
let n_tps_supplied = self.supplied_tps.len();
|
||||
let m_substs = {
|
||||
if n_tps_supplied == 0u {
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else if n_tps_m == 0u {
|
||||
tcx.sess.span_err(
|
||||
self.expr.span,
|
||||
"this method does not take type parameters");
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else if n_tps_supplied != n_tps_m {
|
||||
tcx.sess.span_err(
|
||||
self.expr.span,
|
||||
"incorrect number of type \
|
||||
parameters given for this method");
|
||||
self.fcx.next_ty_vars(n_tps_m)
|
||||
} else {
|
||||
self.supplied_tps
|
||||
}
|
||||
};
|
||||
|
||||
let all_substs = {tps: self_substs.tps + m_substs
|
||||
with self_substs};
|
||||
|
||||
self.fcx.write_ty_substs(self.node_id, fty, all_substs);
|
||||
|
||||
ret origin;
|
||||
}
|
||||
}
|
||||
|
195
src/rustc/middle/typeck/check/regionmanip.rs
Normal file
195
src/rustc/middle/typeck/check/regionmanip.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Helper functions related to manipulating region types.
|
||||
|
||||
// Helper for the other universally_quantify_*() routines. Extracts the bound
|
||||
// regions from bound_tys and then replaces those same regions with fresh
|
||||
// variables in `sty`, returning the resulting type.
|
||||
fn universally_quantify_from_sty(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
bound_tys: [ty::t],
|
||||
sty: ty::sty) -> ty::t {
|
||||
|
||||
#debug["universally_quantify_from_sty(bound_tys=%?)",
|
||||
bound_tys.map {|x| fcx.ty_to_str(x) }];
|
||||
indent {||
|
||||
let tcx = fcx.tcx();
|
||||
let isr = collect_bound_regions_in_tys(tcx, @nil, bound_tys) { |br|
|
||||
let rvar = fcx.next_region_var();
|
||||
#debug["Bound region %s maps to %s",
|
||||
bound_region_to_str(fcx.ccx.tcx, br),
|
||||
region_to_str(fcx.ccx.tcx, rvar)];
|
||||
rvar
|
||||
};
|
||||
let t_res = ty::fold_sty_to_ty(fcx.ccx.tcx, sty) { |t|
|
||||
replace_bound_regions(tcx, span, isr, t)
|
||||
};
|
||||
#debug["Result of universal quant. is %s", fcx.ty_to_str(t_res)];
|
||||
t_res
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces all region parameters in the given type with region variables.
|
||||
// Does not descend into fn types. This is used when deciding whether an impl
|
||||
// applies at a given call site. See also universally_quantify_before_call().
|
||||
fn universally_quantify_regions(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
ty: ty::t) -> ty::t {
|
||||
universally_quantify_from_sty(fcx, span, [ty], ty::get(ty).struct)
|
||||
}
|
||||
|
||||
// Expects a function type. Replaces all region parameters in the arguments
|
||||
// and return type with fresh region variables. This is used when typechecking
|
||||
// function calls, bind expressions, and method calls.
|
||||
fn universally_quantify_regions_before_call(fcx: @fn_ctxt,
|
||||
span: span,
|
||||
ty: ty::t) -> ty::t {
|
||||
|
||||
#debug["universally_quantify_before_call(ty=%s)",
|
||||
fcx.ty_to_str(ty)];
|
||||
|
||||
// This is subtle: we expect `ty` to be a function type, which normally
|
||||
// introduce a level of binding. In this case, we want to process the
|
||||
// types bound by the function but not by any nested functions.
|
||||
// Therefore, we match one level of structure.
|
||||
alt structure_of(fcx, span, ty) {
|
||||
sty @ ty::ty_fn(fty) {
|
||||
let all_tys = fty.inputs.map({|a| a.ty}) + [fty.output];
|
||||
universally_quantify_from_sty(fcx, span, all_tys, sty)
|
||||
}
|
||||
sty {
|
||||
#debug["not a fn ty: %?", sty];
|
||||
|
||||
// if not a function type, we're gonna' report an error
|
||||
// at some point, since the user is trying to call this thing
|
||||
ty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_bound_regions(
|
||||
tcx: ty::ctxt,
|
||||
span: span,
|
||||
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.span_bug(
|
||||
span,
|
||||
#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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the region that &expr should be placed into. If expr is an
|
||||
* lvalue, this will be the region of the lvalue. Otherwise, if region is
|
||||
* an rvalue, the semantics are that the result is stored into a temporary
|
||||
* stack position and so the resulting region will be the enclosing block.
|
||||
*/
|
||||
fn region_of(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
|
||||
fn borrow(fcx: @fn_ctxt, expr: @ast::expr) -> ty::region {
|
||||
ty::encl_region(fcx.ccx.tcx, expr.id)
|
||||
}
|
||||
|
||||
fn deref(fcx: @fn_ctxt, base: @ast::expr) -> ty::region {
|
||||
let base_ty = fcx.expr_ty(base);
|
||||
let base_ty = structurally_resolved_type(fcx, base.span, base_ty);
|
||||
alt ty::get(base_ty).struct {
|
||||
ty::ty_rptr(region, _) { region }
|
||||
ty::ty_box(_) | ty::ty_uniq(_) { borrow(fcx, base) }
|
||||
_ { region_of(fcx, base) }
|
||||
}
|
||||
}
|
||||
|
||||
alt expr.node {
|
||||
ast::expr_path(path) {
|
||||
let defn = lookup_def(fcx, path.span, expr.id);
|
||||
alt defn {
|
||||
ast::def_local(local_id, _) |
|
||||
ast::def_upvar(local_id, _, _) {
|
||||
let local_scope = fcx.ccx.tcx.region_map.get(local_id);
|
||||
ty::re_scope(local_scope)
|
||||
}
|
||||
_ {
|
||||
ty::re_static
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::expr_field(base, _, _) { deref(fcx, base) }
|
||||
ast::expr_index(base, _) { deref(fcx, base) }
|
||||
ast::expr_unary(ast::deref, base) { deref(fcx, base) }
|
||||
_ { borrow(fcx, expr) }
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
tys.foldl(isr) { |isr, t|
|
||||
collect_bound_regions_in_ty(tcx, isr, t, to_r)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_bound_regions_in_ty(
|
||||
tcx: ty::ctxt,
|
||||
isr: isr_alist,
|
||||
ty: ty::t,
|
||||
to_r: fn(ty::bound_region) -> ty::region) -> isr_alist {
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -56,8 +56,10 @@ mod middle {
|
|||
mod alt;
|
||||
mod vtable;
|
||||
mod writeback;
|
||||
mod regionmanip;
|
||||
mod regionck;
|
||||
mod demand;
|
||||
mod method;
|
||||
}
|
||||
mod astconv;
|
||||
mod infer;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue