1
Fork 0

pull method lookup / region manip into their own modules

This commit is contained in:
Niko Matsakis 2012-05-16 08:51:16 -07:00
parent 564aa59a3f
commit 558be3a70f
4 changed files with 566 additions and 557 deletions

View file

@ -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;
}

View 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;
}
}

View 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;
}

View file

@ -56,8 +56,10 @@ mod middle {
mod alt;
mod vtable;
mod writeback;
mod regionmanip;
mod regionck;
mod demand;
mod method;
}
mod astconv;
mod infer;