diff --git a/src/etc/tidy.py b/src/etc/tidy.py index e866d80062d51..2eb32c100acd2 100644 --- a/src/etc/tidy.py +++ b/src/etc/tidy.py @@ -23,6 +23,11 @@ def report_error_name_no(name, no, s): def report_err(s): report_error_name_no(fileinput.filename(), fileinput.filelineno(), s) +def report_warn(s): + print("%s:%d: %s" % (fileinput.filename(), + fileinput.filelineno(), + s)) + def do_license_check(name, contents): if not check_license(name, contents): report_error_name_no(name, 1, "incorrect license") @@ -44,6 +49,9 @@ def do_license_check(name, contents): report_err("FIXME without issue number") if line.find("TODO") != -1: report_err("TODO is deprecated; use FIXME") + idx = line.find("// WARN") + if idx != -1: + report_warn("WARN:" + line[idx + len("// WARN"):]) if (line.find('\t') != -1 and fileinput.filename().find("Makefile") == -1): report_err("tab character") diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 6a09ae14d01dc..590dd9f7ea7e0 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -323,9 +323,6 @@ fn parse_ty(st: @pstate, conv: conv_did) -> ty::t { 'f' => { parse_ty_rust_fn(st, conv) } - 'X' => { - return ty::mk_var(st.tcx, ty::TyVid(parse_int(st) as uint)); - } 'Y' => return ty::mk_type(st.tcx), 'C' => { let proto = parse_proto(st); diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 154fb8d2de85a..d318f00ad6752 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -298,19 +298,8 @@ fn enc_sty(w: io::Writer, cx: @ctxt, +st: ty::sty) { ty::ty_fn(ref f) => { enc_ty_fn(w, cx, (*f)); } - ty::ty_infer(ty::TyVar(id)) => { - w.write_char('X'); - w.write_uint(id.to_uint()); - } - ty::ty_infer(ty::IntVar(id)) => { - w.write_char('X'); - w.write_char('I'); - w.write_uint(id.to_uint()); - } - ty::ty_infer(ty::FloatVar(id)) => { - w.write_char('X'); - w.write_char('F'); - w.write_uint(id.to_uint()); + ty::ty_infer(_) => { + cx.diag.handler().bug(~"Cannot encode inference variable types"); } ty::ty_param({idx: id, def_id: did}) => { w.write_char('p'); diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index 967b1681b1bfb..0fff0c66a3846 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -591,11 +591,53 @@ impl gather_loan_ctxt { } } + ast::pat_vec(_, Some(tail_pat)) => { + // The `tail_pat` here creates a slice into the + // original vector. This is effectively a borrow of + // the elements of the vector being matched. + + let tail_ty = self.tcx().ty(tail_pat); + let (tail_mutbl, tail_r) = + self.vec_slice_info(tail_pat, tail_ty); + let mcx = self.bccx.mc_ctxt(); + let cmt_index = mcx.cat_index(tail_pat, cmt); + self.guarantee_valid(cmt_index, tail_mutbl, tail_r); + } + _ => {} } } } + fn vec_slice_info(&self, + pat: @ast::pat, + tail_ty: ty::t) -> (ast::mutability, ty::Region) + { + /*! + * + * In a pattern like [a, b, ..c], normally `c` has slice type, + * but if you have [a, b, ..ref c], then the type of `ref c` + * will be `&&[]`, so to extract the slice details we have + * to recurse through rptrs. + */ + + match ty::get(tail_ty).sty { + ty::ty_evec(tail_mt, ty::vstore_slice(tail_r)) => { + (tail_mt.mutbl, tail_r) + } + + ty::ty_rptr(_, ref mt) => { + self.vec_slice_info(pat, mt.ty) + } + + _ => { + self.tcx().sess.span_bug( + pat.span, + fmt!("Type of tail pattern is not a slice")); + } + } + } + fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool { pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat) } diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index 1b274a5241c67..d3dc75aad7f91 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -8,6 +8,35 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! + +The `Loan` module deals with borrows of *uniquely mutable* data. We +say that data is uniquely mutable if the current activation (stack +frame) controls the only mutable reference to the data. The most +common way that this can occur is if the current activation owns the +data being borrowed, but it can also occur with `&mut` pointers. The +primary characteristic of uniquely mutable data is that, at any given +time, there is at most one path that can be used to mutate it, and +that path is only accessible from the top stack frame. + +Given that some data found at a path P is being borrowed to a borrowed +pointer with mutability M and lifetime L, the job of the code in this +module is to compute the set of *loans* that are necessary to ensure +that (1) the data found at P outlives L and that (2) if M is mutable +then the path P will not be modified directly or indirectly except +through that pointer. A *loan* is the combination of a path P_L, a +mutability M_L, and a lifetime L_L where: + +- The path P_L indicates what data has been lent. +- The mutability M_L indicates the access rights on the data: + - const: the data cannot be moved + - immutable/mutable: the data cannot be moved or mutated +- The lifetime L_L indicates the *scope* of the loan. + +XXX --- much more needed, don't have time to write this all up now + +*/ + // ---------------------------------------------------------------------- // Loan(Ex, M, S) = Ls holds if ToAddr(Ex) will remain valid for the entirety // of the scope S, presuming that the returned set of loans `Ls` are honored. @@ -39,7 +68,7 @@ impl borrowck_ctxt { scope_region: scope_region, loans: ~[] }; - match lc.loan(cmt, mutbl) { + match lc.loan(cmt, mutbl, true) { Err(ref e) => Err((*e)), Ok(()) => { let LoanContext {loans, _} = move lc; @@ -62,46 +91,25 @@ struct LoanContext { impl LoanContext { fn tcx(&self) -> ty::ctxt { self.bccx.tcx } - fn issue_loan(&self, - cmt: cmt, - scope_ub: ty::Region, - req_mutbl: ast::mutability) -> bckres<()> { - if self.bccx.is_subregion_of(self.scope_region, scope_ub) { - match req_mutbl { - m_mutbl => { - // We do not allow non-mutable data to be loaned - // out as mutable under any circumstances. - if cmt.mutbl != m_mutbl { - return Err({cmt:cmt, - code:err_mutbl(req_mutbl)}); - } - } - m_const | m_imm => { - // However, mutable data can be loaned out as - // immutable (and any data as const). The - // `check_loans` pass will then guarantee that no - // writes occur for the duration of the loan. - } - } + fn loan(&self, + cmt: cmt, + req_mutbl: ast::mutability, + owns_lent_data: bool) -> bckres<()> + { + /*! + * + * The main routine. + * + * # Parameters + * + * - `cmt`: the categorization of the data being borrowed + * - `req_mutbl`: the mutability of the borrowed pointer + * that was created + * - `owns_lent_data`: indicates whether `cmt` owns the + * data that is being lent. See + * discussion in `issue_loan()`. + */ - self.loans.push(Loan { - // Note: cmt.lp must be Some(_) because otherwise this - // loan process does not apply at all. - lp: cmt.lp.get(), - cmt: cmt, - mutbl: req_mutbl - }); - return Ok(()); - } else { - // The loan being requested lives longer than the data - // being loaned out! - return Err({cmt:cmt, - code:err_out_of_scope(scope_ub, - self.scope_region)}); - } - } - - fn loan(&self, cmt: cmt, req_mutbl: ast::mutability) -> bckres<()> { debug!("loan(%s, %s)", self.bccx.cmt_to_repr(cmt), self.bccx.mut_to_str(req_mutbl)); @@ -123,13 +131,14 @@ impl LoanContext { } cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { let local_scope_id = self.tcx().region_map.get(local_id); - self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl) + self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl, + owns_lent_data) } cat_stack_upvar(cmt) => { - self.loan(cmt, req_mutbl) // NDM correct? + self.loan(cmt, req_mutbl, owns_lent_data) } cat_discr(base, _) => { - self.loan(base, req_mutbl) + self.loan(base, req_mutbl, owns_lent_data) } cat_comp(cmt_base, comp_field(_, m)) | cat_comp(cmt_base, comp_index(_, m)) => { @@ -139,36 +148,41 @@ impl LoanContext { // that case, it must also be embedded in an immutable // location, or else the whole structure could be // overwritten and the component along with it. - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m) + self.loan_stable_comp(cmt, cmt_base, req_mutbl, m, + owns_lent_data) } cat_comp(cmt_base, comp_tuple) | cat_comp(cmt_base, comp_anon_field) => { // As above. - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm) + self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm, + owns_lent_data) } cat_comp(cmt_base, comp_variant(enum_did)) => { // For enums, the memory is unstable if there are multiple // variants, because if the enum value is overwritten then // the memory changes type. if ty::enum_is_univariant(self.bccx.tcx, enum_did) { - self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm) + self.loan_stable_comp(cmt, cmt_base, req_mutbl, m_imm, + owns_lent_data) } else { - self.loan_unstable_deref(cmt, cmt_base, req_mutbl) + self.loan_unstable_deref(cmt, cmt_base, req_mutbl, + owns_lent_data) } } cat_deref(cmt_base, _, uniq_ptr) => { // For unique pointers, the memory being pointed out is // unstable because if the unique pointer is overwritten // then the memory is freed. - self.loan_unstable_deref(cmt, cmt_base, req_mutbl) + self.loan_unstable_deref(cmt, cmt_base, req_mutbl, + owns_lent_data) } cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => { // Mutable data can be loaned out as immutable or const. We must // loan out the base as well as the main memory. For example, // if someone borrows `*b`, we want to borrow `b` as immutable // as well. - do self.loan(cmt_base, m_imm).chain |_| { - self.issue_loan(cmt, region, m_const) + do self.loan(cmt_base, m_imm, false).chain |_| { + self.issue_loan(cmt, region, m_const, owns_lent_data) } } cat_deref(_, _, unsafe_ptr) | @@ -189,7 +203,8 @@ impl LoanContext { cmt: cmt, cmt_base: cmt, req_mutbl: ast::mutability, - comp_mutbl: ast::mutability) -> bckres<()> { + comp_mutbl: ast::mutability, + owns_lent_data: bool) -> bckres<()> { // Determine the mutability that the base component must have, // given the required mutability of the pointer (`req_mutbl`) // and the declared mutability of the component (`comp_mutbl`). @@ -243,10 +258,11 @@ impl LoanContext { (m_const, _) => m_const // (5) }; - do self.loan(cmt_base, base_mutbl).chain |_ok| { + do self.loan(cmt_base, base_mutbl, owns_lent_data).chain |_ok| { // can use static for the scope because the base // determines the lifetime, ultimately - self.issue_loan(cmt, ty::re_static, req_mutbl) + self.issue_loan(cmt, ty::re_static, req_mutbl, + owns_lent_data) } } @@ -256,13 +272,69 @@ impl LoanContext { fn loan_unstable_deref(&self, cmt: cmt, cmt_base: cmt, - req_mutbl: ast::mutability) -> bckres<()> { + req_mutbl: ast::mutability, + owns_lent_data: bool) -> bckres<()> + { // Variant components: the base must be immutable, because // if it is overwritten, the types of the embedded data // could change. - do self.loan(cmt_base, m_imm).chain |_| { + do self.loan(cmt_base, m_imm, owns_lent_data).chain |_| { // can use static, as in loan_stable_comp() - self.issue_loan(cmt, ty::re_static, req_mutbl) + self.issue_loan(cmt, ty::re_static, req_mutbl, + owns_lent_data) + } + } + + fn issue_loan(&self, + cmt: cmt, + scope_ub: ty::Region, + req_mutbl: ast::mutability, + owns_lent_data: bool) -> bckres<()> + { + // Subtle: the `scope_ub` is the maximal lifetime of `cmt`. + // Therefore, if `cmt` owns the data being lent, then the + // scope of the loan must be less than `scope_ub`, or else the + // data would be freed while the loan is active. + // + // However, if `cmt` does *not* own the data being lent, then + // it is ok if `cmt` goes out of scope during the loan. This + // can occur when you have an `&mut` parameter that is being + // reborrowed. + + if !owns_lent_data || + self.bccx.is_subregion_of(self.scope_region, scope_ub) + { + match req_mutbl { + m_mutbl => { + // We do not allow non-mutable data to be loaned + // out as mutable under any circumstances. + if cmt.mutbl != m_mutbl { + return Err({cmt:cmt, + code:err_mutbl(req_mutbl)}); + } + } + m_const | m_imm => { + // However, mutable data can be loaned out as + // immutable (and any data as const). The + // `check_loans` pass will then guarantee that no + // writes occur for the duration of the loan. + } + } + + self.loans.push(Loan { + // Note: cmt.lp must be Some(_) because otherwise this + // loan process does not apply at all. + lp: cmt.lp.get(), + cmt: cmt, + mutbl: req_mutbl + }); + return Ok(()); + } else { + // The loan being requested lives longer than the data + // being loaned out! + return Err({cmt:cmt, + code:err_out_of_scope(scope_ub, + self.scope_region)}); } } } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index ab7de6d52ca41..922b76d313b61 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -504,9 +504,13 @@ impl borrowck_ctxt { return @{cat:cat_discr(cmt, match_id),.. *cmt}; } + fn mc_ctxt() -> mem_categorization_ctxt { + mem_categorization_ctxt {tcx: self.tcx, + method_map: self.method_map} + } + fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { - let mc = &mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; + let mc = self.mc_ctxt(); mc.cat_pattern(cmt, pat, op); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 909d1f95fde4b..7c1e96e5c0e44 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -111,6 +111,8 @@ enum special_kind { // a complete categorization of a value indicating where it originated // and how it is located, as well as the mutability of the memory in // which the value is stored. +// +// note: cmt stands for "categorized mutable type". type cmt_ = {id: ast::node_id, // id of expr/pat producing this value span: span, // span of same expr/pat cat: categorization, // categorization of expr @@ -519,8 +521,8 @@ impl &mem_categorization_ctxt { ty: self.tcx.ty(arg)} } - fn cat_rvalue(expr: @ast::expr, expr_ty: ty::t) -> cmt { - @{id:expr.id, span:expr.span, + fn cat_rvalue(elt: N, expr_ty: ty::t) -> cmt { + @{id:elt.id(), span:elt.span(), cat:cat_rvalue, lp:None, mutbl:m_imm, ty:expr_ty} } @@ -641,12 +643,12 @@ impl &mem_categorization_ctxt { } } - fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt { + fn cat_index(elt: N, base_cmt: cmt) -> cmt { let mt = match ty::index(self.tcx, base_cmt.ty) { Some(mt) => mt, None => { self.tcx.sess.span_bug( - expr.span, + elt.span(), fmt!("Explicit index of non-index type `%s`", ty_to_str(self.tcx, base_cmt.ty))); } @@ -673,25 +675,27 @@ impl &mem_categorization_ctxt { }; // (c) the deref is explicit in the resulting cmt - let deref_cmt = @{id:expr.id, span:expr.span, + let deref_cmt = @{id:elt.id(), span:elt.span(), cat:cat_deref(base_cmt, 0u, ptr), lp:deref_lp, mutbl:m, ty:mt.ty}; - comp(expr, deref_cmt, base_cmt.ty, m, mt.ty) + comp(elt, deref_cmt, base_cmt.ty, m, mt.ty) } deref_comp(_) => { // fixed-length vectors have no deref let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); - comp(expr, base_cmt, base_cmt.ty, m, mt.ty) + comp(elt, base_cmt, base_cmt.ty, m, mt.ty) } }; - fn comp(expr: @ast::expr, of_cmt: cmt, - vect: ty::t, mutbl: ast::mutability, ty: ty::t) -> cmt { + fn comp(elt: N, of_cmt: cmt, + vect: ty::t, mutbl: ast::mutability, + ty: ty::t) -> cmt + { let comp = comp_index(vect, mutbl); let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) ); - @{id:expr.id, span:expr.span, + @{id:elt.id(), span:elt.span(), cat:cat_comp(of_cmt, comp), lp:index_lp, mutbl:mutbl, ty:ty} } @@ -721,8 +725,6 @@ impl &mem_categorization_ctxt { fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { - op(cmt, pat); - // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. // @@ -757,13 +759,15 @@ impl &mem_categorization_ctxt { // and the id of `local(x)->@->@` is the id of the `y` pattern. - let _i = indenter(); let tcx = self.tcx; debug!("cat_pattern: id=%d pat=%s cmt=%s", pat.id, pprust::pat_to_str(pat, tcx.sess.intr()), self.cmt_to_repr(cmt)); + let _i = indenter(); + + op(cmt, pat); - match /*bad*/copy pat.node { + match pat.node { ast::pat_wild => { // _ } @@ -771,7 +775,7 @@ impl &mem_categorization_ctxt { ast::pat_enum(_, None) => { // variant(*) } - ast::pat_enum(_, Some(subpats)) => { + ast::pat_enum(_, Some(ref subpats)) => { match self.tcx.def_map.find(pat.id) { Some(ast::def_variant(enum_did, _)) => { // variant(x, y, z) @@ -803,15 +807,8 @@ impl &mem_categorization_ctxt { // nullary variant or identifier: ignore } - ast::pat_rec(field_pats, _) => { - // {f1: p1, ..., fN: pN} - for field_pats.each |fp| { - let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id); - self.cat_pattern(cmt_field, fp.pat, op); - } - } - - ast::pat_struct(_, field_pats, _) => { + ast::pat_rec(ref field_pats, _) | + ast::pat_struct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for field_pats.each |fp| { let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id); @@ -819,7 +816,7 @@ impl &mem_categorization_ctxt { } } - ast::pat_tup(subpats) => { + ast::pat_tup(ref subpats) => { // (p1, ..., pN) for subpats.each |subpat| { let subcmt = self.cat_tuple_elt(*subpat, cmt); @@ -834,7 +831,20 @@ impl &mem_categorization_ctxt { self.cat_pattern(subcmt, subpat, op); } - ast::pat_vec(*) | ast::pat_lit(_) | ast::pat_range(_, _) => { + ast::pat_vec(ref pats, opt_tail_pat) => { + for pats.each |pat| { + let elt_cmt = self.cat_index(*pat, cmt); + self.cat_pattern(elt_cmt, *pat, op); + } + + for opt_tail_pat.each |tail_pat| { + let tail_ty = self.tcx.ty(*tail_pat); + let tail_cmt = self.cat_rvalue(*tail_pat, tail_ty); + self.cat_pattern(tail_cmt, *tail_pat, op); + } + } + + ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 68198ae3f3fc9..60ed64b1c1d5e 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -25,6 +25,7 @@ use core::str; use core::vec; use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm}; use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk, capture_clause}; +use syntax::ast::{bind_by_value, bind_infer, bind_by_ref, bind_by_move}; use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding}; use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label}; use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self}; @@ -4521,6 +4522,10 @@ impl Resolver { struct or enum variant", self.session.str_of(ident)); + self.enforce_default_binding_mode( + pattern, + binding_mode, + "an enum variant"); self.record_def(pattern.id, def); } FoundStructOrEnumVariant(_) => { @@ -4537,6 +4542,10 @@ impl Resolver { constant", self.session.str_of(ident)); + self.enforce_default_binding_mode( + pattern, + binding_mode, + "a constant"); self.record_def(pattern.id, def); } FoundConst(_) => { @@ -5371,6 +5380,32 @@ impl Resolver { self.def_map.insert(node_id, def); } + fn enforce_default_binding_mode(pat: @pat, + pat_binding_mode: binding_mode, + descr: &str) { + match pat_binding_mode { + bind_infer => {} + bind_by_value => { + self.session.span_err( + pat.span, + fmt!("cannot use `copy` binding mode with %s", + descr)); + } + bind_by_move => { + self.session.span_err( + pat.span, + fmt!("cannot use `move` binding mode with %s", + descr)); + } + bind_by_ref(*) => { + self.session.span_err( + pat.span, + fmt!("cannot use `ref` binding mode with %s", + descr)); + } + } + } + // // main function checking // diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c82825795869a..0726104788cda 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -55,7 +55,7 @@ export ProvidedMethodSource; export ProvidedMethodInfo; export ProvidedMethodsMap; export InstantiatedTraitRef; -export TyVid, IntVid, FloatVid, FnVid, RegionVid, Vid; +export TyVid, IntVid, FloatVid, RegionVid, Vid; export br_hashmap; export is_instantiable; export node_id_to_type; @@ -119,6 +119,7 @@ export ty_opaque_box, mk_opaque_box; export ty_float, mk_float, mk_mach_float, type_is_fp; export ty_fn, FnTy, FnTyBase, FnMeta, FnSig, mk_fn; export ty_fn_proto, ty_fn_purity, ty_fn_ret, tys_in_fn_sig; +export ty_vstore; export replace_fn_return_type; 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; @@ -214,7 +215,7 @@ export ty_sort_str; export normalize_ty; export to_str; export bound_const; -export terr_no_integral_type, terr_no_floating_point_type; +export terr_int_mismatch, terr_float_mismatch, terr_sigil_mismatch; export 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; @@ -240,6 +241,7 @@ export AutoRef; export AutoRefKind, AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn; export iter_bound_traits_and_supertraits; export count_traits_and_supertraits; +export IntVarValue, IntType, UintType; // Data types @@ -702,6 +704,12 @@ enum sty { ty_unboxed_vec(mt), } +#[deriving_eq] +enum IntVarValue { + IntType(ast::int_ty), + UintType(ast::uint_ty), +} + enum terr_vstore_kind { terr_vec, terr_str, terr_fn, terr_trait } @@ -739,8 +747,8 @@ enum type_err { terr_sorts(expected_found), terr_self_substs, terr_integer_as_char, - terr_no_integral_type, - terr_no_floating_point_type, + terr_int_mismatch(expected_found), + terr_float_mismatch(expected_found) } enum param_bound { @@ -751,10 +759,16 @@ enum param_bound { bound_trait(t), } +#[deriving_eq] enum TyVid = uint; + +#[deriving_eq] enum IntVid = uint; + +#[deriving_eq] enum FloatVid = uint; -enum FnVid = uint; + +#[deriving_eq] #[auto_encode] #[auto_decode] enum RegionVid = uint; @@ -850,14 +864,6 @@ impl FloatVid: ToStr { pure fn to_str() -> ~str { fmt!("", self.to_uint()) } } -impl FnVid: Vid { - pure fn to_uint() -> uint { *self } -} - -impl FnVid: ToStr { - pure fn to_str() -> ~str { fmt!("", self.to_uint()) } -} - impl RegionVid: Vid { pure fn to_uint() -> uint { *self } } @@ -883,33 +889,36 @@ impl InferTy: ToStr { } } -impl RegionVid : to_bytes::IterBytes { - pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - (**self).iter_bytes(lsb0, f) +impl IntVarValue : ToStr { + pure fn to_str() -> ~str { + match self { + IntType(ref v) => v.to_str(), + UintType(ref v) => v.to_str(), + } } } impl TyVid : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - (**self).iter_bytes(lsb0, f) + self.to_uint().iter_bytes(lsb0, f) } } impl IntVid : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - (**self).iter_bytes(lsb0, f) + self.to_uint().iter_bytes(lsb0, f) } } impl FloatVid : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - (**self).iter_bytes(lsb0, f) + self.to_uint().iter_bytes(lsb0, f) } } -impl FnVid : to_bytes::IterBytes { +impl RegionVid : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - (**self).iter_bytes(lsb0, f) + self.to_uint().iter_bytes(lsb0, f) } } @@ -3005,10 +3014,20 @@ fn is_fn_ty(fty: t) -> bool { } } +pure fn ty_vstore(ty: t) -> vstore { + match get(ty).sty { + ty_evec(_, vstore) => vstore, + ty_estr(vstore) => vstore, + ref s => fail fmt!("ty_vstore() called on invalid sty: %?", s) + } +} + fn ty_region(ty: t) -> Region { match get(ty).sty { ty_rptr(r, _) => r, - ref s => fail fmt!("ty_region() invoked on non-rptr: %?", (*s)) + ty_evec(_, vstore_slice(r)) => r, + ty_estr(vstore_slice(r)) => r, + ref s => fail fmt!("ty_region() invoked on in appropriate ty: %?", (*s)) } } @@ -3564,17 +3583,18 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str { 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" - } terr_integer_as_char => { - ~"integer literals can't be inferred to char type \ - (try an explicit cast)" + fmt!("expected an integral type but found char") } - terr_no_floating_point_type => { - ~"couldn't determine an appropriate floating point type for \ - floating point literal" + terr_int_mismatch(ref values) => { + fmt!("expected %s but found %s", + values.expected.to_str(), + values.found.to_str()) + } + terr_float_mismatch(ref values) => { + fmt!("expected %s but found %s", + values.expected.to_str(), + values.found.to_str()) } } } @@ -4440,31 +4460,6 @@ impl vstore : cmp::Eq { pure fn ne(&self, other: &vstore) -> bool { !(*self).eq(other) } } -impl TyVid : cmp::Eq { - pure fn eq(&self, other: &TyVid) -> bool { *(*self) == *(*other) } - pure fn ne(&self, other: &TyVid) -> bool { *(*self) != *(*other) } -} - -impl IntVid : cmp::Eq { - pure fn eq(&self, other: &IntVid) -> bool { *(*self) == *(*other) } - pure fn ne(&self, other: &IntVid) -> bool { *(*self) != *(*other) } -} - -impl FloatVid : cmp::Eq { - pure fn eq(&self, other: &FloatVid) -> bool { *(*self) == *(*other) } - pure fn ne(&self, other: &FloatVid) -> bool { *(*self) != *(*other) } -} - -impl FnVid : cmp::Eq { - pure fn eq(&self, other: &FnVid) -> bool { *(*self) == *(*other) } - pure fn ne(&self, other: &FnVid) -> bool { *(*self) != *(*other) } -} - -impl RegionVid : cmp::Eq { - pure fn eq(&self, other: &RegionVid) -> bool { *(*self) == *(*other) } - pure fn ne(&self, other: &RegionVid) -> bool { *(*self) != *(*other) } -} - impl Region : cmp::Eq { pure fn eq(&self, other: &Region) -> bool { match (*self) { diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 2e82d531e1559..ebfce27a4c8bb 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -15,7 +15,7 @@ use middle::pat_util::{pat_is_variant_or_struct}; use middle::ty; use middle::typeck::check::demand; use middle::typeck::check::{check_block, check_expr_has_type, fn_ctxt}; -use middle::typeck::check::{instantiate_path, lookup_def, lookup_local}; +use middle::typeck::check::{instantiate_path, lookup_def}; use middle::typeck::check::{structure_of, valid_range_bounds}; use middle::typeck::require_same_types; @@ -365,8 +365,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { fcx.write_ty(pat.id, const_tpt.ty); } ast::pat_ident(bm, name, sub) if pat_is_binding(tcx.def_map, pat) => { - let vid = lookup_local(fcx, pat.span, pat.id); - let mut typ = ty::mk_var(tcx, vid); + let typ = fcx.local_ty(pat.span, pat.id); match bm { ast::bind_by_ref(mutbl) => { @@ -389,8 +388,7 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) { let canon_id = pcx.map.get(ast_util::path_to_ident(name)); if canon_id != pat.id { - let tv_id = lookup_local(fcx, pat.span, canon_id); - let ct = ty::mk_var(tcx, tv_id); + let ct = fcx.local_ty(pat.span, canon_id); demand::eqtype(fcx, pat.span, ct, typ); } fcx.write_ty(pat.id, typ); diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index e4b232500bbf7..c8a644fef101c 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -49,8 +49,11 @@ fn eqtype(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t) { } } -// Checks that the type `actual` can be assigned to `expected`. -fn assign(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) { +// Checks that the type `actual` can be coerced to `expected`. +fn coerce(fcx: @fn_ctxt, + sp: span, + expected: ty::t, + expr: @ast::expr) { let expr_ty = fcx.expr_ty(expr); match fcx.mk_assignty(expr, expr_ty, expected) { result::Ok(()) => { /* ok */ } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 7ef6ae598803e..86f58edd6c8a8 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -700,6 +700,8 @@ impl LookupContext { autoderefs: uint) -> Option { + let (self_ty, autoadjust) = + self.consider_reborrow(self_ty, autoderefs); match self.search_for_method(self_ty) { None => None, Some(move mme) => { @@ -707,13 +709,64 @@ impl LookupContext { adjustment (%u) to %d", autoderefs, self.self_expr.id); - self.fcx.write_autoderef_adjustment( - self.self_expr.id, autoderefs); + self.fcx.write_adjustment(self.self_expr.id, @autoadjust); Some(mme) } } } + fn consider_reborrow(&self, + self_ty: ty::t, + autoderefs: uint) -> (ty::t, ty::AutoAdjustment) + { + /*! + * + * In the event that we are invoking a method with a receiver + * of a linear borrowed type like `&mut T` or `&[mut T]`, + * we will "reborrow" the receiver implicitly. For example, if + * you have a call `r.inc()` and where `r` has type `&mut T`, + * then we treat that like `(&mut *r).inc()`. This avoids + * consuming the original pointer. + * + * You might think that this would be a natural byproduct of + * the auto-deref/auto-ref process. This is true for `@mut T` + * but not for an `&mut T` receiver. With `@mut T`, we would + * begin by testing for methods with a self type `@mut T`, + * then autoderef to `T`, then autoref to `&mut T`. But with + * an `&mut T` receiver the process begins with `&mut T`, only + * without any autoadjustments. + */ + + let tcx = self.tcx(); + return match ty::get(self_ty).sty { + ty::ty_rptr(_, self_mt) if self_mt.mutbl == m_mutbl => { + let region = self.infcx().next_region_var(self.expr.span, + self.expr.id); + (ty::mk_rptr(tcx, region, self_mt), + ty::AutoAdjustment { + autoderefs: autoderefs+1, + autoref: Some(ty::AutoRef {kind: AutoPtr, + region: region, + mutbl: self_mt.mutbl})}) + } + ty::ty_evec(self_mt, vstore_slice(_)) + if self_mt.mutbl == m_mutbl => { + let region = self.infcx().next_region_var(self.expr.span, + self.expr.id); + (ty::mk_evec(tcx, self_mt, vstore_slice(region)), + ty::AutoAdjustment { + autoderefs: autoderefs, + autoref: Some(ty::AutoRef {kind: AutoBorrowVec, + region: region, + mutbl: self_mt.mutbl})}) + } + _ => { + (self_ty, ty::AutoAdjustment {autoderefs: autoderefs, + autoref: None}) + } + }; + } + fn search_for_autosliced_method( &self, self_ty: ty::t, @@ -729,6 +782,7 @@ impl LookupContext { match ty::get(self_ty).sty { ty_evec(mt, vstore_box) | ty_evec(mt, vstore_uniq) | + ty_evec(mt, vstore_slice(_)) | // NDM(#3148) ty_evec(mt, vstore_fixed(_)) => { // First try to borrow to a slice let entry = self.search_for_some_kind_of_autorefd_method( diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index c0c990763fbc5..9b5cbbc4cc8f1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -105,7 +105,7 @@ use middle::typeck::{isr_alist, lookup_def_ccx, method_map_entry}; use middle::typeck::{method_origin, method_self, method_trait, no_params}; use middle::typeck::{require_same_types}; use util::common::{block_query, indenter, loop_query}; -use util::ppaux::{bound_region_to_str, expr_repr}; +use util::ppaux::{bound_region_to_str, expr_repr, pat_repr}; use util::ppaux; use core::either; @@ -127,7 +127,6 @@ use syntax::ast_util; use syntax::codemap::span; use syntax::codemap; use syntax::parse::token::special_idents; -use syntax::print::pprust::{expr_to_str, pat_to_str}; use syntax::print::pprust; use syntax::visit; use syntax; @@ -140,7 +139,6 @@ export regionck; export demand; export method; export fn_ctxt; -export lookup_local; export impl_self_ty; export DerefArgs; export DontDerefArgs; @@ -190,7 +188,7 @@ type self_info = { /// share the inherited fields. struct inherited { infcx: @infer::InferCtxt, - locals: HashMap, + locals: HashMap, node_types: HashMap, node_type_substs: HashMap, adjustments: HashMap @@ -377,8 +375,7 @@ fn check_fn(ccx: @crate_ctxt, } }; - // XXX: Bad copy. - gather_locals(fcx, decl, body, copy arg_tys, self_info); + gather_locals(fcx, decl, body, arg_tys, self_info); check_block(fcx, body); // We unify the tail expr's type with the @@ -415,30 +412,31 @@ fn check_fn(ccx: @crate_ctxt, fn gather_locals(fcx: @fn_ctxt, decl: &ast::fn_decl, body: ast::blk, - arg_tys: ~[ty::t], + arg_tys: &[ty::t], self_info: Option) { let tcx = fcx.ccx.tcx; - let assign = fn@(span: span, nid: ast::node_id, - ty_opt: Option) { - let var_id = fcx.infcx().next_ty_var_id(); - fcx.inh.locals.insert(nid, var_id); + let assign = fn@(nid: ast::node_id, ty_opt: Option) { match ty_opt { - None => {/* nothing to do */ } + None => { + // infer the variable's type + let var_id = fcx.infcx().next_ty_var_id(); + let var_ty = ty::mk_var(fcx.tcx(), var_id); + fcx.inh.locals.insert(nid, var_ty); + } Some(typ) => { - infer::mk_eqty(fcx.infcx(), false, span, - ty::mk_var(tcx, var_id), typ); + // take type that the user specified + fcx.inh.locals.insert(nid, typ); } } }; // Add the self parameter for self_info.each |self_info| { - assign(self_info.explicit_self.span, - self_info.self_id, - Some(self_info.self_ty)); + assign(self_info.self_id, Some(self_info.self_ty)); debug!("self is assigned to %s", - fcx.inh.locals.get(self_info.self_id).to_str()); + fcx.infcx().ty_to_str( + fcx.inh.locals.get(self_info.self_id))); } // Add formal parameters. @@ -446,7 +444,7 @@ fn check_fn(ccx: @crate_ctxt, // Create type variables for each argument. do pat_util::pat_bindings(tcx.def_map, input.pat) |_bm, pat_id, _sp, _path| { - assign(input.ty.span, pat_id, None); + assign(pat_id, None); } // Check the pattern. @@ -467,10 +465,11 @@ fn check_fn(ccx: @crate_ctxt, ast::ty_infer => None, _ => Some(fcx.to_ty(local.node.ty)) }; - assign(local.span, local.node.id, o_ty); - debug!("Local variable %s is assigned to %s", - pat_to_str(local.node.pat, tcx.sess.intr()), - fcx.inh.locals.get(local.node.id).to_str()); + assign(local.node.id, o_ty); + debug!("Local variable %s is assigned type %s", + fcx.pat_to_str(local.node.pat), + fcx.infcx().ty_to_str( + fcx.inh.locals.get(local.node.id))); visit::visit_local(local, e, v); }; @@ -479,10 +478,11 @@ fn check_fn(ccx: @crate_ctxt, match p.node { ast::pat_ident(_, path, _) if pat_util::pat_is_binding(fcx.ccx.tcx.def_map, p) => { - assign(p.span, p.id, None); + assign(p.id, None); debug!("Pattern binding %s is assigned to %s", tcx.sess.str_of(path.idents[0]), - fcx.inh.locals.get(p.id).to_str()); + fcx.infcx().ty_to_str( + fcx.inh.locals.get(p.id))); } _ => {} } @@ -695,6 +695,17 @@ impl @fn_ctxt: region_scope { impl @fn_ctxt { fn tag() -> ~str { fmt!("%x", ptr::addr_of(&(*self)) as uint) } + fn local_ty(span: span, nid: ast::node_id) -> ty::t { + match self.inh.locals.find(nid) { + Some(t) => t, + None => { + self.tcx().sess.span_bug( + span, + fmt!("No type for local variable %?", nid)); + } + } + } + fn expr_to_str(expr: @ast::expr) -> ~str { fmt!("expr(%?:%s)", expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr())) @@ -756,6 +767,10 @@ impl @fn_ctxt { expr_repr(self.tcx(), expr) } + fn pat_to_str(pat: @ast::pat) -> ~str { + pat_repr(self.tcx(), pat) + } + fn expr_ty(ex: @ast::expr) -> ty::t { match self.inh.node_types.find(ex.id) { Some(t) => t, @@ -809,7 +824,7 @@ impl @fn_ctxt { fn mk_assignty(expr: @ast::expr, sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> { - match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) { + match infer::mk_coercety(self.infcx(), false, expr.span, sub, sup) { Ok(None) => result::Ok(()), Err(ref e) => result::Err((*e)), Ok(Some(adjustment)) => { @@ -820,7 +835,7 @@ impl @fn_ctxt { } fn can_mk_assignty(sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> { - infer::can_mk_assignty(self.infcx(), sub, sup) + infer::can_mk_coercety(self.infcx(), sub, sup) } fn mk_eqty(a_is_expected: bool, span: span, @@ -983,12 +998,12 @@ fn check_expr_has_type( } } -fn check_expr_assignable_to_type( +fn check_expr_coercable_to_type( fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool { do check_expr_with_unifier(fcx, expr, Some(expected)) { - demand::assign(fcx, expr.span, expected, expr) + demand::coerce(fcx, expr.span, expected, expr) } } @@ -1222,7 +1237,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } // mismatch error happens in here - bot |= check_expr_assignable_to_type( + bot |= check_expr_coercable_to_type( fcx, *arg, formal_ty); } @@ -1240,7 +1255,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, -> bool { let mut bot = check_expr(fcx, lhs); let lhs_type = fcx.expr_ty(lhs); - bot |= check_expr_assignable_to_type(fcx, rhs, lhs_type); + bot |= check_expr_has_type(fcx, rhs, lhs_type); fcx.write_ty(id, ty::mk_nil(fcx.ccx.tcx)); return bot; } @@ -1356,10 +1371,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, fn check_for(fcx: @fn_ctxt, local: @ast::local, element_ty: ty::t, body: ast::blk, node_id: ast::node_id) -> bool { - let locid = lookup_local(fcx, local.span, local.node.id); - demand::suptype(fcx, local.span, - ty::mk_var(fcx.ccx.tcx, locid), - element_ty); + let local_ty = fcx.local_ty(local.span, local.node.id); + demand::suptype(fcx, local.span, local_ty, element_ty); let bot = check_decl_local(fcx, local); check_block_no_value(fcx, body); fcx.write_nil(node_id); @@ -1600,7 +1613,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, let fty = ty::mk_fn(tcx, copy fn_ty); debug!("check_expr_fn_with_unifier %s fty=%s", - expr_to_str(expr, tcx.sess.intr()), + fcx.expr_to_str(expr), fcx.infcx().ty_to_str(fty)); fcx.write_ty(expr.id, fty); @@ -1736,7 +1749,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, ty::lookup_field_type( tcx, class_id, field_id, substitutions); bot |= - check_expr_assignable_to_type( + check_expr_coercable_to_type( fcx, field.node.expr, expected_field_type); @@ -2548,15 +2561,15 @@ fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) { fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id, init: @ast::expr) -> bool { - let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.span, nid)); - return check_expr_assignable_to_type(fcx, init, lty); + let local_ty = fcx.local_ty(init.span, nid); + return check_expr_coercable_to_type(fcx, init, local_ty); } fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { let mut bot = false; let tcx = fcx.ccx.tcx; - let t = ty::mk_var(tcx, fcx.inh.locals.get(local.node.id)); + let t = fcx.local_ty(local.span, local.node.id); fcx.write_ty(local.node.id, t); match local.node.init { @@ -2713,7 +2726,7 @@ fn check_enum_variants(ccx: @crate_ctxt, do v.node.disr_expr.iter |e_ref| { let e = *e_ref; debug!("disr expr, checking %s", - expr_to_str(e, ccx.tcx.sess.intr())); + pprust::expr_to_str(e, ccx.tcx.sess.intr())); let declty = ty::mk_int(ccx.tcx); let fcx = blank_fn_ctxt(ccx, rty, e.id); check_const_with_ty(fcx, e.span, e, declty); @@ -2816,17 +2829,6 @@ fn check_enum_variants(ccx: @crate_ctxt, check_instantiable(ccx.tcx, sp, id); } -pub fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid { - match fcx.inh.locals.find(id) { - Some(x) => x, - _ => { - fcx.ccx.tcx.sess.span_fatal( - sp, - ~"internal error looking up a local var") - } - } -} - fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def { lookup_def_ccx(fcx.ccx, sp, id) } @@ -2838,9 +2840,8 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> match defn { ast::def_arg(nid, _, _) | ast::def_local(nid, _) | ast::def_self(nid, _) | ast::def_binding(nid, _) => { - assert (fcx.inh.locals.contains_key(nid)); - let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid)); - return no_params(typ); + let typ = fcx.local_ty(sp, nid); + return no_params(typ); } ast::def_fn(_, ast::extern_fn) => { // extern functions are just u8 pointers diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 05f23bddb0860..fe2fd4a6a22a1 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -30,7 +30,7 @@ this point a bit better. use core::prelude::*; use middle::freevars::get_freevars; -use middle::pat_util::pat_bindings; +use middle::pat_util::{pat_bindings, pat_is_binding}; use middle::ty::{encl_region, re_scope}; use middle::ty::{ty_fn_proto, vstore_box, vstore_fixed, vstore_slice}; use middle::ty::{vstore_uniq}; @@ -73,35 +73,44 @@ fn encl_region_of_def(fcx: @fn_ctxt, def: ast::def) -> ty::Region { } impl @rcx { - /// Try to resolve the type for the given node. - /// - /// Note one important point: we do not attempt to resolve *region - /// variables* here. This is because regionck is essentially adding - /// constraints to those region variables and so may yet influence - /// how they are resolved. - /// - /// Consider this silly example: - /// - /// fn borrow(x: &int) -> &int {x} - /// fn foo(x: @int) -> int { /* block: B */ - /// let b = borrow(x); /* region: */ - /// *b - /// } - /// - /// Here, the region of `b` will be ``. `` is constrainted - /// to be some subregion of the block B and some superregion of - /// the call. If we forced it now, we'd choose the smaller region - /// (the call). But that would make the *b illegal. Since we don't - /// resolve, the type of b will be `&.int` and then `*b` will require - /// that `` be bigger than the let and the `*b` expression, so we - /// will effectively resolve `` to be the block B. - fn resolve_type(unresolved_ty: ty::t) -> fres { - resolve_type(self.fcx.infcx(), unresolved_ty, - resolve_and_force_all_but_regions) + fn resolve_type(unresolved_ty: ty::t) -> ty::t { + /*! + * Try to resolve the type for the given node, returning + * t_err if an error results. Note that we never care + * about the details of the error, the same error will be + * detected and reported in the writeback phase. + * + * Note one important point: we do not attempt to resolve + * *region variables* here. This is because regionck is + * essentially adding constraints to those region variables + * and so may yet influence how they are resolved. + * + * Consider this silly example: + * + * fn borrow(x: &int) -> &int {x} + * fn foo(x: @int) -> int { // block: B + * let b = borrow(x); // region: + * *b + * } + * + * Here, the region of `b` will be ``. `` is + * constrainted to be some subregion of the block B and some + * superregion of the call. If we forced it now, we'd choose + * the smaller region (the call). But that would make the *b + * illegal. Since we don't resolve, the type of b will be + * `&.int` and then `*b` will require that `` be + * bigger than the let and the `*b` expression, so we will + * effectively resolve `` to be the block B. + */ + match resolve_type(self.fcx.infcx(), unresolved_ty, + resolve_and_force_all_but_regions) { + Ok(t) => t, + Err(_) => ty::mk_err(self.fcx.tcx()) + } } /// Try to resolve the type for the given node. - fn resolve_node_type(id: ast::node_id) -> fres { + fn resolve_node_type(id: ast::node_id) -> ty::t { self.resolve_type(self.fcx.node_ty(id)) } } @@ -170,8 +179,13 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) { } fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { - debug!("visit_expr(e=%s)", - pprust::expr_to_str(expr, rcx.fcx.tcx().sess.intr())); + debug!("visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); + + for rcx.fcx.inh.adjustments.find(expr.id).each |adjustment| { + for adjustment.autoref.each |autoref| { + guarantor::for_autoref(rcx, expr, *adjustment, autoref); + } + } match /*bad*/copy expr.node { ast::expr_path(*) => { @@ -242,41 +256,35 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { // particular case. There is an extensive comment on the // function check_cast_for_escaping_regions() in kind.rs // explaining how it goes about doing that. - match rcx.resolve_node_type(expr.id) { - result::Err(_) => { return; /*typeck will fail anyhow*/ } - result::Ok(target_ty) => { - match ty::get(target_ty).sty { - ty::ty_trait(_, _, vstore_slice(trait_region)) => { - let source_ty = rcx.fcx.expr_ty(source); - constrain_regions_in_type(rcx, trait_region, - expr.span, source_ty); - } - _ => () - } + let target_ty = rcx.resolve_node_type(expr.id); + match ty::get(target_ty).sty { + ty::ty_trait(_, _, vstore_slice(trait_region)) => { + let source_ty = rcx.fcx.expr_ty(source); + constrain_regions_in_type(rcx, trait_region, + expr.span, source_ty); } - }; + _ => () + } } - ast::expr_addr_of(*) => { - // FIXME(#3148) -- in some cases, we need to capture a - // dependency between the regions found in operand the - // resulting region type. See #3148 for more details. + ast::expr_addr_of(_, base) => { + guarantor::for_addr_of(rcx, expr, base); + } + + ast::expr_match(discr, ref arms) => { + guarantor::for_match(rcx, discr, *arms); } ast::expr_fn(*) | ast::expr_fn_block(*) => { - match rcx.resolve_node_type(expr.id) { - result::Err(_) => return, // Typechecking will fail anyhow. - result::Ok(function_type) => { - match ty::get(function_type).sty { - ty::ty_fn(ref fn_ty) => { - if fn_ty.meta.proto == ast::ProtoBorrowed { - constrain_free_variables( - rcx, fn_ty.meta.region, expr); - } - } - _ => () + let function_type = rcx.resolve_node_type(expr.id); + match ty::get(function_type).sty { + ty::ty_fn(ref fn_ty) => { + if fn_ty.meta.proto == ast::ProtoBorrowed { + constrain_free_variables( + rcx, fn_ty.meta.region, expr); } } + _ => () } } @@ -405,15 +413,10 @@ fn constrain_regions_in_type_of_node( // Try to resolve the type. If we encounter an error, then typeck // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. - let ty = match rcx.resolve_node_type(id) { - result::Err(_) => return true, - result::Ok(ty) => ty - }; - + let ty = rcx.resolve_node_type(id); debug!("constrain_regions_in_type_of_node(\ ty=%s, id=%d, encl_region=%?)", ty_to_str(tcx, ty), id, encl_region); - constrain_regions_in_type(rcx, encl_region, span, ty) } @@ -477,3 +480,451 @@ fn constrain_regions_in_type( } } } + +mod guarantor { + /*! + * + * The routines in this module are aiming to deal with the case + * where the lifetime resulting from a borrow is linked to the + * lifetime of the thing being borrowed. Imagine you have a + * borrowed pointer `b` with lifetime L1 and you have an + * expression `&*b`. The result of this borrow will be another + * borrowed pointer with lifetime L2 (which is an inference + * variable). The borrow checker is going to enforce the + * constraint that L2 < L1, because otherwise you are re-borrowing + * data for a lifetime larger than the original loan. However, + * without the routines in this module, the region inferencer would + * not know of this dependency and thus it might infer the + * lifetime of L2 to be greater than L1 (issue #3148). + * + * There are a number of troublesome scenarios in the test + * `region-dependent-addr-of.rs`, but here is one example: + * + * struct Foo { i: int } + * struct Bar { foo: Foo } + * fn get_i(x: &a/Bar) -> &a/int { + * let foo = &x.foo; // Lifetime L1 + * &foo.i // Lifetime L2 + * } + * + * Note that this comes up either with `&` expressions, `ref` + * bindings, and `autorefs`, which are the three ways to introduce + * a borrow. + * + * The key point here is that when you are borrowing a value that + * is "guaranteed" by a borrowed pointer, you must link the + * lifetime of that borrowed pointer (L1, here) to the lifetime of + * the borrow itself (L2). What do I mean by "guaranteed" by a + * borrowed pointer? Well, I would say the data "owned" by the + * borrowed pointer, except that a borrowed pointer never owns its + * contents, but the relation is the same. That is, I mean any + * data that is reached by first derefencing a borrowed pointer + * and then either traversing interior offsets or owned pointers. + * We say that the guarantor of such data it the region of the borrowed + * pointer that was traversed. + * + * NB: I really wanted to use the `mem_categorization` code here + * but I cannot because final type resolution hasn't happened yet. + * So this is very similar logic to what you would find there, + * but more special purpose. + */ + + use core::prelude::*; + use middle::typeck::check::regionck::{rcx, infallibly_mk_subr}; + use middle::ty; + use syntax::ast; + use syntax::codemap::span; + use util::ppaux::{ty_to_str}; + + pub fn for_addr_of(rcx: @rcx, expr: @ast::expr, base: @ast::expr) { + /*! + * + * Computes the guarantor for an expression `&base` and then + * ensures that the lifetime of the resulting pointer is linked. + */ + + debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base)); + let _i = ::util::common::indenter(); + + let guarantor = guarantor(rcx, base); + link(rcx, expr.span, expr.id, guarantor); + } + + pub fn for_match(rcx: @rcx, discr: @ast::expr, arms: &[ast::arm]) { + /*! + * + * Computes the guarantors for any ref bindings in a match and + * then ensures that the lifetime of the resulting pointer is + * linked. + */ + + let discr_guarantor = guarantor(rcx, discr); + for arms.each |arm| { + for arm.pats.each |pat| { + link_ref_bindings_in_pat(rcx, *pat, discr_guarantor); + } + } + } + + pub fn for_autoref(rcx: @rcx, + expr: @ast::expr, + adjustment: &ty::AutoAdjustment, + autoref: &ty::AutoRef) + { + /*! + * + * Computes the guarantor for an expression that has an + * autoref adjustment and links it to the lifetime of the + * autoref. This is only important when auto re-borrowing + * region pointers. + */ + + debug!("guarantor::for_autoref(expr=%s)", rcx.fcx.expr_to_str(expr)); + let _i = ::util::common::indenter(); + + let mut expr_ct = categorize_unadjusted(rcx, expr); + expr_ct = apply_autoderefs( + rcx, expr, adjustment.autoderefs, expr_ct); + for expr_ct.cat.guarantor.each |g| { + infallibly_mk_subr(rcx, true, expr.span, autoref.region, *g); + } + } + + fn link( + rcx: @rcx, + span: span, + id: ast::node_id, + guarantor: Option) + { + /*! + * + * Links the lifetime of the borrowed pointer resulting from a borrow + * to the lifetime of its guarantor. + */ + + debug!("opt_constrain_region(id=%?, guarantor=%?)", id, guarantor); + + let bound = match guarantor { + None => { return; } + Some(r) => { r } + }; + + // this routine is used for the result of ref bindings and & + // expressions, both of which always yield a region variable, so + // mk_subr should never fail. + let rptr_ty = rcx.resolve_node_type(id); + if !ty::type_contains_err(rptr_ty) { + debug!("rptr_ty=%s", ty_to_str(rcx.fcx.ccx.tcx, rptr_ty)); + let r = ty::ty_region(rptr_ty); + infallibly_mk_subr(rcx, true, span, r, bound); + } + } + + enum PointerCat { + NotPointer, + OwnedPointer, + BorrowedPointer(ty::Region), + OtherPointer + } + + struct ExprCategorization { + guarantor: Option, + pointer: PointerCat + } + + struct ExprCategorizationType { + cat: ExprCategorization, + ty: ty::t + } + + fn guarantor(rcx: @rcx, expr: @ast::expr) -> Option { + debug!("guarantor(expr=%s)", rcx.fcx.expr_to_str(expr)); + match expr.node { + ast::expr_unary(ast::deref, b) => { + let cat = categorize(rcx, b); + guarantor_of_deref(&cat) + } + ast::expr_field(b, _, _) => { + categorize(rcx, b).guarantor + } + ast::expr_index(b, _) => { + let cat = categorize(rcx, b); + guarantor_of_deref(&cat) + } + + ast::expr_paren(e) => { + guarantor(rcx, e) + } + + ast::expr_path(*) => { + // Either a variable or constant and hence resides + // in constant memory or on the stack frame. Either way, + // not guaranteed by a region pointer. + None + } + + // All of these expressions are rvalues and hence their + // value is not guaranteed by a region pointer. + ast::expr_mac(*) | + ast::expr_lit(_) | + ast::expr_unary(*) | + ast::expr_addr_of(*) | + ast::expr_binary(*) | + ast::expr_vstore(*) | + ast::expr_break(*) | + ast::expr_again(*) | + ast::expr_ret(*) | + ast::expr_log(*) | + ast::expr_fail(*) | + ast::expr_assert(*) | + ast::expr_while(*) | + ast::expr_loop(*) | + ast::expr_assign(*) | + ast::expr_swap(*) | + ast::expr_assign_op(*) | + ast::expr_cast(*) | + ast::expr_call(*) | + ast::expr_method_call(*) | + ast::expr_rec(*) | + ast::expr_struct(*) | + ast::expr_tup(*) | + ast::expr_if(*) | + ast::expr_match(*) | + ast::expr_fn(*) | + ast::expr_fn_block(*) | + ast::expr_loop_body(*) | + ast::expr_do_body(*) | + ast::expr_block(*) | + ast::expr_copy(*) | + ast::expr_unary_move(*) | + ast::expr_repeat(*) | + ast::expr_vec(*) => { + assert !ty::expr_is_lval( + rcx.fcx.tcx(), rcx.fcx.ccx.method_map, expr); + None + } + } + } + + fn categorize(rcx: @rcx, + expr: @ast::expr) -> ExprCategorization + { + debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr)); + let _i = ::util::common::indenter(); + + let mut expr_ct = categorize_unadjusted(rcx, expr); + debug!("before adjustments, cat=%?", expr_ct.cat); + + for rcx.fcx.inh.adjustments.find(expr.id).each |adjustment| { + debug!("adjustment=%?", adjustment); + + expr_ct = apply_autoderefs( + rcx, expr, adjustment.autoderefs, expr_ct); + + for adjustment.autoref.each |autoref| { + expr_ct.cat.guarantor = None; + expr_ct.cat.pointer = BorrowedPointer(autoref.region); + debug!("autoref, cat=%?", expr_ct.cat); + } + } + + debug!("result=%?", expr_ct.cat); + return expr_ct.cat; + } + + fn categorize_unadjusted(rcx: @rcx, + expr: @ast::expr) -> ExprCategorizationType { + debug!("categorize_unadjusted(expr=%s)", rcx.fcx.expr_to_str(expr)); + let _i = ::util::common::indenter(); + + let guarantor = { + if rcx.fcx.ccx.method_map.contains_key(expr.id) { + None + } else { + guarantor(rcx, expr) + } + }; + + let expr_ty = rcx.resolve_node_type(expr.id); + ExprCategorizationType { + cat: ExprCategorization { + guarantor: guarantor, + pointer: pointer_categorize(expr_ty) + }, + ty: expr_ty + } + } + + fn apply_autoderefs( + +rcx: @rcx, + +expr: @ast::expr, + +autoderefs: uint, + +ct: ExprCategorizationType) -> ExprCategorizationType + { + let mut ct = ct; + let tcx = rcx.fcx.ccx.tcx; + for uint::range(0, autoderefs) |_| { + ct.cat.guarantor = guarantor_of_deref(&ct.cat); + + match ty::deref(tcx, ct.ty, true) { + Some(mt) => { + ct.ty = mt.ty; + ct.cat.pointer = pointer_categorize(ct.ty); + } + None => { + tcx.sess.span_bug( + expr.span, + fmt!("Autoderef but type not derefable: %s", + ty_to_str(tcx, ct.ty))); + } + } + + debug!("autoderef, cat=%?", ct.cat); + } + return ct; + } + + fn pointer_categorize(ty: ty::t) -> PointerCat { + match ty::get(ty).sty { + ty::ty_rptr(r, _) | ty::ty_evec(_, ty::vstore_slice(r)) | + ty::ty_estr(ty::vstore_slice(r)) => { + BorrowedPointer(r) + } + ty::ty_uniq(*) | ty::ty_estr(ty::vstore_uniq) | + ty::ty_evec(_, ty::vstore_uniq) => { + OwnedPointer + } + ty::ty_box(*) | ty::ty_ptr(*) | + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) => { + OtherPointer + } + _ => { + NotPointer + } + } + } + + fn guarantor_of_deref(cat: &ExprCategorization) -> Option { + match cat.pointer { + NotPointer => cat.guarantor, + BorrowedPointer(r) => Some(r), + OwnedPointer => cat.guarantor, + OtherPointer => None + } + } + + fn link_ref_bindings_in_pat( + rcx: @rcx, + pat: @ast::pat, + guarantor: Option) + { + /*! + * + * Descends through the pattern, tracking the guarantor + * of the value being matched. When a ref binding is encountered, + * links the lifetime of that ref binding to the lifetime of + * the guarantor. We begin with the guarantor of the + * discriminant but of course as we go we may pass through + * other pointers. + */ + + debug!("link_ref_bindings_in_pat(pat=%s, guarantor=%?)", + rcx.fcx.pat_to_str(pat), guarantor); + let _i = ::util::common::indenter(); + + match pat.node { + ast::pat_wild => {} + ast::pat_ident(ast::bind_by_ref(_), _, opt_p) => { + link(rcx, pat.span, pat.id, guarantor); + + for opt_p.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + ast::pat_ident(_, _, opt_p) => { + for opt_p.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + ast::pat_enum(*) => {} + ast::pat_rec(ref fpats, _) | + ast::pat_struct(_, ref fpats, _) => { + for fpats.each |fpat| { + link_ref_bindings_in_pat(rcx, fpat.pat, guarantor); + } + } + ast::pat_tup(ref ps) => { + link_ref_bindings_in_pats(rcx, ps, guarantor) + } + ast::pat_box(p) => { + link_ref_bindings_in_pat(rcx, p, None) + } + ast::pat_uniq(p) => { + link_ref_bindings_in_pat(rcx, p, guarantor) + } + ast::pat_region(p) => { + let rptr_ty = rcx.resolve_node_type(pat.id); + if !ty::type_contains_err(rptr_ty) { + let r = ty::ty_region(rptr_ty); + link_ref_bindings_in_pat(rcx, p, Some(r)); + } + } + ast::pat_lit(*) => {} + ast::pat_range(*) => {} + ast::pat_vec(ref ps, ref opt_tail_pat) => { + let vec_ty = rcx.resolve_node_type(pat.id); + if !ty::type_contains_err(vec_ty) { + let vstore = ty::ty_vstore(vec_ty); + let guarantor1 = match vstore { + ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, + ty::vstore_slice(r) => Some(r), + ty::vstore_box => None + }; + + link_ref_bindings_in_pats(rcx, ps, guarantor1); + + for opt_tail_pat.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + } + } + } + + fn link_ref_bindings_in_pats(rcx: @rcx, + pats: &~[@ast::pat], + guarantor: Option) + { + for pats.each |pat| { + link_ref_bindings_in_pat(rcx, *pat, guarantor); + } + } + +} + +fn infallibly_mk_subr(rcx: @rcx, + a_is_expected: bool, + span: span, + a: ty::Region, + b: ty::Region) +{ + /*! + * + * Constraints `a` to be a subregion of `b`. In many cases, we + * know that this can never yield an error due to the way that + * region inferencing works. Therefore just report a bug if we + * ever see `Err(_)`. + */ + + match rcx.fcx.mk_subr(a_is_expected, span, a, b) { + result::Ok(()) => {} + result::Err(e) => { + rcx.fcx.ccx.tcx.sess.span_bug( + span, + fmt!("Supposedly infallible attempt to \ + make %? < %? failed: %?", + a, b, e)); + } + } +} \ No newline at end of file diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 1a665aa756611..e5dc91b7f179a 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -16,7 +16,7 @@ use core::prelude::*; use middle::pat_util; use middle::ty; -use middle::typeck::check::{fn_ctxt, lookup_local, self_info}; +use middle::typeck::check::{fn_ctxt, self_info}; use middle::typeck::infer::{force_all, resolve_all, resolve_region}; use middle::typeck::infer::{resolve_type}; use middle::typeck::infer; @@ -216,8 +216,7 @@ fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) { } fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) { if !wbcx.success { return; } - let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); - let var_ty = ty::mk_var(wbcx.fcx.tcx(), var_id); + let var_ty = wbcx.fcx.local_ty(l.span, l.node.id); match resolve_type(wbcx.fcx.infcx(), var_ty, resolve_all | force_all) { Ok(lty) => { debug!("Type for local %s (id %d) resolved to %s", diff --git a/src/librustc/middle/typeck/infer/assignment.rs b/src/librustc/middle/typeck/infer/assignment.rs deleted file mode 100644 index fef63cf9a42cc..0000000000000 --- a/src/librustc/middle/typeck/infer/assignment.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ______________________________________________________________________ -// Type assignment -// -// True if rvalues of type `a` can be assigned to lvalues of type `b`. -// This may cause borrowing to the region scope enclosing `a_node_id`. -// -// The strategy here is somewhat non-obvious. The problem is -// that the constraint we wish to contend with is not a subtyping -// constraint. Currently, for variables, we only track what it -// must be a subtype of, not what types it must be assignable to -// (or from). Possibly, we should track that, but I leave that -// refactoring for another day. -// -// Instead, we look at each variable involved and try to extract -// *some* sort of bound. Typically, the type a is the argument -// supplied to a call; it typically has a *lower bound* (which -// comes from having been assigned a value). What we'd actually -// *like* here is an upper-bound, but we generally don't have -// one. The type b is the expected type and it typically has a -// lower-bound too, which is good. -// -// The way we deal with the fact that we often don't have the -// bounds we need is to be a bit careful. We try to get *some* -// bound from each side, preferring the upper from a and the -// lower from b. If we fail to get a bound from both sides, then -// we just fall back to requiring that a <: b. -// -// Assuming we have a bound from both sides, we will then examine -// these bounds and see if they have the form (@M_a T_a, &rb.M_b T_b) -// (resp. ~M_a T_a, ~[M_a T_a], etc). If they do not, we fall back to -// subtyping. -// -// If they *do*, then we know that the two types could never be -// subtypes of one another. We will then construct a type @const T_b -// and ensure that type a is a subtype of that. This allows for the -// possibility of assigning from a type like (say) @~[mut T1] to a type -// &~[T2] where T1 <: T2. This might seem surprising, since the `@` -// points at mutable memory but the `&` points at immutable memory. -// This would in fact be unsound, except for the borrowck, which comes -// later and guarantees that such mutability conversions are safe. -// See borrowck for more details. Next we require that the region for -// the enclosing scope be a superregion of the region r. -// -// You might wonder why we don't make the type &e.const T_a where e is -// the enclosing region and check that &e.const T_a <: B. The reason -// is that the type of A is (generally) just a *lower-bound*, so this -// would be imposing that lower-bound also as the upper-bound on type -// A. But this upper-bound might be stricter than what is truly -// needed. - -use core::prelude::*; - -use middle::ty::TyVar; -use middle::ty; -use middle::typeck::infer::{ares, cres}; -use middle::typeck::infer::combine::CombineFields; -use middle::typeck::infer::sub::Sub; -use middle::typeck::infer::to_str::InferStr; -use util::common::{indent, indenter}; - -use core::option; -use syntax::ast::{m_const, m_imm, m_mutbl}; -use syntax::ast; - -fn to_ares(+c: cres) -> ares { - match c { - Ok(_) => Ok(None), - Err(ref e) => Err((*e)) - } -} - -// Note: Assign is not actually a combiner, in that it does not -// conform to the same interface, though it performs a similar -// function. -enum Assign = CombineFields; - -impl Assign { - fn tys(a: ty::t, b: ty::t) -> ares { - debug!("Assign.tys(%s => %s)", - a.inf_str(self.infcx), - b.inf_str(self.infcx)); - let _r = indenter(); - - debug!("Assign.tys: copying first type"); - let copy_a = copy ty::get(a).sty; - debug!("Assign.tys: copying second type"); - let copy_b = copy ty::get(b).sty; - debug!("Assign.tys: performing match"); - - let r = match (copy_a, copy_b) { - (ty::ty_bot, _) => { - Ok(None) - } - - (ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { - let nde_a = self.infcx.get(&self.infcx.ty_var_bindings, a_id); - let nde_b = self.infcx.get(&self.infcx.ty_var_bindings, b_id); - let a_bounds = nde_a.possible_types; - let b_bounds = nde_b.possible_types; - - let a_bnd = option::or(a_bounds.ub, a_bounds.lb); - let b_bnd = option::or(b_bounds.lb, b_bounds.ub); - self.assign_tys_or_sub(a, b, a_bnd, b_bnd) - } - - (ty::ty_infer(TyVar(a_id)), _) => { - let nde_a = self.infcx.get(&self.infcx.ty_var_bindings, a_id); - let a_bounds = nde_a.possible_types; - - let a_bnd = option::or(a_bounds.ub, a_bounds.lb); - self.assign_tys_or_sub(a, b, a_bnd, Some(b)) - } - - (_, ty::ty_infer(TyVar(b_id))) => { - let nde_b = self.infcx.get(&self.infcx.ty_var_bindings, b_id); - let b_bounds = nde_b.possible_types; - - let b_bnd = option::or(b_bounds.lb, b_bounds.ub); - self.assign_tys_or_sub(a, b, Some(a), b_bnd) - } - - (_, _) => { - self.assign_tys_or_sub(a, b, Some(a), Some(b)) - } - }; - - debug!("Assign.tys end"); - - move r - } -} - -priv impl Assign { - fn assign_tys_or_sub( - a: ty::t, b: ty::t, - +a_bnd: Option, +b_bnd: Option) -> ares { - - debug!("Assign.assign_tys_or_sub(%s => %s, %s => %s)", - a.inf_str(self.infcx), b.inf_str(self.infcx), - a_bnd.inf_str(self.infcx), b_bnd.inf_str(self.infcx)); - let _r = indenter(); - - fn is_borrowable(v: ty::vstore) -> bool { - match v { - ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => true, - ty::vstore_slice(_) => false - } - } - - fn borrowable_protos(a_p: ast::Proto, b_p: ast::Proto) -> bool { - match (a_p, b_p) { - (ast::ProtoBox, ast::ProtoBorrowed) => true, - (ast::ProtoUniq, ast::ProtoBorrowed) => true, - _ => false - } - } - - match (a_bnd, b_bnd) { - (Some(a_bnd), Some(b_bnd)) => { - match (/*bad*/copy ty::get(a_bnd).sty, - /*bad*/copy ty::get(b_bnd).sty) { - // check for a case where a non-region pointer (@, ~) is - // being assigned to a region pointer: - (ty::ty_box(_), ty::ty_rptr(r_b, mt_b)) => { - let nr_b = ty::mk_box(self.infcx.tcx, - ty::mt {ty: mt_b.ty, - mutbl: m_const}); - self.try_assign(1, ty::AutoPtr, - a, nr_b, - mt_b.mutbl, r_b) - } - (ty::ty_uniq(_), ty::ty_rptr(r_b, mt_b)) => { - let nr_b = ty::mk_uniq(self.infcx.tcx, - ty::mt {ty: mt_b.ty, - mutbl: m_const}); - self.try_assign(1, ty::AutoPtr, - a, nr_b, - mt_b.mutbl, r_b) - } - (ty::ty_estr(vs_a), - ty::ty_estr(ty::vstore_slice(r_b))) - if is_borrowable(vs_a) => { - let nr_b = ty::mk_estr(self.infcx.tcx, vs_a); - self.try_assign(0, ty::AutoBorrowVec, - a, nr_b, - m_imm, r_b) - } - - (ty::ty_evec(_, vs_a), - ty::ty_evec(mt_b, ty::vstore_slice(r_b))) - if is_borrowable(vs_a) => { - let nr_b = ty::mk_evec(self.infcx.tcx, - ty::mt {ty: mt_b.ty, - mutbl: m_const}, - vs_a); - self.try_assign(0, ty::AutoBorrowVec, - a, nr_b, - mt_b.mutbl, r_b) - } - - (ty::ty_fn(ref a_f), ty::ty_fn(ref b_f)) - if borrowable_protos(a_f.meta.proto, b_f.meta.proto) => { - let nr_b = ty::mk_fn(self.infcx.tcx, ty::FnTyBase { - meta: ty::FnMeta {proto: a_f.meta.proto, - ..b_f.meta}, - sig: copy b_f.sig - }); - self.try_assign(0, ty::AutoBorrowFn, - a, nr_b, m_imm, b_f.meta.region) - } - - (ty::ty_fn(ref a_f), ty::ty_fn(ref b_f)) - if a_f.meta.proto == ast::ProtoBare => { - let b1_f = ty::FnTyBase { - meta: ty::FnMeta {proto: ast::ProtoBare, - ..b_f.meta}, - sig: copy b_f.sig - }; - // Eventually we will need to add some sort of - // adjustment here so that trans can add an - // extra NULL env pointer: - to_ares(Sub(*self).fns(a_f, &b1_f)) - } - - // check for &T being assigned to *T: - (ty::ty_rptr(_, ref a_t), ty::ty_ptr(ref b_t)) => { - to_ares(Sub(*self).mts(*a_t, *b_t)) - } - - // otherwise, assignment follows normal subtype rules: - _ => { - to_ares(Sub(*self).tys(a, b)) - } - } - } - _ => { - // if insufficient bounds were available, just follow - // normal subtype rules: - to_ares(Sub(*self).tys(a, b)) - } - } - } - - /// Given an assignment from a type like `@a` to `&r_b/m nr_b`, - /// this function checks that `a <: nr_b`. In that case, the - /// assignment is permitted, so it constructs a fresh region - /// variable `r_a >= r_b` and returns a corresponding assignment - /// record. See the discussion at the top of this file for more - /// details. - fn try_assign(autoderefs: uint, - kind: ty::AutoRefKind, - a: ty::t, - nr_b: ty::t, - m: ast::mutability, - r_b: ty::Region) -> ares { - - debug!("try_assign(a=%s, nr_b=%s, m=%?, r_b=%s)", - a.inf_str(self.infcx), - nr_b.inf_str(self.infcx), - m, - r_b.inf_str(self.infcx)); - - do indent { - let sub = Sub(*self); - do sub.tys(a, nr_b).chain |_t| { - let r_a = self.infcx.next_region_var_nb(self.span); - do sub.contraregions(r_a, r_b).chain |_r| { - Ok(Some(@ty::AutoAdjustment { - autoderefs: autoderefs, - autoref: Some(ty::AutoRef { - kind: kind, - region: r_a, - mutbl: m - }) - })) - } - } - } - } -} - diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs new file mode 100644 index 0000000000000..f0fe2deffdfa8 --- /dev/null +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -0,0 +1,377 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/*! + +# Type Coercion + +Under certain circumstances we will coerce from one type to another, +for example by auto-borrowing. This occurs in situations where the +compiler has a firm 'expected type' that was supplied from the user, +and where the actual type is similar to that expected type in purpose +but not in representation (so actual subtyping is inappropriate). + +## Reborrowing + +Note that if we are expecting a borrowed pointer, we will *reborrow* +even if the argument provided was already a borrowed pointer. This is +useful for freezing mut/const things (that is, when the expected is &T +but you have &const T or &mut T) and also for avoiding the linearity +of mut things (when the expected is &mut T and you have &mut T). See +the various `src/test/run-pass/coerce-reborrow-*.rs` tests for +examples of where this is useful. + +## Subtle note + +When deciding what type coercions to consider, we do not attempt to +resolve any type variables we may encounter. This is because `b` +represents the expected type "as the user wrote it", meaning that if +the user defined a generic function like + + fn foo(a: A, b: A) { ... } + +and then we wrote `foo(&1, @2)`, we will not auto-borrow +either argument. In older code we went to some lengths to +resolve the `b` variable, which could mean that we'd +auto-borrow later arguments but not earlier ones, which +seems very confusing. + +## Subtler note + +However, right now, if the user manually specifies the +values for the type variables, as so: + + foo::<&int>(@1, @2) + +then we *will* auto-borrow, because we can't distinguish this from a +function that declared `&int`. This is inconsistent but it's easiest +at the moment. The right thing to do, I think, is to consider the +*unsubstituted* type when deciding whether to auto-borrow, but the +*substituted* type when considering the bounds and so forth. But most +of our methods don't give access to the unsubstituted type, and +rightly so because they'd be error-prone. So maybe the thing to do is +to actually determine the kind of coercions that should occur +separately and pass them in. Or maybe it's ok as is. Anyway, it's +sort of a minor point so I've opted to leave it for later---after all +we may want to adjust precisely when coercions occur. + +*/ + +use core::prelude::*; + +use middle::ty::{TyVar, AutoPtr, AutoBorrowVec, AutoBorrowFn}; +use middle::ty::{AutoAdjustment, AutoRef}; +use middle::ty::{vstore_slice, vstore_box, vstore_uniq, vstore_fixed}; +use middle::ty::{FnMeta, FnTyBase, mt}; +use middle::ty; +use middle::typeck::infer::{CoerceResult, resolve_type}; +use middle::typeck::infer::combine::CombineFields; +use middle::typeck::infer::sub::Sub; +use middle::typeck::infer::to_str::InferStr; +use middle::typeck::infer::resolve::try_resolve_tvar_shallow; +use util::common::{indent, indenter}; + +use core::option; +use syntax::ast::{m_const, m_imm, m_mutbl}; +use syntax::ast; + +// Note: Coerce is not actually a combiner, in that it does not +// conform to the same interface, though it performs a similar +// function. +pub enum Coerce = CombineFields; + +impl Coerce { + fn tys(&self, a: ty::t, b: ty::t) -> CoerceResult { + debug!("Coerce.tys(%s => %s)", + a.inf_str(self.infcx), + b.inf_str(self.infcx)); + let _indent = indenter(); + + // Examine the supertype and consider auto-borrowing. + // + // Note: does not attempt to resolve type variables we encounter. + // See above for details. + match ty::get(b).sty { + ty::ty_rptr(_, mt_b) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_pointer(a, sty_a, b, mt_b) + }; + } + + ty::ty_estr(vstore_slice(_)) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_string(a, sty_a, b) + }; + } + + ty::ty_evec(mt_b, vstore_slice(_)) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_vector(a, sty_a, b, mt_b) + }; + } + + ty::ty_fn(ref b_f) if b_f.meta.proto == ast::ProtoBorrowed => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_fn(a, sty_a, b) + }; + } + + ty::ty_ptr(_) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_unsafe_ptr(a, sty_a, b) + }; + } + + _ => {} + } + + do self.unpack_actual_value(a) |sty_a| { + match *sty_a { + ty::ty_fn(ref a_f) if a_f.meta.proto == ast::ProtoBare => { + // Bare functions are coercable to any closure type. + // + // FIXME(#3320) this should go away and be + // replaced with proper inference, got a patch + // underway - ndm + self.coerce_from_bare_fn(a, a_f, b) + } + _ => { + // Otherwise, just use subtyping rules. + self.subtype(a, b) + } + } + } + } + + fn subtype(&self, a: ty::t, b: ty::t) -> CoerceResult { + match Sub(**self).tys(a, b) { + Ok(_) => Ok(None), // No coercion required. + Err(ref e) => Err(*e) + } + } + + fn unpack_actual_value(&self, + a: ty::t, + f: &fn(&ty::sty) -> CoerceResult) -> CoerceResult + { + match resolve_type(self.infcx, a, try_resolve_tvar_shallow) { + Ok(t) => { + f(&ty::get(t).sty) + } + Err(e) => { + self.infcx.tcx.sess.span_bug( + self.span, + fmt!("Failed to resolve even without \ + any force options: %?", e)); + } + } + } + + fn coerce_borrowed_pointer(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t, + mt_b: ty::mt) -> CoerceResult + { + debug!("coerce_borrowed_pointer(a=%s, sty_a=%?, b=%s, mt_b=%?)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx), mt_b); + + // If we have a parameter of type `&M T_a` and the value + // provided is `expr`, we will be adding an implicit borrow, + // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, + // to type check, we will construct the type that `&M*expr` would + // yield. + + let sub = Sub(**self); + let r_borrow = self.infcx.next_region_var_nb(self.span); + + let inner_ty = match *sty_a { + ty::ty_box(mt_a) => mt_a.ty, + ty::ty_uniq(mt_a) => mt_a.ty, + ty::ty_rptr(_, mt_a) => mt_a.ty, + _ => { + return self.subtype(a, b); + } + }; + + let a_borrowed = ty::mk_rptr(self.infcx.tcx, + r_borrow, + mt {ty: inner_ty, mutbl: mt_b.mutbl}); + if_ok!(sub.tys(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 1, + autoref: Some(AutoRef { + kind: AutoPtr, + region: r_borrow, + mutbl: mt_b.mutbl + }) + })) + } + + fn coerce_borrowed_string(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) -> CoerceResult + { + debug!("coerce_borrowed_string(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + match *sty_a { + ty::ty_estr(vstore_box) | + ty::ty_estr(vstore_uniq) => {} + _ => { + return self.subtype(a, b); + } + }; + + let r_a = self.infcx.next_region_var_nb(self.span); + let a_borrowed = ty::mk_estr(self.infcx.tcx, vstore_slice(r_a)); + if_ok!(self.subtype(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 0, + autoref: Some(AutoRef { + kind: AutoBorrowVec, + region: r_a, + mutbl: m_imm + }) + })) + } + + fn coerce_borrowed_vector(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t, + mt_b: ty::mt) -> CoerceResult + { + debug!("coerce_borrowed_vector(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + let sub = Sub(**self); + let r_borrow = self.infcx.next_region_var_nb(self.span); + let ty_inner = match *sty_a { + ty::ty_evec(mt, _) => mt.ty, + _ => { + return self.subtype(a, b); + } + }; + + let a_borrowed = ty::mk_evec(self.infcx.tcx, + mt {ty: ty_inner, mutbl: mt_b.mutbl}, + vstore_slice(r_borrow)); + if_ok!(sub.tys(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 0, + autoref: Some(AutoRef { + kind: AutoBorrowVec, + region: r_borrow, + mutbl: mt_b.mutbl + }) + })) + } + + fn coerce_borrowed_fn(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) -> CoerceResult + { + debug!("coerce_borrowed_fn(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + let fn_ty = match *sty_a { + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBox => {f} + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoUniq => {f} + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBare => { + return self.coerce_from_bare_fn(a, f, b); + } + _ => { + return self.subtype(a, b); + } + }; + + let r_borrow = self.infcx.next_region_var_nb(self.span); + let meta = FnMeta {proto: ast::ProtoBorrowed, + region: r_borrow, + ..fn_ty.meta}; + let a_borrowed = ty::mk_fn(self.infcx.tcx, + FnTyBase {meta: meta, + sig: copy fn_ty.sig}); + + if_ok!(self.subtype(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 0, + autoref: Some(AutoRef { + kind: AutoBorrowFn, + region: r_borrow, + mutbl: m_imm + }) + })) + } + + fn coerce_from_bare_fn(&self, + a: ty::t, + fn_ty_a: &ty::FnTy, + b: ty::t) -> CoerceResult + { + do self.unpack_actual_value(b) |sty_b| { + self.coerce_from_bare_fn_post_unpack(a, fn_ty_a, b, sty_b) + } + } + + fn coerce_from_bare_fn_post_unpack(&self, + a: ty::t, + fn_ty_a: &ty::FnTy, + b: ty::t, + sty_b: &ty::sty) -> CoerceResult + { + debug!("coerce_from_bare_fn(a=%s, b=%s)", + a.inf_str(self.infcx), b.inf_str(self.infcx)); + + let fn_ty_b = match *sty_b { + ty::ty_fn(ref f) if f.meta.proto != ast::ProtoBare => {f} + _ => { + return self.subtype(a, b); + } + }; + + // for now, bare fn and closures have the same + // representation + let a_adapted = ty::mk_fn(self.infcx.tcx, + FnTyBase {meta: copy fn_ty_b.meta, + sig: copy fn_ty_a.sig}); + self.subtype(a_adapted, b) + } + + fn coerce_unsafe_ptr(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) -> CoerceResult + { + debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + let mt_a = match *sty_a { + ty::ty_rptr(_, mt) => mt, + _ => { + return self.subtype(a, b); + } + }; + + // borrowed pointers and unsafe pointers have the same + // representation, so just check that the types which they + // point at are compatible: + let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a); + self.subtype(a_unsafe, b) + } +} diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index c5e99bc5c0362..e9946ae7c13e6 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -57,6 +57,7 @@ use core::prelude::*; use middle::ty::{FloatVar, FnTyBase, FnMeta, FnSig, IntVar, TyVar}; +use middle::ty::{IntType, UintType}; use middle::ty; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lub::Lub; @@ -112,8 +113,8 @@ pub struct CombineFields { } fn expected_found( - self: &C, +a: T, +b: T) -> ty::expected_found { - + self: &C, +a: T, +b: T) -> ty::expected_found +{ if self.a_is_expected() { ty::expected_found {expected: move a, found: move b} } else { @@ -392,7 +393,7 @@ fn super_tys( self: &C, a: ty::t, b: ty::t) -> cres { let tcx = self.infcx().tcx; - match (/*bad*/copy ty::get(a).sty, /*bad*/copy ty::get(b).sty) { + return match (/*bad*/copy ty::get(a).sty, /*bad*/copy ty::get(b).sty) { // The "subtype" ought to be handling cases involving bot or var: (ty::ty_bot, _) | (_, ty::ty_bot) | @@ -405,53 +406,46 @@ fn super_tys( b.inf_str(self.infcx()))); } - // Relate integral variables to other types - (ty::ty_infer(IntVar(a_id)), ty::ty_infer(IntVar(b_id))) => { - if_ok!(self.infcx().simple_vars(&self.infcx().int_var_bindings, - ty::terr_no_integral_type, - a_id, b_id)); - Ok(a) - } - (ty::ty_infer(IntVar(v_id)), ty::ty_int(v)) | - (ty::ty_int(v), ty::ty_infer(IntVar(v_id))) => { - if v == ast::ty_char { - Err(ty::terr_integer_as_char) - } else { - if_ok!(self.infcx().simple_var_t(&self.infcx().int_var_bindings, - ty::terr_no_integral_type, - v_id, IntType(v))); - Ok(ty::mk_mach_int(tcx, v)) + // Relate integral variables to other types + (ty::ty_infer(IntVar(a_id)), ty::ty_infer(IntVar(b_id))) => { + if_ok!(self.infcx().simple_vars(self.a_is_expected(), + a_id, b_id)); + Ok(a) + } + (ty::ty_infer(IntVar(v_id)), ty::ty_int(v)) => { + unify_integral_variable(self, self.a_is_expected(), + v_id, IntType(v)) + } + (ty::ty_int(v), ty::ty_infer(IntVar(v_id))) => { + unify_integral_variable(self, !self.a_is_expected(), + v_id, IntType(v)) + } + (ty::ty_infer(IntVar(v_id)), ty::ty_uint(v)) => { + unify_integral_variable(self, self.a_is_expected(), + v_id, UintType(v)) + } + (ty::ty_uint(v), ty::ty_infer(IntVar(v_id))) => { + unify_integral_variable(self, !self.a_is_expected(), + v_id, UintType(v)) } - } - (ty::ty_infer(IntVar(v_id)), ty::ty_uint(v)) | - (ty::ty_uint(v), ty::ty_infer(IntVar(v_id))) => { - if_ok!(self.infcx().simple_var_t(&self.infcx().int_var_bindings, - ty::terr_no_integral_type, - v_id, UintType(v))); - Ok(ty::mk_mach_uint(tcx, v)) - } - // Relate floating-point variables to other types - (ty::ty_infer(FloatVar(a_id)), ty::ty_infer(FloatVar(b_id))) => { - if_ok!(self.infcx().simple_vars(&self.infcx().float_var_bindings, - ty::terr_no_floating_point_type, - a_id, b_id)); - Ok(a) - } - (ty::ty_infer(FloatVar(v_id)), ty::ty_float(v)) | - (ty::ty_float(v), ty::ty_infer(FloatVar(v_id))) => { - if_ok!(self.infcx().simple_var_t(&self.infcx().float_var_bindings, - ty::terr_no_floating_point_type, - v_id, v)); - Ok(ty::mk_mach_float(tcx, v)) - } + // Relate floating-point variables to other types + (ty::ty_infer(FloatVar(a_id)), ty::ty_infer(FloatVar(b_id))) => { + if_ok!(self.infcx().simple_vars(self.a_is_expected(), + a_id, b_id)); + Ok(a) + } + (ty::ty_infer(FloatVar(v_id)), ty::ty_float(v)) => { + unify_float_variable(self, self.a_is_expected(), v_id, v) + } + (ty::ty_float(v), ty::ty_infer(FloatVar(v_id))) => { + unify_float_variable(self, !self.a_is_expected(), v_id, v) + } (ty::ty_int(_), _) | (ty::ty_uint(_), _) | (ty::ty_float(_), _) => { - let as_ = /*bad*/copy ty::get(a).sty; - let bs = /*bad*/copy ty::get(b).sty; - if as_ == bs { + if ty::get(a).sty == ty::get(b).sty { Ok(a) } else { Err(ty::terr_sorts(expected_found(self, a, b))) @@ -516,11 +510,9 @@ fn super_tys( } (ty::ty_rptr(a_r, a_mt), ty::ty_rptr(b_r, b_mt)) => { - do self.contraregions(a_r, b_r).chain |r| { - do self.mts(a_mt, b_mt).chain |mt| { - Ok(ty::mk_rptr(tcx, r, mt)) - } - } + let r = if_ok!(self.contraregions(a_r, b_r)); + let mt = if_ok!(self.mts(a_mt, b_mt)); + Ok(ty::mk_rptr(tcx, r, mt)) } (ty::ty_evec(a_mt, vs_a), ty::ty_evec(b_mt, vs_b)) => { @@ -565,5 +557,34 @@ fn super_tys( } _ => Err(ty::terr_sorts(expected_found(self, a, b))) + }; + + fn unify_integral_variable( + self: &C, + vid_is_expected: bool, + vid: ty::IntVid, + val: ty::IntVarValue) -> cres + { + let tcx = self.infcx().tcx; + if val == IntType(ast::ty_char) { + Err(ty::terr_integer_as_char) + } else { + if_ok!(self.infcx().simple_var_t(vid_is_expected, vid, val)); + match val { + IntType(v) => Ok(ty::mk_mach_int(tcx, v)), + UintType(v) => Ok(ty::mk_mach_uint(tcx, v)) + } + } } -} + + fn unify_float_variable( + self: &C, + vid_is_expected: bool, + vid: ty::FloatVid, + val: ast::float_ty) -> cres + { + let tcx = self.infcx().tcx; + if_ok!(self.infcx().simple_var_t(vid_is_expected, vid, val)); + Ok(ty::mk_mach_float(tcx, val)) + } +} \ No newline at end of file diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 9783aee0848c9..14b5a3fb4e877 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -89,9 +89,9 @@ impl FnMeta: LatticeValue { } impl CombineFields { - fn var_sub_var( + fn var_sub_var>>( &self, - vb: &ValsAndBindings>, +a_id: V, +b_id: V) -> ures { @@ -102,8 +102,8 @@ impl CombineFields { * top of infer.rs*/ // Need to make sub_id a subtype of sup_id. - let node_a = self.infcx.get(vb, a_id); - let node_b = self.infcx.get(vb, b_id); + let node_a = self.infcx.get(a_id); + let node_b = self.infcx.get(b_id); let a_id = node_a.root; let b_id = node_b.root; let a_bounds = node_a.possible_types; @@ -135,17 +135,17 @@ impl CombineFields { // A remains a subtype of B. Actually, there are other options, // but that's the route we choose to take. - self.infcx.unify(vb, &node_a, &node_b, |new_root, new_rank| { - self.set_var_to_merged_bounds(vb, new_root, + self.infcx.unify(&node_a, &node_b, |new_root, new_rank| { + self.set_var_to_merged_bounds(new_root, &a_bounds, &b_bounds, new_rank) }) } /// make variable a subtype of T - fn var_sub_t( + fn var_sub_t>>( &self, - vb: &ValsAndBindings>, +a_id: V, +b: T) -> ures { @@ -153,7 +153,7 @@ impl CombineFields { * * Make a variable (`a_id`) a subtype of the concrete type `b` */ - let node_a = self.infcx.get(vb, a_id); + let node_a = self.infcx.get(a_id); let a_id = node_a.root; let a_bounds = &node_a.possible_types; let b_bounds = &{lb: None, ub: Some(b)}; @@ -164,12 +164,12 @@ impl CombineFields { b.inf_str(self.infcx)); self.set_var_to_merged_bounds( - vb, a_id, a_bounds, b_bounds, node_a.rank) + a_id, a_bounds, b_bounds, node_a.rank) } - fn t_sub_var( + fn t_sub_var>>( &self, - vb: &ValsAndBindings>, +a: T, +b_id: V) -> ures { @@ -178,7 +178,7 @@ impl CombineFields { * Make a concrete type (`a`) a subtype of the variable `b_id` */ let a_bounds = &{lb: Some(a), ub: None}; - let node_b = self.infcx.get(vb, b_id); + let node_b = self.infcx.get(b_id); let b_id = node_b.root; let b_bounds = &node_b.possible_types; @@ -188,7 +188,7 @@ impl CombineFields { b_bounds.inf_str(self.infcx)); self.set_var_to_merged_bounds( - vb, b_id, a_bounds, b_bounds, node_b.rank) + b_id, a_bounds, b_bounds, node_b.rank) } fn merge_bnd( @@ -219,10 +219,9 @@ impl CombineFields { } } - fn set_var_to_merged_bounds( + fn set_var_to_merged_bounds>>( &self, - vb: &ValsAndBindings>, +v_id: V, a: &Bounds, b: &Bounds, @@ -278,7 +277,7 @@ impl CombineFields { // the new bounds must themselves // be relatable: let () = if_ok!(self.bnds(&bounds.lb, &bounds.ub)); - self.infcx.set(vb, v_id, Root(bounds, rank)); + self.infcx.set(v_id, Root(bounds, rank)); uok() } @@ -369,8 +368,7 @@ fn super_lattice_tys( (_, ty::ty_bot) => { return self.ty_bot(a); } (ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { - let r = if_ok!(lattice_vars(self, &self.infcx().ty_var_bindings, - a_id, b_id, + let r = if_ok!(lattice_vars(self, a_id, b_id, |x, y| self.tys(*x, *y))); return match r { VarResult(v) => Ok(ty::mk_var(tcx, v)), @@ -379,14 +377,12 @@ fn super_lattice_tys( } (ty::ty_infer(TyVar(a_id)), _) => { - return lattice_var_and_t(self, &self.infcx().ty_var_bindings, - a_id, &b, + return lattice_var_and_t(self, a_id, &b, |x, y| self.tys(*x, *y)); } (_, ty::ty_infer(TyVar(b_id))) => { - return lattice_var_and_t(self, &self.infcx().ty_var_bindings, - b_id, &a, + return lattice_var_and_t(self, b_id, &a, |x, y| self.tys(*x, *y)); } @@ -419,17 +415,16 @@ enum LatticeVarResult { * result is a variable. This is indicated with a `VarResult` * return. */ fn lattice_vars( + T:Copy InferStr LatticeValue, + V:Copy Eq ToStr Vid UnifyVid>>( self: &L, // defines whether we want LUB or GLB - vb: &ValsAndBindings>, // relevant variable bindings +a_vid: V, // first variable +b_vid: V, // second variable lattice_dir_op: LatticeDirOp) // LUB or GLB operation on types -> cres> { - let nde_a = self.infcx().get(vb, a_vid); - let nde_b = self.infcx().get(vb, b_vid); + let nde_a = self.infcx().get(a_vid); + let nde_b = self.infcx().get(b_vid); let a_vid = nde_a.root; let b_vid = nde_b.root; let a_bounds = &nde_a.possible_types; @@ -461,22 +456,21 @@ fn lattice_vars( + T:Copy InferStr LatticeValue, + V:Copy Eq ToStr Vid UnifyVid>>( self: &L, - vb: &ValsAndBindings>, +a_id: V, b: &T, lattice_dir_op: LatticeDirOp) -> cres { - let nde_a = self.infcx().get(vb, a_id); + let nde_a = self.infcx().get(a_id); let a_id = nde_a.root; let a_bounds = &nde_a.possible_types; @@ -501,7 +495,7 @@ fn lattice_var_and_t = Option; type Bounds = {lb: Bound, ub: Bound}; @@ -349,13 +348,7 @@ type Bounds = {lb: Bound, ub: Bound}; type cres = Result; // "combine result" type ures = cres<()>; // "unify result" type fres = Result; // "fixup result" -type ares = cres>; // "assignment result" - -#[deriving_eq] -enum IntVarValue { - IntType(ast::int_ty), - UintType(ast::uint_ty), -} +type CoerceResult = cres>; struct InferCtxt { tcx: ty::ctxt, @@ -364,22 +357,14 @@ struct InferCtxt { // types that might instantiate a general type variable have an // order, represented by its upper and lower bounds. ty_var_bindings: ValsAndBindings>, - - // Number of type variables created thus far. mut ty_var_counter: uint, - // The types that might instantiate an integral type variable are - // represented by an int_ty_set. + // Map from integral variable to the kind of integer it represents int_var_bindings: ValsAndBindings>, - - // Number of integral variables created thus far. mut int_var_counter: uint, - // The types that might instantiate a floating-point type variable are - // represented by an float_ty_set. + // Map from floating variable to the kind of float it represents float_var_bindings: ValsAndBindings>, - - // Number of floating-point variables created thus far. mut float_var_counter: uint, // For region variables. @@ -471,22 +456,23 @@ fn mk_eqty(cx: @InferCtxt, a_is_expected: bool, span: span, }.to_ures() } -fn mk_assignty(cx: @InferCtxt, a_is_expected: bool, span: span, - a: ty::t, b: ty::t) -> ares { - debug!("mk_assignty(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); +fn mk_coercety(cx: @InferCtxt, a_is_expected: bool, span: span, + a: ty::t, b: ty::t) -> CoerceResult +{ + debug!("mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.commit { - Assign(cx.combine_fields(a_is_expected, span)).tys(a, b) + Coerce(cx.combine_fields(a_is_expected, span)).tys(a, b) } } } -fn can_mk_assignty(cx: @InferCtxt, a: ty::t, b: ty::t) -> ures { - debug!("can_mk_assignty(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); +fn can_mk_coercety(cx: @InferCtxt, a: ty::t, b: ty::t) -> ures { + debug!("can_mk_coercety(%s -> %s)", a.inf_str(cx), b.inf_str(cx)); do indent { do cx.probe { let span = ast_util::dummy_sp(); - Assign(cx.combine_fields(true, span)).tys(a, b) + Coerce(cx.combine_fields(true, span)).tys(a, b) } }.to_ures() } @@ -582,6 +568,7 @@ fn rollback_to( struct Snapshot { ty_var_bindings_len: uint, int_var_bindings_len: uint, + float_var_bindings_len: uint, region_vars_snapshot: uint, } @@ -607,6 +594,8 @@ impl @InferCtxt { self.ty_var_bindings.bindings.len(), int_var_bindings_len: self.int_var_bindings.bindings.len(), + float_var_bindings_len: + self.float_var_bindings.bindings.len(), region_vars_snapshot: self.region_vars.start_snapshot(), } @@ -616,9 +605,11 @@ impl @InferCtxt { debug!("rollback!"); rollback_to(&self.ty_var_bindings, snapshot.ty_var_bindings_len); - // FIXME(#3211) -- int_var not transactional + // FIXME(#3211) -- int_var and float_var not transactional //rollback_to(&self.int_var_bindings, // snapshot.int_var_bindings_len); + //rollback_to(&self.float_var_bindings, + // snapshot.float_var_bindings_len); self.region_vars.rollback_to(snapshot.region_vars_snapshot); } @@ -664,6 +655,16 @@ impl @InferCtxt { } } +fn next_simple_var( + +counter: &mut uint, + +bindings: &ValsAndBindings>) -> uint +{ + let id = *counter; + *counter += 1; + bindings.vals.insert(id, Root(None, 0)); + return id; +} + impl @InferCtxt { fn next_ty_var_id() -> TyVid { let id = self.ty_var_counter; @@ -682,11 +683,8 @@ impl @InferCtxt { } fn next_int_var_id() -> IntVid { - let id = self.int_var_counter; - self.int_var_counter += 1; - - self.int_var_bindings.vals.insert(id, Root(None, 0)); - return IntVid(id); + IntVid(next_simple_var(&mut self.int_var_counter, + &self.int_var_bindings)) } fn next_int_var() -> ty::t { @@ -694,11 +692,8 @@ impl @InferCtxt { } fn next_float_var_id() -> FloatVid { - let id = self.float_var_counter; - self.float_var_counter += 1; - - self.float_var_bindings.vals.insert(id, Root(None, 0)); - return FloatVid(id); + FloatVid(next_simple_var(&mut self.float_var_counter, + &self.float_var_bindings)) } fn next_float_var() -> ty::t { diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index 23be3b208271c..b25c4db8a90b7 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -49,11 +49,10 @@ use core::prelude::*; use middle::ty::{FloatVar, FloatVid, IntVar, IntVid, RegionVid, TyVar, TyVid}; -use middle::ty::{type_is_bot}; +use middle::ty::{type_is_bot, IntType, UintType}; use middle::ty; use middle::typeck::infer::{cyclic_ty, fixup_err, fres, InferCtxt}; use middle::typeck::infer::{region_var_bound_by_region_var, unresolved_ty}; -use middle::typeck::infer::{IntType, UintType}; use middle::typeck::infer::to_str::InferStr; use middle::typeck::infer::unify::Root; use util::common::{indent, indenter}; @@ -79,6 +78,7 @@ const force_all: uint = 0b1111100000; const not_regions: uint = !(force_rvar | resolve_rvar); +const try_resolve_tvar_shallow: uint = 0; const resolve_and_force_all_but_regions: uint = (resolve_all | force_all) & not_regions; @@ -219,7 +219,7 @@ impl ResolveState { // tend to carry more restrictions or higher // perf. penalties, so it pays to know more. - let nde = self.infcx.get(&self.infcx.ty_var_bindings, vid); + let nde = self.infcx.get(vid); let bounds = nde.possible_types; let t1 = match bounds { @@ -243,7 +243,7 @@ impl ResolveState { return ty::mk_int_var(self.infcx.tcx, vid); } - let node = self.infcx.get(&self.infcx.int_var_bindings, vid); + let node = self.infcx.get(vid); match node.possible_types { Some(IntType(t)) => ty::mk_mach_int(self.infcx.tcx, t), Some(UintType(t)) => ty::mk_mach_uint(self.infcx.tcx, t), @@ -251,9 +251,8 @@ impl ResolveState { if self.should(force_ivar) { // As a last resort, default to int. let ty = ty::mk_int(self.infcx.tcx); - self.infcx.set( - &self.infcx.int_var_bindings, vid, - Root(Some(IntType(ast::ty_i)), node.rank)); + self.infcx.set(vid, + Root(Some(IntType(ast::ty_i)), node.rank)); ty } else { ty::mk_int_var(self.infcx.tcx, vid) @@ -267,17 +266,14 @@ impl ResolveState { return ty::mk_float_var(self.infcx.tcx, vid); } - let node = self.infcx.get(&self.infcx.float_var_bindings, vid); + let node = self.infcx.get(vid); match node.possible_types { Some(t) => ty::mk_mach_float(self.infcx.tcx, t), None => { if self.should(force_fvar) { // As a last resort, default to float. let ty = ty::mk_float(self.infcx.tcx); - self.infcx.set( - &self.infcx.float_var_bindings, - vid, - Root(Some(ast::ty_f), node.rank)); + self.infcx.set(vid, Root(Some(ast::ty_f), node.rank)); ty } else { ty::mk_float_var(self.infcx.tcx, vid) diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index aa6721fb22983..4252580ac46cd 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -102,38 +102,31 @@ impl Sub: Combine { debug!("%s.tys(%s, %s)", self.tag(), a.inf_str(self.infcx), b.inf_str(self.infcx)); if a == b { return Ok(a); } - do indent { - match (ty::get(a).sty, ty::get(b).sty) { - (ty::ty_bot, _) => { - Ok(a) - } + let _indenter = indenter(); + match (ty::get(a).sty, ty::get(b).sty) { + (ty::ty_bot, _) => { + Ok(a) + } - (ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { - do self.var_sub_var(&self.infcx.ty_var_bindings, - a_id, b_id).then { - Ok(a) - } - } - (ty::ty_infer(TyVar(a_id)), _) => { - do self.var_sub_t(&self.infcx.ty_var_bindings, - a_id, b).then { - Ok(a) - } - } - (_, ty::ty_infer(TyVar(b_id))) => { - do self.t_sub_var(&self.infcx.ty_var_bindings, - a, b_id).then { - Ok(a) - } - } + (ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { + if_ok!(self.var_sub_var(a_id, b_id)); + Ok(a) + } + (ty::ty_infer(TyVar(a_id)), _) => { + if_ok!(self.var_sub_t(a_id, b)); + Ok(a) + } + (_, ty::ty_infer(TyVar(b_id))) => { + if_ok!(self.t_sub_var(a, b_id)); + Ok(a) + } - (_, ty::ty_bot) => { - Err(ty::terr_sorts(expected_found(&self, a, b))) - } + (_, ty::ty_bot) => { + Err(ty::terr_sorts(expected_found(&self, a, b))) + } - _ => { - super_tys(&self, a, b) - } + _ => { + super_tys(&self, a, b) } } } diff --git a/src/librustc/middle/typeck/infer/to_str.rs b/src/librustc/middle/typeck/infer/to_str.rs index 42f516fe6d880..a6d316db42872 100644 --- a/src/librustc/middle/typeck/infer/to_str.rs +++ b/src/librustc/middle/typeck/infer/to_str.rs @@ -10,10 +10,10 @@ use core::prelude::*; -use middle::ty::{FnMeta, FnTyBase, FnSig, FnVid, Vid}; +use middle::ty::{FnMeta, FnTyBase, FnSig, Vid}; +use middle::ty::{IntVarValue, IntType, UintType}; use middle::ty; use middle::typeck::infer::{Bound, Bounds}; -use middle::typeck::infer::{IntVarValue, IntType, UintType}; use middle::typeck::infer::InferCtxt; use middle::typeck::infer::unify::{Redirect, Root, VarValue}; use util::ppaux::{mt_to_str, ty_to_str}; @@ -25,23 +25,23 @@ use core::uint; use core::str; pub trait InferStr { - fn inf_str(cx: @InferCtxt) -> ~str; + fn inf_str(&self, cx: &InferCtxt) -> ~str; } impl ty::t : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { - ty_to_str(cx.tcx, self) + fn inf_str(&self, cx: &InferCtxt) -> ~str { + ty_to_str(cx.tcx, *self) } } impl FnMeta : InferStr { - fn inf_str(_cx: @InferCtxt) -> ~str { - fmt!("%?", self) + fn inf_str(&self, _cx: &InferCtxt) -> ~str { + fmt!("%?", *self) } } impl FnSig : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { + fn inf_str(&self, cx: &InferCtxt) -> ~str { fmt!("(%s) -> %s", str::connect(self.inputs.map(|a| a.ty.inf_str(cx)), ", "), self.output.inf_str(cx)) @@ -49,26 +49,26 @@ impl FnSig : InferStr { } impl FnTyBase : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { + fn inf_str(&self, cx: &InferCtxt) -> ~str { fmt!("%s%s", self.meta.inf_str(cx), self.sig.inf_str(cx)) } } impl ty::mt : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { - mt_to_str(cx.tcx, self) + fn inf_str(&self, cx: &InferCtxt) -> ~str { + mt_to_str(cx.tcx, *self) } } impl ty::Region : InferStr { - fn inf_str(_cx: @InferCtxt) -> ~str { - fmt!("%?", self) + fn inf_str(&self, _cx: &InferCtxt) -> ~str { + fmt!("%?", *self) } } impl Bound : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { - match self { + fn inf_str(&self, cx: &InferCtxt) -> ~str { + match *self { Some(ref v) => v.inf_str(cx), None => ~"none" } @@ -76,7 +76,7 @@ impl Bound : InferStr { } impl Bounds : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { + fn inf_str(&self, cx: &InferCtxt) -> ~str { fmt!("{%s <: %s}", self.lb.inf_str(cx), self.ub.inf_str(cx)) @@ -84,8 +84,8 @@ impl Bounds : InferStr { } impl VarValue : InferStr { - fn inf_str(cx: @InferCtxt) -> ~str { - match self { + fn inf_str(&self, cx: &InferCtxt) -> ~str { + match *self { Redirect(ref vid) => fmt!("Redirect(%s)", vid.to_str()), Root(ref pt, rk) => fmt!("Root(%s, %s)", pt.inf_str(cx), uint::to_str(rk, 10u)) @@ -94,17 +94,13 @@ impl VarValue : InferStr { } impl IntVarValue : InferStr { - fn inf_str(_cx: @InferCtxt) -> ~str { - match self { - IntType(t) => ast_util::int_ty_to_str(t), - UintType(t) => ast_util::uint_ty_to_str(t) - } + fn inf_str(&self, _cx: &InferCtxt) -> ~str { + self.to_str() } } impl ast::float_ty : InferStr { - fn inf_str(_cx: @InferCtxt) -> ~str { - ast_util::float_ty_to_str(self) + fn inf_str(&self, _cx: &InferCtxt) -> ~str { + self.to_str() } } - diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs index 77bd46eea2dfd..6c831427b031e 100644 --- a/src/librustc/middle/typeck/infer/unify.rs +++ b/src/librustc/middle/typeck/infer/unify.rs @@ -9,24 +9,24 @@ // except according to those terms. use core::prelude::*; +use core::result; +use std::smallintmap::SmallIntMap; -use middle::ty::Vid; +use middle::ty::{Vid, expected_found, IntVarValue}; use middle::ty; use middle::typeck::infer::{Bound, Bounds, cres, uok, ures}; use middle::typeck::infer::combine::Combine; use middle::typeck::infer::InferCtxt; use middle::typeck::infer::to_str::InferStr; +use syntax::ast; use util::common::{indent, indenter}; -use core::result; -use std::smallintmap::SmallIntMap; - enum VarValue { Redirect(V), Root(T, uint), } -struct ValsAndBindings { +struct ValsAndBindings { vals: SmallIntMap>, mut bindings: ~[(V, VarValue)], } @@ -37,11 +37,15 @@ struct Node { rank: uint, } -impl @InferCtxt { - fn get( - vb: &ValsAndBindings, - vid: V) - -> Node +trait UnifyVid { + static fn appropriate_vals_and_bindings(infcx: &v/InferCtxt) + -> &v/ValsAndBindings; +} + +impl InferCtxt { + fn get>( + &self, + +vid: V) -> Node { /*! * @@ -50,6 +54,7 @@ impl @InferCtxt { * http://en.wikipedia.org/wiki/Disjoint-set_data_structure */ + let vb = UnifyVid::appropriate_vals_and_bindings(self); let vid_u = vid.to_uint(); match vb.vals.find(vid_u) { None => { @@ -57,9 +62,9 @@ impl @InferCtxt { } Some(ref var_val) => { match (*var_val) { - Redirect(ref vid) => { - let node = self.get(vb, (*vid)); - if node.root.ne(vid) { + Redirect(vid) => { + let node: Node = self.get(vid); + if node.root != vid { // Path compression vb.vals.insert(vid.to_uint(), Redirect(node.root)); } @@ -73,9 +78,9 @@ impl @InferCtxt { } } - fn set( - vb: &ValsAndBindings, - vid: V, + fn set>( + &self, + +vid: V, +new_v: VarValue) { /*! @@ -83,6 +88,7 @@ impl @InferCtxt { * Sets the value for `vid` to `new_v`. `vid` MUST be a root node! */ + let vb = UnifyVid::appropriate_vals_and_bindings(self); let old_v = vb.vals.get(vid.to_uint()); vb.bindings.push((vid, old_v)); vb.vals.insert(vid.to_uint(), new_v); @@ -91,8 +97,8 @@ impl @InferCtxt { vid.to_str(), old_v.inf_str(self), new_v.inf_str(self)); } - fn unify( - vb: &ValsAndBindings, + fn unify, R>( + &self, node_a: &Node, node_b: &Node, op: &fn(new_root: V, new_rank: uint) -> R @@ -108,17 +114,17 @@ impl @InferCtxt { if node_a.rank > node_b.rank { // a has greater rank, so a should become b's parent, // i.e., b should redirect to a. - self.set(vb, node_b.root, Redirect(node_a.root)); + self.set(node_b.root, Redirect(node_a.root)); op(node_a.root, node_a.rank) } else if node_a.rank < node_b.rank { // b has greater rank, so a should redirect to b. - self.set(vb, node_a.root, Redirect(node_b.root)); + self.set(node_a.root, Redirect(node_b.root)); op(node_b.root, node_b.rank) } else { // If equal, redirect one to the other and increment the // other's rank. assert node_a.rank == node_b.rank; - self.set(vb, node_b.root, Redirect(node_a.root)); + self.set(node_b.root, Redirect(node_a.root)); op(node_a.root, node_a.rank + 1) } } @@ -129,12 +135,30 @@ impl @InferCtxt { // Code to handle simple variables like ints, floats---anything that // doesn't have a subtyping relationship we need to worry about. -impl @InferCtxt { - fn simple_vars( - vb: &ValsAndBindings>, - err: ty::type_err, - a_id: V, - b_id: V) -> ures +trait SimplyUnifiable { + static fn to_type_err(expected_found) -> ty::type_err; +} + +fn mk_err(+a_is_expected: bool, + +a_t: T, + +b_t: T) -> ures +{ + if a_is_expected { + Err(SimplyUnifiable::to_type_err( + ty::expected_found {expected: a_t, found: b_t})) + } else { + Err(SimplyUnifiable::to_type_err( + ty::expected_found {expected: b_t, found: a_t})) + } +} + +impl InferCtxt { + fn simple_vars>>( + &self, + +a_is_expected: bool, + +a_id: V, + +b_id: V) -> ures { /*! * @@ -143,8 +167,8 @@ impl @InferCtxt { * have already been associated with a value, then those two * values must be the same. */ - let node_a = self.get(vb, a_id); - let node_b = self.get(vb, b_id); + let node_a = self.get(a_id); + let node_b = self.get(b_id); let a_id = node_a.root; let b_id = node_b.root; @@ -155,22 +179,24 @@ impl @InferCtxt { (&None, &None) => None, (&Some(ref v), &None) | (&None, &Some(ref v)) => Some(*v), (&Some(ref v1), &Some(ref v2)) => { - if *v1 != *v2 { return Err(err); } + if *v1 != *v2 { + return mk_err(a_is_expected, *v1, *v2); + } Some(*v1) } }; - self.unify(vb, &node_a, &node_b, |new_root, new_rank| { - self.set(vb, new_root, Root(combined, new_rank)); + self.unify(&node_a, &node_b, |new_root, new_rank| { + self.set(new_root, Root(combined, new_rank)); }); return uok(); } - fn simple_var_t( - vb: &ValsAndBindings>, - err: ty::type_err, - a_id: V, - b: T) -> ures + fn simple_var_t>>( + +a_is_expected: bool, + +a_id: V, + +b: T) -> ures { /*! * @@ -179,19 +205,66 @@ impl @InferCtxt { * if `a_id` already has a value, it must be the same as * `b`. */ - let node_a = self.get(vb, a_id); + let node_a = self.get(a_id); let a_id = node_a.root; - if node_a.possible_types.is_none() { - self.set(vb, a_id, Root(Some(b), node_a.rank)); - return uok(); - } + match node_a.possible_types { + None => { + self.set(a_id, Root(Some(b), node_a.rank)); + return uok(); + } - if node_a.possible_types == Some(b) { - return uok(); + Some(ref a_t) => { + if *a_t == b { + return uok(); + } else { + return mk_err(a_is_expected, *a_t, b); + } + } } + } +} - return Err(err); +// ______________________________________________________________________ + +impl ty::TyVid : UnifyVid> { + static fn appropriate_vals_and_bindings(infcx: &v/InferCtxt) + -> &v/ValsAndBindings> + { + return &infcx.ty_var_bindings; + } +} + +impl ty::IntVid : UnifyVid> { + static fn appropriate_vals_and_bindings(infcx: &v/InferCtxt) + -> &v/ValsAndBindings> + { + return &infcx.int_var_bindings; + } +} + +impl IntVarValue : SimplyUnifiable { + static fn to_type_err(err: expected_found) + -> ty::type_err + { + return ty::terr_int_mismatch(err); } } +impl ty::FloatVid : UnifyVid> { + static fn appropriate_vals_and_bindings(infcx: &v/InferCtxt) + -> &v/ValsAndBindings> + { + return &infcx.float_var_bindings; + } +} + +impl ast::float_ty : SimplyUnifiable { + static fn to_type_err(err: expected_found) + -> ty::type_err + { + return ty::terr_float_mismatch(err); + } +} + + diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6b6912915fd25..ac079f8f04450 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -270,6 +270,12 @@ fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str { pprust::expr_to_str(expr, cx.sess.intr())) } +fn pat_repr(cx: ctxt, pat: @ast::pat) -> ~str { + fmt!("pat(%d: %s)", + pat.id, + pprust::pat_to_str(pat, cx.sess.intr())) +} + fn tys_to_str(cx: ctxt, ts: &[t]) -> ~str { let tstrs = ts.map(|t| ty_to_str(cx, *t)); fmt!("(%s)", str::connect(tstrs, ", ")) diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index feabb678d6686..5f16f7155b61c 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -25,11 +25,11 @@ use core::prelude::*; // FIXME (#2347): Should not be @; there's a bug somewhere in rustc that // requires this to be. -struct SmallIntMap_ { +struct SmallIntMap_ { v: DVec>, } -pub enum SmallIntMap { +pub enum SmallIntMap { SmallIntMap_(@SmallIntMap_) } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 65216e55493ce..502a372b330c3 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -973,6 +973,12 @@ enum trait_method { #[auto_decode] enum int_ty { ty_i, ty_char, ty_i8, ty_i16, ty_i32, ty_i64, } +impl int_ty : ToStr { + pure fn to_str() -> ~str { + ::ast_util::int_ty_to_str(self) + } +} + impl int_ty : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { (*self as u8).iter_bytes(lsb0, f) @@ -1003,6 +1009,12 @@ impl int_ty : cmp::Eq { #[auto_decode] enum uint_ty { ty_u, ty_u8, ty_u16, ty_u32, ty_u64, } +impl uint_ty : ToStr { + pure fn to_str() -> ~str { + ::ast_util::uint_ty_to_str(self) + } +} + impl uint_ty : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { (*self as u8).iter_bytes(lsb0, f) @@ -1031,6 +1043,12 @@ impl uint_ty : cmp::Eq { #[auto_decode] enum float_ty { ty_f, ty_f32, ty_f64, } +impl float_ty : ToStr { + pure fn to_str() -> ~str { + ::ast_util::float_ty_to_str(self) + } +} + impl float_ty : to_bytes::IterBytes { pure fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { (*self as u8).iter_bytes(lsb0, f) diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs index 77f230311358e..1bb516e831fd5 100644 --- a/src/libsyntax/ext/auto_encode.rs +++ b/src/libsyntax/ext/auto_encode.rs @@ -879,7 +879,7 @@ fn ser_variant( let pat_node = if pats.is_empty() { ast::pat_ident( - ast::bind_by_ref(ast::m_imm), + ast::bind_infer, cx.path(span, ~[v_name]), None ) diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5a8a1d8753dee..5ff6953960619 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -267,8 +267,14 @@ fn mk_pat(cx: ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat { @ast::pat { id: cx.next_id(), node: pat, span: span } } fn mk_pat_ident(cx: ext_ctxt, span: span, ident: ast::ident) -> @ast::pat { + mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_value) +} +fn mk_pat_ident_with_binding_mode(cx: ext_ctxt, + span: span, + ident: ast::ident, + bm: ast::binding_mode) -> @ast::pat { let path = mk_raw_path(span, ~[ ident ]); - let pat = ast::pat_ident(ast::bind_by_value, path, None); + let pat = ast::pat_ident(bm, path, None); mk_pat(cx, span, move pat) } fn mk_pat_enum(cx: ext_ctxt, diff --git a/src/libsyntax/ext/deriving.rs b/src/libsyntax/ext/deriving.rs index d7059fc197783..d542b104e541e 100644 --- a/src/libsyntax/ext/deriving.rs +++ b/src/libsyntax/ext/deriving.rs @@ -363,7 +363,8 @@ fn create_enum_variant_pattern(cx: ext_ctxt, match variant.node.kind { tuple_variant_kind(ref variant_args) => { if variant_args.len() == 0 { - return build::mk_pat_ident(cx, span, variant_ident); + return build::mk_pat_ident_with_binding_mode( + cx, span, variant_ident, ast::bind_infer); } let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs index 839d4137ba923..19cdfe784d2b0 100644 --- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs +++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs @@ -16,9 +16,9 @@ use std::map; fn main() { let buggy_map :HashMap = HashMap::(); - buggy_map.insert(42, ~1); //~ ERROR illegal borrow - + buggy_map.insert(42, &*~1); //~ ERROR illegal borrow + // but it is ok if we use a temporary let tmp = ~2; - buggy_map.insert(43, tmp); + buggy_map.insert(43, &*tmp); } diff --git a/src/test/compile-fail/alt-vec-illegal-tail-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs similarity index 67% rename from src/test/compile-fail/alt-vec-illegal-tail-loan.rs rename to src/test/compile-fail/borrowck-vec-pattern-element-loan.rs index 01f2707721677..358917de85fb9 100644 --- a/src/test/compile-fail/alt-vec-illegal-tail-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs @@ -1,9 +1,7 @@ -// xfail-test - fn a() -> &[int] { let vec = [1, 2, 3, 4]; - let tail = match vec { - [_a, ..tail] => tail, //~ ERROR illegal borrow + let tail = match vec { //~ ERROR illegal borrow + [_a, ..tail] => tail, _ => fail ~"foo" }; move tail diff --git a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs new file mode 100644 index 0000000000000..27902100373a9 --- /dev/null +++ b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs @@ -0,0 +1,12 @@ +fn a() { + let mut v = ~[1, 2, 3]; + match v { + [_a, ..tail] => { + v.push(tail[0] + tail[1]); //~ ERROR conflicts with prior loan + } + _ => {} + }; +} + +fn main() {} + diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs new file mode 100644 index 0000000000000..50feff707adfa --- /dev/null +++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs @@ -0,0 +1,21 @@ +fn a() { + let mut vec = [~1, ~2, ~3]; + match vec { + [~ref _a] => { + vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + } + _ => fail ~"foo" + } +} + +fn b() { + let mut vec = [~1, ~2, ~3]; + match vec { + [.._b] => { + vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + } + } +} + +fn main() {} + diff --git a/src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs similarity index 65% rename from src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs rename to src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs index 36d019058ae79..6477fd9fb2cf9 100644 --- a/src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs @@ -1,7 +1,7 @@ fn a() -> &int { let vec = [1, 2, 3, 4]; - let tail = match vec { - [_a, ..tail] => &tail[0], //~ ERROR illegal borrow + let tail = match vec { //~ ERROR illegal borrow + [_a, ..tail] => &tail[0], _ => fail ~"foo" }; move tail diff --git a/src/test/compile-fail/coerce-bad-variance.rs b/src/test/compile-fail/coerce-bad-variance.rs new file mode 100644 index 0000000000000..c4cdbcb67e2be --- /dev/null +++ b/src/test/compile-fail/coerce-bad-variance.rs @@ -0,0 +1,17 @@ +fn mutate(x: &mut @const int) { + *x = @3; +} + +fn give_away1(y: @mut @mut int) { + mutate(y); //~ ERROR values differ in mutability +} + +fn give_away2(y: @mut @const int) { + mutate(y); +} + +fn give_away3(y: @mut @int) { + mutate(y); //~ ERROR values differ in mutability +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/issue-4500.rs b/src/test/compile-fail/issue-4500.rs index 83938293d7548..356a64498219a 100644 --- a/src/test/compile-fail/issue-4500.rs +++ b/src/test/compile-fail/issue-4500.rs @@ -10,6 +10,5 @@ fn main () { let mut _p: & int = & 4; - _p = ~3; //~ ERROR illegal borrow: borrowed value does not live long enough - //~^ NOTE ...but borrowed value is only valid for the statement + _p = &*~3; //~ ERROR illegal borrow } diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index c7c6aec9f1df8..69f07e3e77492 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -20,11 +20,12 @@ fn repeater(v: @A) -> repeat { } fn main() { - // Here, an error results as the type of y is inferred to - // repeater<</3> where lt is the block. - let y = { - let x: &blk/int = &3; //~ ERROR cannot infer an appropriate lifetime + // Error results because the type of is inferred to be + // repeat<&blk/int> where blk is the lifetime of the block below. + + let y = { //~ ERROR reference is not valid + let x: &blk/int = &3; repeater(@x) }; - assert 3 == *(y.get()); + assert 3 == *(y.get()); //~ ERROR reference is not valid } \ No newline at end of file diff --git a/src/test/compile-fail/regions-scoping.rs b/src/test/compile-fail/regions-scoping.rs index 1f59e9a8128cc..f999242973345 100644 --- a/src/test/compile-fail/regions-scoping.rs +++ b/src/test/compile-fail/regions-scoping.rs @@ -25,8 +25,7 @@ fn nested(x: &x/int) { // (1) //~^ ERROR cannot infer an appropriate lifetime return z(y, x, x); - //~^ ERROR mismatched types: expected `&x/int` but found `&y/int` - //~^^ ERROR mismatched types: expected `&y/int` but found `&x/int` + //~^ ERROR cannot infer an appropriate lifetime } ) |foo| { diff --git a/src/test/run-pass/coerce-reborrow-imm-ptr-arg.rs b/src/test/run-pass/coerce-reborrow-imm-ptr-arg.rs new file mode 100644 index 0000000000000..3c9748f29d986 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-imm-ptr-arg.rs @@ -0,0 +1,17 @@ +pure fn negate(x: &int) -> int { + -*x +} + +fn negate_mut(y: &mut int) -> int { + negate(y) +} + +fn negate_imm(y: &int) -> int { + negate(y) +} + +fn negate_const(y: &const int) -> int { + negate(y) +} + +fn main() {} diff --git a/src/test/run-pass/coerce-reborrow-imm-ptr-rcvr.rs b/src/test/run-pass/coerce-reborrow-imm-ptr-rcvr.rs new file mode 100644 index 0000000000000..0d8f40677f8b8 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-imm-ptr-rcvr.rs @@ -0,0 +1,16 @@ +struct SpeechMaker { + speeches: uint +} + +impl SpeechMaker { + pure fn how_many(&self) -> uint { self.speeches } +} + +fn foo(speaker: &const SpeechMaker) -> uint { + speaker.how_many() + 33 +} + +fn main() { + let mut lincoln = SpeechMaker {speeches: 22}; + assert foo(&const lincoln) == 55; +} diff --git a/src/test/run-pass/coerce-reborrow-imm-vec-arg.rs b/src/test/run-pass/coerce-reborrow-imm-vec-arg.rs new file mode 100644 index 0000000000000..54a6b35b8baa7 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-imm-vec-arg.rs @@ -0,0 +1,19 @@ +pure fn sum(x: &[int]) -> int { + let mut sum = 0; + for x.each |y| { sum += *y; } + return sum; +} + +fn sum_mut(y: &[mut int]) -> int { + sum(y) +} + +fn sum_imm(y: &[int]) -> int { + sum(y) +} + +fn sum_const(y: &[const int]) -> int { + sum(y) +} + +fn main() {} \ No newline at end of file diff --git a/src/test/run-pass/coerce-reborrow-imm-vec-rcvr.rs b/src/test/run-pass/coerce-reborrow-imm-vec-rcvr.rs new file mode 100644 index 0000000000000..24fb5cbd88301 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-imm-vec-rcvr.rs @@ -0,0 +1,18 @@ +fn foo(v: &[const uint]) -> ~[uint] { + v.to_vec() +} + +fn bar(v: &[mut uint]) -> ~[uint] { + v.to_vec() +} + +fn bip(v: &[uint]) -> ~[uint] { + v.to_vec() +} + +fn main() { + let mut the_vec = ~[1, 2, 3, 100]; + assert the_vec == foo(the_vec); + assert the_vec == bar(the_vec); + assert the_vec == bip(the_vec); +} diff --git a/src/test/run-pass/coerce-reborrow-mut-ptr-arg.rs b/src/test/run-pass/coerce-reborrow-mut-ptr-arg.rs new file mode 100644 index 0000000000000..4579907dfbd49 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-mut-ptr-arg.rs @@ -0,0 +1,22 @@ +struct SpeechMaker { + speeches: uint +} + +fn talk(x: &mut SpeechMaker) { + x.speeches += 1; +} + +fn give_a_few_speeches(speaker: &mut SpeechMaker) { + + // Here speaker is reborrowed for each call, so we don't get errors + // about speaker being moved. + + talk(speaker); + talk(speaker); + talk(speaker); +} + +fn main() { + let mut lincoln = SpeechMaker {speeches: 22}; + give_a_few_speeches(&mut lincoln); +} diff --git a/src/test/run-pass/coerce-reborrow-mut-ptr-rcvr.rs b/src/test/run-pass/coerce-reborrow-mut-ptr-rcvr.rs new file mode 100644 index 0000000000000..c915c01416e8e --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-mut-ptr-rcvr.rs @@ -0,0 +1,24 @@ +struct SpeechMaker { + speeches: uint +} + +impl SpeechMaker { + fn talk(&mut self) { + self.speeches += 1; + } +} + +fn give_a_few_speeches(speaker: &mut SpeechMaker) { + + // Here speaker is reborrowed for each call, so we don't get errors + // about speaker being moved. + + speaker.talk(); + speaker.talk(); + speaker.talk(); +} + +fn main() { + let mut lincoln = SpeechMaker {speeches: 22}; + give_a_few_speeches(&mut lincoln); +} diff --git a/src/test/run-pass/coerce-reborrow-mut-vec-arg.rs b/src/test/run-pass/coerce-reborrow-mut-vec-arg.rs new file mode 100644 index 0000000000000..0cce52e7dc8d3 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-mut-vec-arg.rs @@ -0,0 +1,15 @@ +trait Reverser { + fn reverse(&self); +} + +fn bar(v: &[mut uint]) { + vec::reverse(v); + vec::reverse(v); + vec::reverse(v); +} + +fn main() { + let mut the_vec = ~[1, 2, 3, 100]; + bar(the_vec); + assert the_vec == ~[100, 3, 2, 1]; +} diff --git a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs new file mode 100644 index 0000000000000..9fb748f049fd6 --- /dev/null +++ b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs @@ -0,0 +1,21 @@ +trait Reverser { + fn reverse(&self); +} + +impl &[mut uint] : Reverser { + fn reverse(&self) { + vec::reverse(*self); + } +} + +fn bar(v: &[mut uint]) { + v.reverse(); + v.reverse(); + v.reverse(); +} + +fn main() { + let mut the_vec = ~[1, 2, 3, 100]; + bar(the_vec); + assert the_vec == ~[100, 3, 2, 1]; +} diff --git a/src/test/run-pass/issue-3026.rs b/src/test/run-pass/issue-3026.rs index 04932676f3d63..8a7ebb8d129e0 100644 --- a/src/test/run-pass/issue-3026.rs +++ b/src/test/run-pass/issue-3026.rs @@ -17,5 +17,5 @@ use std::map; fn main() { let buggy_map :HashMap = HashMap::(); let x = ~1; - buggy_map.insert(42, x); + buggy_map.insert(42, &*x); } diff --git a/src/test/run-pass/let-assignability.rs b/src/test/run-pass/let-assignability.rs index 2978585674524..453d556b13c99 100644 --- a/src/test/run-pass/let-assignability.rs +++ b/src/test/run-pass/let-assignability.rs @@ -14,15 +14,7 @@ fn f() { io::println(b); } -fn g() { - let c = ~"world"; - let d: &str; - d = c; - io::println(d); -} - fn main() { f(); - g(); } diff --git a/src/test/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/region-dependent-addr-of.rs new file mode 100644 index 0000000000000..6765c1a11ae78 --- /dev/null +++ b/src/test/run-pass/region-dependent-addr-of.rs @@ -0,0 +1,115 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { + value: B +} + +struct B { + v1: int, + v2: [int * 3], + v3: ~[int], + v4: C, + v5: ~C, + v6: Option +} + +struct C { + f: int +} + +fn get_v1(a: &v/A) -> &v/int { + // Region inferencer must deduce that &v < L2 < L1 + let foo = &a.value; // L1 + &foo.v1 // L2 +} + +fn get_v2(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v2[i] +} + +fn get_v3(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v3[i] +} + +fn get_v4(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v4.f +} + +fn get_v5(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v5.f +} + +fn get_v6_a(a: &v/A, i: uint) -> &v/int { + match a.value.v6 { + Some(ref v) => &v.f, + None => fail + } +} + +fn get_v6_b(a: &v/A, i: uint) -> &v/int { + match *a { + A { value: B { v6: Some(ref v), _ } } => &v.f, + _ => fail + } +} + +fn get_v6_c(a: &v/A, i: uint) -> &v/int { + match a { + &A { value: B { v6: Some(ref v), _ } } => &v.f, + _ => fail + } +} + +fn get_v5_ref(a: &v/A, i: uint) -> &v/int { + match &a.value { + &B {v5: ~C {f: ref v}, _} => v + } +} + +fn main() { + let a = A {value: B {v1: 22, + v2: [23, 24, 25], + v3: ~[26, 27, 28], + v4: C { f: 29 }, + v5: ~C { f: 30 }, + v6: Some(C { f: 31 })}}; + + let p = get_v1(&a); + assert *p == a.value.v1; + + let p = get_v2(&a, 1); + assert *p == a.value.v2[1]; + + let p = get_v3(&a, 1); + assert *p == a.value.v3[1]; + + let p = get_v4(&a, 1); + assert *p == a.value.v4.f; + + let p = get_v5(&a, 1); + assert *p == a.value.v5.f; + + let p = get_v6_a(&a, 1); + assert *p == a.value.v6.get().f; + + let p = get_v6_b(&a, 1); + assert *p == a.value.v6.get().f; + + let p = get_v6_c(&a, 1); + assert *p == a.value.v6.get().f; + + let p = get_v5_ref(&a, 1); + assert *p == a.value.v5.f; +} diff --git a/src/test/run-pass/region-return-interior-of-option-in-self.rs b/src/test/run-pass/region-return-interior-of-option-in-self.rs deleted file mode 100644 index fef62e805bcd6..0000000000000 --- a/src/test/run-pass/region-return-interior-of-option-in-self.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-test (#3148) - -struct cell { - value: T; -} - -struct cells { - vals: ~[option>]; -} - -impl &cells { - fn get(idx: uint) -> &self/T { - match self.vals[idx] { - some(ref v) => &v.value, - none => fail - } - } -} - -fn main() {} diff --git a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs index 6b005bf0e1218..0d093a1b4b0fd 100644 --- a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs +++ b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs @@ -2,7 +2,7 @@ fn main() { let x = &[1, 2, 3, 4, 5]; if !x.is_empty() { let el = match x { - [1, ..ref tail] => &tail[0], + [1, ..ref tail] => &tail[0], _ => ::core::util::unreachable() }; io::println(fmt!("%d", *el));