#[warn(deprecated_mode)]; #[warn(deprecated_pattern)]; import std::{map, smallintmap}; import result::result; import std::map::hashmap; import driver::session; import session::session; import syntax::{ast, ast_map}; import syntax::ast_util; import syntax::ast_util::{is_local, local_def, new_def_hash}; import syntax::codemap::span; import metadata::csearch; import util::ppaux::{region_to_str, explain_region, vstore_to_str}; import middle::lint; import middle::lint::{get_lint_level, allow}; import syntax::ast::*; import syntax::print::pprust::*; import util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str}; export tv_vid, tvi_vid, region_vid, vid; export br_hashmap; export is_instantiable; export node_id_to_type; export node_id_to_type_params; export arg; export args_eq; export block_ty; export class_items_as_fields, class_items_as_mutable_fields; export ctxt; export deref, deref_sty; export index, index_sty; export def_has_ty_params; export expr_has_ty_params; export expr_ty; export expr_ty_params_and_ty; export expr_is_lval; export field_ty; export fold_ty, fold_sty_to_ty, fold_region, fold_regions; export fold_regions_and_ty, walk_regions_and_ty; export field; export field_idx; export get_field; export get_fields; export get_element_type; export has_dtor; export is_binopable; export is_pred_ty; export lookup_class_field, lookup_class_fields; export lookup_class_method_by_name; export lookup_field_type; export lookup_item_type; export lookup_public_fields; export method; export method_idx; export mk_class; export mk_ctxt; export mk_with_id, type_def_id; export mt; export node_type_table; export pat_ty; export sequence_element_type; export stmt_node_id; export sty; export subst, subst_tps, substs_is_noop, substs_to_str, substs; export t; export new_ty_hash; export enum_variants, substd_enum_variants, enum_is_univariant; export trait_methods, store_trait_methods, impl_traits; export enum_variant_with_id; export ty_dtor; export ty_param_bounds_and_ty; export ty_bool, mk_bool, type_is_bool; export ty_bot, mk_bot, type_is_bot; export ty_box, mk_box, mk_imm_box, type_is_box, type_is_boxed; export ty_opaque_closure_ptr, mk_opaque_closure_ptr; export ty_opaque_box, mk_opaque_box; export ty_float, mk_float, mk_mach_float, type_is_fp; export ty_fn, fn_ty, mk_fn; export ty_fn_proto, ty_fn_ret, ty_fn_ret_style, tys_in_fn_ty; export ty_int, mk_int, mk_mach_int, mk_char; export mk_i8, mk_u8, mk_i16, mk_u16, mk_i32, mk_u32, mk_i64, mk_u64; export ty_estr, mk_estr, type_is_str; export ty_evec, mk_evec, type_is_vec; export ty_unboxed_vec, mk_unboxed_vec, mk_mut_unboxed_vec; export vstore, vstore_fixed, vstore_uniq, vstore_box, vstore_slice; export ty_nil, mk_nil, type_is_nil; export ty_trait, mk_trait; export ty_param, mk_param, ty_params_to_tys; export ty_ptr, mk_ptr, mk_mut_ptr, mk_imm_ptr, mk_nil_ptr, type_is_unsafe_ptr; export ty_rptr, mk_rptr, mk_mut_rptr, mk_imm_rptr; export ty_rec, mk_rec; export ty_enum, mk_enum, type_is_enum; export ty_tup, mk_tup; export ty_type, mk_type; export ty_uint, mk_uint, mk_mach_uint; export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box; export ty_var, mk_var, type_is_var; export ty_var_integral, mk_var_integral, type_is_var_integral; export ty_self, mk_self, type_has_self; export ty_class; export region, bound_region, encl_region; export re_bound, re_free, re_scope, re_static, re_var; export br_self, br_anon, br_named, br_cap_avoid; export get, type_has_params, type_needs_infer, type_has_regions; export type_is_region_ptr; export type_id; export tbox_has_flag; export ty_var_id; export ty_to_def_id; export ty_fn_args; export ty_region; export kind, kind_implicitly_copyable, kind_send_copy, kind_copyable; export kind_noncopyable, kind_const; export kind_can_be_copied, kind_can_be_sent, kind_can_be_implicitly_copied; export kind_is_safe_for_default_mode; export kind_is_owned; export proto_kind, kind_lteq, type_kind; export operators; export type_err, terr_vstore_kind; export type_err_to_str; export expected_found; export type_needs_drop; export type_is_empty; export type_is_integral; export type_is_numeric; export type_is_pod; export type_is_scalar; export type_is_immediate; export type_is_sequence; export type_is_signed; export type_is_structural; export type_is_copyable; export type_is_slice; export type_is_unique; export type_is_c_like_enum; export type_structurally_contains; export type_structurally_contains_uniques; export type_autoderef, deref, deref_sty; export type_param; export type_needs_unwind_cleanup; export canon_mode; export resolved_mode; export arg_mode; export unify_mode; export set_default_mode; export variant_info; export walk_ty, maybe_walk_ty; export occurs_check; export closure_kind; export ck_block; export ck_box; export ck_uniq; export param_bound, param_bounds, bound_copy, bound_owned; export bound_send, bound_trait; export param_bounds_to_kind; export default_arg_mode_for_ty; export item_path; export item_path_str; export ast_ty_to_ty_cache_entry; export atttce_unresolved, atttce_resolved; export mach_sty; export ty_sort_str; export normalize_ty; export to_str; export borrow, serialize_borrow, deserialize_borrow; export bound_const; export terr_no_integral_type, terr_ty_param_size, terr_self_substs; export terr_in_field, terr_record_fields, terr_vstores_differ, terr_arg_count; export terr_sorts, terr_vec, terr_str, terr_record_size, terr_tuple_size; export terr_regions_does_not_outlive, terr_mutability, terr_purity_mismatch; export terr_regions_not_same, terr_regions_no_overlap; export terr_proto_mismatch; export terr_ret_style_mismatch; export terr_fn, terr_trait; export purity_to_str; export param_tys_in_type; export eval_repeat_count; export fn_proto, proto_bare, proto_vstore; export ast_proto_to_proto; export is_blockish; export method_call_bounds; export hash_region; // Data types // Note: after typeck, you should use resolved_mode() to convert this mode // into an rmode, which will take into account the results of mode inference. type arg = {mode: ast::mode, ty: t}; type field = {ident: ast::ident, mt: mt}; type param_bounds = @~[param_bound]; type method = {ident: ast::ident, tps: @~[param_bounds], fty: fn_ty, self_ty: ast::self_ty_, purity: ast::purity, vis: ast::visibility}; type mt = {ty: t, mutbl: ast::mutability}; enum vstore { vstore_fixed(uint), vstore_uniq, vstore_box, vstore_slice(region) } type field_ty = { ident: ident, id: def_id, vis: ast::visibility, mutability: ast::class_mutability }; // Contains information needed to resolve types and (in the future) look up // the types of AST nodes. type creader_cache = hashmap<{cnum: int, pos: uint, len: uint}, t>; type intern_key = {struct: sty, o_def_id: option}; enum ast_ty_to_ty_cache_entry { atttce_unresolved, /* not resolved yet */ atttce_resolved(t) /* resolved to a type, irrespective of region */ } // N.B.: Borrows from inlined content are not accurately deserialized. This // is because we don't need the details in trans, we only care if there is an // entry in the table or not. type borrow = { region: ty::region, mutbl: ast::mutability }; type ctxt = @{diag: syntax::diagnostic::span_handler, interner: hashmap, mut next_id: uint, vecs_implicitly_copyable: bool, cstore: metadata::cstore::cstore, sess: session::session, def_map: resolve3::DefMap, region_map: middle::region::region_map, region_paramd_items: middle::region::region_paramd_items, // Stores the types for various nodes in the AST. Note that this table // is not guaranteed to be populated until after typeck. See // typeck::check::fn_ctxt for details. node_types: node_type_table, // Stores the type parameters which were substituted to obtain the type // of this node. This only applies to nodes that refer to entities // parameterized by type parameters, such as generic fns, types, or // other items. node_type_substs: hashmap, items: ast_map::map, intrinsic_defs: hashmap, freevars: freevars::freevar_map, tcache: type_cache, rcache: creader_cache, ccache: constness_cache, short_names_cache: hashmap, needs_drop_cache: hashmap, needs_unwind_cleanup_cache: hashmap, kind_cache: hashmap, ast_ty_to_ty_cache: hashmap<@ast::ty, ast_ty_to_ty_cache_entry>, enum_var_cache: hashmap, trait_method_cache: hashmap, ty_param_bounds: hashmap, inferred_modes: hashmap, // maps the id of borrowed expr to scope of borrowed ptr borrowings: hashmap, normalized_cache: hashmap}; enum tbox_flag { has_params = 1, has_self = 2, needs_infer = 4, has_regions = 8, // a meta-flag: subst may be required if the type has parameters, a self // type, or references bound regions needs_subst = 1 | 2 | 8 } type t_box = @{struct: sty, id: uint, flags: uint, o_def_id: option}; // To reduce refcounting cost, we're representing types as unsafe pointers // throughout the compiler. These are simply casted t_box values. Use ty::get // to cast them back to a box. (Without the cast, compiler performance suffers // ~15%.) This does mean that a t value relies on the ctxt to keep its box // alive, and using ty::get is unsafe when the ctxt is no longer alive. enum t_opaque {} type t = *t_opaque; pure fn get(t: t) -> t_box unsafe { let t2 = unsafe::reinterpret_cast::(t); let t3 = t2; unsafe::forget(t2); t3 } pure fn tbox_has_flag(tb: t_box, flag: tbox_flag) -> bool { (tb.flags & (flag as uint)) != 0u } pure fn type_has_params(t: t) -> bool { tbox_has_flag(get(t), has_params) } pure fn type_has_self(t: t) -> bool { tbox_has_flag(get(t), has_self) } pure fn type_needs_infer(t: t) -> bool { tbox_has_flag(get(t), needs_infer) } pure fn type_has_regions(t: t) -> bool { tbox_has_flag(get(t), has_regions) } pure fn type_def_id(t: t) -> option { get(t).o_def_id } pure fn type_id(t: t) -> uint { get(t).id } enum closure_kind { ck_block, ck_box, ck_uniq, } enum fn_proto { proto_bare, // supertype of all other protocols proto_vstore(vstore) } /// Innards of a function type: /// /// - `purity` is the function's effect (pure, impure, unsafe). /// - `proto` is the protocol (fn@, fn~, etc). /// - `bound` is the parameter bounds on the function's upvars. /// - `inputs` is the list of arguments and their modes. /// - `output` is the return type. /// - `ret_style` indicates whether the function returns a value or fails. type fn_ty = {purity: ast::purity, proto: fn_proto, bounds: @~[param_bound], inputs: ~[arg], output: t, ret_style: ret_style}; type param_ty = {idx: uint, def_id: def_id}; /// Representation of regions: enum region { /// Bound regions are found (primarily) in function types. They indicate /// region parameters that have yet to be replaced with actual regions /// (analogous to type parameters, except that due to the monomorphic /// nature of our type system, bound type parameters are always replaced /// with fresh type variables whenever an item is referenced, so type /// parameters only appear "free" in types. Regions in contrast can /// appear free or bound.). When a function is called, all bound regions /// tied to that function's node-id are replaced with fresh region /// variables whose value is then inferred. re_bound(bound_region), /// When checking a function body, the types of all arguments and so forth /// that refer to bound region parameters are modified to refer to free /// region parameters. re_free(node_id, bound_region), /// A concrete region naming some expression within the current function. re_scope(node_id), /// Static data that has an "infinite" lifetime. re_static, /// A region variable. Should not exist after typeck. re_var(region_vid) } enum bound_region { /// The self region for classes, impls (&T in a type defn or &self/T) br_self, /// An anonymous region parameter for a given fn (&T) br_anon(uint), /// Named region parameters for functions (a in &a/T) br_named(ast::ident), /// Handles capture-avoiding substitution in a rather subtle case. If you /// have a closure whose argument types are being inferred based on the /// expected type, and the expected type includes bound regions, then we /// will wrap those bound regions in a br_cap_avoid() with the id of the /// fn expression. This ensures that the names are not "captured" by the /// enclosing scope, which may define the same names. For an example of /// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs /// and regions-ret-borrowed-1.rs. br_cap_avoid(ast::node_id, @bound_region), } type opt_region = option; /// The type substs represents the kinds of things that can be substituted to /// convert a polytype into a monotype. Note however that substituting bound /// regions other than `self` is done through a different mechanism. /// /// `tps` represents the type parameters in scope. They are indexed according /// to the order in which they were declared. /// /// `self_r` indicates the region parameter `self` that is present on nominal /// types (enums, classes) declared as having a region parameter. `self_r` /// should always be none for types that are not region-parameterized and /// some(_) for types that are. The only bound region parameter that should /// appear within a region-parameterized type is `self`. /// /// `self_ty` is the type to which `self` should be remapped, if any. The /// `self` type is rather funny in that it can only appear on traits and /// is always substituted away to the implementing type for a trait. type substs = { self_r: opt_region, self_ty: option, tps: ~[t] }; // NB: If you change this, you'll probably want to change the corresponding // AST structure in libsyntax/ast.rs as well. enum sty { ty_nil, ty_bot, ty_bool, ty_int(ast::int_ty), ty_uint(ast::uint_ty), ty_float(ast::float_ty), ty_estr(vstore), ty_enum(def_id, substs), ty_box(mt), ty_uniq(mt), ty_evec(mt, vstore), ty_ptr(mt), ty_rptr(region, mt), ty_rec(~[field]), ty_fn(fn_ty), ty_trait(def_id, substs, vstore), ty_class(def_id, substs), ty_tup(~[t]), ty_var(tv_vid), // type variable during typechecking ty_var_integral(tvi_vid), // type variable during typechecking, for // integral types only ty_param(param_ty), // type parameter ty_self, // special, implicit `self` type parameter // "Fake" types, used for trans purposes ty_type, // type_desc* ty_opaque_box, // used by monomorphizer to represent any @ box ty_opaque_closure_ptr(closure_kind), // ptr to env for fn, fn@, fn~ ty_unboxed_vec(mt), } enum terr_vstore_kind { terr_vec, terr_str, terr_fn, terr_trait } struct expected_found { expected: T; found: T; } // Data structures used in type unification enum type_err { terr_mismatch, terr_ret_style_mismatch(expected_found), terr_purity_mismatch(expected_found), terr_mutability, terr_proto_mismatch(expected_found), terr_box_mutability, terr_ptr_mutability, terr_ref_mutability, terr_vec_mutability, terr_tuple_size(expected_found), terr_ty_param_size(expected_found), terr_record_size(expected_found), terr_record_mutability, terr_record_fields(expected_found), terr_arg_count, terr_mode_mismatch(expected_found), terr_regions_does_not_outlive(region, region), terr_regions_not_same(region, region), terr_regions_no_overlap(region, region), terr_vstores_differ(terr_vstore_kind, expected_found), terr_in_field(@type_err, ast::ident), terr_sorts(expected_found), terr_self_substs, terr_no_integral_type, } enum param_bound { bound_copy, bound_owned, bound_send, bound_const, bound_trait(t), } enum tv_vid = uint; enum tvi_vid = uint; enum region_vid = uint; trait vid { pure fn to_uint() -> uint; pure fn to_str() -> ~str; } impl tv_vid: vid { pure fn to_uint() -> uint { *self } pure fn to_str() -> ~str { fmt!{"", self.to_uint()} } } impl tvi_vid: vid { pure fn to_uint() -> uint { *self } pure fn to_str() -> ~str { fmt!{"", self.to_uint()} } } impl region_vid: vid { pure fn to_uint() -> uint { *self } pure fn to_str() -> ~str { fmt!{"%?", self} } } trait purity_to_str { pure fn to_str() -> ~str; } impl purity: purity_to_str { pure fn to_str() -> ~str { purity_to_str(self) } } fn param_bounds_to_kind(bounds: param_bounds) -> kind { let mut kind = kind_noncopyable(); for vec::each(*bounds) |bound| { match bound { bound_copy => { kind = raise_kind(kind, kind_implicitly_copyable()); } bound_owned => { kind = raise_kind(kind, kind_owned()); } bound_send => { kind = raise_kind(kind, kind_send_only() | kind_owned()); } bound_const => { kind = raise_kind(kind, kind_const()); } bound_trait(_) => () } } kind } /// A polytype. /// /// - `bounds`: The list of bounds for each type parameter. The length of the /// list also tells you how many type parameters there are. /// /// - `rp`: true if the type is region-parameterized. Types can have at /// most one region parameter, always called `&self`. /// /// - `ty`: the base type. May have reference to the (unsubstituted) bound /// region `&self` or to (unsubstituted) ty_param types type ty_param_bounds_and_ty = {bounds: @~[param_bounds], rp: bool, ty: t}; type type_cache = hashmap; type constness_cache = hashmap; type node_type_table = @smallintmap::smallintmap; fn mk_rcache() -> creader_cache { type val = {cnum: int, pos: uint, len: uint}; pure fn hash_cache_entry(k: &val) -> uint { (k.cnum as uint) + k.pos + k.len } pure fn eq_cache_entries(a: &val, b: &val) -> bool { a.cnum == b.cnum && a.pos == b.pos && a.len == b.len } return map::hashmap(hash_cache_entry, eq_cache_entries); } fn new_ty_hash() -> map::hashmap { map::hashmap(|t: &t| type_id(*t), |a: &t, b: &t| type_id(*a) == type_id(*b)) } fn mk_ctxt(s: session::session, dm: resolve3::DefMap, amap: ast_map::map, freevars: freevars::freevar_map, region_map: middle::region::region_map, region_paramd_items: middle::region::region_paramd_items) -> ctxt { pure fn hash_intern_key(k: &intern_key) -> uint { hash_type_structure(&k.struct) + option::map_default(k.o_def_id, 0u, |d| ast_util::hash_def(&d)) } let interner = map::hashmap(hash_intern_key, sys::shape_eq); let vecs_implicitly_copyable = get_lint_level(s.lint_settings.default_settings, lint::vecs_implicitly_copyable) == allow; @{diag: s.diagnostic(), interner: interner, mut next_id: 0u, vecs_implicitly_copyable: vecs_implicitly_copyable, cstore: s.cstore, sess: s, def_map: dm, region_map: region_map, region_paramd_items: region_paramd_items, node_types: @smallintmap::mk(), node_type_substs: map::int_hash(), items: amap, intrinsic_defs: map::uint_hash(), freevars: freevars, tcache: ast_util::new_def_hash(), rcache: mk_rcache(), ccache: ast_util::new_def_hash(), short_names_cache: new_ty_hash(), needs_drop_cache: new_ty_hash(), needs_unwind_cleanup_cache: new_ty_hash(), kind_cache: new_ty_hash(), ast_ty_to_ty_cache: map::hashmap( ast_util::hash_ty, ast_util::eq_ty), enum_var_cache: new_def_hash(), trait_method_cache: new_def_hash(), ty_param_bounds: map::int_hash(), inferred_modes: map::int_hash(), borrowings: map::int_hash(), normalized_cache: new_ty_hash()} } // Type constructors fn mk_t(cx: ctxt, +st: sty) -> t { mk_t_with_id(cx, st, none) } // Interns a type/name combination, stores the resulting box in cx.interner, // and returns the box as cast to an unsafe ptr (see comments for t above). fn mk_t_with_id(cx: ctxt, +st: sty, o_def_id: option) -> t { let key = {struct: st, o_def_id: o_def_id}; match cx.interner.find(key) { some(t) => unsafe { return unsafe::reinterpret_cast(t); }, _ => () } let mut flags = 0u; fn rflags(r: region) -> uint { (has_regions as uint) | { match r { ty::re_var(_) => needs_infer as uint, _ => 0u } } } fn sflags(substs: &substs) -> uint { let mut f = 0u; for substs.tps.each |tt| { f |= get(tt).flags; } substs.self_r.iter(|r| f |= rflags(r)); return f; } match st { ty_estr(vstore_slice(r)) => { flags |= rflags(r); } ty_evec(mt, vstore_slice(r)) => { flags |= rflags(r); flags |= get(mt.ty).flags; } ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box => (), ty_param(_) => flags |= has_params as uint, ty_var(_) | ty_var_integral(_) => flags |= needs_infer as uint, ty_self => flags |= has_self as uint, ty_enum(_, ref substs) | ty_class(_, ref substs) | ty_trait(_, ref substs, _) => { flags |= sflags(substs); } ty_box(m) | ty_uniq(m) | ty_evec(m, _) | ty_ptr(m) | ty_unboxed_vec(m) => { flags |= get(m.ty).flags; } ty_rptr(r, m) => { flags |= rflags(r); flags |= get(m.ty).flags; } ty_rec(flds) => for flds.each |f| { flags |= get(f.mt.ty).flags; }, ty_tup(ts) => for ts.each |tt| { flags |= get(tt).flags; }, ty_fn(ref f) => { match f.proto { ty::proto_vstore(vstore_slice(r)) => flags |= rflags(r), ty::proto_bare | ty::proto_vstore(_) => {} } for f.inputs.each |a| { flags |= get(a.ty).flags; } flags |= get(f.output).flags; } } let t = @{struct: st, id: cx.next_id, flags: flags, o_def_id: o_def_id}; cx.interner.insert(key, t); cx.next_id += 1u; unsafe { unsafe::reinterpret_cast(t) } } fn mk_nil(cx: ctxt) -> t { mk_t(cx, ty_nil) } fn mk_bot(cx: ctxt) -> t { mk_t(cx, ty_bot) } fn mk_bool(cx: ctxt) -> t { mk_t(cx, ty_bool) } fn mk_int(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i)) } fn mk_i8(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i8)) } fn mk_i16(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i16)) } fn mk_i32(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i32)) } fn mk_i64(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i64)) } fn mk_float(cx: ctxt) -> t { mk_t(cx, ty_float(ast::ty_f)) } fn mk_uint(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u)) } fn mk_u8(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u8)) } fn mk_u16(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u16)) } fn mk_u32(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u32)) } fn mk_u64(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u64)) } fn mk_mach_int(cx: ctxt, tm: ast::int_ty) -> t { mk_t(cx, ty_int(tm)) } fn mk_mach_uint(cx: ctxt, tm: ast::uint_ty) -> t { mk_t(cx, ty_uint(tm)) } fn mk_mach_float(cx: ctxt, tm: ast::float_ty) -> t { mk_t(cx, ty_float(tm)) } fn mk_char(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_char)) } fn mk_estr(cx: ctxt, t: vstore) -> t { mk_t(cx, ty_estr(t)) } fn mk_enum(cx: ctxt, did: ast::def_id, +substs: substs) -> t { // take a copy of substs so that we own the vectors inside mk_t(cx, ty_enum(did, substs)) } fn mk_box(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_box(tm)) } fn mk_imm_box(cx: ctxt, ty: t) -> t { mk_box(cx, {ty: ty, mutbl: ast::m_imm}) } fn mk_uniq(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_uniq(tm)) } fn mk_imm_uniq(cx: ctxt, ty: t) -> t { mk_uniq(cx, {ty: ty, mutbl: ast::m_imm}) } fn mk_ptr(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_ptr(tm)) } fn mk_rptr(cx: ctxt, r: region, tm: mt) -> t { mk_t(cx, ty_rptr(r, tm)) } fn mk_mut_rptr(cx: ctxt, r: region, ty: t) -> t { mk_rptr(cx, r, {ty: ty, mutbl: ast::m_mutbl}) } fn mk_imm_rptr(cx: ctxt, r: region, ty: t) -> t { mk_rptr(cx, r, {ty: ty, mutbl: ast::m_imm}) } fn mk_mut_ptr(cx: ctxt, ty: t) -> t { mk_ptr(cx, {ty: ty, mutbl: ast::m_mutbl}) } fn mk_imm_ptr(cx: ctxt, ty: t) -> t { mk_ptr(cx, {ty: ty, mutbl: ast::m_imm}) } fn mk_nil_ptr(cx: ctxt) -> t { mk_ptr(cx, {ty: mk_nil(cx), mutbl: ast::m_imm}) } fn mk_evec(cx: ctxt, tm: mt, t: vstore) -> t { mk_t(cx, ty_evec(tm, t)) } fn mk_unboxed_vec(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_unboxed_vec(tm)) } fn mk_mut_unboxed_vec(cx: ctxt, ty: t) -> t { mk_t(cx, ty_unboxed_vec({ty: ty, mutbl: ast::m_imm})) } fn mk_rec(cx: ctxt, fs: ~[field]) -> t { mk_t(cx, ty_rec(fs)) } fn mk_tup(cx: ctxt, ts: ~[t]) -> t { mk_t(cx, ty_tup(ts)) } // take a copy because we want to own the various vectors inside fn mk_fn(cx: ctxt, +fty: fn_ty) -> t { mk_t(cx, ty_fn(fty)) } fn mk_trait(cx: ctxt, did: ast::def_id, +substs: substs, vstore: vstore) -> t { // take a copy of substs so that we own the vectors inside mk_t(cx, ty_trait(did, substs, vstore)) } fn mk_class(cx: ctxt, class_id: ast::def_id, +substs: substs) -> t { // take a copy of substs so that we own the vectors inside mk_t(cx, ty_class(class_id, substs)) } fn mk_var(cx: ctxt, v: tv_vid) -> t { mk_t(cx, ty_var(v)) } fn mk_var_integral(cx: ctxt, v: tvi_vid) -> t { mk_t(cx, ty_var_integral(v)) } fn mk_self(cx: ctxt) -> t { mk_t(cx, ty_self) } fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { mk_t(cx, ty_param({idx: n, def_id: k})) } fn mk_type(cx: ctxt) -> t { mk_t(cx, ty_type) } fn mk_opaque_closure_ptr(cx: ctxt, ck: closure_kind) -> t { mk_t(cx, ty_opaque_closure_ptr(ck)) } fn mk_opaque_box(cx: ctxt) -> t { mk_t(cx, ty_opaque_box) } fn mk_with_id(cx: ctxt, base: t, def_id: ast::def_id) -> t { mk_t_with_id(cx, get(base).struct, some(def_id)) } // Converts s to its machine type equivalent pure fn mach_sty(cfg: @session::config, t: t) -> sty { match get(t).struct { ty_int(ast::ty_i) => ty_int(cfg.int_type), ty_uint(ast::ty_u) => ty_uint(cfg.uint_type), ty_float(ast::ty_f) => ty_float(cfg.float_type), s => s } } fn default_arg_mode_for_ty(ty: ty::t) -> ast::rmode { if ty::type_is_immediate(ty) { ast::by_val } else { ast::by_ref } } // Returns the narrowest lifetime enclosing the evaluation of the expression // with id `id`. fn encl_region(cx: ctxt, id: ast::node_id) -> ty::region { match cx.region_map.find(id) { some(encl_scope) => ty::re_scope(encl_scope), none => ty::re_static } } fn walk_ty(ty: t, f: fn(t)) { maybe_walk_ty(ty, |t| { f(t); true }); } fn maybe_walk_ty(ty: t, f: fn(t) -> bool) { if !f(ty) { return; } match get(ty).struct { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_estr(_) | ty_type | ty_opaque_box | ty_self | ty_opaque_closure_ptr(_) | ty_var(_) | ty_var_integral(_) | ty_param(_) => { } ty_box(tm) | ty_evec(tm, _) | ty_unboxed_vec(tm) | ty_ptr(tm) | ty_rptr(_, tm) => { maybe_walk_ty(tm.ty, f); } ty_enum(_, substs) | ty_class(_, substs) | ty_trait(_, substs, _) => { for substs.tps.each |subty| { maybe_walk_ty(subty, f); } } ty_rec(fields) => { for fields.each |fl| { maybe_walk_ty(fl.mt.ty, f); } } ty_tup(ts) => { for ts.each |tt| { maybe_walk_ty(tt, f); } } ty_fn(ref ft) => { for ft.inputs.each |a| { maybe_walk_ty(a.ty, f); } maybe_walk_ty(ft.output, f); } ty_uniq(tm) => { maybe_walk_ty(tm.ty, f); } } } fn fold_sty_to_ty(tcx: ty::ctxt, sty: &sty, foldop: fn(t) -> t) -> t { mk_t(tcx, fold_sty(sty, foldop)) } fn fold_sty(sty: &sty, fldop: fn(t) -> t) -> sty { fn fold_substs(substs: &substs, fldop: fn(t) -> t) -> substs { {self_r: substs.self_r, self_ty: substs.self_ty.map(|t| fldop(t)), tps: substs.tps.map(|t| fldop(t))} } match *sty { ty_box(tm) => { ty_box({ty: fldop(tm.ty), mutbl: tm.mutbl}) } ty_uniq(tm) => { ty_uniq({ty: fldop(tm.ty), mutbl: tm.mutbl}) } ty_ptr(tm) => { ty_ptr({ty: fldop(tm.ty), mutbl: tm.mutbl}) } ty_unboxed_vec(tm) => { ty_unboxed_vec({ty: fldop(tm.ty), mutbl: tm.mutbl}) } ty_evec(tm, vst) => { ty_evec({ty: fldop(tm.ty), mutbl: tm.mutbl}, vst) } ty_enum(tid, ref substs) => { ty_enum(tid, fold_substs(substs, fldop)) } ty_trait(did, ref substs, vst) => { ty_trait(did, fold_substs(substs, fldop), vst) } ty_rec(fields) => { let new_fields = do vec::map(fields) |fl| { let new_ty = fldop(fl.mt.ty); let new_mt = {ty: new_ty, mutbl: fl.mt.mutbl}; {ident: fl.ident, mt: new_mt} }; ty_rec(new_fields) } ty_tup(ts) => { let new_ts = vec::map(ts, |tt| fldop(tt)); ty_tup(new_ts) } ty_fn(ref f) => { let new_args = vec::map(f.inputs, |a| { let new_ty = fldop(a.ty); {mode: a.mode, ty: new_ty} }); let new_output = fldop(f.output); ty_fn({inputs: new_args, output: new_output with *f}) } ty_rptr(r, tm) => { ty_rptr(r, {ty: fldop(tm.ty), mutbl: tm.mutbl}) } ty_class(did, ref substs) => { ty_class(did, fold_substs(substs, fldop)) } ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_estr(_) | ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box | ty_var(_) | ty_var_integral(_) | ty_param(*) | ty_self => { *sty } } } // Folds types from the bottom up. fn fold_ty(cx: ctxt, t0: t, fldop: fn(t) -> t) -> t { let sty = fold_sty(&get(t0).struct, |t| fold_ty(cx, fldop(t), fldop)); fldop(mk_t(cx, sty)) } fn walk_regions_and_ty( cx: ctxt, ty: t, walkr: fn(r: region), walkt: fn(t: t) -> bool) { if (walkt(ty)) { fold_regions_and_ty( cx, ty, |r| { walkr(r); r }, |t| { walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t }, |t| { walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t }); } } fn fold_regions_and_ty( cx: ctxt, ty: t, fldr: fn(r: region) -> region, fldfnt: fn(t: t) -> t, fldt: fn(t: t) -> t) -> t { fn fold_substs( substs: &substs, fldr: fn(r: region) -> region, fldt: fn(t: t) -> t) -> substs { {self_r: substs.self_r.map(|r| fldr(r)), self_ty: substs.self_ty.map(|t| fldt(t)), tps: substs.tps.map(|t| fldt(t))} } let tb = ty::get(ty); match tb.struct { ty::ty_rptr(r, mt) => { let m_r = fldr(r); let m_t = fldt(mt.ty); ty::mk_rptr(cx, m_r, {ty: m_t, mutbl: mt.mutbl}) } ty_estr(vstore_slice(r)) => { let m_r = fldr(r); ty::mk_estr(cx, vstore_slice(m_r)) } ty_evec(mt, vstore_slice(r)) => { let m_r = fldr(r); let m_t = fldt(mt.ty); ty::mk_evec(cx, {ty: m_t, mutbl: mt.mutbl}, vstore_slice(m_r)) } ty_enum(def_id, ref substs) => { ty::mk_enum(cx, def_id, fold_substs(substs, fldr, fldt)) } ty_class(def_id, ref substs) => { ty::mk_class(cx, def_id, fold_substs(substs, fldr, fldt)) } ty_trait(def_id, ref substs, vst) => { ty::mk_trait(cx, def_id, fold_substs(substs, fldr, fldt), vst) } ref sty @ ty_fn(f) => { let new_proto; match f.proto { proto_bare => new_proto = proto_bare, proto_vstore(vstore_slice(r)) => new_proto = proto_vstore(vstore_slice(fldr(r))), proto_vstore(old_vstore) => new_proto = proto_vstore(old_vstore) } let new_args = vec::map(f.inputs, |a| { let new_ty = fldfnt(a.ty); {mode: a.mode, ty: new_ty} }); let new_output = fldfnt(f.output); ty::mk_fn(cx, { inputs: new_args, output: new_output, proto: new_proto with f }) } ref sty => { fold_sty_to_ty(cx, sty, |t| fldt(t)) } } } // n.b. this function is intended to eventually replace fold_region() below, // that is why its name is so similar. fn fold_regions( cx: ctxt, ty: t, fldr: fn(r: region, in_fn: bool) -> region) -> t { fn do_fold(cx: ctxt, ty: t, in_fn: bool, fldr: fn(region, bool) -> region) -> t { if !type_has_regions(ty) { return ty; } fold_regions_and_ty( cx, ty, |r| fldr(r, in_fn), |t| do_fold(cx, t, true, fldr), |t| do_fold(cx, t, in_fn, fldr)) } do_fold(cx, ty, false, fldr) } fn fold_region(cx: ctxt, t0: t, fldop: fn(region, bool) -> region) -> t { fn do_fold(cx: ctxt, t0: t, under_r: bool, fldop: fn(region, bool) -> region) -> t { let tb = get(t0); if !tbox_has_flag(tb, has_regions) { return t0; } match tb.struct { ty_rptr(r, {ty: t1, mutbl: m}) => { let m_r = fldop(r, under_r); let m_t1 = do_fold(cx, t1, true, fldop); ty::mk_rptr(cx, m_r, {ty: m_t1, mutbl: m}) } ty_estr(vstore_slice(r)) => { let m_r = fldop(r, under_r); ty::mk_estr(cx, vstore_slice(m_r)) } ty_evec({ty: t1, mutbl: m}, vstore_slice(r)) => { let m_r = fldop(r, under_r); let m_t1 = do_fold(cx, t1, true, fldop); ty::mk_evec(cx, {ty: m_t1, mutbl: m}, vstore_slice(m_r)) } ty_fn(_) => { // do not recurse into functions, which introduce fresh bindings t0 } ref sty => { do fold_sty_to_ty(cx, sty) |t| { do_fold(cx, t, under_r, fldop) } } } } do_fold(cx, t0, false, fldop) } // Substitute *only* type parameters. Used in trans where regions are erased. fn subst_tps(cx: ctxt, tps: &[t], typ: t) -> t { if tps.len() == 0u { return typ; } let tb = ty::get(typ); if !tbox_has_flag(tb, has_params) { return typ; } match tb.struct { ty_param(p) => tps[p.idx], ref sty => fold_sty_to_ty(cx, sty, |t| subst_tps(cx, tps, t)) } } fn substs_is_noop(substs: &substs) -> bool { substs.tps.len() == 0u && substs.self_r.is_none() && substs.self_ty.is_none() } fn substs_to_str(cx: ctxt, substs: &substs) -> ~str { fmt!{"substs(self_r=%s, self_ty=%s, tps=%?)", substs.self_r.map_default(~"none", |r| region_to_str(cx, r)), substs.self_ty.map_default(~"none", |t| ty_to_str(cx, t)), substs.tps.map(|t| ty_to_str(cx, t))} } fn subst(cx: ctxt, substs: &substs, typ: t) -> t { debug!{"subst(substs=%s, typ=%s)", substs_to_str(cx, substs), ty_to_str(cx, typ)}; if substs_is_noop(substs) { return typ; } let r = do_subst(cx, substs, typ); debug!{" r = %s", ty_to_str(cx, r)}; return r; fn do_subst(cx: ctxt, substs: &substs, typ: t) -> t { let tb = get(typ); if !tbox_has_flag(tb, needs_subst) { return typ; } match tb.struct { ty_param(p) => substs.tps[p.idx], ty_self => substs.self_ty.get(), _ => { fold_regions_and_ty( cx, typ, |r| match r { re_bound(br_self) => substs.self_r.get(), _ => r }, |t| do_subst(cx, substs, t), |t| do_subst(cx, substs, t)) } } } } // Type utilities fn type_is_nil(ty: t) -> bool { get(ty).struct == ty_nil } fn type_is_bot(ty: t) -> bool { get(ty).struct == ty_bot } fn type_is_var(ty: t) -> bool { match get(ty).struct { ty_var(_) => true, _ => false } } fn type_is_var_integral(ty: t) -> bool { match get(ty).struct { ty_var_integral(_) => true, _ => false } } fn type_is_bool(ty: t) -> bool { get(ty).struct == ty_bool } fn type_is_structural(ty: t) -> bool { match get(ty).struct { ty_rec(_) | ty_class(*) | ty_tup(_) | ty_enum(*) | ty_fn(_) | ty_trait(*) | ty_evec(_, vstore_fixed(_)) | ty_estr(vstore_fixed(_)) | ty_evec(_, vstore_slice(_)) | ty_estr(vstore_slice(_)) => true, _ => false } } fn type_is_copyable(cx: ctxt, ty: t) -> bool { return kind_can_be_copied(type_kind(cx, ty)); } fn type_is_sequence(ty: t) -> bool { match get(ty).struct { ty_estr(_) | ty_evec(_, _) => true, _ => false } } fn type_is_str(ty: t) -> bool { match get(ty).struct { ty_estr(_) => true, _ => false } } fn sequence_element_type(cx: ctxt, ty: t) -> t { match get(ty).struct { ty_estr(_) => return mk_mach_uint(cx, ast::ty_u8), ty_evec(mt, _) | ty_unboxed_vec(mt) => return mt.ty, _ => cx.sess.bug( ~"sequence_element_type called on non-sequence value"), } } fn get_element_type(ty: t, i: uint) -> t { match get(ty).struct { ty_rec(flds) => return flds[i].mt.ty, ty_tup(ts) => return ts[i], _ => fail ~"get_element_type called on invalid type" } } pure fn type_is_box(ty: t) -> bool { match get(ty).struct { ty_box(_) => return true, _ => return false } } pure fn type_is_boxed(ty: t) -> bool { match get(ty).struct { ty_box(_) | ty_opaque_box | ty_evec(_, vstore_box) | ty_estr(vstore_box) => true, _ => false } } pure fn type_is_region_ptr(ty: t) -> bool { match get(ty).struct { ty_rptr(_, _) => true, _ => false } } pure fn type_is_slice(ty: t) -> bool { match get(ty).struct { ty_evec(_, vstore_slice(_)) | ty_estr(vstore_slice(_)) => true, _ => return false } } pure fn type_is_unique_box(ty: t) -> bool { match get(ty).struct { ty_uniq(_) => return true, _ => return false } } pure fn type_is_unsafe_ptr(ty: t) -> bool { match get(ty).struct { ty_ptr(_) => return true, _ => return false } } pure fn type_is_vec(ty: t) -> bool { return match get(ty).struct { ty_evec(_, _) | ty_unboxed_vec(_) => true, ty_estr(_) => true, _ => false }; } pure fn type_is_unique(ty: t) -> bool { match get(ty).struct { ty_uniq(_) => return true, ty_evec(_, vstore_uniq) => true, ty_estr(vstore_uniq) => true, _ => return false } } /* A scalar type is one that denotes an atomic datum, with no sub-components. (A ty_ptr is scalar because it represents a non-managed pointer, so its contents are abstract to rustc.) */ pure fn type_is_scalar(ty: t) -> bool { match get(ty).struct { ty_nil | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | ty_var_integral(_) | ty_type | ty_ptr(_) => true, _ => false } } fn type_is_immediate(ty: t) -> bool { return type_is_scalar(ty) || type_is_boxed(ty) || type_is_unique(ty) || type_is_region_ptr(ty); } fn type_needs_drop(cx: ctxt, ty: t) -> bool { match cx.needs_drop_cache.find(ty) { some(result) => return result, none => {/* fall through */ } } let mut accum = false; let result = match get(ty).struct { // scalar types ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | ty_type | ty_ptr(_) | ty_rptr(_, _) | ty_estr(vstore_fixed(_)) | ty_estr(vstore_slice(_)) | ty_evec(_, vstore_slice(_)) => false, ty_evec(mt, vstore_fixed(_)) => type_needs_drop(cx, mt.ty), ty_unboxed_vec(mt) => type_needs_drop(cx, mt.ty), ty_rec(flds) => { for flds.each |f| { if type_needs_drop(cx, f.mt.ty) { accum = true; } } accum } ty_class(did, ref substs) => { // Any class with a dtor needs a drop option::is_some(ty_dtor(cx, did)) || { for vec::each(ty::class_items_as_fields(cx, did, substs)) |f| { if type_needs_drop(cx, f.mt.ty) { accum = true; } } accum } } ty_tup(elts) => { for elts.each |m| { if type_needs_drop(cx, m) { accum = true; } } accum } ty_enum(did, ref substs) => { let variants = enum_variants(cx, did); for vec::each(*variants) |variant| { for variant.args.each |aty| { // Perform any type parameter substitutions. let arg_ty = subst(cx, substs, aty); if type_needs_drop(cx, arg_ty) { accum = true; } } if accum { break; } } accum } ty_fn(ref fty) => { match fty.proto { proto_bare | proto_vstore(vstore_slice(_)) => false, _ => true } } _ => true }; cx.needs_drop_cache.insert(ty, result); return result; } // Some things don't need cleanups during unwinding because the // task can free them all at once later. Currently only things // that only contain scalars and shared boxes can avoid unwind // cleanups. fn type_needs_unwind_cleanup(cx: ctxt, ty: t) -> bool { match cx.needs_unwind_cleanup_cache.find(ty) { some(result) => return result, none => () } let tycache = new_ty_hash(); let needs_unwind_cleanup = type_needs_unwind_cleanup_(cx, ty, tycache, false); cx.needs_unwind_cleanup_cache.insert(ty, needs_unwind_cleanup); return needs_unwind_cleanup; } fn type_needs_unwind_cleanup_(cx: ctxt, ty: t, tycache: map::hashmap, encountered_box: bool) -> bool { // Prevent infinite recursion match tycache.find(ty) { some(_) => return false, none => { tycache.insert(ty, ()); } } let mut encountered_box = encountered_box; let mut needs_unwind_cleanup = false; do maybe_walk_ty(ty) |ty| { let old_encountered_box = encountered_box; let result = match get(ty).struct { ty_box(_) | ty_opaque_box => { encountered_box = true; true } ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_rec(_) | ty_tup(_) | ty_ptr(_) => { true } ty_enum(did, ref substs) => { for vec::each(*enum_variants(cx, did)) |v| { for v.args.each |aty| { let t = subst(cx, substs, aty); needs_unwind_cleanup |= type_needs_unwind_cleanup_(cx, t, tycache, encountered_box); } } !needs_unwind_cleanup } ty_uniq(_) | ty_estr(vstore_uniq) | ty_estr(vstore_box) | ty_evec(_, vstore_uniq) | ty_evec(_, vstore_box) => { // Once we're inside a box, the annihilator will find // it and destroy it. if !encountered_box { needs_unwind_cleanup = true; false } else { true } } _ => { needs_unwind_cleanup = true; false } }; encountered_box = old_encountered_box; result } return needs_unwind_cleanup; } enum kind { kind_(u32) } /// can be copied (implicitly or explicitly) const KIND_MASK_COPY : u32 = 0b000000000000000000000000001_u32; /// can be sent: no shared box, borrowed ptr (must imply OWNED) const KIND_MASK_SEND : u32 = 0b000000000000000000000000010_u32; /// is owned (no borrowed ptrs) const KIND_MASK_OWNED : u32 = 0b000000000000000000000000100_u32; /// is deeply immutable const KIND_MASK_CONST : u32 = 0b000000000000000000000001000_u32; /// can be implicitly copied (must imply COPY) const KIND_MASK_IMPLICIT : u32 = 0b000000000000000000000010000_u32; /// safe for default mode (subset of KIND_MASK_IMPLICIT) const KIND_MASK_DEFAULT_MODE : u32 = 0b000000000000000000000100000_u32; fn kind_noncopyable() -> kind { kind_(0u32) } fn kind_copyable() -> kind { kind_(KIND_MASK_COPY) } fn kind_implicitly_copyable() -> kind { kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY) } fn kind_safe_for_default_mode() -> kind { // similar to implicit copy, but always includes vectors and strings kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT | KIND_MASK_COPY) } fn kind_implicitly_sendable() -> kind { kind_(KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_SEND) } fn kind_safe_for_default_mode_send() -> kind { // similar to implicit copy, but always includes vectors and strings kind_(KIND_MASK_DEFAULT_MODE | KIND_MASK_IMPLICIT | KIND_MASK_COPY | KIND_MASK_SEND) } fn kind_send_copy() -> kind { kind_(KIND_MASK_COPY | KIND_MASK_SEND) } fn kind_send_only() -> kind { kind_(KIND_MASK_SEND) } fn kind_const() -> kind { kind_(KIND_MASK_CONST) } fn kind_owned() -> kind { kind_(KIND_MASK_OWNED) } fn kind_top() -> kind { kind_(0xffffffffu32) } fn remove_const(k: kind) -> kind { k - kind_const() } fn remove_implicit(k: kind) -> kind { k - kind_(KIND_MASK_IMPLICIT | KIND_MASK_DEFAULT_MODE) } fn remove_send(k: kind) -> kind { k - kind_(KIND_MASK_SEND) } fn remove_owned_send(k: kind) -> kind { k - kind_(KIND_MASK_OWNED) - kind_(KIND_MASK_SEND) } fn remove_copyable(k: kind) -> kind { k - kind_(KIND_MASK_COPY | KIND_MASK_DEFAULT_MODE) } impl kind: ops::bitand { pure fn bitand(other: kind) -> kind { unchecked { lower_kind(self, other) } } } impl kind: ops::bitor { pure fn bitor(other: kind) -> kind { unchecked { raise_kind(self, other) } } } impl kind: ops::sub { pure fn sub(other: kind) -> kind { unchecked { kind_(*self & !*other) } } } // Using these query functions is preferable to direct comparison or matching // against the kind constants, as we may modify the kind hierarchy in the // future. pure fn kind_can_be_implicitly_copied(k: kind) -> bool { *k & KIND_MASK_IMPLICIT == KIND_MASK_IMPLICIT } pure fn kind_is_safe_for_default_mode(k: kind) -> bool { *k & KIND_MASK_DEFAULT_MODE == KIND_MASK_DEFAULT_MODE } pure fn kind_can_be_copied(k: kind) -> bool { *k & KIND_MASK_COPY == KIND_MASK_COPY } pure fn kind_can_be_sent(k: kind) -> bool { *k & KIND_MASK_SEND == KIND_MASK_SEND } pure fn kind_is_owned(k: kind) -> bool { *k & KIND_MASK_OWNED == KIND_MASK_OWNED } fn proto_kind(p: fn_proto) -> kind { match p { proto_vstore(vstore_slice(_)) => kind_noncopyable() | kind_(KIND_MASK_DEFAULT_MODE), proto_vstore(vstore_box) => kind_safe_for_default_mode() | kind_owned(), proto_vstore(vstore_uniq) => kind_send_copy() | kind_owned(), proto_vstore(vstore_fixed(_)) => fail ~"fixed vstore protos are not allowed", proto_bare => kind_safe_for_default_mode_send() | kind_const() | kind_owned() } } fn kind_lteq(a: kind, b: kind) -> bool { *a & *b == *a } fn lower_kind(a: kind, b: kind) -> kind { kind_(*a & *b) } fn raise_kind(a: kind, b: kind) -> kind { kind_(*a | *b) } #[test] fn test_kinds() { // The kind "lattice" is defined by the subset operation on the // set of permitted operations. assert kind_lteq(kind_send_copy(), kind_send_copy()); assert kind_lteq(kind_copyable(), kind_send_copy()); assert kind_lteq(kind_copyable(), kind_copyable()); assert kind_lteq(kind_noncopyable(), kind_send_copy()); assert kind_lteq(kind_noncopyable(), kind_copyable()); assert kind_lteq(kind_noncopyable(), kind_noncopyable()); assert kind_lteq(kind_copyable(), kind_implicitly_copyable()); assert kind_lteq(kind_copyable(), kind_implicitly_sendable()); assert kind_lteq(kind_send_copy(), kind_implicitly_sendable()); assert !kind_lteq(kind_send_copy(), kind_implicitly_copyable()); assert !kind_lteq(kind_copyable(), kind_send_only()); } // Return the most permissive kind that a composite object containing a field // with the given mutability can have. // This is used to prevent objects containing mutable state from being // implicitly copied and to compute whether things have const kind. fn mutability_kind(m: mutability) -> kind { match (m) { m_mutbl => remove_const(remove_implicit(kind_top())), m_const => remove_implicit(kind_top()), m_imm => kind_top() } } fn mutable_type_kind(cx: ctxt, ty: mt) -> kind { lower_kind(mutability_kind(ty.mutbl), type_kind(cx, ty.ty)) } fn type_kind(cx: ctxt, ty: t) -> kind { match cx.kind_cache.find(ty) { some(result) => return result, none => {/* fall through */ } } // Insert a default in case we loop back on self recursively. cx.kind_cache.insert(ty, kind_top()); let mut result = match get(ty).struct { // Scalar and unique types are sendable, constant, and owned ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_ptr(_) => { kind_safe_for_default_mode_send() | kind_const() | kind_owned() } // Implicit copyability of strs is configurable ty_estr(vstore_uniq) => { if cx.vecs_implicitly_copyable { kind_implicitly_sendable() | kind_const() | kind_owned() } else { kind_send_copy() | kind_const() | kind_owned() } } // functions depend on the protocol ty_fn(ref f) => proto_kind(f.proto), // Those with refcounts raise noncopyable to copyable, // lower sendable to copyable. Therefore just set result to copyable. ty_box(tm) => { remove_send(mutable_type_kind(cx, tm) | kind_safe_for_default_mode()) } // Trait instances are (for now) like shared boxes, basically ty_trait(_, _, _) => kind_safe_for_default_mode() | kind_owned(), // Region pointers are copyable but NOT owned nor sendable ty_rptr(_, _) => kind_safe_for_default_mode(), // Unique boxes and vecs have the kind of their contained type, // but unique boxes can't be implicitly copyable. ty_uniq(tm) => remove_implicit(mutable_type_kind(cx, tm)), // Implicit copyability of vecs is configurable ty_evec(tm, vstore_uniq) => { if cx.vecs_implicitly_copyable { mutable_type_kind(cx, tm) } else { remove_implicit(mutable_type_kind(cx, tm)) } } // Slices, refcounted evecs are copyable; uniques depend on the their // contained type, but aren't implicitly copyable. Fixed vectors have // the kind of the element they contain, taking mutability into account. ty_evec(tm, vstore_box) => { remove_send(kind_safe_for_default_mode() | mutable_type_kind(cx, tm)) } ty_evec(tm, vstore_slice(_)) => { remove_owned_send(kind_safe_for_default_mode() | mutable_type_kind(cx, tm)) } ty_evec(tm, vstore_fixed(_)) => { mutable_type_kind(cx, tm) } // All estrs are copyable; uniques and interiors are sendable. ty_estr(vstore_box) => { kind_safe_for_default_mode() | kind_const() | kind_owned() } ty_estr(vstore_slice(_)) => { kind_safe_for_default_mode() | kind_const() } ty_estr(vstore_fixed(_)) => { kind_safe_for_default_mode_send() | kind_const() | kind_owned() } // Records lower to the lowest of their members. ty_rec(flds) => { let mut lowest = kind_top(); for flds.each |f| { lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt)); } lowest } ty_class(did, ref substs) => { // Classes are sendable if all their fields are sendable, // likewise for copyable... // also factor out this code, copied from the records case let mut lowest = kind_top(); let flds = class_items_as_fields(cx, did, substs); for flds.each |f| { lowest = lower_kind(lowest, mutable_type_kind(cx, f.mt)); } // ...but classes with dtors are never copyable (they can be // sendable) if ty::has_dtor(cx, did) { lowest = remove_copyable(lowest); } lowest } // Tuples lower to the lowest of their members. ty_tup(tys) => { let mut lowest = kind_top(); for tys.each |ty| { lowest = lower_kind(lowest, type_kind(cx, ty)); } lowest } // Enums lower to the lowest of their variants. ty_enum(did, ref substs) => { let mut lowest = kind_top(); let variants = enum_variants(cx, did); if vec::len(*variants) == 0u { lowest = kind_send_only() | kind_owned(); } else { for vec::each(*variants) |variant| { for variant.args.each |aty| { // Perform any type parameter substitutions. let arg_ty = subst(cx, substs, aty); lowest = lower_kind(lowest, type_kind(cx, arg_ty)); if lowest == kind_noncopyable() { break; } } } } lowest } ty_param(p) => { param_bounds_to_kind(cx.ty_param_bounds.get(p.def_id.node)) } // self is a special type parameter that can only appear in traits; it // is never bounded in any way, hence it has the bottom kind. ty_self => kind_noncopyable(), ty_var(_) | ty_var_integral(_) => { cx.sess.bug(~"Asked to compute kind of a type variable"); } ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box | ty_unboxed_vec(_) => { cx.sess.bug(~"Asked to compute kind of fictitious type"); } }; // arbitrary threshold to prevent by-value copying of big records if kind_is_safe_for_default_mode(result) { if type_size(cx, ty) > 4 { result -= kind_(KIND_MASK_DEFAULT_MODE); } } cx.kind_cache.insert(ty, result); return result; } /// gives a rough estimate of how much space it takes to represent /// an instance of `ty`. Used for the mode transition. fn type_size(cx: ctxt, ty: t) -> uint { match get(ty).struct { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_ptr(_) | ty_box(_) | ty_uniq(_) | ty_estr(vstore_uniq) | ty_trait(*) | ty_rptr(*) | ty_evec(_, vstore_uniq) | ty_evec(_, vstore_box) | ty_estr(vstore_box) => { 1 } ty_evec(_, vstore_slice(_)) | ty_estr(vstore_slice(_)) | ty_fn(_) => { 2 } ty_evec(t, vstore_fixed(n)) => { type_size(cx, t.ty) * n } ty_estr(vstore_fixed(n)) => { n } ty_rec(flds) => { flds.foldl(0, |s, f| s + type_size(cx, f.mt.ty)) } ty_class(did, ref substs) => { let flds = class_items_as_fields(cx, did, substs); flds.foldl(0, |s, f| s + type_size(cx, f.mt.ty)) } ty_tup(tys) => { tys.foldl(0, |s, t| s + type_size(cx, t)) } ty_enum(did, ref substs) => { let variants = substd_enum_variants(cx, did, substs); variants.foldl( // find max size of any variant 0, |m, v| uint::max(m, // find size of this variant: v.args.foldl(0, |s, a| s + type_size(cx, a)))) } ty_param(_) | ty_self => { 1 } ty_var(_) | ty_var_integral(_) => { cx.sess.bug(~"Asked to compute kind of a type variable"); } ty_type | ty_opaque_closure_ptr(_) | ty_opaque_box | ty_unboxed_vec(_) => { cx.sess.bug(~"Asked to compute kind of fictitious type"); } } } // True if instantiating an instance of `r_ty` requires an instance of `r_ty`. fn is_instantiable(cx: ctxt, r_ty: t) -> bool { fn type_requires(cx: ctxt, seen: @mut ~[def_id], r_ty: t, ty: t) -> bool { debug!{"type_requires(%s, %s)?", ty_to_str(cx, r_ty), ty_to_str(cx, ty)}; let r = { get(r_ty).struct == get(ty).struct || subtypes_require(cx, seen, r_ty, ty) }; debug!{"type_requires(%s, %s)? %b", ty_to_str(cx, r_ty), ty_to_str(cx, ty), r}; return r; } fn subtypes_require(cx: ctxt, seen: @mut ~[def_id], r_ty: t, ty: t) -> bool { debug!{"subtypes_require(%s, %s)?", ty_to_str(cx, r_ty), ty_to_str(cx, ty)}; let r = match get(ty).struct { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_estr(_) | ty_fn(_) | ty_var(_) | ty_var_integral(_) | ty_param(_) | ty_self | ty_type | ty_opaque_box | ty_opaque_closure_ptr(_) | ty_evec(_, _) | ty_unboxed_vec(_) => { false } ty_box(mt) | ty_uniq(mt) | ty_rptr(_, mt) => { return type_requires(cx, seen, r_ty, mt.ty); } ty_ptr(mt) => { false // unsafe ptrs can always be NULL } ty_rec(fields) => { do vec::any(fields) |field| { type_requires(cx, seen, r_ty, field.mt.ty) } } ty_trait(_, _, _) => { false } ty_class(did, _) if vec::contains(*seen, did) => { false } ty_class(did, ref substs) => { vec::push(*seen, did); let r = vec::any(class_items_as_fields(cx, did, substs), |f| type_requires(cx, seen, r_ty, f.mt.ty)); vec::pop(*seen); r } ty_tup(ts) => { vec::any(ts, |t| type_requires(cx, seen, r_ty, t)) } ty_enum(did, _) if vec::contains(*seen, did) => { false } ty_enum(did, ref substs) => { vec::push(*seen, did); let vs = enum_variants(cx, did); let r = vec::len(*vs) > 0u && vec::all(*vs, |variant| { vec::any(variant.args, |aty| { let sty = subst(cx, substs, aty); type_requires(cx, seen, r_ty, sty) }) }); vec::pop(*seen); r } }; debug!{"subtypes_require(%s, %s)? %b", ty_to_str(cx, r_ty), ty_to_str(cx, ty), r}; return r; } let seen = @mut ~[]; !subtypes_require(cx, seen, r_ty, r_ty) } fn type_structurally_contains(cx: ctxt, ty: t, test: fn(x: &sty) -> bool) -> bool { let sty = &get(ty).struct; debug!{"type_structurally_contains: %s", ty_to_str(cx, ty)}; if test(sty) { return true; } match *sty { ty_enum(did, ref substs) => { for vec::each(*enum_variants(cx, did)) |variant| { for variant.args.each |aty| { let sty = subst(cx, substs, aty); if type_structurally_contains(cx, sty, test) { return true; } } } return false; } ty_rec(fields) => { for fields.each |field| { if type_structurally_contains(cx, field.mt.ty, test) { return true; } } return false; } ty_class(did, ref substs) => { for lookup_class_fields(cx, did).each |field| { let ft = lookup_field_type(cx, did, field.id, substs); if type_structurally_contains(cx, ft, test) { return true; } } return false; } ty_tup(ts) => { for ts.each |tt| { if type_structurally_contains(cx, tt, test) { return true; } } return false; } ty_evec(mt, vstore_fixed(_)) => { return type_structurally_contains(cx, mt.ty, test); } _ => return false } } fn type_structurally_contains_uniques(cx: ctxt, ty: t) -> bool { return type_structurally_contains(cx, ty, |sty| { match *sty { ty_uniq(_) | ty_evec(_, vstore_uniq) | ty_estr(vstore_uniq) => true, _ => false, } }); } fn type_is_integral(ty: t) -> bool { match get(ty).struct { ty_var_integral(_) | ty_int(_) | ty_uint(_) | ty_bool => true, _ => false } } fn type_is_fp(ty: t) -> bool { match get(ty).struct { ty_float(_) => true, _ => false } } fn type_is_numeric(ty: t) -> bool { return type_is_integral(ty) || type_is_fp(ty); } fn type_is_signed(ty: t) -> bool { match get(ty).struct { ty_int(_) => true, _ => false } } // Whether a type is Plain Old Data -- meaning it does not contain pointers // that the cycle collector might care about. fn type_is_pod(cx: ctxt, ty: t) -> bool { let mut result = true; match get(ty).struct { // Scalar types ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | ty_type | ty_ptr(_) => result = true, // Boxed types ty_box(_) | ty_uniq(_) | ty_fn(_) | ty_estr(vstore_uniq) | ty_estr(vstore_box) | ty_evec(_, vstore_uniq) | ty_evec(_, vstore_box) | ty_trait(_, _, _) | ty_rptr(_,_) | ty_opaque_box => result = false, // Structural types ty_enum(did, ref substs) => { let variants = enum_variants(cx, did); for vec::each(*variants) |variant| { let tup_ty = mk_tup(cx, variant.args); // Perform any type parameter substitutions. let tup_ty = subst(cx, substs, tup_ty); if !type_is_pod(cx, tup_ty) { result = false; } } } ty_rec(flds) => { for flds.each |f| { if !type_is_pod(cx, f.mt.ty) { result = false; } } } ty_tup(elts) => { for elts.each |elt| { if !type_is_pod(cx, elt) { result = false; } } } ty_estr(vstore_fixed(_)) => result = true, ty_evec(mt, vstore_fixed(_)) | ty_unboxed_vec(mt) => { result = type_is_pod(cx, mt.ty); } ty_param(_) => result = false, ty_opaque_closure_ptr(_) => result = true, ty_class(did, ref substs) => { result = vec::any(lookup_class_fields(cx, did), |f| { let fty = ty::lookup_item_type(cx, f.id); let sty = subst(cx, substs, fty.ty); type_is_pod(cx, sty) }); } ty_estr(vstore_slice(*)) | ty_evec(_, vstore_slice(*)) => { result = false; } ty_var(*) | ty_var_integral(*) | ty_self(*) => { cx.sess.bug(~"non concrete type in type_is_pod"); } } return result; } fn type_is_enum(ty: t) -> bool { match get(ty).struct { ty_enum(_, _) => return true, _ => return false } } // Whether a type is enum like, that is a enum type with only nullary // constructors fn type_is_c_like_enum(cx: ctxt, ty: t) -> bool { match get(ty).struct { ty_enum(did, ref substs) => { let variants = enum_variants(cx, did); let some_n_ary = vec::any(*variants, |v| vec::len(v.args) > 0u); return !some_n_ary; } _ => return false } } fn type_param(ty: t) -> option { match get(ty).struct { ty_param(p) => return some(p.idx), _ => {/* fall through */ } } return none; } // Returns the type and mutability of *t. // // The parameter `expl` indicates if this is an *explicit* dereference. Some // types---notably unsafe ptrs---can only be dereferenced explicitly. fn deref(cx: ctxt, t: t, expl: bool) -> option { deref_sty(cx, &get(t).struct, expl) } fn deref_sty(cx: ctxt, sty: &sty, expl: bool) -> option { match *sty { ty_rptr(_, mt) | ty_box(mt) | ty_uniq(mt) => { some(mt) } ty_ptr(mt) if expl => { some(mt) } ty_enum(did, ref substs) => { let variants = enum_variants(cx, did); if vec::len(*variants) == 1u && vec::len(variants[0].args) == 1u { let v_t = subst(cx, substs, variants[0].args[0]); some({ty: v_t, mutbl: ast::m_imm}) } else { none } } _ => none } } fn type_autoderef(cx: ctxt, t: t) -> t { let mut t = t; loop { match deref(cx, t, false) { none => return t, some(mt) => t = mt.ty } } } // Returns the type and mutability of t[i] fn index(cx: ctxt, t: t) -> option { index_sty(cx, &get(t).struct) } fn index_sty(cx: ctxt, sty: &sty) -> option { match *sty { ty_evec(mt, _) => some(mt), ty_estr(_) => some({ty: mk_u8(cx), mutbl: ast::m_imm}), _ => none } } pure fn hash_bound_region(br: &bound_region) -> uint { match *br { // no idea if this is any good ty::br_self => 0u, ty::br_anon(idx) => 1u | (idx << 2), ty::br_named(ident) => 2u | (ident << 2), ty::br_cap_avoid(id, br) => 3u | (id as uint << 2) | hash_bound_region(br) } } fn br_hashmap() -> hashmap { map::hashmap(hash_bound_region, sys::shape_eq) } pure fn hash_region(r: ®ion) -> uint { match *r { // no idea if this is any good re_bound(br) => (hash_bound_region(&br)) << 2u | 0u, re_free(id, br) => ((id as uint) << 4u) | (hash_bound_region(&br)) << 2u | 1u, re_scope(id) => ((id as uint) << 2u) | 2u, re_var(id) => (id.to_uint() << 2u) | 3u, re_bot => 4u } } // Type hashing. pure fn hash_type_structure(st: &sty) -> uint { pure fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n } pure fn hash_def(id: uint, did: ast::def_id) -> uint { let h = (id << 2u) + (did.crate as uint); (h << 2u) + (did.node as uint) } pure fn hash_subty(id: uint, subty: t) -> uint { (id << 2u) + type_id(subty) } pure fn hash_subtys(id: uint, subtys: ~[t]) -> uint { let mut h = id; for vec::each(subtys) |s| { h = (h << 2u) + type_id(s) } h } pure fn hash_substs(h: uint, substs: &substs) -> uint { let h = hash_subtys(h, substs.tps); h + substs.self_r.map_default(0u, |r| hash_region(&r)) } match *st { ty_nil => 0u, ty_bool => 1u, ty_int(t) => match t { ast::ty_i => 2u, ast::ty_char => 3u, ast::ty_i8 => 4u, ast::ty_i16 => 5u, ast::ty_i32 => 6u, ast::ty_i64 => 7u }, ty_uint(t) => match t { ast::ty_u => 8u, ast::ty_u8 => 9u, ast::ty_u16 => 10u, ast::ty_u32 => 11u, ast::ty_u64 => 12u }, ty_float(t) => match t { ast::ty_f => 13u, ast::ty_f32 => 14u, ast::ty_f64 => 15u }, ty_estr(_) => 16u, ty_enum(did, ref substs) => { let mut h = hash_def(18u, did); hash_substs(h, substs) } ty_box(mt) => hash_subty(19u, mt.ty), ty_evec(mt, _) => hash_subty(20u, mt.ty), ty_unboxed_vec(mt) => hash_subty(22u, mt.ty), ty_tup(ts) => hash_subtys(25u, ts), ty_rec(fields) => { let mut h = 26u; for vec::each(fields) |f| { h = hash_subty(h, f.mt.ty); } h } ty_fn(ref f) => { let mut h = 27u; for vec::each(f.inputs) |a| { h = hash_subty(h, a.ty); } hash_subty(h, f.output) } ty_self => 28u, ty_var(v) => hash_uint(29u, v.to_uint()), ty_var_integral(v) => hash_uint(30u, v.to_uint()), ty_param(p) => hash_def(hash_uint(31u, p.idx), p.def_id), ty_type => 32u, ty_bot => 34u, ty_ptr(mt) => hash_subty(35u, mt.ty), ty_uniq(mt) => hash_subty(37u, mt.ty), ty_trait(did, ref substs, _) => { let mut h = hash_def(40u, did); hash_substs(h, substs) } ty_opaque_closure_ptr(ck_block) => 41u, ty_opaque_closure_ptr(ck_box) => 42u, ty_opaque_closure_ptr(ck_uniq) => 43u, ty_opaque_box => 44u, ty_class(did, ref substs) => { let mut h = hash_def(45u, did); hash_substs(h, substs) } ty_rptr(region, mt) => { let mut h = (46u << 2u) + hash_region(®ion); hash_subty(h, mt.ty) } } } fn node_id_to_type(cx: ctxt, id: ast::node_id) -> t { //io::println(fmt!("%?/%?", id, cx.node_types.size())); match smallintmap::find(*cx.node_types, id as uint) { some(t) => t, none => cx.sess.bug( fmt!{"node_id_to_type: unbound node ID %s", ast_map::node_id_to_str(cx.items, id, cx.sess.parse_sess.interner)}) } } fn node_id_to_type_params(cx: ctxt, id: ast::node_id) -> ~[t] { match cx.node_type_substs.find(id) { none => return ~[], some(ts) => return ts } } fn node_id_has_type_params(cx: ctxt, id: ast::node_id) -> bool { return cx.node_type_substs.contains_key(id); } // Type accessors for substructures of types fn ty_fn_args(fty: t) -> ~[arg] { match get(fty).struct { ty_fn(ref f) => f.inputs, _ => fail ~"ty_fn_args() called on non-fn type" } } fn ty_fn_proto(fty: t) -> fn_proto { match get(fty).struct { ty_fn(ref f) => f.proto, _ => fail ~"ty_fn_proto() called on non-fn type" } } pure fn ty_fn_ret(fty: t) -> t { match get(fty).struct { ty_fn(ref f) => f.output, _ => fail ~"ty_fn_ret() called on non-fn type" } } fn ty_fn_ret_style(fty: t) -> ast::ret_style { match get(fty).struct { ty_fn(ref f) => f.ret_style, _ => fail ~"ty_fn_ret_style() called on non-fn type" } } fn is_fn_ty(fty: t) -> bool { match get(fty).struct { ty_fn(_) => true, _ => false } } fn ty_region(ty: t) -> region { match get(ty).struct { ty_rptr(r, _) => r, s => fail fmt!{"ty_region() invoked on non-rptr: %?", s} } } // Returns a vec of all the input and output types of fty. fn tys_in_fn_ty(fty: &fn_ty) -> ~[t] { vec::append_one(fty.inputs.map(|a| a.ty), fty.output) } // Just checks whether it's a fn that returns bool, // not its purity. fn is_pred_ty(fty: t) -> bool { is_fn_ty(fty) && type_is_bool(ty_fn_ret(fty)) } fn ty_var_id(typ: t) -> tv_vid { match get(typ).struct { ty_var(vid) => return vid, _ => { error!{"ty_var_id called on non-var ty"}; fail; } } } fn ty_var_integral_id(typ: t) -> tvi_vid { match get(typ).struct { ty_var_integral(vid) => return vid, _ => { error!{"ty_var_integral_id called on ty other than \ ty_var_integral"}; fail; } } } // Type accessors for AST nodes fn block_ty(cx: ctxt, b: &ast::blk) -> t { return node_id_to_type(cx, b.node.id); } // Returns the type of a pattern as a monotype. Like @expr_ty, this function // doesn't provide type parameter substitutions. fn pat_ty(cx: ctxt, pat: @ast::pat) -> t { return node_id_to_type(cx, pat.id); } // Returns the type of an expression as a monotype. // // NB: This type doesn't provide type parameter substitutions; e.g. if you // ask for the type of "id" in "id(3)", it will return "fn(&int) -> int" // instead of "fn(t) -> T with T = int". If this isn't what you want, see // expr_ty_params_and_ty() below. fn expr_ty(cx: ctxt, expr: @ast::expr) -> t { return node_id_to_type(cx, expr.id); } fn expr_ty_params_and_ty(cx: ctxt, expr: @ast::expr) -> {params: ~[t], ty: t} { return {params: node_id_to_type_params(cx, expr.id), ty: node_id_to_type(cx, expr.id)}; } fn expr_has_ty_params(cx: ctxt, expr: @ast::expr) -> bool { return node_id_has_type_params(cx, expr.id); } fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map, id: ast::node_id) -> option<@~[param_bounds]> { do method_map.find(id).map |method| { match method.origin { typeck::method_static(did) => { // n.b.: When we encode class/impl methods, the bounds // that we encode include both the class/impl bounds // and then the method bounds themselves... ty::lookup_item_type(tcx, did).bounds } typeck::method_param({trait_id:trt_id, method_num:n_mth, _}) | typeck::method_trait(trt_id, n_mth) => { // ...trait methods bounds, in contrast, include only the // method bounds, so we must preprend the tps from the // trait itself. This ought to be harmonized. let trt_bounds = ty::lookup_item_type(tcx, trt_id).bounds; let mth = ty::trait_methods(tcx, trt_id)[n_mth]; @(vec::append(*trt_bounds, *mth.tps)) } } } } fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool { match e.node { ast::expr_path(_) | ast::expr_unary(ast::deref, _) => true, ast::expr_field(_, _, _) | ast::expr_index(_, _) => { !method_map.contains_key(e.id) } _ => false } } fn stmt_node_id(s: @ast::stmt) -> ast::node_id { match s.node { ast::stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) => { return id; } } } fn field_idx(id: ast::ident, fields: ~[field]) -> option { let mut i = 0u; for fields.each |f| { if f.ident == id { return some(i); } i += 1u; } return none; } fn get_field(rec_ty: t, id: ast::ident) -> field { match check vec::find(get_fields(rec_ty), |f| f.ident == id) { some(f) => f } } fn get_fields(rec_ty:t) -> ~[field] { match check get(rec_ty).struct { ty_rec(fields) => fields } } fn method_idx(id: ast::ident, meths: &[method]) -> option { let mut i = 0u; for meths.each |m| { if m.ident == id { return some(i); } i += 1u; } return none; } /// Returns a vector containing the indices of all type parameters that appear /// in `ty`. The vector may contain duplicates. Probably should be converted /// to a bitset or some other representation. fn param_tys_in_type(ty: t) -> ~[param_ty] { let mut rslt = ~[]; do walk_ty(ty) |ty| { match get(ty).struct { ty_param(p) => { vec::push(rslt, p); } _ => () } } rslt } fn occurs_check(tcx: ctxt, sp: span, vid: tv_vid, rt: t) { // Returns a vec of all the type variables occurring in `ty`. It may // contain duplicates. (Integral type vars aren't counted.) fn vars_in_type(ty: t) -> ~[tv_vid] { let mut rslt = ~[]; do walk_ty(ty) |ty| { match get(ty).struct { ty_var(v) => vec::push(rslt, v), _ => () } } rslt } // Fast path if !type_needs_infer(rt) { return; } // Occurs check! if vec::contains(vars_in_type(rt), vid) { // Maybe this should be span_err -- however, there's an // assertion later on that the type doesn't contain // variables, so in this case we have to be sure to die. tcx.sess.span_fatal (sp, ~"type inference failed because I \ could not find a type\n that's both of the form " + ty_to_str(tcx, mk_var(tcx, vid)) + ~" and of the form " + ty_to_str(tcx, rt) + ~" - such a type would have to be infinitely large."); } } // Maintains a little union-set tree for inferred modes. `canon()` returns // the current head value for `m0`. fn canon(tbl: hashmap>, +m0: ast::inferable) -> ast::inferable { match m0 { ast::infer(id) => match tbl.find(id) { none => m0, some(m1) => { let cm1 = canon(tbl, m1); // path compression: if cm1 != m1 { tbl.insert(id, cm1); } cm1 } }, _ => m0 } } // Maintains a little union-set tree for inferred modes. `resolve_mode()` // returns the current head value for `m0`. fn canon_mode(cx: ctxt, m0: ast::mode) -> ast::mode { canon(cx.inferred_modes, m0) } // Returns the head value for mode, failing if `m` was a infer(_) that // was never inferred. This should be safe for use after typeck. fn resolved_mode(cx: ctxt, m: ast::mode) -> ast::rmode { match canon_mode(cx, m) { ast::infer(_) => { cx.sess.bug(fmt!{"mode %? was never resolved", m}); } ast::expl(m0) => m0 } } fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { resolved_mode(cx, a.mode) } // Unifies `m1` and `m2`. Returns unified value or failure code. fn unify_mode(cx: ctxt, modes: expected_found) -> result { let m1 = modes.expected; let m2 = modes.found; match (canon_mode(cx, m1), canon_mode(cx, m2)) { (m1, m2) if (m1 == m2) => { result::ok(m1) } (ast::infer(id1), ast::infer(id2)) => { cx.inferred_modes.insert(id2, m1); result::ok(m1) } (ast::infer(id), m) | (m, ast::infer(id)) => { cx.inferred_modes.insert(id, m); result::ok(m1) } (m1, m2) => { result::err(terr_mode_mismatch(modes)) } } } // If `m` was never unified, unifies it with `m_def`. Returns the final value // for `m`. fn set_default_mode(cx: ctxt, m: ast::mode, m_def: ast::rmode) { match canon_mode(cx, m) { ast::infer(id) => { cx.inferred_modes.insert(id, ast::expl(m_def)); } ast::expl(_) => () } } fn ty_sort_str(cx: ctxt, t: t) -> ~str { match get(t).struct { ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_estr(_) | ty_type | ty_opaque_box | ty_opaque_closure_ptr(_) => { ty_to_str(cx, t) } ty_enum(id, _) => fmt!{"enum %s", item_path_str(cx, id)}, ty_box(_) => ~"@-ptr", ty_uniq(_) => ~"~-ptr", ty_evec(_, _) => ~"vector", ty_unboxed_vec(_) => ~"unboxed vector", ty_ptr(_) => ~"*-ptr", ty_rptr(_, _) => ~"&-ptr", ty_rec(_) => ~"record", ty_fn(_) => ~"fn", ty_trait(id, _, _) => fmt!{"trait %s", item_path_str(cx, id)}, ty_class(id, _) => fmt!{"class %s", item_path_str(cx, id)}, ty_tup(_) => ~"tuple", ty_var(_) => ~"variable", ty_var_integral(_) => ~"integral variable", ty_param(_) => ~"type parameter", ty_self => ~"self" } } fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~str { match k { terr_vec => ~"[]", terr_str => ~"str", terr_fn => ~"fn", terr_trait => ~"trait" } } match *err { terr_mismatch => ~"types differ", terr_ret_style_mismatch(values) => { fn to_str(s: ast::ret_style) -> ~str { match s { ast::noreturn => ~"non-returning", ast::return_val => ~"return-by-value" } } fmt!("expected %s function, found %s function", to_str(values.expected), to_str(values.expected)) } terr_purity_mismatch(values) => { fmt!{"expected %s fn but found %s fn", purity_to_str(values.expected), purity_to_str(values.found)} } terr_proto_mismatch(values) => { fmt!{"expected %s closure, found %s closure", proto_ty_to_str(cx, values.expected), proto_ty_to_str(cx, values.found)} } terr_mutability => ~"values differ in mutability", terr_box_mutability => ~"boxed values differ in mutability", terr_vec_mutability => ~"vectors differ in mutability", terr_ptr_mutability => ~"pointers differ in mutability", terr_ref_mutability => ~"references differ in mutability", terr_ty_param_size(values) => { fmt!("expected a type with %? type params \ but found one with %? type params", values.expected, values.found) } terr_tuple_size(values) => { fmt!("expected a tuple with %? elements \ but found one with %? elements", values.expected, values.found) } terr_record_size(values) => { fmt!("expected a record with %? fields \ but found one with %? fields", values.expected, values.found) } terr_record_mutability => { ~"record elements differ in mutability" } terr_record_fields(values) => { fmt!("expected a record with field `%s` but found one with field \ `%s`", cx.sess.str_of(values.expected), cx.sess.str_of(values.found)) } terr_arg_count => ~"incorrect number of function parameters", terr_mode_mismatch(values) => { fmt!("expected argument mode %s, but found %s", mode_to_str(values.expected), mode_to_str(values.found)) } terr_regions_does_not_outlive(subregion, superregion) => { fmt!{"%s does not necessarily outlive %s", explain_region(cx, superregion), explain_region(cx, subregion)} } terr_regions_not_same(region1, region2) => { fmt!{"%s is not the same as %s", explain_region(cx, region1), explain_region(cx, region2)} } terr_regions_no_overlap(region1, region2) => { fmt!("%s does not intersect %s", explain_region(cx, region1), explain_region(cx, region2)) } terr_vstores_differ(k, values) => { fmt!("%s storage differs: expected %s but found %s", terr_vstore_kind_to_str(k), vstore_to_str(cx, values.expected), vstore_to_str(cx, values.found)) } terr_in_field(err, fname) => { fmt!("in field `%s`, %s", cx.sess.str_of(fname), type_err_to_str(cx, err)) } terr_sorts(values) => { fmt!{"expected %s but found %s", ty_sort_str(cx, values.expected), ty_sort_str(cx, values.found)} } terr_self_substs => { ~"inconsistent self substitution" // XXX this is more of a bug } terr_no_integral_type => { ~"couldn't determine an appropriate integral type for integer \ literal" } } } fn def_has_ty_params(def: ast::def) -> bool { match def { ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_, _) => true, _ => false } } fn store_trait_methods(cx: ctxt, id: ast::node_id, ms: @~[method]) { cx.trait_method_cache.insert(ast_util::local_def(id), ms); } fn trait_methods(cx: ctxt, id: ast::def_id) -> @~[method] { match cx.trait_method_cache.find(id) { // Local traits are supposed to have been added explicitly. some(ms) => ms, _ => { // If the lookup in trait_method_cache fails, assume that the trait // method we're trying to look up is in a different crate, and look // for it there. assert id.crate != ast::local_crate; let result = csearch::get_trait_methods(cx, id); // Store the trait method in the local trait_method_cache so that // future lookups succeed. cx.trait_method_cache.insert(id, result); result } } } fn impl_traits(cx: ctxt, id: ast::def_id) -> ~[t] { if id.crate == ast::local_crate { debug!{"(impl_traits) searching for trait impl %?", id}; match cx.items.find(id.node) { some(ast_map::node_item(@{ node: ast::item_impl(_, trait_refs, _, _), _}, _)) => { do vec::map(trait_refs) |trait_ref| { node_id_to_type(cx, trait_ref.ref_id) } } some(ast_map::node_item(@{node: ast::item_class(*), _},_)) => { match cx.def_map.find(id.node) { some(def_ty(trait_id)) => { // XXX: Doesn't work cross-crate. debug!{"(impl_traits) found trait id %?", trait_id}; ~[node_id_to_type(cx, trait_id.node)] } some(x) => { cx.sess.bug(fmt!{"impl_traits: trait ref is in trait map \ but is bound to %?", x}); } none => { ~[] } } } _ => ~[] } } else { csearch::get_impl_traits(cx, id) } } fn ty_to_def_id(ty: t) -> option { match get(ty).struct { ty_trait(id, _, _) | ty_class(id, _) | ty_enum(id, _) => some(id), _ => none } } // Enum information type variant_info = @{args: ~[t], ctor_ty: t, name: ast::ident, id: ast::def_id, disr_val: int}; fn substd_enum_variants(cx: ctxt, id: ast::def_id, substs: &substs) -> ~[variant_info] { do vec::map(*enum_variants(cx, id)) |variant_info| { let substd_args = vec::map(variant_info.args, |aty| subst(cx, substs, aty)); let substd_ctor_ty = subst(cx, substs, variant_info.ctor_ty); @{args: substd_args, ctor_ty: substd_ctor_ty with *variant_info} } } fn item_path_str(cx: ctxt, id: ast::def_id) -> ~str { ast_map::path_to_str(item_path(cx, id), cx.sess.parse_sess.interner) } /* If class_id names a class with a dtor, return some(the dtor's id). Otherwise return none. */ fn ty_dtor(cx: ctxt, class_id: def_id) -> option { if is_local(class_id) { match cx.items.find(class_id.node) { some(ast_map::node_item(@{ node: ast::item_class(@{ dtor: some(dtor), _ }, _), _ }, _)) => some(local_def(dtor.node.id)), _ => none } } else { csearch::class_dtor(cx.sess.cstore, class_id) } } fn has_dtor(cx: ctxt, class_id: def_id) -> bool { option::is_some(ty_dtor(cx, class_id)) } fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path { if id.crate != ast::local_crate { csearch::get_item_path(cx, id) } else { let node = cx.items.get(id.node); match node { ast_map::node_item(item, path) => { let item_elt = match item.node { item_mod(_) | item_foreign_mod(_) => { ast_map::path_mod(item.ident) } _ => { ast_map::path_name(item.ident) } }; vec::append_one(*path, item_elt) } ast_map::node_foreign_item(nitem, _, path) => { vec::append_one(*path, ast_map::path_name(nitem.ident)) } ast_map::node_method(method, _, path) => { vec::append_one(*path, ast_map::path_name(method.ident)) } ast_map::node_trait_method(trait_method, _, path) => { let method = ast_util::trait_method_to_ty_method(*trait_method); vec::append_one(*path, ast_map::path_name(method.ident)) } ast_map::node_variant(variant, _, path) => { vec::append_one(vec::init(*path), ast_map::path_name(variant.node.name)) } ast_map::node_ctor(nm, _, _, _, path) => { vec::append_one(*path, ast_map::path_name(nm)) } ast_map::node_dtor(_, _, _, path) => { vec::append_one(*path, ast_map::path_name( syntax::parse::token::special_idents::literally_dtor)) } ast_map::node_stmt(*) | ast_map::node_expr(*) | ast_map::node_arg(*) | ast_map::node_local(*) | ast_map::node_export(*) | ast_map::node_block(*) => { cx.sess.bug(fmt!{"cannot find item_path for node %?", node}); } } } } fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool { vec::len(*enum_variants(cx, id)) == 1u } fn type_is_empty(cx: ctxt, t: t) -> bool { match ty::get(t).struct { ty_enum(did, _) => (*enum_variants(cx, did)).is_empty(), _ => false } } fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] { match cx.enum_var_cache.find(id) { some(variants) => return variants, _ => { /* fallthrough */ } } let result = if ast::local_crate != id.crate { @csearch::get_enum_variants(cx, id) } else { /* Although both this code and check_enum_variants in typeck/check call eval_const_expr, it should never get called twice for the same expr, since check_enum_variants also updates the enum_var_cache */ match cx.items.get(id.node) { ast_map::node_item(@{node: ast::item_enum(enum_definition, _), _}, _) => { let variants = enum_definition.variants; let mut disr_val = -1; @vec::map(variants, |variant| { match variant.node.kind { ast::tuple_variant_kind(args) => { let ctor_ty = node_id_to_type(cx, variant.node.id); let arg_tys = { if vec::len(args) > 0u { ty_fn_args(ctor_ty).map(|a| a.ty) } else { ~[] } }; match variant.node.disr_expr { some (ex) => { // FIXME: issue #1417 disr_val = match const_eval::eval_const_expr(cx, ex) { const_eval::const_int(val) => val as int, _ => cx.sess.bug(~"tag_variants: bad disr expr") } } _ => disr_val += 1 } @{args: arg_tys, ctor_ty: ctor_ty, name: variant.node.name, id: ast_util::local_def(variant.node.id), disr_val: disr_val } } ast::struct_variant_kind(_) => { fail ~"struct variant kinds unimpl in enum_variants" } ast::enum_variant_kind(_) => { fail ~"enum variant kinds unimpl in enum_variants" } } }) } _ => cx.sess.bug(~"tag_variants: id not bound to an enum") } }; cx.enum_var_cache.insert(id, result); result } // Returns information about the enum variant with the given ID: fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id, variant_id: ast::def_id) -> variant_info { let variants = enum_variants(cx, enum_id); let mut i = 0u; while i < vec::len::(*variants) { let variant = variants[i]; if ast_util::def_eq(&variant.id, &variant_id) { return variant; } i += 1u; } cx.sess.bug(~"enum_variant_with_id(): no variant exists with that ID"); } // If the given item is in an external crate, looks up its type and adds it to // the type cache. Returns the type parameters and type. fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty { match cx.tcache.find(did) { some(tpt) => return tpt, none => { // The item is in this crate. The caller should have added it to the // type cache already assert did.crate != ast::local_crate; let tyt = csearch::get_type(cx, did); cx.tcache.insert(did, tyt); return tyt; } } } // Look up a field ID, whether or not it's local // Takes a list of type substs in case the class is generic fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id, substs: &substs) -> ty::t { let t = if id.crate == ast::local_crate { node_id_to_type(tcx, id.node) } else { match tcx.tcache.find(id) { some(tpt) => tpt.ty, none => { let tpt = csearch::get_field_type(tcx, class_id, id); tcx.tcache.insert(id, tpt); tpt.ty } } }; subst(tcx, substs, t) } // Look up the list of field names and IDs for a given class // Fails if the id is not bound to a class. fn lookup_class_fields(cx: ctxt, did: ast::def_id) -> ~[field_ty] { if did.crate == ast::local_crate { match cx.items.find(did.node) { some(ast_map::node_item(i,_)) => { match i.node { ast::item_class(struct_def, _) => { class_field_tys(struct_def.fields) } _ => cx.sess.bug(~"class ID bound to non-class") } } _ => { cx.sess.bug( fmt!{"class ID not bound to an item: %s", ast_map::node_id_to_str(cx.items, did.node, cx.sess.parse_sess.interner)}); } } } else { return csearch::get_class_fields(cx, did); } } fn lookup_class_field(cx: ctxt, parent: ast::def_id, field_id: ast::def_id) -> field_ty { match vec::find(lookup_class_fields(cx, parent), |f| f.id.node == field_id.node) { some(t) => t, none => cx.sess.bug(~"class ID not found in parent's fields") } } fn lookup_public_fields(cx: ctxt, did: ast::def_id) -> ~[field_ty] { vec::filter(lookup_class_fields(cx, did), is_public) } pure fn is_public(f: field_ty) -> bool { // XXX: This is wrong. match f.vis { public | inherited => true, private => false } } /* Given a class def_id and a method name, return the method's def_id. Needed so we can do static dispatch for methods Doesn't care about the method's privacy. (It's assumed that the caller already checked that.) */ fn lookup_class_method_by_name(cx:ctxt, did: ast::def_id, name: ident, sp: span) -> def_id { // Look up the list of method names and IDs for a given class // Fails if the id is not bound to a class. fn lookup_class_method_ids(cx: ctxt, did: ast::def_id) -> ~[{name: ident, id: node_id, vis: visibility}] { assert is_local(did); match cx.items.find(did.node) { some(ast_map::node_item(@{ node: item_class(struct_def, _), _ }, _)) => { vec::map(struct_def.methods, |m| {name: m.ident, id: m.id, vis: m.vis}) } _ => { cx.sess.bug(~"lookup_class_method_ids: id not bound to a class"); } } } if is_local(did) { let ms = lookup_class_method_ids(cx, did); for ms.each |m| { if m.name == name { return ast_util::local_def(m.id); } } cx.sess.span_fatal(sp, fmt!{"Class doesn't have a method \ named %s", cx.sess.str_of(name)}); } else { csearch::get_class_method(cx.sess.cstore, did, name) } } fn class_field_tys(fields: ~[@struct_field]) -> ~[field_ty] { let mut rslt = ~[]; for fields.each |field| { match field.node.kind { named_field(ident, mutability, visibility) => { vec::push(rslt, {ident: ident, id: ast_util::local_def(field.node.id), vis: visibility, mutability: mutability}); } unnamed_field => {} } } rslt } // Return a list of fields corresponding to the class's items // (as if the class was a record). trans uses this // Takes a list of substs with which to instantiate field types // Keep in mind that this function reports that all fields are // mutable, regardless of how they were declared. It's meant to // be used in trans. fn class_items_as_mutable_fields(cx:ctxt, did: ast::def_id, substs: &substs) -> ~[field] { class_item_fields(cx, did, substs, |_mt| m_mutbl) } // Same as class_items_as_mutable_fields, but doesn't change // mutability. fn class_items_as_fields(cx:ctxt, did: ast::def_id, substs: &substs) -> ~[field] { class_item_fields(cx, did, substs, |mt| match mt { class_mutable => m_mutbl, class_immutable => m_imm }) } fn class_item_fields(cx:ctxt, did: ast::def_id, substs: &substs, frob_mutability: fn(class_mutability) -> mutability) -> ~[field] { let mut rslt = ~[]; for lookup_class_fields(cx, did).each |f| { // consider all instance vars mut, because the // constructor may mutate all vars vec::push(rslt, {ident: f.ident, mt: {ty: lookup_field_type(cx, did, f.id, substs), mutbl: frob_mutability(f.mutability)}}); } rslt } fn is_binopable(_cx: ctxt, ty: t, op: ast::binop) -> bool { const tycat_other: int = 0; const tycat_bool: int = 1; const tycat_int: int = 2; const tycat_float: int = 3; const tycat_struct: int = 4; const tycat_bot: int = 5; const opcat_add: int = 0; const opcat_sub: int = 1; const opcat_mult: int = 2; const opcat_shift: int = 3; const opcat_rel: int = 4; const opcat_eq: int = 5; const opcat_bit: int = 6; const opcat_logic: int = 7; fn opcat(op: ast::binop) -> int { match op { ast::add => opcat_add, ast::subtract => opcat_sub, ast::mul => opcat_mult, ast::div => opcat_mult, ast::rem => opcat_mult, ast::and => opcat_logic, ast::or => opcat_logic, ast::bitxor => opcat_bit, ast::bitand => opcat_bit, ast::bitor => opcat_bit, ast::shl => opcat_shift, ast::shr => opcat_shift, ast::eq => opcat_eq, ast::ne => opcat_eq, ast::lt => opcat_rel, ast::le => opcat_rel, ast::ge => opcat_rel, ast::gt => opcat_rel } } fn tycat(ty: t) -> int { match get(ty).struct { ty_bool => tycat_bool, ty_int(_) | ty_uint(_) | ty_var_integral(_) => tycat_int, ty_float(_) => tycat_float, ty_rec(_) | ty_tup(_) | ty_enum(_, _) => tycat_struct, ty_bot => tycat_bot, _ => tycat_other } } const t: bool = true; const f: bool = false; let tbl = ~[ /*. add, shift, bit . sub, rel, logic . mult, eq, */ /*other*/ ~[f, f, f, f, t, t, f, f], /*bool*/ ~[f, f, f, f, t, t, t, t], /*int*/ ~[t, t, t, t, t, t, t, f], /*float*/ ~[t, t, t, f, t, t, f, f], /*bot*/ ~[f, f, f, f, t, t, f, f], /*struct*/ ~[t, t, t, t, t, t, t, t]]; return tbl[tycat(ty)][opcat(op)]; } fn ty_params_to_tys(tcx: ty::ctxt, tps: ~[ast::ty_param]) -> ~[t] { vec::from_fn(tps.len(), |i| { ty::mk_param(tcx, i, ast_util::local_def(tps[i].id)) }) } /// Returns an equivalent type with all the typedefs and self regions removed. fn normalize_ty(cx: ctxt, t: t) -> t { fn normalize_mt(cx: ctxt, mt: mt) -> mt { { ty: normalize_ty(cx, mt.ty), mutbl: mt.mutbl } } fn normalize_vstore(vstore: vstore) -> vstore { match vstore { vstore_fixed(*) | vstore_uniq | vstore_box => vstore, vstore_slice(_) => vstore_slice(re_static) } } match cx.normalized_cache.find(t) { some(t) => return t, none => () } let t = match get(t).struct { ty_evec(mt, vstore) => // This type has a vstore. Get rid of it mk_evec(cx, normalize_mt(cx, mt), normalize_vstore(vstore)), ty_estr(vstore) => // This type has a vstore. Get rid of it mk_estr(cx, normalize_vstore(vstore)), ty_rptr(region, mt) => // This type has a region. Get rid of it mk_rptr(cx, re_static, normalize_mt(cx, mt)), ty_fn({purity: purity, proto: proto_vstore(vstore), bounds: bounds, inputs: inputs, output: output, ret_style: ret_style}) => mk_fn(cx, { purity: purity, proto: proto_vstore(normalize_vstore(vstore)), bounds: bounds, inputs: inputs, output: output, ret_style: ret_style }), ty_enum(did, r) => match r.self_r { some(_) => // This enum has a self region. Get rid of it mk_enum(cx, did, {self_r: none, self_ty: none, tps: r.tps}), none => t }, ty_class(did, r) => match r.self_r { some(_) => // Ditto. mk_class(cx, did, {self_r: none, self_ty: none, tps: r.tps}), none => t }, _ => t }; // FIXME #2187: This also reduced int types to their compatible machine // types, which isn't necessary after #2187 let t = mk_t(cx, mach_sty(cx.sess.targ_cfg, t)); let sty = fold_sty(&get(t).struct, |t| { normalize_ty(cx, t) }); let t_norm = mk_t(cx, sty); cx.normalized_cache.insert(t, t_norm); return t_norm; } // Returns the repeat count for a repeating vector expression. fn eval_repeat_count(tcx: ctxt, count_expr: @ast::expr, span: span) -> uint { match const_eval::eval_const_expr(tcx, count_expr) { const_eval::const_int(count) => return count as uint, const_eval::const_uint(count) => return count as uint, const_eval::const_float(count) => { tcx.sess.span_err(span, ~"expected signed or unsigned integer for \ repeat count but found float"); return count as uint; } const_eval::const_str(_) => { tcx.sess.span_err(span, ~"expected signed or unsigned integer for \ repeat count but found string"); return 0; } } } pure fn is_blockish(proto: fn_proto) -> bool { match proto { proto_vstore(vstore_slice(_)) => true, proto_vstore(vstore_box) | proto_vstore(vstore_uniq) | proto_bare => false, proto_vstore(vstore_fixed(_)) => fail ~"fixed vstore not allowed here" } } // Local Variables: // mode: rust // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // End: