diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index a1e95e2d2520..02e0f48012ca 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -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)`: 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, + ret_ty: ty::t, + // Used by loop bodies that return from the outer function + indirect_ret_ty: option, + purity: ast::purity, + proto: ast::proto, + infcx: infer::infer_ctxt, + locals: hashmap, + 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, + node_type_substs: hashmap, + + 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::(...) - include_private: bool -}; - -impl methods for lookup { - // Entrypoint: - fn method() -> option { - // 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 { - 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 { - - 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 { - - 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 . - 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 { - 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 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, @@ -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, - ret_ty: ty::t, - // Used by loop bodies that return from the outer function - indirect_ret_ty: option, - purity: ast::purity, - proto: ast::proto, - infcx: infer::infer_ctxt, - locals: hashmap, - 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)`: 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, - node_type_substs: hashmap, - - 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; -} diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs new file mode 100644 index 000000000000..18ea67e2ad40 --- /dev/null +++ b/src/rustc/middle/typeck/check/method.rs @@ -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::(...) + include_private: bool +}; + +impl methods for lookup { + // Entrypoint: + fn method() -> option { + // 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 { + 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 { + + 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 { + + 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 . + 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 { + 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; + } +} + diff --git a/src/rustc/middle/typeck/check/regionmanip.rs b/src/rustc/middle/typeck/check/regionmanip.rs new file mode 100644 index 000000000000..7fb4409d9701 --- /dev/null +++ b/src/rustc/middle/typeck/check/regionmanip.rs @@ -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; +} diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 702a04de8e2d..74a48820d9da 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -56,8 +56,10 @@ mod middle { mod alt; mod vtable; mod writeback; + mod regionmanip; mod regionck; mod demand; + mod method; } mod astconv; mod infer;