diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 6f9b767a870..f5f82bf2690 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -27,6 +27,11 @@ import util::common::{indent, indenter}; import std::list; import list::{list, nil, cons}; +// from internal typeck modules: +import astconv::{ast_ty_to_ty, in_anon_rscope, + ast_region_to_region, region_scope, + ast_conv}; + export check_crate; export method_map; export method_origin, serialize_method_origin, deserialize_method_origin; @@ -329,11 +334,12 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint, } ast::vstore_uniq { ty::vstore_uniq } ast::vstore_box { ty::vstore_box } - ast::vstore_slice(r) { + ast::vstore_slice(a_r) { alt fcx.block_region() { result::ok(b_r) { let rscope = in_anon_rscope(fcx, b_r); - ty::vstore_slice(ast_region_to_region(fcx, rscope, e.span, r)) + let r = astconv::ast_region_to_region(fcx, rscope, e.span, a_r); + ty::vstore_slice(r) } result::err(msg) { fcx.ccx.tcx.sess.span_err(e.span, msg); @@ -344,142 +350,6 @@ fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint, } } -iface ast_conv { - fn tcx() -> ty::ctxt; - fn ccx() -> @crate_ctxt; - fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty; - - // what type should we use when a type is omitted? - fn ty_infer(span: span) -> ty::t; -} - -impl of ast_conv for @crate_ctxt { - fn tcx() -> ty::ctxt { self.tcx } - fn ccx() -> @crate_ctxt { self } - - fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty { - if id.crate != ast::local_crate { - csearch::get_type(self.tcx, id) - } else { - alt self.tcx.items.find(id.node) { - some(ast_map::node_item(item, _)) { - ty_of_item(self, item) - } - some(ast_map::node_native_item(native_item, _, _)) { - ty_of_native_item(self, native_item) - } - x { - self.tcx.sess.bug(#fmt["unexpected sort of item \ - in get_item_ty(): %?", x]); - } - } - } - } - - fn ty_infer(span: span) -> ty::t { - self.tcx.sess.span_bug(span, - "found `ty_infer` in unexpected place"); - } -} - -impl of ast_conv for @fn_ctxt { - fn tcx() -> ty::ctxt { self.ccx.tcx } - fn ccx() -> @crate_ctxt { self.ccx } - - fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty { - ty::lookup_item_type(self.tcx(), id) - } - - fn ty_infer(_span: span) -> ty::t { - self.next_ty_var() - } -} - -iface region_scope { - fn anon_region() -> result; - fn named_region(id: str) -> result; -} - -enum empty_rscope { empty_rscope } -impl of region_scope for empty_rscope { - fn anon_region() -> result { - result::err("region types are not allowed here") - } - fn named_region(id: str) -> result { - if id == "static" { result::ok(ty::re_static) } - else { result::err("only the static region is allowed here") } - } -} - -enum type_rscope = ast::region_param; -impl of region_scope for type_rscope { - fn anon_region() -> result { - alt *self { - ast::rp_self { result::ok(ty::re_bound(ty::br_self)) } - ast::rp_none { - result::err("to use region types here, the containing type \ - must be declared with a region bound") - } - } - } - fn named_region(id: str) -> result { - empty_rscope.named_region(id).chain_err { |_e| - if id == "self" { self.anon_region() } - else { - result::err("named regions other than `self` are not \ - allowed as part of a type declaration") - } - } - } -} - -impl of region_scope for @fn_ctxt { - fn anon_region() -> result { - result::ok(self.next_region_var()) - } - fn named_region(id: str) -> result { - empty_rscope.named_region(id).chain_err { |_e| - alt self.in_scope_regions.find(ty::br_named(id)) { - some(r) { result::ok(r) } - none if id == "blk" { self.block_region() } - none { - result::err(#fmt["named region `%s` not in scope here", id]) - } - } - } - } -} - -enum anon_rscope = {anon: ty::region, base: region_scope}; -fn in_anon_rscope(self: RS, r: ty::region) - -> @anon_rscope { - @anon_rscope({anon: r, base: self as region_scope}) -} -impl of region_scope for @anon_rscope { - fn anon_region() -> result { - result::ok(self.anon) - } - fn named_region(id: str) -> result { - self.base.named_region(id) - } -} - -enum binding_rscope = {base: region_scope}; -fn in_binding_rscope(self: RS) -> @binding_rscope { - let base = self as region_scope; - @binding_rscope({base: base}) -} -impl of region_scope for @binding_rscope { - fn anon_region() -> result { - result::ok(ty::re_bound(ty::br_anon)) - } - fn named_region(id: str) -> result { - self.base.named_region(id).chain_err {|_e| - result::ok(ty::re_bound(ty::br_named(id))) - } - } -} - fn get_region_reporting_err(tcx: ty::ctxt, span: span, res: result) -> ty::region { @@ -493,321 +363,6 @@ fn get_region_reporting_err(tcx: ty::ctxt, } } -fn ast_region_to_region( - self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region { - - let res = alt a_r.node { - ast::re_anon { rscope.anon_region() } - ast::re_named(id) { rscope.named_region(id) } - }; - - get_region_reporting_err(self.tcx(), span, res) -} - -fn ast_path_to_substs_and_ty( - self: AC, rscope: RS, did: ast::def_id, - path: @ast::path) -> ty_param_substs_and_ty { - - let tcx = self.tcx(); - let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} = - self.get_item_ty(did); - - // If the type is parameterized by the self region, then replace self - // region with the current anon region binding (in other words, - // whatever & would get replaced with). - let self_r = alt (decl_rp, path.rp) { - (ast::rp_none, none) { - none - } - (ast::rp_none, some(_)) { - tcx.sess.span_err( - path.span, - #fmt["No region bound is permitted on %s, \ - which is not declared as containing region pointers", - ty::item_path_str(tcx, did)]); - none - } - (ast::rp_self, none) { - let res = rscope.anon_region(); - let r = get_region_reporting_err(self.tcx(), path.span, res); - some(r) - } - (ast::rp_self, some(r)) { - some(ast_region_to_region(self, rscope, path.span, r)) - } - }; - - // Convert the type parameters supplied by the user. - if !vec::same_length(*decl_bounds, path.types) { - self.tcx().sess.span_fatal( - path.span, - #fmt["wrong number of type arguments, expected %u but found %u", - (*decl_bounds).len(), path.types.len()]); - } - let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) }; - - let substs = {self_r:self_r, self_ty:none, tps:tps}; - {substs: substs, ty: ty::subst(tcx, substs, decl_ty)} -} - -fn ast_path_to_ty( - self: AC, - rscope: RS, - did: ast::def_id, - path: @ast::path, - path_id: ast::node_id) -> ty_param_substs_and_ty { - - // Lookup the polytype of the item and then substitute the provided types - // for any type/region parameters. - let tcx = self.tcx(); - let {substs: substs, ty: ty} = - ast_path_to_substs_and_ty(self, rscope, did, path); - write_ty_to_tcx(tcx, path_id, ty); - write_substs_to_tcx(tcx, path_id, substs.tps); - ret {substs: substs, ty: ty}; -} - -/* - Instantiates the path for the given iface reference, assuming that - it's bound to a valid iface type. Returns the def_id for the defining - iface. Fails if the type is a type other than an iface type. - */ -fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref, - rp: ast::region_param) - -> (ast::def_id, ty_param_substs_and_ty) { - - let sp = t.path.span, err = "can only implement interface types", - sess = ccx.tcx.sess; - - let rscope = type_rscope(rp); - - alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) { - ast::def_ty(t_id) { - let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id); - alt ty::get(tpt.ty).struct { - ty::ty_iface(*) { - (t_id, tpt) - } - _ { sess.span_fatal(sp, err); } - } - } - _ { - sess.span_fatal(sp, err); - } - } -} - -const NO_REGIONS: uint = 1u; -const NO_TPS: uint = 2u; - -// Parses the programmer's textual representation of a type into our -// internal notion of a type. `getter` is a function that returns the type -// corresponding to a definition ID: -fn ast_ty_to_ty( - self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t { - - fn ast_mt_to_mt( - self: AC, rscope: RS, mt: ast::mt) -> ty::mt { - - ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl}; - } - - fn mk_vstore( - self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t { - - let tcx = self.tcx(); - let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty); - - alt ty::get(seq_ty).struct { - ty::ty_vec(mt) { - ret ty::mk_evec(tcx, mt, vst); - } - - ty::ty_str { - ret ty::mk_estr(tcx, vst); - } - - _ { - tcx.sess.span_err( - a_seq_ty.span, - #fmt["Bound not allowed on a %s.", - ty::ty_sort_str(tcx, seq_ty)]); - ret seq_ty; - } - } - } - - fn check_path_args(tcx: ty::ctxt, - path: @ast::path, - flags: uint) { - if (flags & NO_TPS) != 0u { - if path.types.len() > 0u { - tcx.sess.span_err( - path.span, - "Type parameters are not allowed on this type."); - } - } - - if (flags & NO_REGIONS) != 0u { - if path.rp.is_some() { - tcx.sess.span_err( - path.span, - "Region parameters are not allowed on this type."); - } - } - } - - let tcx = self.tcx(); - - alt tcx.ast_ty_to_ty_cache.find(ast_ty) { - some(ty::atttce_resolved(ty)) { ret ty; } - some(ty::atttce_unresolved) { - tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \ - insert a enum in the cycle, \ - if this is desired)"); - } - none { /* go on */ } - } - - tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_unresolved); - let typ = alt ast_ty.node { - ast::ty_nil { ty::mk_nil(tcx) } - ast::ty_bot { ty::mk_bot(tcx) } - ast::ty_box(mt) { - ty::mk_box(tcx, ast_mt_to_mt(self, rscope, mt)) - } - ast::ty_uniq(mt) { - ty::mk_uniq(tcx, ast_mt_to_mt(self, rscope, mt)) - } - ast::ty_vec(mt) { - ty::mk_vec(tcx, ast_mt_to_mt(self, rscope, mt)) - } - ast::ty_ptr(mt) { - ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt)) - } - ast::ty_rptr(region, mt) { - let r = ast_region_to_region(self, rscope, ast_ty.span, region); - let mt = ast_mt_to_mt(self, in_anon_rscope(rscope, r), mt); - ty::mk_rptr(tcx, r, mt) - } - ast::ty_tup(fields) { - let flds = vec::map(fields) { |t| ast_ty_to_ty(self, rscope, t) }; - ty::mk_tup(tcx, flds) - } - ast::ty_rec(fields) { - let flds = fields.map {|f| - let tm = ast_mt_to_mt(self, rscope, f.node.mt); - {ident: f.node.ident, mt: tm} - }; - ty::mk_rec(tcx, flds) - } - ast::ty_fn(proto, decl) { - ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none)) - } - ast::ty_path(path, id) { - let a_def = alt tcx.def_map.find(id) { - none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s", - path_to_str(path))); } - some(d) { d }}; - alt a_def { - ast::def_ty(did) | ast::def_class(did) { - ast_path_to_ty(self, rscope, did, path, id).ty - } - ast::def_prim_ty(nty) { - alt nty { - ast::ty_bool { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_bool(tcx) - } - ast::ty_int(it) { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_int(tcx, it) - } - ast::ty_uint(uit) { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_uint(tcx, uit) - } - ast::ty_float(ft) { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_mach_float(tcx, ft) - } - ast::ty_str { - check_path_args(tcx, path, NO_TPS); - // This is a bit of a hack, but basically str/& needs to be - // converted into a vstore: - alt path.rp { - none { - ty::mk_str(tcx) - } - some(ast_r) { - let r = ast_region_to_region(self, rscope, - ast_ty.span, ast_r); - ty::mk_estr(tcx, ty::vstore_slice(r)) - } - } - } - } - } - ast::def_ty_param(id, n) { - check_path_args(tcx, path, NO_TPS | NO_REGIONS); - ty::mk_param(tcx, n, id) - } - ast::def_self(_) { - // n.b.: resolve guarantees that the self type only appears in an - // iface, which we rely upon in various places when creating - // substs - ty::mk_self(tcx) - } - _ { - tcx.sess.span_fatal(ast_ty.span, - "found type name used as a variable"); - } - } - } - ast::ty_vstore(a_t, ast::vstore_slice(a_r)) { - let r = ast_region_to_region(self, rscope, ast_ty.span, a_r); - mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r)) - } - ast::ty_vstore(a_t, ast::vstore_uniq) { - mk_vstore(self, rscope, a_t, ty::vstore_uniq) - } - ast::ty_vstore(a_t, ast::vstore_box) { - mk_vstore(self, rscope, a_t, ty::vstore_box) - } - ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) { - mk_vstore(self, rscope, a_t, ty::vstore_fixed(u)) - } - ast::ty_vstore(_, ast::vstore_fixed(none)) { - tcx.sess.span_bug( - ast_ty.span, - "implied fixed length for bound"); - } - ast::ty_constr(t, cs) { - let mut out_cs = []; - for cs.each {|constr| - out_cs += [ty::ast_constr_to_constr(tcx, constr)]; - } - ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs) - } - ast::ty_infer { - // ty_infer should only appear as the type of arguments or return - // values in a fn_expr, or as the type of local variables. Both of - // these cases are handled specially and should not descend into this - // routine. - self.tcx().sess.span_bug( - ast_ty.span, - "found `ty_infer` in unexpected place"); - } - ast::ty_mac(_) { - tcx.sess.span_bug(ast_ty.span, - "found `ty_mac` in unexpected place"); - } - }; - - tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_resolved(typ)); - ret typ; -} - fn check_bounds_are_used(ccx: @crate_ctxt, span: span, tps: [ast::ty_param], @@ -851,102 +406,6 @@ fn check_bounds_are_used(ccx: @crate_ctxt, } } -fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) - -> ty::ty_param_bounds_and_ty { - - let def_id = local_def(it.id); - let tcx = ccx.tcx; - alt tcx.tcache.find(def_id) { - some(tpt) { ret tpt; } - _ {} - } - alt it.node { - ast::item_const(t, _) { - let typ = ccx.to_ty(empty_rscope, t); - let tpt = no_params(typ); - tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_fn(decl, tps, _) { - let bounds = ty_param_bounds(ccx, tps); - let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, - decl, none); - let tpt = {bounds: bounds, - rp: ast::rp_none, // functions do not have a self - ty: ty::mk_fn(ccx.tcx, tofd)}; - ccx.tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_ty(t, tps, rp) { - alt tcx.tcache.find(local_def(it.id)) { - some(tpt) { ret tpt; } - none { } - } - - let tpt = { - let ty = { - let t0 = ccx.to_ty(type_rscope(rp), t); - // Do not associate a def id with a named, parameterized type - // like "foo". This is because otherwise ty_to_str will - // print the name as merely "foo", as it has no way to - // reconstruct the value of X. - if !vec::is_empty(tps) { t0 } else { - ty::mk_with_id(tcx, t0, def_id) - } - }; - {bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty} - }; - - check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty); - - tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_res(decl, tps, _, _, _, rp) { - let {bounds, substs} = mk_substs(ccx, tps, rp); - let t_arg = ty_of_arg(ccx, type_rscope(rp), - decl.inputs[0], none); - let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs); - let t_res = {bounds: bounds, rp: rp, ty: t}; - tcx.tcache.insert(local_def(it.id), t_res); - ret t_res; - } - ast::item_enum(_, tps, rp) { - // Create a new generic polytype. - let {bounds, substs} = mk_substs(ccx, tps, rp); - let t = ty::mk_enum(tcx, local_def(it.id), substs); - let tpt = {bounds: bounds, rp: rp, ty: t}; - tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_iface(tps, rp, ms) { - let {bounds, substs} = mk_substs(ccx, tps, rp); - let t = ty::mk_iface(tcx, local_def(it.id), substs); - let tpt = {bounds: bounds, rp: rp, ty: t}; - tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_class(tps, _, _, _, _, rp) { - let {bounds,substs} = mk_substs(ccx, tps, rp); - let t = ty::mk_class(tcx, local_def(it.id), substs); - let tpt = {bounds: bounds, rp: rp, ty: t}; - tcx.tcache.insert(local_def(it.id), tpt); - ret tpt; - } - ast::item_impl(*) | ast::item_mod(_) | - ast::item_native_mod(_) { fail; } - } -} -fn ty_of_native_item(ccx: @crate_ctxt, it: @ast::native_item) - -> ty::ty_param_bounds_and_ty { - alt it.node { - ast::native_item_fn(fn_decl, params) { - ret ty_of_native_fn_decl(ccx, fn_decl, params, - local_def(it.id)); - } - } -} - type next_region_param_id = { mut id: uint }; fn collect_bound_regions_in_tys( @@ -1047,166 +506,6 @@ fn replace_bound_regions( } } -fn ty_of_arg( - self: AC, rscope: RS, a: ast::arg, - expected_ty: option) -> ty::arg { - - let ty = alt a.ty.node { - ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty} - ast::ty_infer {self.ty_infer(a.ty.span)} - _ {ast_ty_to_ty(self, rscope, a.ty)} - }; - - let mode = { - alt a.mode { - ast::infer(_) if expected_ty.is_some() { - result::get(ty::unify_mode(self.tcx(), a.mode, - expected_ty.get().mode)) - } - ast::infer(_) { - alt ty::get(ty).struct { - // If the type is not specified, then this must be a fn expr. - // Leave the mode as infer(_), it will get inferred based - // on constraints elsewhere. - ty::ty_var(_) {a.mode} - - // If the type is known, then use the default for that type. - // Here we unify m and the default. This should update the - // tables in tcx but should never fail, because nothing else - // will have been unified with m yet: - _ { - let m1 = ast::expl(ty::default_arg_mode_for_ty(ty)); - result::get(ty::unify_mode(self.tcx(), a.mode, m1)) - } - } - } - ast::expl(_) {a.mode} - } - }; - - {mode: mode, ty: ty} -} - -type expected_tys = option<{inputs: [ty::arg], - output: ty::t}>; - -fn ty_of_fn_decl( - self: AC, rscope: RS, - proto: ast::proto, - decl: ast::fn_decl, - expected_tys: expected_tys) -> ty::fn_ty { - - #debug["ty_of_fn_decl"]; - indent {|| - // new region names that appear inside of the fn decl are bound to - // that function type - let rb = in_binding_rscope(rscope); - - let input_tys = decl.inputs.mapi { |i, a| - let expected_arg_ty = expected_tys.chain { |e| - // no guarantee that the correct number of expected args - // were supplied - if i < e.inputs.len() {some(e.inputs[i])} else {none} - }; - ty_of_arg(self, rb, a, expected_arg_ty) - }; - - let expected_ret_ty = expected_tys.map { |e| e.output }; - let output_ty = alt decl.output.node { - ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()} - ast::ty_infer {self.ty_infer(decl.output.span)} - _ {ast_ty_to_ty(self, rb, decl.output)} - }; - - let out_constrs = vec::map(decl.constraints) {|constr| - ty::ast_constr_to_constr(self.tcx(), constr) - }; - {proto: proto, inputs: input_tys, - output: output_ty, ret_style: decl.cf, constraints: out_constrs} - } -} - -fn ty_of_native_fn_decl(ccx: @crate_ctxt, - decl: ast::fn_decl, - ty_params: [ast::ty_param], - def_id: ast::def_id) -> ty::ty_param_bounds_and_ty { - - let bounds = ty_param_bounds(ccx, ty_params); - let rb = in_binding_rscope(empty_rscope); - let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) }; - let output_ty = ast_ty_to_ty(ccx, rb, decl.output); - - let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare, - inputs: input_tys, - output: output_ty, - ret_style: ast::return_val, - constraints: []}); - let tpt = {bounds: bounds, rp: ast::rp_none, ty: t_fn}; - ccx.tcx.tcache.insert(def_id, tpt); - ret tpt; -} - -fn ty_param_bounds(ccx: @crate_ctxt, - params: [ast::ty_param]) -> @[ty::param_bounds] { - - fn compute_bounds(ccx: @crate_ctxt, - param: ast::ty_param) -> ty::param_bounds { - @vec::flat_map(*param.bounds) { |b| - alt b { - ast::bound_send { [ty::bound_send] } - ast::bound_copy { [ty::bound_copy] } - ast::bound_iface(t) { - let ity = ast_ty_to_ty(ccx, empty_rscope, t); - alt ty::get(ity).struct { - ty::ty_iface(*) { - [ty::bound_iface(ity)] - } - _ { - ccx.tcx.sess.span_err( - t.span, "type parameter bounds must be \ - interface types"); - [] - } - } - } - } - } - } - - @params.map { |param| - alt ccx.tcx.ty_param_bounds.find(param.id) { - some(bs) { bs } - none { - let bounds = compute_bounds(ccx, param); - ccx.tcx.ty_param_bounds.insert(param.id, bounds); - bounds - } - } - } -} - -fn ty_of_method(ccx: @crate_ctxt, - m: @ast::method, - rp: ast::region_param) -> ty::method { - {ident: m.ident, - tps: ty_param_bounds(ccx, m.tps), - fty: ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, - m.decl, none), - purity: m.decl.purity, - vis: m.vis} -} - -fn ty_of_ty_method(self: @crate_ctxt, - m: ast::ty_method, - rp: ast::region_param) -> ty::method { - {ident: m.ident, - tps: ty_param_bounds(self, m.tps), - fty: ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, - m.decl, none), - // assume public, because this is only invoked on iface methods - purity: m.decl.purity, vis: ast::public} -} - // Functions that write types into the node type table fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) { #debug["write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty)]; @@ -1365,7 +664,7 @@ fn mk_ty_params(ccx: @crate_ctxt, atps: [ast::ty_param]) -> {bounds: @[ty::param_bounds], params: [ty::t]} { let mut i = 0u; - let bounds = ty_param_bounds(ccx, atps); + let bounds = astconv::ty_param_bounds(ccx, atps); {bounds: bounds, params: vec::map(atps, {|atp| let t = ty::mk_param(ccx.tcx, i, local_def(atp.id)); @@ -1430,326 +729,6 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, } } -// Item collection - a pair of bootstrap passes: -// -// (1) Collect the IDs of all type items (typedefs) and store them in a table. -// -// (2) Translate the AST fragments that describe types to determine a type for -// each item. When we encounter a named type, we consult the table built -// in pass 1 to find its item, and recursively translate it. -// -// We then annotate the AST with the resulting types and return the annotated -// AST, along with a table mapping item IDs to their types. -mod collect { - fn get_enum_variant_types(ccx: @crate_ctxt, - enum_ty: ty::t, - variants: [ast::variant], - ty_params: [ast::ty_param], - rp: ast::region_param) { - let tcx = ccx.tcx; - - // Create a set of parameter types shared among all the variants. - for variants.each {|variant| - // Nullary enum constructors get turned into constants; n-ary enum - // constructors get turned into functions. - let result_ty = if vec::len(variant.node.args) == 0u { - enum_ty - } else { - let rs = type_rscope(rp); - let args = variant.node.args.map { |va| - let arg_ty = ccx.to_ty(rs, va.ty); - {mode: ast::expl(ast::by_copy), ty: arg_ty} - }; - ty::mk_fn(tcx, {proto: ast::proto_box, - inputs: args, - output: enum_ty, - ret_style: ast::return_val, - constraints: []}) - }; - let tpt = {bounds: ty_param_bounds(ccx, ty_params), - rp: rp, - ty: result_ty}; - tcx.tcache.insert(local_def(variant.node.id), tpt); - write_ty_to_tcx(tcx, variant.node.id, result_ty); - } - } - - fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) { - fn store_methods(ccx: @crate_ctxt, id: ast::node_id, - stuff: [T], f: fn@(T) -> ty::method) { - ty::store_iface_methods(ccx.tcx, id, @vec::map(stuff, f)); - } - - let tcx = ccx.tcx; - alt check tcx.items.get(id) { - ast_map::node_item(@{node: ast::item_iface(_, rp, ms), _}, _) { - store_methods::(ccx, id, ms) {|m| - ty_of_ty_method(ccx, m, rp) - }; - } - ast_map::node_item(@{node: ast::item_class(_,_,its,_,_,rp), _}, _) { - let (_,ms) = split_class_items(its); - // All methods need to be stored, since lookup_method - // relies on the same method cache for self-calls - store_methods::<@ast::method>(ccx, id, ms) {|m| - ty_of_method(ccx, m, rp) - }; - } - } - } - - fn check_methods_against_iface(ccx: @crate_ctxt, - tps: [ast::ty_param], - rp: ast::region_param, - selfty: ty::t, - a_ifacety: @ast::iface_ref, - ms: [@ast::method]) { - - let tcx = ccx.tcx; - let i_bounds = ty_param_bounds(ccx, tps); - let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty); - let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp); - if did.crate == ast::local_crate { - ensure_iface_methods(ccx, did.node); - } - for vec::each(*ty::iface_methods(tcx, did)) {|if_m| - alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) { - some({mty: m, id, span}) { - if m.purity != if_m.purity { - ccx.tcx.sess.span_err( - span, #fmt["method `%s`'s purity \ - not match the iface method's \ - purity", m.ident]); - } - let mt = compare_impl_method( - ccx.tcx, span, m, vec::len(tps), - if_m, tpt.substs, selfty); - let old = tcx.tcache.get(local_def(id)); - if old.ty != mt { - tcx.tcache.insert( - local_def(id), - {bounds: old.bounds, - rp: old.rp, - ty: mt}); - write_ty_to_tcx(tcx, id, mt); - } - } - none { - tcx.sess.span_err( - a_ifacety.path.span, - #fmt["missing method `%s`", if_m.ident]); - } - } // alt - } // |if_m| - } // fn - - fn convert_class_item(ccx: @crate_ctxt, - rp: ast::region_param, - v: ast_util::ivar) { - /* we want to do something here, b/c within the - scope of the class, it's ok to refer to fields & - methods unqualified */ - /* they have these types *within the scope* of the - class. outside the class, it's done with expr_field */ - let tt = ccx.to_ty(type_rscope(rp), v.ty); - write_ty_to_tcx(ccx.tcx, v.id, tt); - } - - fn convert_methods(ccx: @crate_ctxt, - ms: [@ast::method], - rp: ast::region_param, - i_bounds: @[ty::param_bounds], - self_ty: ty::t) - -> [{mty: ty::method, id: ast::node_id, span: span}] { - - let tcx = ccx.tcx; - vec::map(ms) { |m| - write_ty_to_tcx(tcx, m.self_id, self_ty); - let bounds = ty_param_bounds(ccx, m.tps); - let mty = ty_of_method(ccx, m, rp); - let fty = ty::mk_fn(tcx, mty.fty); - tcx.tcache.insert( - local_def(m.id), - // n.b. This code is kind of sketchy (concat'ing i_bounds - // with bounds), but removing *i_bounds breaks other stuff - {bounds: @(*i_bounds + *bounds), rp: rp, ty: fty}); - write_ty_to_tcx(tcx, m.id, fty); - {mty: mty, id: m.id, span: m.span} - } - } - - fn convert(ccx: @crate_ctxt, it: @ast::item) { - let tcx = ccx.tcx; - alt it.node { - // These don't define types. - ast::item_mod(_) {} - ast::item_native_mod(m) { - if syntax::attr::native_abi(it.attrs) == - either::right(ast::native_abi_rust_intrinsic) { - for m.items.each { |item| check_intrinsic_type(ccx, item); } - } - } - ast::item_enum(variants, ty_params, rp) { - let tpt = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, tpt.ty); - get_enum_variant_types(ccx, tpt.ty, variants, - ty_params, rp); - } - ast::item_impl(tps, rp, ifce, selfty, ms) { - let i_bounds = ty_param_bounds(ccx, tps); - let selfty = ccx.to_ty(type_rscope(rp), selfty); - write_ty_to_tcx(tcx, it.id, selfty); - tcx.tcache.insert(local_def(it.id), - {bounds: i_bounds, - rp: rp, - ty: selfty}); - alt ifce { - some(t) { - check_methods_against_iface( - ccx, tps, rp, - selfty, t, ms); - } - _ { - // Still have to do this to write method types - // into the table - convert_methods( - ccx, ms, rp, - i_bounds, selfty); - } - } - } - ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) { - let {bounds, substs} = mk_substs(ccx, tps, rp); - let def_id = local_def(it.id); - let t_arg = ty_of_arg(ccx, type_rscope(rp), - decl.inputs[0], none); - let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs); - - let t_ctor = ty::mk_fn(tcx, { - proto: ast::proto_box, - inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}], - output: t_res, - ret_style: ast::return_val, constraints: [] - }); - let t_dtor = ty::mk_fn(tcx, { - proto: ast::proto_box, - inputs: [t_arg], output: ty::mk_nil(tcx), - ret_style: ast::return_val, constraints: [] - }); - write_ty_to_tcx(tcx, it.id, t_res); - write_ty_to_tcx(tcx, ctor_id, t_ctor); - tcx.tcache.insert(local_def(ctor_id), - {bounds: bounds, - rp: rp, - ty: t_ctor}); - tcx.tcache.insert(def_id, {bounds: bounds, - rp: rp, - ty: t_res}); - write_ty_to_tcx(tcx, dtor_id, t_dtor); - } - ast::item_iface(*) { - let tpt = ty_of_item(ccx, it); - #debug["item_iface(it.id=%d, tpt.ty=%s)", - it.id, ty_to_str(tcx, tpt.ty)]; - write_ty_to_tcx(tcx, it.id, tpt.ty); - ensure_iface_methods(ccx, it.id); - } - ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) { - // Write the class type - let tpt = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, tpt.ty); - // Write the ctor type - let t_ctor = - ty::mk_fn(tcx, - ty_of_fn_decl(ccx, - empty_rscope, - ast::proto_any, - ctor.node.dec, - none)); - write_ty_to_tcx(tcx, ctor.node.id, t_ctor); - tcx.tcache.insert(local_def(ctor.node.id), - {bounds: tpt.bounds, - rp: ast::rp_none, - ty: t_ctor}); - option::iter(m_dtor) {|dtor| - // Write the dtor type - let t_dtor = ty::mk_fn(tcx, - // not sure about empty_rscope - // FIXME - ty_of_fn_decl(ccx, - empty_rscope, - ast::proto_any, - ast_util::dtor_dec(), - none)); - write_ty_to_tcx(tcx, dtor.node.id, t_dtor); - tcx.tcache.insert(local_def(dtor.node.id), - {bounds: tpt.bounds, - rp: ast::rp_none, - ty: t_dtor}); - }; - ensure_iface_methods(ccx, it.id); - /* FIXME: check for proper public/privateness */ - // Write the type of each of the members - let (fields, methods) = split_class_items(members); - for fields.each {|f| - convert_class_item(ccx, rp, f); - } - // The selfty is just the class type - let {bounds:_, substs} = mk_substs(ccx, tps, rp); - let selfty = ty::mk_class(tcx, local_def(it.id), substs); - // Need to convert all methods so we can check internal - // references to private methods - - // NDM to TJC---I think we ought to be using bounds here, not @[]. - // But doing so causes errors later on. - convert_methods(ccx, methods, rp, @[], selfty); - - /* - Finally, check that the class really implements the ifaces - that it claims to implement. - */ - for ifaces.each { |ifce| - check_methods_against_iface(ccx, tps, rp, selfty, - ifce, methods); - let t = ty::node_id_to_type(tcx, ifce.id); - - // FIXME: This assumes classes only implement - // non-parameterized ifaces. add a test case for - // a class implementing a parameterized iface. - // -- tjc (#1726) - tcx.tcache.insert(local_def(ifce.id), no_params(t)); - } - } - _ { - // This call populates the type cache with the converted type - // of the item in passing. All we have to do here is to write - // it into the node type table. - let tpt = ty_of_item(ccx, it); - write_ty_to_tcx(tcx, it.id, tpt.ty); - } - } - } - fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) { - // As above, this call populates the type table with the converted - // type of the native item. We simply write it into the node type - // table. - let tpt = ty_of_native_item(ccx, i); - alt i.node { - ast::native_item_fn(_, _) { - write_ty_to_tcx(ccx.tcx, i.id, tpt.ty); - } - } - } - fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) { - visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ - visit_item: bind convert(ccx, _), - visit_native_item: bind convert_native(ccx, _) - with *visit::default_simple_visitor() - })); - } -} - - fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t { let mut t1 = t; let mut enum_dids = []; @@ -1866,196 +845,6 @@ fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool { } } - -// Type resolution: the phase that finds all the types in the AST with -// unresolved type variables and replaces "ty_var" types with their -// substitutions. -mod writeback { - - export resolve_type_vars_in_fn; - export resolve_type_vars_in_expr; - - fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) -> - option { - if !ty::type_needs_infer(typ) { ret some(typ); } - alt infer::resolve_deep(fcx.infcx, typ, true) { - result::ok(new_type) { ret some(new_type); } - result::err(e) { - if !fcx.ccx.tcx.sess.has_errors() { - fcx.ccx.tcx.sess.span_err( - sp, - #fmt["cannot determine a type \ - for this expression: %s", - infer::fixup_err_to_str(e)]) - } - ret none; - } - } - } - fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id) - -> option { - let fcx = wbcx.fcx, tcx = fcx.ccx.tcx; - let n_ty = fcx.node_ty(id); - alt resolve_type_vars_in_type(fcx, sp, n_ty) { - none { - wbcx.success = false; - ret none; - } - - some(t) { - #debug["resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)", - id, ty_to_str(tcx, n_ty), ty_to_str(tcx, t)]; - write_ty_to_tcx(tcx, id, t); - alt fcx.opt_node_ty_substs(id) { - some(substs) { - let mut new_tps = []; - for substs.tps.each {|subst| - alt resolve_type_vars_in_type(fcx, sp, subst) { - some(t) { new_tps += [t]; } - none { wbcx.success = false; ret none; } - } - } - write_substs_to_tcx(tcx, id, new_tps); - } - none {} - } - ret some(t); - } - } - } - - fn maybe_resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, - id: ast::node_id) - -> option { - if wbcx.fcx.node_types.contains_key(id as uint) { - resolve_type_vars_for_node(wbcx, sp, id) - } else { - none - } - } - - type wb_ctxt = - // As soon as we hit an error we have to stop resolving - // the entire function - {fcx: @fn_ctxt, mut success: bool}; - type wb_vt = visit::vt; - - fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) { - if !wbcx.success { ret; } - resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s)); - visit::visit_stmt(s, wbcx, v); - } - fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) { - if !wbcx.success { ret; } - resolve_type_vars_for_node(wbcx, e.span, e.id); - alt e.node { - ast::expr_fn(_, decl, _, _) | - ast::expr_fn_block(decl, _, _) { - vec::iter(decl.inputs) {|input| - let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); - - // Just in case we never constrained the mode to anything, - // constrain it to the default for the type in question. - alt (r_ty, input.mode) { - (some(t), ast::infer(_)) { - let tcx = wbcx.fcx.ccx.tcx; - let m_def = ty::default_arg_mode_for_ty(t); - ty::set_default_mode(tcx, input.mode, m_def); - } - _ {} - } - } - } - - ast::expr_new(_, alloc_id, _) { - resolve_type_vars_for_node(wbcx, e.span, alloc_id); - } - - ast::expr_binary(_, _, _) | ast::expr_unary(_, _) | - ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) { - maybe_resolve_type_vars_for_node(wbcx, e.span, - ast_util::op_expr_callee_id(e)); - } - - _ { } - } - visit::visit_expr(e, wbcx, v); - } - fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) { - if !wbcx.success { ret; } - resolve_type_vars_for_node(wbcx, b.span, b.node.id); - visit::visit_block(b, wbcx, v); - } - fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) { - if !wbcx.success { ret; } - resolve_type_vars_for_node(wbcx, p.span, p.id); - #debug["Type for pattern binding %s (id %d) resolved to %s", - pat_to_str(p), p.id, - wbcx.fcx.ty_to_str( - ty::node_id_to_type(wbcx.fcx.ccx.tcx, - p.id))]; - visit::visit_pat(p, wbcx, v); - } - fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) { - if !wbcx.success { ret; } - let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); - alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) { - result::ok(lty) { - #debug["Type for local %s (id %d) resolved to %s", - pat_to_str(l.node.pat), l.node.id, - wbcx.fcx.ty_to_str(lty)]; - write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.node.id, lty); - } - result::err(e) { - wbcx.fcx.ccx.tcx.sess.span_err( - l.span, - #fmt["cannot determine a type \ - for this local variable: %s", - infer::fixup_err_to_str(e)]); - wbcx.success = false; - } - } - visit::visit_local(l, wbcx, v); - } - fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) { - // Ignore items - } - - fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool { - let wbcx = {fcx: fcx, mut success: true}; - let visit = - visit::mk_vt(@{visit_item: visit_item, - visit_stmt: visit_stmt, - visit_expr: visit_expr, - visit_block: visit_block, - visit_pat: visit_pat, - visit_local: visit_local - with *visit::default_visitor()}); - visit.visit_expr(e, wbcx, visit); - ret wbcx.success; - } - - fn resolve_type_vars_in_fn(fcx: @fn_ctxt, - decl: ast::fn_decl, - blk: ast::blk) -> bool { - let wbcx = {fcx: fcx, mut success: true}; - let visit = - visit::mk_vt(@{visit_item: visit_item, - visit_stmt: visit_stmt, - visit_expr: visit_expr, - visit_block: visit_block, - visit_pat: visit_pat, - visit_local: visit_local - with *visit::default_visitor()}); - visit.visit_block(blk, wbcx, visit); - for decl.inputs.each {|arg| - resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id); - } - ret wbcx.success; - } - -} - fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::native_item) { fn param(ccx: @crate_ctxt, n: uint) -> ty::t { ty::mk_param(ccx.tcx, n, local_def(0)) @@ -2099,7 +888,7 @@ fn check_intrinsic_type(ccx: @crate_ctxt, it: @ast::native_item) { inputs: inputs, output: output, ret_style: ast::return_val, constraints: []}); - let i_ty = ty_of_native_item(ccx, it); + let i_ty = astconv::ty_of_native_item(ccx, it); let i_n_tps = (*i_ty.bounds).len(); if i_n_tps != n_tps { tcx.sess.span_err(it.span, #fmt("intrinsic has wrong number \ @@ -2454,7 +1243,7 @@ fn impl_self_ty(fcx: @fn_ctxt, did: ast::def_id) -> ty_param_substs_and_ty { _}, _)) { {n_tps: ts.len(), rp: rp, - raw_ty: fcx.ccx.to_ty(type_rscope(rp), st)} + raw_ty: fcx.ccx.to_ty(astconv::type_rscope(rp), st)} } some(ast_map::node_item(@{node: ast::item_class(ts, _,_,_,_,rp), id: class_id, _},_)) { @@ -3170,8 +1959,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, // construct the function type let fty = ty::mk_fn(tcx, - ty_of_fn_decl(fcx, fcx, proto, decl, - expected_tys)); + astconv::ty_of_fn_decl(fcx, fcx, proto, decl, + expected_tys)); #debug("check_expr_fn_with_unifier %s fty=%s", expr_to_str(expr), fcx.ty_to_str(fty)); @@ -4407,7 +3196,7 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_member], rp: ast::region_param) -> class_map { let rslt = int_hash::(); - let rs = type_rscope(rp); + let rs = astconv::type_rscope(rp); for members.each { |m| alt m.node { ast::instance_var(_,t,_,id,_) { @@ -4416,7 +3205,7 @@ fn class_types(ccx: @crate_ctxt, members: [@ast::class_member], ast::class_method(mth) { rslt.insert(mth.id, ty::mk_fn(ccx.tcx, - ty_of_method(ccx, mth, rp).fty)); + collect::ty_of_method(ccx, mth, rp).fty)); } } } @@ -4447,7 +3236,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { check_bare_fn(ccx, decl, body, dtor_id, none); } ast::item_impl(tps, rp, _, ty, ms) { - let self_ty = ccx.to_ty(type_rscope(rp), ty); + let self_ty = ccx.to_ty(astconv::type_rscope(rp), ty); for ms.each {|m| check_method(ccx, m, self_ty);} } ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) { @@ -4545,278 +3334,6 @@ fn check_for_main_fn(ccx: @crate_ctxt, crate: @ast::crate) { } } -mod vtable { - fn has_iface_bounds(tps: [ty::param_bounds]) -> bool { - vec::any(tps, {|bs| - vec::any(*bs, {|b| - alt b { ty::bound_iface(_) { true } _ { false } } - }) - }) - } - - fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, - bounds: @[ty::param_bounds], substs: ty::substs, - allow_unsafe: bool) -> vtable_res { - let tcx = fcx.ccx.tcx; - let mut result = [], i = 0u; - for substs.tps.each {|ty| - for vec::each(*bounds[i]) {|bound| - alt bound { - ty::bound_iface(i_ty) { - let i_ty = ty::subst(tcx, substs, i_ty); - result += [lookup_vtable(fcx, isc, sp, ty, i_ty, - allow_unsafe)]; - } - _ {} - } - } - i += 1u; - } - @result - } - - fn fixup_substs(fcx: @fn_ctxt, sp: span, - id: ast::def_id, substs: ty::substs) -> ty::substs { - let tcx = fcx.ccx.tcx; - // use a dummy type just to package up the substs that need fixing up - let t = ty::mk_iface(tcx, id, substs); - let t_f = fixup_ty(fcx, sp, t); - alt check ty::get(t_f).struct { - ty::ty_iface(_, substs_f) { substs_f } - } - } - - fn relate_iface_tys(fcx: @fn_ctxt, sp: span, - exp_iface_ty: ty::t, act_iface_ty: ty::t) { - demand::suptype(fcx, sp, exp_iface_ty, act_iface_ty) - } - - /* - Look up the vtable to use when treating an item of type - as if it has type - */ - fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, - ty: ty::t, iface_ty: ty::t, allow_unsafe: bool) - -> vtable_origin { - - #debug["lookup_vtable(ty=%s, iface_ty=%s)", - fcx.ty_to_str(ty), fcx.ty_to_str(iface_ty)]; - let _i = indenter(); - - let tcx = fcx.ccx.tcx; - let (iface_id, iface_substs) = alt check ty::get(iface_ty).struct { - ty::ty_iface(did, substs) { (did, substs) } - }; - let ty = fixup_ty(fcx, sp, ty); - alt ty::get(ty).struct { - ty::ty_param(n, did) { - let mut n_bound = 0u; - for vec::each(*tcx.ty_param_bounds.get(did.node)) { |bound| - alt bound { - ty::bound_send | ty::bound_copy { /* ignore */ } - ty::bound_iface(ity) { - alt check ty::get(ity).struct { - ty::ty_iface(idid, substs) { - if iface_id == idid { - relate_iface_tys(fcx, sp, iface_ty, ity); - ret vtable_param(n, n_bound); - } - } - } - n_bound += 1u; - } - } - } - } - - ty::ty_iface(did, substs) if iface_id == did { - relate_iface_tys(fcx, sp, iface_ty, ty); - if !allow_unsafe { - for vec::each(*ty::iface_methods(tcx, did)) {|m| - if ty::type_has_self(ty::mk_fn(tcx, m.fty)) { - tcx.sess.span_err( - sp, "a boxed iface with self types may not be \ - passed as a bounded type"); - } else if (*m.tps).len() > 0u { - tcx.sess.span_err( - sp, "a boxed iface with generic methods may not \ - be passed as a bounded type"); - - } - } - } - ret vtable_iface(did, substs.tps); - } - - _ { - let mut found = []; - - for list::each(isc) {|impls| - /* For each impl in scope... */ - for vec::each(*impls) {|im| - // im = one specific impl - // find the iface that im implements (if any) - let of_ty = alt ty::impl_iface(tcx, im.did) { - some(of_ty) { of_ty } - _ { cont; } - }; - - // it must have the same id as the expected one - alt ty::get(of_ty).struct { - ty::ty_iface(id, _) if id != iface_id { cont; } - _ { /* ok */ } - } - - // check whether the type unifies with the type - // that the impl is for, and continue if not - let {substs: substs, ty: for_ty} = - impl_self_ty(fcx, im.did); - let im_bs = ty::lookup_item_type(tcx, im.did).bounds; - alt fcx.mk_subty(ty, for_ty) { - result::err(_) { cont; } - result::ok(()) { } - } - - // check that desired iface type unifies - let of_ty = ty::subst(tcx, substs, of_ty); - relate_iface_tys(fcx, sp, iface_ty, of_ty); - - // recursively process the bounds - let iface_tps = iface_substs.tps; - let substs_f = fixup_substs(fcx, sp, iface_id, substs); - connect_iface_tps(fcx, sp, substs_f.tps, - iface_tps, im.did); - let subres = lookup_vtables(fcx, isc, sp, - im_bs, substs_f, false); - found += [vtable_static(im.did, substs_f.tps, subres)]; - } - - alt found.len() { - 0u { /* fallthrough */ } - 1u { ret found[0]; } - _ { - fcx.ccx.tcx.sess.span_err( - sp, "multiple applicable methods in scope"); - ret found[0]; - } - } - } - } - } - - tcx.sess.span_fatal( - sp, "failed to find an implementation of interface " + - ty_to_str(tcx, iface_ty) + " for " + - ty_to_str(tcx, ty)); - } - - fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t { - let tcx = fcx.ccx.tcx; - alt infer::resolve_deep(fcx.infcx, ty, true) { - result::ok(new_type) { new_type } - result::err(e) { - tcx.sess.span_fatal( - sp, - #fmt["cannot determine a type \ - for this bounded type parameter: %s", - infer::fixup_err_to_str(e)]) - } - } - } - - fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t], - iface_tys: [ty::t], impl_did: ast::def_id) { - let tcx = fcx.ccx.tcx; - let ity = option::get(ty::impl_iface(tcx, impl_did)); - let iface_ty = ty::subst_tps(tcx, impl_tys, ity); - alt check ty::get(iface_ty).struct { - ty::ty_iface(_, substs) { - vec::iter2(substs.tps, iface_tys, - {|a, b| demand::suptype(fcx, sp, a, b);}); - } - } - } - - fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) { - let cx = fcx.ccx; - alt ex.node { - ast::expr_path(*) { - alt fcx.opt_node_ty_substs(ex.id) { - some(substs) { - let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); - let item_ty = ty::lookup_item_type(cx.tcx, did); - if has_iface_bounds(*item_ty.bounds) { - let impls = cx.impl_map.get(ex.id); - cx.vtable_map.insert(ex.id, lookup_vtables( - fcx, impls, ex.span, - item_ty.bounds, substs, false)); - } - } - _ {} - } - } - // Must resolve bounds on methods with bounded params - ast::expr_field(*) | ast::expr_binary(*) | - ast::expr_unary(*) | ast::expr_assign_op(*) | - ast::expr_index(*) { - alt cx.method_map.find(ex.id) { - some(method_static(did)) { - let bounds = ty::lookup_item_type(cx.tcx, did).bounds; - if has_iface_bounds(*bounds) { - let callee_id = alt ex.node { - ast::expr_field(_, _, _) { ex.id } - _ { ast_util::op_expr_callee_id(ex) } - }; - let substs = fcx.node_ty_substs(callee_id); - let iscs = cx.impl_map.get(ex.id); - cx.vtable_map.insert(callee_id, lookup_vtables( - fcx, iscs, ex.span, bounds, substs, false)); - } - } - _ {} - } - } - ast::expr_cast(src, _) { - let target_ty = fcx.expr_ty(ex); - alt ty::get(target_ty).struct { - ty::ty_iface(*) { - /* Casting to an interface type. - Look up all impls for the cast expr... - */ - let impls = cx.impl_map.get(ex.id); - /* - Look up vtables for the type we're casting to, - passing in the source and target type - */ - let vtable = lookup_vtable(fcx, impls, ex.span, - fcx.expr_ty(src), target_ty, - true); - /* - Map this expression to that vtable (that is: "ex has - vtable ") - */ - cx.vtable_map.insert(ex.id, @[vtable]); - } - _ {} - } - } - _ {} - } - visit::visit_expr(ex, fcx, v); - } - - // Detect points where an interface-bounded type parameter is - // instantiated, resolve the impls for the parameters. - fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) { - visit::visit_block(bl, fcx, visit::mk_vt(@{ - visit_expr: resolve_expr, - visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt, - _v: visit::vt<@fn_ctxt>) {} - with *visit::default_visitor() - })); - } -} - fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map, crate: @ast::crate) -> (method_map, vtable_map) { let ccx = @{impl_map: impl_map, diff --git a/src/rustc/middle/typeck/astconv.rs b/src/rustc/middle/typeck/astconv.rs new file mode 100644 index 00000000000..8f5eacf7708 --- /dev/null +++ b/src/rustc/middle/typeck/astconv.rs @@ -0,0 +1,711 @@ +#[doc = " + +Conversion from AST representation of types to the ty.rs representation. + +The main routine here is `ast_ty_to_ty()`: each use is parameterized +by an instance of `ast_conv` and a `region_scope`. + +The `ast_conv` interface is the conversion context. It has two +implementations, one for the crate context and one for the function +context. The main purpose is to provide the `get_item_ty()` hook +which looks up the type of an item by its def-id. This can be done in +two ways: in the initial phase, when a crate context is provided, this +will potentially trigger a call to `ty_of_item()`. Later, when a +function context is used, this will simply be a lookup. + +The `region_scope` interface controls how region references are +handled. It has two methods which are used to resolve anonymous +region references (e.g., `&T`) and named region references (e.g., +`&a.T`). There are numerous region scopes that can be used, but most +commonly you want either `empty_rscope`, which permits only the static +region, or `type_rscope`, which permits the self region if the type in +question is parameterized by a region. + +"]; + +iface ast_conv { + fn tcx() -> ty::ctxt; + fn ccx() -> @crate_ctxt; + fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty; + + // what type should we use when a type is omitted? + fn ty_infer(span: span) -> ty::t; +} + +impl of ast_conv for @crate_ctxt { + fn tcx() -> ty::ctxt { self.tcx } + fn ccx() -> @crate_ctxt { self } + + fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty { + if id.crate != ast::local_crate { + csearch::get_type(self.tcx, id) + } else { + alt self.tcx.items.find(id.node) { + some(ast_map::node_item(item, _)) { + ty_of_item(self, item) + } + some(ast_map::node_native_item(native_item, _, _)) { + ty_of_native_item(self, native_item) + } + x { + self.tcx.sess.bug(#fmt["unexpected sort of item \ + in get_item_ty(): %?", x]); + } + } + } + } + + fn ty_infer(span: span) -> ty::t { + self.tcx.sess.span_bug(span, + "found `ty_infer` in unexpected place"); + } +} + +impl of ast_conv for @fn_ctxt { + fn tcx() -> ty::ctxt { self.ccx.tcx } + fn ccx() -> @crate_ctxt { self.ccx } + + fn get_item_ty(id: ast::def_id) -> ty::ty_param_bounds_and_ty { + ty::lookup_item_type(self.tcx(), id) + } + + fn ty_infer(_span: span) -> ty::t { + self.next_ty_var() + } +} + +iface region_scope { + fn anon_region() -> result; + fn named_region(id: str) -> result; +} + +enum empty_rscope { empty_rscope } +impl of region_scope for empty_rscope { + fn anon_region() -> result { + result::err("region types are not allowed here") + } + fn named_region(id: str) -> result { + if id == "static" { result::ok(ty::re_static) } + else { result::err("only the static region is allowed here") } + } +} + +enum type_rscope = ast::region_param; +impl of region_scope for type_rscope { + fn anon_region() -> result { + alt *self { + ast::rp_self { result::ok(ty::re_bound(ty::br_self)) } + ast::rp_none { + result::err("to use region types here, the containing type \ + must be declared with a region bound") + } + } + } + fn named_region(id: str) -> result { + empty_rscope.named_region(id).chain_err { |_e| + if id == "self" { self.anon_region() } + else { + result::err("named regions other than `self` are not \ + allowed as part of a type declaration") + } + } + } +} + +impl of region_scope for @fn_ctxt { + fn anon_region() -> result { + result::ok(self.next_region_var()) + } + fn named_region(id: str) -> result { + empty_rscope.named_region(id).chain_err { |_e| + alt self.in_scope_regions.find(ty::br_named(id)) { + some(r) { result::ok(r) } + none if id == "blk" { self.block_region() } + none { + result::err(#fmt["named region `%s` not in scope here", id]) + } + } + } + } +} + +enum anon_rscope = {anon: ty::region, base: region_scope}; +fn in_anon_rscope(self: RS, r: ty::region) + -> @anon_rscope { + @anon_rscope({anon: r, base: self as region_scope}) +} +impl of region_scope for @anon_rscope { + fn anon_region() -> result { + result::ok(self.anon) + } + fn named_region(id: str) -> result { + self.base.named_region(id) + } +} + +enum binding_rscope = {base: region_scope}; +fn in_binding_rscope(self: RS) -> @binding_rscope { + let base = self as region_scope; + @binding_rscope({base: base}) +} +impl of region_scope for @binding_rscope { + fn anon_region() -> result { + result::ok(ty::re_bound(ty::br_anon)) + } + fn named_region(id: str) -> result { + self.base.named_region(id).chain_err {|_e| + result::ok(ty::re_bound(ty::br_named(id))) + } + } +} + +fn ast_region_to_region( + self: AC, rscope: RS, span: span, a_r: @ast::region) -> ty::region { + + let res = alt a_r.node { + ast::re_anon { rscope.anon_region() } + ast::re_named(id) { rscope.named_region(id) } + }; + + get_region_reporting_err(self.tcx(), span, res) +} + +fn ast_path_to_substs_and_ty( + self: AC, rscope: RS, did: ast::def_id, + path: @ast::path) -> ty_param_substs_and_ty { + + let tcx = self.tcx(); + let {bounds: decl_bounds, rp: decl_rp, ty: decl_ty} = + self.get_item_ty(did); + + // If the type is parameterized by the self region, then replace self + // region with the current anon region binding (in other words, + // whatever & would get replaced with). + let self_r = alt (decl_rp, path.rp) { + (ast::rp_none, none) { + none + } + (ast::rp_none, some(_)) { + tcx.sess.span_err( + path.span, + #fmt["No region bound is permitted on %s, \ + which is not declared as containing region pointers", + ty::item_path_str(tcx, did)]); + none + } + (ast::rp_self, none) { + let res = rscope.anon_region(); + let r = get_region_reporting_err(self.tcx(), path.span, res); + some(r) + } + (ast::rp_self, some(r)) { + some(ast_region_to_region(self, rscope, path.span, r)) + } + }; + + // Convert the type parameters supplied by the user. + if !vec::same_length(*decl_bounds, path.types) { + self.tcx().sess.span_fatal( + path.span, + #fmt["wrong number of type arguments, expected %u but found %u", + (*decl_bounds).len(), path.types.len()]); + } + let tps = path.types.map { |a_t| ast_ty_to_ty(self, rscope, a_t) }; + + let substs = {self_r:self_r, self_ty:none, tps:tps}; + {substs: substs, ty: ty::subst(tcx, substs, decl_ty)} +} + +fn ast_path_to_ty( + self: AC, + rscope: RS, + did: ast::def_id, + path: @ast::path, + path_id: ast::node_id) -> ty_param_substs_and_ty { + + // Lookup the polytype of the item and then substitute the provided types + // for any type/region parameters. + let tcx = self.tcx(); + let {substs: substs, ty: ty} = + ast_path_to_substs_and_ty(self, rscope, did, path); + write_ty_to_tcx(tcx, path_id, ty); + write_substs_to_tcx(tcx, path_id, substs.tps); + ret {substs: substs, ty: ty}; +} + +/* + Instantiates the path for the given iface reference, assuming that + it's bound to a valid iface type. Returns the def_id for the defining + iface. Fails if the type is a type other than an iface type. + */ +fn instantiate_iface_ref(ccx: @crate_ctxt, t: @ast::iface_ref, + rp: ast::region_param) + -> (ast::def_id, ty_param_substs_and_ty) { + + let sp = t.path.span, err = "can only implement interface types", + sess = ccx.tcx.sess; + + let rscope = type_rscope(rp); + + alt lookup_def_tcx(ccx.tcx, t.path.span, t.id) { + ast::def_ty(t_id) { + let tpt = ast_path_to_ty(ccx, rscope, t_id, t.path, t.id); + alt ty::get(tpt.ty).struct { + ty::ty_iface(*) { + (t_id, tpt) + } + _ { sess.span_fatal(sp, err); } + } + } + _ { + sess.span_fatal(sp, err); + } + } +} + +const NO_REGIONS: uint = 1u; +const NO_TPS: uint = 2u; + +// Parses the programmer's textual representation of a type into our +// internal notion of a type. `getter` is a function that returns the type +// corresponding to a definition ID: +fn ast_ty_to_ty( + self: AC, rscope: RS, &&ast_ty: @ast::ty) -> ty::t { + + fn ast_mt_to_mt( + self: AC, rscope: RS, mt: ast::mt) -> ty::mt { + + ret {ty: ast_ty_to_ty(self, rscope, mt.ty), mutbl: mt.mutbl}; + } + + fn mk_vstore( + self: AC, rscope: RS, a_seq_ty: @ast::ty, vst: ty::vstore) -> ty::t { + + let tcx = self.tcx(); + let seq_ty = ast_ty_to_ty(self, rscope, a_seq_ty); + + alt ty::get(seq_ty).struct { + ty::ty_vec(mt) { + ret ty::mk_evec(tcx, mt, vst); + } + + ty::ty_str { + ret ty::mk_estr(tcx, vst); + } + + _ { + tcx.sess.span_err( + a_seq_ty.span, + #fmt["Bound not allowed on a %s.", + ty::ty_sort_str(tcx, seq_ty)]); + ret seq_ty; + } + } + } + + fn check_path_args(tcx: ty::ctxt, + path: @ast::path, + flags: uint) { + if (flags & NO_TPS) != 0u { + if path.types.len() > 0u { + tcx.sess.span_err( + path.span, + "Type parameters are not allowed on this type."); + } + } + + if (flags & NO_REGIONS) != 0u { + if path.rp.is_some() { + tcx.sess.span_err( + path.span, + "Region parameters are not allowed on this type."); + } + } + } + + let tcx = self.tcx(); + + alt tcx.ast_ty_to_ty_cache.find(ast_ty) { + some(ty::atttce_resolved(ty)) { ret ty; } + some(ty::atttce_unresolved) { + tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \ + insert a enum in the cycle, \ + if this is desired)"); + } + none { /* go on */ } + } + + tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_unresolved); + let typ = alt ast_ty.node { + ast::ty_nil { ty::mk_nil(tcx) } + ast::ty_bot { ty::mk_bot(tcx) } + ast::ty_box(mt) { + ty::mk_box(tcx, ast_mt_to_mt(self, rscope, mt)) + } + ast::ty_uniq(mt) { + ty::mk_uniq(tcx, ast_mt_to_mt(self, rscope, mt)) + } + ast::ty_vec(mt) { + ty::mk_vec(tcx, ast_mt_to_mt(self, rscope, mt)) + } + ast::ty_ptr(mt) { + ty::mk_ptr(tcx, ast_mt_to_mt(self, rscope, mt)) + } + ast::ty_rptr(region, mt) { + let r = ast_region_to_region(self, rscope, ast_ty.span, region); + let mt = ast_mt_to_mt(self, in_anon_rscope(rscope, r), mt); + ty::mk_rptr(tcx, r, mt) + } + ast::ty_tup(fields) { + let flds = vec::map(fields) { |t| ast_ty_to_ty(self, rscope, t) }; + ty::mk_tup(tcx, flds) + } + ast::ty_rec(fields) { + let flds = fields.map {|f| + let tm = ast_mt_to_mt(self, rscope, f.node.mt); + {ident: f.node.ident, mt: tm} + }; + ty::mk_rec(tcx, flds) + } + ast::ty_fn(proto, decl) { + ty::mk_fn(tcx, ty_of_fn_decl(self, rscope, proto, decl, none)) + } + ast::ty_path(path, id) { + let a_def = alt tcx.def_map.find(id) { + none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s", + path_to_str(path))); } + some(d) { d }}; + alt a_def { + ast::def_ty(did) | ast::def_class(did) { + ast_path_to_ty(self, rscope, did, path, id).ty + } + ast::def_prim_ty(nty) { + alt nty { + ast::ty_bool { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_bool(tcx) + } + ast::ty_int(it) { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_mach_int(tcx, it) + } + ast::ty_uint(uit) { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_mach_uint(tcx, uit) + } + ast::ty_float(ft) { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_mach_float(tcx, ft) + } + ast::ty_str { + check_path_args(tcx, path, NO_TPS); + // This is a bit of a hack, but basically str/& needs to be + // converted into a vstore: + alt path.rp { + none { + ty::mk_str(tcx) + } + some(ast_r) { + let r = ast_region_to_region(self, rscope, + ast_ty.span, ast_r); + ty::mk_estr(tcx, ty::vstore_slice(r)) + } + } + } + } + } + ast::def_ty_param(id, n) { + check_path_args(tcx, path, NO_TPS | NO_REGIONS); + ty::mk_param(tcx, n, id) + } + ast::def_self(_) { + // n.b.: resolve guarantees that the self type only appears in an + // iface, which we rely upon in various places when creating + // substs + ty::mk_self(tcx) + } + _ { + tcx.sess.span_fatal(ast_ty.span, + "found type name used as a variable"); + } + } + } + ast::ty_vstore(a_t, ast::vstore_slice(a_r)) { + let r = ast_region_to_region(self, rscope, ast_ty.span, a_r); + mk_vstore(self, in_anon_rscope(rscope, r), a_t, ty::vstore_slice(r)) + } + ast::ty_vstore(a_t, ast::vstore_uniq) { + mk_vstore(self, rscope, a_t, ty::vstore_uniq) + } + ast::ty_vstore(a_t, ast::vstore_box) { + mk_vstore(self, rscope, a_t, ty::vstore_box) + } + ast::ty_vstore(a_t, ast::vstore_fixed(some(u))) { + mk_vstore(self, rscope, a_t, ty::vstore_fixed(u)) + } + ast::ty_vstore(_, ast::vstore_fixed(none)) { + tcx.sess.span_bug( + ast_ty.span, + "implied fixed length for bound"); + } + ast::ty_constr(t, cs) { + let mut out_cs = []; + for cs.each {|constr| + out_cs += [ty::ast_constr_to_constr(tcx, constr)]; + } + ty::mk_constr(tcx, ast_ty_to_ty(self, rscope, t), out_cs) + } + ast::ty_infer { + // ty_infer should only appear as the type of arguments or return + // values in a fn_expr, or as the type of local variables. Both of + // these cases are handled specially and should not descend into this + // routine. + self.tcx().sess.span_bug( + ast_ty.span, + "found `ty_infer` in unexpected place"); + } + ast::ty_mac(_) { + tcx.sess.span_bug(ast_ty.span, + "found `ty_mac` in unexpected place"); + } + }; + + tcx.ast_ty_to_ty_cache.insert(ast_ty, ty::atttce_resolved(typ)); + ret typ; +} + +fn ty_of_item(ccx: @crate_ctxt, it: @ast::item) + -> ty::ty_param_bounds_and_ty { + + let def_id = local_def(it.id); + let tcx = ccx.tcx; + alt tcx.tcache.find(def_id) { + some(tpt) { ret tpt; } + _ {} + } + alt it.node { + ast::item_const(t, _) { + let typ = ccx.to_ty(empty_rscope, t); + let tpt = no_params(typ); + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_fn(decl, tps, _) { + let bounds = ty_param_bounds(ccx, tps); + let tofd = ty_of_fn_decl(ccx, empty_rscope, ast::proto_bare, + decl, none); + let tpt = {bounds: bounds, + rp: ast::rp_none, // functions do not have a self + ty: ty::mk_fn(ccx.tcx, tofd)}; + ccx.tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_ty(t, tps, rp) { + alt tcx.tcache.find(local_def(it.id)) { + some(tpt) { ret tpt; } + none { } + } + + let tpt = { + let ty = { + let t0 = ccx.to_ty(type_rscope(rp), t); + // Do not associate a def id with a named, parameterized type + // like "foo". This is because otherwise ty_to_str will + // print the name as merely "foo", as it has no way to + // reconstruct the value of X. + if !vec::is_empty(tps) { t0 } else { + ty::mk_with_id(tcx, t0, def_id) + } + }; + {bounds: ty_param_bounds(ccx, tps), rp: rp, ty: ty} + }; + + check_bounds_are_used(ccx, t.span, tps, rp, tpt.ty); + + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_res(decl, tps, _, _, _, rp) { + let {bounds, substs} = mk_substs(ccx, tps, rp); + let t_arg = ty_of_arg(ccx, type_rscope(rp), + decl.inputs[0], none); + let t = ty::mk_res(tcx, local_def(it.id), t_arg.ty, substs); + let t_res = {bounds: bounds, rp: rp, ty: t}; + tcx.tcache.insert(local_def(it.id), t_res); + ret t_res; + } + ast::item_enum(_, tps, rp) { + // Create a new generic polytype. + let {bounds, substs} = mk_substs(ccx, tps, rp); + let t = ty::mk_enum(tcx, local_def(it.id), substs); + let tpt = {bounds: bounds, rp: rp, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_iface(tps, rp, ms) { + let {bounds, substs} = mk_substs(ccx, tps, rp); + let t = ty::mk_iface(tcx, local_def(it.id), substs); + let tpt = {bounds: bounds, rp: rp, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_class(tps, _, _, _, _, rp) { + let {bounds,substs} = mk_substs(ccx, tps, rp); + let t = ty::mk_class(tcx, local_def(it.id), substs); + let tpt = {bounds: bounds, rp: rp, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_impl(*) | ast::item_mod(_) | + ast::item_native_mod(_) { fail; } + } +} + +fn ty_of_native_item(ccx: @crate_ctxt, it: @ast::native_item) + -> ty::ty_param_bounds_and_ty { + alt it.node { + ast::native_item_fn(fn_decl, params) { + ret ty_of_native_fn_decl(ccx, fn_decl, params, + local_def(it.id)); + } + } +} + +fn ty_of_arg( + self: AC, rscope: RS, a: ast::arg, + expected_ty: option) -> ty::arg { + + let ty = alt a.ty.node { + ast::ty_infer if expected_ty.is_some() {expected_ty.get().ty} + ast::ty_infer {self.ty_infer(a.ty.span)} + _ {ast_ty_to_ty(self, rscope, a.ty)} + }; + + let mode = { + alt a.mode { + ast::infer(_) if expected_ty.is_some() { + result::get(ty::unify_mode(self.tcx(), a.mode, + expected_ty.get().mode)) + } + ast::infer(_) { + alt ty::get(ty).struct { + // If the type is not specified, then this must be a fn expr. + // Leave the mode as infer(_), it will get inferred based + // on constraints elsewhere. + ty::ty_var(_) {a.mode} + + // If the type is known, then use the default for that type. + // Here we unify m and the default. This should update the + // tables in tcx but should never fail, because nothing else + // will have been unified with m yet: + _ { + let m1 = ast::expl(ty::default_arg_mode_for_ty(ty)); + result::get(ty::unify_mode(self.tcx(), a.mode, m1)) + } + } + } + ast::expl(_) {a.mode} + } + }; + + {mode: mode, ty: ty} +} + +type expected_tys = option<{inputs: [ty::arg], + output: ty::t}>; + +fn ty_of_fn_decl( + self: AC, rscope: RS, + proto: ast::proto, + decl: ast::fn_decl, + expected_tys: expected_tys) -> ty::fn_ty { + + #debug["ty_of_fn_decl"]; + indent {|| + // new region names that appear inside of the fn decl are bound to + // that function type + let rb = in_binding_rscope(rscope); + + let input_tys = decl.inputs.mapi { |i, a| + let expected_arg_ty = expected_tys.chain { |e| + // no guarantee that the correct number of expected args + // were supplied + if i < e.inputs.len() {some(e.inputs[i])} else {none} + }; + ty_of_arg(self, rb, a, expected_arg_ty) + }; + + let expected_ret_ty = expected_tys.map { |e| e.output }; + let output_ty = alt decl.output.node { + ast::ty_infer if expected_ret_ty.is_some() {expected_ret_ty.get()} + ast::ty_infer {self.ty_infer(decl.output.span)} + _ {ast_ty_to_ty(self, rb, decl.output)} + }; + + let out_constrs = vec::map(decl.constraints) {|constr| + ty::ast_constr_to_constr(self.tcx(), constr) + }; + {proto: proto, inputs: input_tys, + output: output_ty, ret_style: decl.cf, constraints: out_constrs} + } +} + + +fn ty_param_bounds(ccx: @crate_ctxt, + params: [ast::ty_param]) -> @[ty::param_bounds] { + + fn compute_bounds(ccx: @crate_ctxt, + param: ast::ty_param) -> ty::param_bounds { + @vec::flat_map(*param.bounds) { |b| + alt b { + ast::bound_send { [ty::bound_send] } + ast::bound_copy { [ty::bound_copy] } + ast::bound_iface(t) { + let ity = ast_ty_to_ty(ccx, empty_rscope, t); + alt ty::get(ity).struct { + ty::ty_iface(*) { + [ty::bound_iface(ity)] + } + _ { + ccx.tcx.sess.span_err( + t.span, "type parameter bounds must be \ + interface types"); + [] + } + } + } + } + } + } + + @params.map { |param| + alt ccx.tcx.ty_param_bounds.find(param.id) { + some(bs) { bs } + none { + let bounds = compute_bounds(ccx, param); + ccx.tcx.ty_param_bounds.insert(param.id, bounds); + bounds + } + } + } +} + +fn ty_of_native_fn_decl(ccx: @crate_ctxt, + decl: ast::fn_decl, + ty_params: [ast::ty_param], + def_id: ast::def_id) -> ty::ty_param_bounds_and_ty { + + let bounds = ty_param_bounds(ccx, ty_params); + let rb = in_binding_rscope(empty_rscope); + let input_tys = decl.inputs.map { |a| ty_of_arg(ccx, rb, a, none) }; + let output_ty = ast_ty_to_ty(ccx, rb, decl.output); + + let t_fn = ty::mk_fn(ccx.tcx, {proto: ast::proto_bare, + inputs: input_tys, + output: output_ty, + ret_style: ast::return_val, + constraints: []}); + let tpt = {bounds: bounds, rp: ast::rp_none, ty: t_fn}; + ccx.tcx.tcache.insert(def_id, tpt); + ret tpt; +} diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs new file mode 100644 index 00000000000..d1283375b5b --- /dev/null +++ b/src/rustc/middle/typeck/collect.rs @@ -0,0 +1,343 @@ +import astconv::{type_rscope, instantiate_iface_ref, ty_of_item, + empty_rscope, ty_of_native_item, ast_conv}; + +// Item collection - a pair of bootstrap passes: +// +// (1) Collect the IDs of all type items (typedefs) and store them in a table. +// +// (2) Translate the AST fragments that describe types to determine a type for +// each item. When we encounter a named type, we consult the table built +// in pass 1 to find its item, and recursively translate it. +// +// We then annotate the AST with the resulting types and return the annotated +// AST, along with a table mapping item IDs to their types. +fn get_enum_variant_types(ccx: @crate_ctxt, + enum_ty: ty::t, + variants: [ast::variant], + ty_params: [ast::ty_param], + rp: ast::region_param) { + let tcx = ccx.tcx; + + // Create a set of parameter types shared among all the variants. + for variants.each {|variant| + // Nullary enum constructors get turned into constants; n-ary enum + // constructors get turned into functions. + let result_ty = if vec::len(variant.node.args) == 0u { + enum_ty + } else { + let rs = type_rscope(rp); + let args = variant.node.args.map { |va| + let arg_ty = ccx.to_ty(rs, va.ty); + {mode: ast::expl(ast::by_copy), ty: arg_ty} + }; + ty::mk_fn(tcx, {proto: ast::proto_box, + inputs: args, + output: enum_ty, + ret_style: ast::return_val, + constraints: []}) + }; + let tpt = {bounds: astconv::ty_param_bounds(ccx, ty_params), + rp: rp, + ty: result_ty}; + tcx.tcache.insert(local_def(variant.node.id), tpt); + write_ty_to_tcx(tcx, variant.node.id, result_ty); + } +} + +fn ensure_iface_methods(ccx: @crate_ctxt, id: ast::node_id) { + fn store_methods(ccx: @crate_ctxt, id: ast::node_id, + stuff: [T], f: fn@(T) -> ty::method) { + ty::store_iface_methods(ccx.tcx, id, @vec::map(stuff, f)); + } + + let tcx = ccx.tcx; + alt check tcx.items.get(id) { + ast_map::node_item(@{node: ast::item_iface(_, rp, ms), _}, _) { + store_methods::(ccx, id, ms) {|m| + ty_of_ty_method(ccx, m, rp) + }; + } + ast_map::node_item(@{node: ast::item_class(_,_,its,_,_,rp), _}, _) { + let (_,ms) = split_class_items(its); + // All methods need to be stored, since lookup_method + // relies on the same method cache for self-calls + store_methods::<@ast::method>(ccx, id, ms) {|m| + ty_of_method(ccx, m, rp) + }; + } + } +} + +fn check_methods_against_iface(ccx: @crate_ctxt, + tps: [ast::ty_param], + rp: ast::region_param, + selfty: ty::t, + a_ifacety: @ast::iface_ref, + ms: [@ast::method]) { + + let tcx = ccx.tcx; + let i_bounds = astconv::ty_param_bounds(ccx, tps); + let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty); + let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp); + if did.crate == ast::local_crate { + ensure_iface_methods(ccx, did.node); + } + for vec::each(*ty::iface_methods(tcx, did)) {|if_m| + alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) { + some({mty: m, id, span}) { + if m.purity != if_m.purity { + ccx.tcx.sess.span_err( + span, #fmt["method `%s`'s purity \ + not match the iface method's \ + purity", m.ident]); + } + let mt = compare_impl_method( + ccx.tcx, span, m, vec::len(tps), + if_m, tpt.substs, selfty); + let old = tcx.tcache.get(local_def(id)); + if old.ty != mt { + tcx.tcache.insert( + local_def(id), + {bounds: old.bounds, + rp: old.rp, + ty: mt}); + write_ty_to_tcx(tcx, id, mt); + } + } + none { + tcx.sess.span_err( + a_ifacety.path.span, + #fmt["missing method `%s`", if_m.ident]); + } + } // alt + } // |if_m| +} // fn + +fn convert_class_item(ccx: @crate_ctxt, + rp: ast::region_param, + v: ast_util::ivar) { + /* we want to do something here, b/c within the + scope of the class, it's ok to refer to fields & + methods unqualified */ + /* they have these types *within the scope* of the + class. outside the class, it's done with expr_field */ + let tt = ccx.to_ty(type_rscope(rp), v.ty); + write_ty_to_tcx(ccx.tcx, v.id, tt); +} + +fn convert_methods(ccx: @crate_ctxt, + ms: [@ast::method], + rp: ast::region_param, + i_bounds: @[ty::param_bounds], + self_ty: ty::t) + -> [{mty: ty::method, id: ast::node_id, span: span}] { + + let tcx = ccx.tcx; + vec::map(ms) { |m| + write_ty_to_tcx(tcx, m.self_id, self_ty); + let bounds = astconv::ty_param_bounds(ccx, m.tps); + let mty = ty_of_method(ccx, m, rp); + let fty = ty::mk_fn(tcx, mty.fty); + tcx.tcache.insert( + local_def(m.id), + // n.b. This code is kind of sketchy (concat'ing i_bounds + // with bounds), but removing *i_bounds breaks other stuff + {bounds: @(*i_bounds + *bounds), rp: rp, ty: fty}); + write_ty_to_tcx(tcx, m.id, fty); + {mty: mty, id: m.id, span: m.span} + } +} + +fn convert(ccx: @crate_ctxt, it: @ast::item) { + let tcx = ccx.tcx; + alt it.node { + // These don't define types. + ast::item_mod(_) {} + ast::item_native_mod(m) { + if syntax::attr::native_abi(it.attrs) == + either::right(ast::native_abi_rust_intrinsic) { + for m.items.each { |item| check_intrinsic_type(ccx, item); } + } + } + ast::item_enum(variants, ty_params, rp) { + let tpt = ty_of_item(ccx, it); + write_ty_to_tcx(tcx, it.id, tpt.ty); + get_enum_variant_types(ccx, tpt.ty, variants, + ty_params, rp); + } + ast::item_impl(tps, rp, ifce, selfty, ms) { + let i_bounds = astconv::ty_param_bounds(ccx, tps); + let selfty = ccx.to_ty(type_rscope(rp), selfty); + write_ty_to_tcx(tcx, it.id, selfty); + tcx.tcache.insert(local_def(it.id), + {bounds: i_bounds, + rp: rp, + ty: selfty}); + alt ifce { + some(t) { + check_methods_against_iface( + ccx, tps, rp, + selfty, t, ms); + } + _ { + // Still have to do this to write method types + // into the table + convert_methods( + ccx, ms, rp, + i_bounds, selfty); + } + } + } + ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) { + let {bounds, substs} = mk_substs(ccx, tps, rp); + let def_id = local_def(it.id); + let t_arg = astconv::ty_of_arg(ccx, type_rscope(rp), + decl.inputs[0], none); + let t_res = ty::mk_res(tcx, def_id, t_arg.ty, substs); + + let t_ctor = ty::mk_fn(tcx, { + proto: ast::proto_box, + inputs: [{mode: ast::expl(ast::by_copy), ty: t_arg.ty}], + output: t_res, + ret_style: ast::return_val, constraints: [] + }); + let t_dtor = ty::mk_fn(tcx, { + proto: ast::proto_box, + inputs: [t_arg], output: ty::mk_nil(tcx), + ret_style: ast::return_val, constraints: [] + }); + write_ty_to_tcx(tcx, it.id, t_res); + write_ty_to_tcx(tcx, ctor_id, t_ctor); + tcx.tcache.insert(local_def(ctor_id), + {bounds: bounds, + rp: rp, + ty: t_ctor}); + tcx.tcache.insert(def_id, {bounds: bounds, + rp: rp, + ty: t_res}); + write_ty_to_tcx(tcx, dtor_id, t_dtor); + } + ast::item_iface(*) { + let tpt = ty_of_item(ccx, it); + #debug["item_iface(it.id=%d, tpt.ty=%s)", + it.id, ty_to_str(tcx, tpt.ty)]; + write_ty_to_tcx(tcx, it.id, tpt.ty); + ensure_iface_methods(ccx, it.id); + } + ast::item_class(tps, ifaces, members, ctor, m_dtor, rp) { + // Write the class type + let tpt = ty_of_item(ccx, it); + write_ty_to_tcx(tcx, it.id, tpt.ty); + // Write the ctor type + let t_ctor = + ty::mk_fn( + tcx, + astconv::ty_of_fn_decl(ccx, + empty_rscope, + ast::proto_any, + ctor.node.dec, + none)); + write_ty_to_tcx(tcx, ctor.node.id, t_ctor); + tcx.tcache.insert(local_def(ctor.node.id), + {bounds: tpt.bounds, + rp: ast::rp_none, + ty: t_ctor}); + option::iter(m_dtor) {|dtor| + // Write the dtor type + let t_dtor = ty::mk_fn( + tcx, + // not sure about empty_rscope + // FIXME + astconv::ty_of_fn_decl(ccx, + empty_rscope, + ast::proto_any, + ast_util::dtor_dec(), + none)); + write_ty_to_tcx(tcx, dtor.node.id, t_dtor); + tcx.tcache.insert(local_def(dtor.node.id), + {bounds: tpt.bounds, + rp: ast::rp_none, + ty: t_dtor}); + }; + ensure_iface_methods(ccx, it.id); + /* FIXME: check for proper public/privateness */ + // Write the type of each of the members + let (fields, methods) = split_class_items(members); + for fields.each {|f| + convert_class_item(ccx, rp, f); + } + // The selfty is just the class type + let {bounds:_, substs} = mk_substs(ccx, tps, rp); + let selfty = ty::mk_class(tcx, local_def(it.id), substs); + // Need to convert all methods so we can check internal + // references to private methods + + // NDM to TJC---I think we ought to be using bounds here, not @[]. + // But doing so causes errors later on. + convert_methods(ccx, methods, rp, @[], selfty); + + /* + Finally, check that the class really implements the ifaces + that it claims to implement. + */ + for ifaces.each { |ifce| + check_methods_against_iface(ccx, tps, rp, selfty, + ifce, methods); + let t = ty::node_id_to_type(tcx, ifce.id); + + // FIXME: This assumes classes only implement + // non-parameterized ifaces. add a test case for + // a class implementing a parameterized iface. + // -- tjc (#1726) + tcx.tcache.insert(local_def(ifce.id), no_params(t)); + } + } + _ { + // This call populates the type cache with the converted type + // of the item in passing. All we have to do here is to write + // it into the node type table. + let tpt = ty_of_item(ccx, it); + write_ty_to_tcx(tcx, it.id, tpt.ty); + } + } +} +fn convert_native(ccx: @crate_ctxt, i: @ast::native_item) { + // As above, this call populates the type table with the converted + // type of the native item. We simply write it into the node type + // table. + let tpt = ty_of_native_item(ccx, i); + alt i.node { + ast::native_item_fn(_, _) { + write_ty_to_tcx(ccx.tcx, i.id, tpt.ty); + } + } +} +fn collect_item_types(ccx: @crate_ctxt, crate: @ast::crate) { + visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ + visit_item: bind convert(ccx, _), + visit_native_item: bind convert_native(ccx, _) + with *visit::default_simple_visitor() + })); +} + +fn ty_of_method(ccx: @crate_ctxt, + m: @ast::method, + rp: ast::region_param) -> ty::method { + {ident: m.ident, + tps: astconv::ty_param_bounds(ccx, m.tps), + fty: astconv::ty_of_fn_decl(ccx, type_rscope(rp), ast::proto_bare, + m.decl, none), + purity: m.decl.purity, + vis: m.vis} +} + +fn ty_of_ty_method(self: @crate_ctxt, + m: ast::ty_method, + rp: ast::region_param) -> ty::method { + {ident: m.ident, + tps: astconv::ty_param_bounds(self, m.tps), + fty: astconv::ty_of_fn_decl(self, type_rscope(rp), ast::proto_bare, + m.decl, none), + // assume public, because this is only invoked on iface methods + purity: m.decl.purity, vis: ast::public} +} diff --git a/src/rustc/middle/typeck/vtable.rs b/src/rustc/middle/typeck/vtable.rs new file mode 100644 index 00000000000..293ea3144dc --- /dev/null +++ b/src/rustc/middle/typeck/vtable.rs @@ -0,0 +1,271 @@ +fn has_iface_bounds(tps: [ty::param_bounds]) -> bool { + vec::any(tps, {|bs| + vec::any(*bs, {|b| + alt b { ty::bound_iface(_) { true } _ { false } } + }) + }) +} + +fn lookup_vtables(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + bounds: @[ty::param_bounds], substs: ty::substs, + allow_unsafe: bool) -> vtable_res { + let tcx = fcx.ccx.tcx; + let mut result = [], i = 0u; + for substs.tps.each {|ty| + for vec::each(*bounds[i]) {|bound| + alt bound { + ty::bound_iface(i_ty) { + let i_ty = ty::subst(tcx, substs, i_ty); + result += [lookup_vtable(fcx, isc, sp, ty, i_ty, + allow_unsafe)]; + } + _ {} + } + } + i += 1u; + } + @result +} + +fn fixup_substs(fcx: @fn_ctxt, sp: span, + id: ast::def_id, substs: ty::substs) -> ty::substs { + let tcx = fcx.ccx.tcx; + // use a dummy type just to package up the substs that need fixing up + let t = ty::mk_iface(tcx, id, substs); + let t_f = fixup_ty(fcx, sp, t); + alt check ty::get(t_f).struct { + ty::ty_iface(_, substs_f) { substs_f } + } +} + +fn relate_iface_tys(fcx: @fn_ctxt, sp: span, + exp_iface_ty: ty::t, act_iface_ty: ty::t) { + demand::suptype(fcx, sp, exp_iface_ty, act_iface_ty) +} + +/* +Look up the vtable to use when treating an item of type +as if it has type +*/ +fn lookup_vtable(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + ty: ty::t, iface_ty: ty::t, allow_unsafe: bool) + -> vtable_origin { + + #debug["lookup_vtable(ty=%s, iface_ty=%s)", + fcx.ty_to_str(ty), fcx.ty_to_str(iface_ty)]; + let _i = indenter(); + + let tcx = fcx.ccx.tcx; + let (iface_id, iface_substs) = alt check ty::get(iface_ty).struct { + ty::ty_iface(did, substs) { (did, substs) } + }; + let ty = fixup_ty(fcx, sp, ty); + alt ty::get(ty).struct { + ty::ty_param(n, did) { + let mut n_bound = 0u; + for vec::each(*tcx.ty_param_bounds.get(did.node)) { |bound| + alt bound { + ty::bound_send | ty::bound_copy { /* ignore */ } + ty::bound_iface(ity) { + alt check ty::get(ity).struct { + ty::ty_iface(idid, substs) { + if iface_id == idid { + relate_iface_tys(fcx, sp, iface_ty, ity); + ret vtable_param(n, n_bound); + } + } + } + n_bound += 1u; + } + } + } + } + + ty::ty_iface(did, substs) if iface_id == did { + relate_iface_tys(fcx, sp, iface_ty, ty); + if !allow_unsafe { + for vec::each(*ty::iface_methods(tcx, did)) {|m| + if ty::type_has_self(ty::mk_fn(tcx, m.fty)) { + tcx.sess.span_err( + sp, "a boxed iface with self types may not be \ + passed as a bounded type"); + } else if (*m.tps).len() > 0u { + tcx.sess.span_err( + sp, "a boxed iface with generic methods may not \ + be passed as a bounded type"); + + } + } + } + ret vtable_iface(did, substs.tps); + } + + _ { + let mut found = []; + + for list::each(isc) {|impls| + /* For each impl in scope... */ + for vec::each(*impls) {|im| + // im = one specific impl + // find the iface that im implements (if any) + let of_ty = alt ty::impl_iface(tcx, im.did) { + some(of_ty) { of_ty } + _ { cont; } + }; + + // it must have the same id as the expected one + alt ty::get(of_ty).struct { + ty::ty_iface(id, _) if id != iface_id { cont; } + _ { /* ok */ } + } + + // check whether the type unifies with the type + // that the impl is for, and continue if not + let {substs: substs, ty: for_ty} = + impl_self_ty(fcx, im.did); + let im_bs = ty::lookup_item_type(tcx, im.did).bounds; + alt fcx.mk_subty(ty, for_ty) { + result::err(_) { cont; } + result::ok(()) { } + } + + // check that desired iface type unifies + let of_ty = ty::subst(tcx, substs, of_ty); + relate_iface_tys(fcx, sp, iface_ty, of_ty); + + // recursively process the bounds + let iface_tps = iface_substs.tps; + let substs_f = fixup_substs(fcx, sp, iface_id, substs); + connect_iface_tps(fcx, sp, substs_f.tps, + iface_tps, im.did); + let subres = lookup_vtables(fcx, isc, sp, + im_bs, substs_f, false); + found += [vtable_static(im.did, substs_f.tps, subres)]; + } + + alt found.len() { + 0u { /* fallthrough */ } + 1u { ret found[0]; } + _ { + fcx.ccx.tcx.sess.span_err( + sp, "multiple applicable methods in scope"); + ret found[0]; + } + } + } + } + } + + tcx.sess.span_fatal( + sp, "failed to find an implementation of interface " + + ty_to_str(tcx, iface_ty) + " for " + + ty_to_str(tcx, ty)); + } + +fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t { + let tcx = fcx.ccx.tcx; + alt infer::resolve_deep(fcx.infcx, ty, true) { + result::ok(new_type) { new_type } + result::err(e) { + tcx.sess.span_fatal( + sp, + #fmt["cannot determine a type \ + for this bounded type parameter: %s", + infer::fixup_err_to_str(e)]) + } + } +} + +fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t], + iface_tys: [ty::t], impl_did: ast::def_id) { + let tcx = fcx.ccx.tcx; + let ity = option::get(ty::impl_iface(tcx, impl_did)); + let iface_ty = ty::subst_tps(tcx, impl_tys, ity); + alt check ty::get(iface_ty).struct { + ty::ty_iface(_, substs) { + vec::iter2(substs.tps, iface_tys, + {|a, b| demand::suptype(fcx, sp, a, b);}); + } + } +} + +fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) { + let cx = fcx.ccx; + alt ex.node { + ast::expr_path(*) { + alt fcx.opt_node_ty_substs(ex.id) { + some(substs) { + let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); + let item_ty = ty::lookup_item_type(cx.tcx, did); + if has_iface_bounds(*item_ty.bounds) { + let impls = cx.impl_map.get(ex.id); + cx.vtable_map.insert(ex.id, lookup_vtables( + fcx, impls, ex.span, + item_ty.bounds, substs, false)); + } + } + _ {} + } + } + // Must resolve bounds on methods with bounded params + ast::expr_field(*) | ast::expr_binary(*) | + ast::expr_unary(*) | ast::expr_assign_op(*) | + ast::expr_index(*) { + alt cx.method_map.find(ex.id) { + some(method_static(did)) { + let bounds = ty::lookup_item_type(cx.tcx, did).bounds; + if has_iface_bounds(*bounds) { + let callee_id = alt ex.node { + ast::expr_field(_, _, _) { ex.id } + _ { ast_util::op_expr_callee_id(ex) } + }; + let substs = fcx.node_ty_substs(callee_id); + let iscs = cx.impl_map.get(ex.id); + cx.vtable_map.insert(callee_id, lookup_vtables( + fcx, iscs, ex.span, bounds, substs, false)); + } + } + _ {} + } + } + ast::expr_cast(src, _) { + let target_ty = fcx.expr_ty(ex); + alt ty::get(target_ty).struct { + ty::ty_iface(*) { + /* Casting to an interface type. + Look up all impls for the cast expr... + */ + let impls = cx.impl_map.get(ex.id); + /* + Look up vtables for the type we're casting to, + passing in the source and target type + */ + let vtable = lookup_vtable(fcx, impls, ex.span, + fcx.expr_ty(src), target_ty, + true); + /* + Map this expression to that vtable (that is: "ex has + vtable ") + */ + cx.vtable_map.insert(ex.id, @[vtable]); + } + _ {} + } + } + _ {} + } + visit::visit_expr(ex, fcx, v); +} + +// Detect points where an interface-bounded type parameter is +// instantiated, resolve the impls for the parameters. +fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) { + visit::visit_block(bl, fcx, visit::mk_vt(@{ + visit_expr: resolve_expr, + visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt, + _v: visit::vt<@fn_ctxt>) {} + with *visit::default_visitor() + })); +} + + diff --git a/src/rustc/middle/typeck/writeback.rs b/src/rustc/middle/typeck/writeback.rs new file mode 100644 index 00000000000..914c4787925 --- /dev/null +++ b/src/rustc/middle/typeck/writeback.rs @@ -0,0 +1,184 @@ +// Type resolution: the phase that finds all the types in the AST with +// unresolved type variables and replaces "ty_var" types with their +// substitutions. +export resolve_type_vars_in_fn; +export resolve_type_vars_in_expr; + +fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) -> + option { + if !ty::type_needs_infer(typ) { ret some(typ); } + alt infer::resolve_deep(fcx.infcx, typ, true) { + result::ok(new_type) { ret some(new_type); } + result::err(e) { + if !fcx.ccx.tcx.sess.has_errors() { + fcx.ccx.tcx.sess.span_err( + sp, + #fmt["cannot determine a type \ + for this expression: %s", + infer::fixup_err_to_str(e)]) + } + ret none; + } + } +} +fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id) + -> option { + let fcx = wbcx.fcx, tcx = fcx.ccx.tcx; + let n_ty = fcx.node_ty(id); + alt resolve_type_vars_in_type(fcx, sp, n_ty) { + none { + wbcx.success = false; + ret none; + } + + some(t) { + #debug["resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)", + id, ty_to_str(tcx, n_ty), ty_to_str(tcx, t)]; + write_ty_to_tcx(tcx, id, t); + alt fcx.opt_node_ty_substs(id) { + some(substs) { + let mut new_tps = []; + for substs.tps.each {|subst| + alt resolve_type_vars_in_type(fcx, sp, subst) { + some(t) { new_tps += [t]; } + none { wbcx.success = false; ret none; } + } + } + write_substs_to_tcx(tcx, id, new_tps); + } + none {} + } + ret some(t); + } + } +} + +fn maybe_resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, + id: ast::node_id) + -> option { + if wbcx.fcx.node_types.contains_key(id as uint) { + resolve_type_vars_for_node(wbcx, sp, id) + } else { + none + } +} + +type wb_ctxt = + // As soon as we hit an error we have to stop resolving + // the entire function + {fcx: @fn_ctxt, mut success: bool}; +type wb_vt = visit::vt; + +fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s)); + visit::visit_stmt(s, wbcx, v); +} +fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, e.span, e.id); + alt e.node { + ast::expr_fn(_, decl, _, _) | + ast::expr_fn_block(decl, _, _) { + vec::iter(decl.inputs) {|input| + let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); + + // Just in case we never constrained the mode to anything, + // constrain it to the default for the type in question. + alt (r_ty, input.mode) { + (some(t), ast::infer(_)) { + let tcx = wbcx.fcx.ccx.tcx; + let m_def = ty::default_arg_mode_for_ty(t); + ty::set_default_mode(tcx, input.mode, m_def); + } + _ {} + } + } + } + + ast::expr_new(_, alloc_id, _) { + resolve_type_vars_for_node(wbcx, e.span, alloc_id); + } + + ast::expr_binary(_, _, _) | ast::expr_unary(_, _) | + ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) { + maybe_resolve_type_vars_for_node(wbcx, e.span, + ast_util::op_expr_callee_id(e)); + } + + _ { } + } + visit::visit_expr(e, wbcx, v); +} +fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, b.span, b.node.id); + visit::visit_block(b, wbcx, v); +} +fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, p.span, p.id); + #debug["Type for pattern binding %s (id %d) resolved to %s", + pat_to_str(p), p.id, + wbcx.fcx.ty_to_str( + ty::node_id_to_type(wbcx.fcx.ccx.tcx, + p.id))]; + visit::visit_pat(p, wbcx, v); +} +fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); + alt infer::resolve_deep_var(wbcx.fcx.infcx, var_id, true) { + result::ok(lty) { + #debug["Type for local %s (id %d) resolved to %s", + pat_to_str(l.node.pat), l.node.id, + wbcx.fcx.ty_to_str(lty)]; + write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.node.id, lty); + } + result::err(e) { + wbcx.fcx.ccx.tcx.sess.span_err( + l.span, + #fmt["cannot determine a type \ + for this local variable: %s", + infer::fixup_err_to_str(e)]); + wbcx.success = false; + } + } + visit::visit_local(l, wbcx, v); +} +fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) { + // Ignore items +} + +fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool { + let wbcx = {fcx: fcx, mut success: true}; + let visit = + visit::mk_vt(@{visit_item: visit_item, + visit_stmt: visit_stmt, + visit_expr: visit_expr, + visit_block: visit_block, + visit_pat: visit_pat, + visit_local: visit_local + with *visit::default_visitor()}); + visit.visit_expr(e, wbcx, visit); + ret wbcx.success; +} + +fn resolve_type_vars_in_fn(fcx: @fn_ctxt, + decl: ast::fn_decl, + blk: ast::blk) -> bool { + let wbcx = {fcx: fcx, mut success: true}; + let visit = + visit::mk_vt(@{visit_item: visit_item, + visit_stmt: visit_stmt, + visit_expr: visit_expr, + visit_block: visit_block, + visit_pat: visit_pat, + visit_local: visit_local + with *visit::default_visitor()}); + visit.visit_block(blk, wbcx, visit); + for decl.inputs.each {|arg| + resolve_type_vars_for_node(wbcx, arg.ty.span, arg.id); + } + ret wbcx.success; +} diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 378091da2e9..a1ae3943998 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -52,7 +52,12 @@ mod middle { mod infer; mod ast_map; mod resolve; - mod typeck; + mod typeck { + mod astconv; + mod collect; + mod vtable; + mod writeback; + } mod check_loop; mod check_alt; mod check_const;