From 01bf87eb74987796c9f77f20251346882a2fbc70 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Jan 2013 16:24:45 -0800 Subject: [PATCH 01/14] Link the lifetimes of regions resulting from borrows of the contents of other borrowed pointers to the lifetimes of the borrowed value. Fixes #3148. --- src/librustc/middle/mem_categorization.rs | 2 + src/librustc/middle/resolve.rs | 35 ++ src/librustc/middle/ty.rs | 9 + src/librustc/middle/typeck/check/mod.rs | 13 +- src/librustc/middle/typeck/check/regionck.rs | 514 ++++++++++++++++-- src/librustc/util/ppaux.rs | 6 + src/libsyntax/ext/auto_encode.rs | 2 +- src/libsyntax/ext/build.rs | 8 +- src/libsyntax/ext/deriving.rs | 3 +- src/test/run-pass/region-dependent-addr-of.rs | 112 ++++ ...egion-return-interior-of-option-in-self.rs | 30 - 11 files changed, 640 insertions(+), 94 deletions(-) create mode 100644 src/test/run-pass/region-dependent-addr-of.rs delete mode 100644 src/test/run-pass/region-return-interior-of-option-in-self.rs diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 909d1f95fde4b..6cd14d332bf55 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 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..2e0d7026769ef 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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; @@ -3005,6 +3006,14 @@ 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, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index c0c990763fbc5..029cbfab494e5 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; @@ -469,7 +468,7 @@ fn check_fn(ccx: @crate_ctxt, }; 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.pat_to_str(local.node.pat), fcx.inh.locals.get(local.node.id).to_str()); visit::visit_local(local, e, v); }; @@ -756,6 +755,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, @@ -1600,7 +1603,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); @@ -2713,7 +2716,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); diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 05f23bddb0860..419e6269957cf 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) -> Option { + /*! + * Try to resolve the type for the given node, returning + * None 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) => Some(t), + Err(_) => None + } } /// 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) -> Option { self.resolve_type(self.fcx.node_ty(id)) } } @@ -170,8 +179,7 @@ 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)); match /*bad*/copy expr.node { ast::expr_path(*) => { @@ -242,40 +250,36 @@ 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); - } - _ => () + for rcx.resolve_node_type(expr.id).each |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); } + _ => () } - }; + } + } + + ast::expr_addr_of(_, base) => { + guarantor::for_addr_of(rcx, expr, base); } - 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_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); - } + for rcx.resolve_node_type(expr.id).each |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); } - _ => () } + _ => () } } } @@ -406,8 +410,8 @@ fn constrain_regions_in_type_of_node( // 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 + None => { return true; } + Some(ty) => { ty } }; debug!("constrain_regions_in_type_of_node(\ @@ -477,3 +481,401 @@ 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. + */ + + 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); + } + } + } + + 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. + for rcx.resolve_node_type(id).each |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 + } + + 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 tcx = rcx.fcx.ccx.tcx; + if rcx.fcx.ccx.method_map.contains_key(expr.id) { + debug!("method call"); + return id_categorization(rcx, None, expr.id); + } + + let expr_ty = match rcx.resolve_node_type(expr.id) { + None => { return id_categorization(rcx, None, expr.id); } + Some(t) => { t } + }; + let mut cat = ExprCategorization { + guarantor: guarantor(rcx, expr), + pointer: pointer_categorize(expr_ty) + }; + + debug!("before adjustments, cat=%?", cat); + + for rcx.fcx.inh.adjustments.find(expr.id).each |adjustment| { + debug!("adjustment=%?", adjustment); + for uint::range(0, adjustment.autoderefs) |_| { + cat.guarantor = guarantor_of_deref(&cat); + + match ty::deref(tcx, expr_ty, true) { + Some(t) => { + cat.pointer = pointer_categorize(t.ty); + } + None => { + tcx.sess.span_bug( + expr.span, + fmt!("Autoderef but type not derefable: %s", + ty_to_str(tcx, expr_ty))); + } + } + + debug!("autoderef, cat=%?", cat); + } + + for adjustment.autoref.each |autoref| { + cat.guarantor = None; + cat.pointer = BorrowedPointer(autoref.region); + debug!("autoref, cat=%?", cat); + } + } + + debug!("result=%?", cat); + return cat; + } + + fn pointer_categorize(ty: ty::t) -> PointerCat { + match ty::get(ty).sty { + ty::ty_rptr(r, _) | ty::ty_evec(_, vstore_slice(r)) | + ty::ty_estr(vstore_slice(r)) => { + BorrowedPointer(r) + } + ty::ty_uniq(*) | ty::ty_estr(vstore_uniq) | + ty::ty_evec(_, vstore_uniq) => { + OwnedPointer + } + ty::ty_box(*) | ty::ty_ptr(*) | + ty::ty_evec(_, vstore_box) | + ty::ty_estr(vstore_box) => { + OtherPointer + } + _ => { + NotPointer + } + } + } + + fn id_categorization(rcx: @rcx, + guarantor: Option, + id: ast::node_id) -> ExprCategorization + { + let pointer = match rcx.resolve_node_type(id) { + None => NotPointer, + Some(t) => pointer_categorize(t) + }; + + ExprCategorization {guarantor: guarantor, + pointer: pointer} + } + + 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) => { + for rcx.resolve_node_type(pat.id).each |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) => { + for rcx.resolve_node_type(pat.id).each |vec_ty| { + let vstore = ty::ty_vstore(*vec_ty); + let guarantor1 = match vstore { + vstore_fixed(_) | vstore_uniq => guarantor, + vstore_slice(r) => Some(r), + 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/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/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/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/region-dependent-addr-of.rs new file mode 100644 index 0000000000000..594556fffed95 --- /dev/null +++ b/src/test/run-pass/region-dependent-addr-of.rs @@ -0,0 +1,112 @@ +// 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; +} 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() {} From 403f33f208429cc4fe324ba912739136db82d50b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Jan 2013 19:33:48 -0800 Subject: [PATCH 02/14] Integrate vec patterns into borrow checker. The tail portion of the pattern effectively borrows a vector, but the borrow checker knew nothing about this. --- src/librustc/middle/borrowck/gather_loans.rs | 42 +++++++++++++ src/librustc/middle/borrowck/mod.rs | 8 ++- src/librustc/middle/mem_categorization.rs | 60 +++++++++++-------- src/librustc/middle/ty.rs | 4 +- src/librustc/middle/typeck/check/regionck.rs | 29 +++++---- ...s => borrowck-vec-pattern-element-loan.rs} | 6 +- .../borrowck-vec-pattern-loan-from-mut.rs | 12 ++++ .../borrowck-vec-pattern-nesting.rs | 21 +++++++ ...borrowck-vec-pattern-tail-element-loan.rs} | 4 +- src/test/run-pass/region-dependent-addr-of.rs | 3 + .../vec-matching-legal-tail-element-borrow.rs | 2 +- 11 files changed, 144 insertions(+), 47 deletions(-) rename src/test/compile-fail/{alt-vec-illegal-tail-loan.rs => borrowck-vec-pattern-element-loan.rs} (67%) create mode 100644 src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs create mode 100644 src/test/compile-fail/borrowck-vec-pattern-nesting.rs rename src/test/compile-fail/{alt-vec-illegal-tail-element-loan.rs => borrowck-vec-pattern-tail-element-loan.rs} (65%) 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/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 6cd14d332bf55..7c1e96e5c0e44 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -521,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} } @@ -643,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))); } @@ -675,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} } @@ -723,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. // @@ -759,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(); - match /*bad*/copy pat.node { + op(cmt, pat); + + match pat.node { ast::pat_wild => { // _ } @@ -773,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) @@ -805,7 +807,8 @@ impl &mem_categorization_ctxt { // nullary variant or identifier: ignore } - ast::pat_rec(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); @@ -813,15 +816,7 @@ impl &mem_categorization_ctxt { } } - ast::pat_struct(_, 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_tup(subpats) => { + ast::pat_tup(ref subpats) => { // (p1, ..., pN) for subpats.each |subpat| { let subcmt = self.cat_tuple_elt(*subpat, cmt); @@ -836,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/ty.rs b/src/librustc/middle/ty.rs index 2e0d7026769ef..840726b804179 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3017,7 +3017,9 @@ pure fn ty_vstore(ty: t) -> vstore { 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)) } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 419e6269957cf..a4aa645da0b59 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -530,6 +530,13 @@ mod guarantor { * 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) { /*! * @@ -726,17 +733,17 @@ mod guarantor { fn pointer_categorize(ty: ty::t) -> PointerCat { match ty::get(ty).sty { - ty::ty_rptr(r, _) | ty::ty_evec(_, vstore_slice(r)) | - ty::ty_estr(vstore_slice(r)) => { + 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(vstore_uniq) | - ty::ty_evec(_, vstore_uniq) => { + 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(_, vstore_box) | - ty::ty_estr(vstore_box) => { + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) => { OtherPointer } _ => { @@ -828,9 +835,9 @@ mod guarantor { for rcx.resolve_node_type(pat.id).each |vec_ty| { let vstore = ty::ty_vstore(*vec_ty); let guarantor1 = match vstore { - vstore_fixed(_) | vstore_uniq => guarantor, - vstore_slice(r) => Some(r), - vstore_box => None + 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); @@ -844,8 +851,8 @@ mod guarantor { } fn link_ref_bindings_in_pats(rcx: @rcx, - pats: &~[@ast::pat], - guarantor: Option) + pats: &~[@ast::pat], + guarantor: Option) { for pats.each |pat| { link_ref_bindings_in_pat(rcx, *pat, guarantor); 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/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/region-dependent-addr-of.rs index 594556fffed95..6765c1a11ae78 100644 --- a/src/test/run-pass/region-dependent-addr-of.rs +++ b/src/test/run-pass/region-dependent-addr-of.rs @@ -109,4 +109,7 @@ fn main() { 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/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)); From 03e9e4f5f99265f88dee1b9d1b3f624a10d7e40e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Jan 2013 22:03:29 -0800 Subject: [PATCH 03/14] Detect "// WARN" in tidy and print out, well, a warning! Useful for notating FIXME-style-situations that you want to be reminded of before you commit. --- src/etc/tidy.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/etc/tidy.py b/src/etc/tidy.py index e866d80062d51..a823bbb227722 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") + if line.find("// WARN") != -1: + mo = re.match("// WARN (.*)", line) + report_warn("WARN: " + mo.group(1)) if (line.find('\t') != -1 and fileinput.filename().find("Makefile") == -1): report_err("tab character") From 159542b74e65e53520b473d74d5874425f240ad4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Jan 2013 06:27:32 -0800 Subject: [PATCH 04/14] Make tidy actually work --- src/etc/tidy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/etc/tidy.py b/src/etc/tidy.py index a823bbb227722..2eb32c100acd2 100644 --- a/src/etc/tidy.py +++ b/src/etc/tidy.py @@ -49,9 +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") - if line.find("// WARN") != -1: - mo = re.match("// WARN (.*)", line) - report_warn("WARN: " + mo.group(1)) + 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") From 0434a9e9426f56043a0f685605efad1c3b35f361 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Jan 2013 10:03:28 -0800 Subject: [PATCH 05/14] Extend region guarantor logic to handle autoref, which will be necessary for reborrowing. --- src/librustc/middle/typeck/check/regionck.rs | 208 +++++++++++-------- 1 file changed, 125 insertions(+), 83 deletions(-) diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index a4aa645da0b59..fe2fd4a6a22a1 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -73,10 +73,10 @@ fn encl_region_of_def(fcx: @fn_ctxt, def: ast::def) -> ty::Region { } impl @rcx { - fn resolve_type(unresolved_ty: ty::t) -> Option { + fn resolve_type(unresolved_ty: ty::t) -> ty::t { /*! * Try to resolve the type for the given node, returning - * None if an error results. Note that we never care + * 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. * @@ -104,13 +104,13 @@ impl @rcx { */ match resolve_type(self.fcx.infcx(), unresolved_ty, resolve_and_force_all_but_regions) { - Ok(t) => Some(t), - Err(_) => None + 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) -> Option { + fn resolve_node_type(id: ast::node_id) -> ty::t { self.resolve_type(self.fcx.node_ty(id)) } } @@ -181,6 +181,12 @@ 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)", 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(*) => { // Avoid checking the use of local variables, as we @@ -250,15 +256,14 @@ 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. - for rcx.resolve_node_type(expr.id).each |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); } + _ => () } } @@ -271,16 +276,15 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { } ast::expr_fn(*) | ast::expr_fn_block(*) => { - for rcx.resolve_node_type(expr.id).each |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); } - _ => () } + _ => () } } @@ -409,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) { - None => { return true; } - Some(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) } @@ -567,6 +566,30 @@ mod 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, @@ -589,9 +612,10 @@ mod guarantor { // 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. - for rcx.resolve_node_type(id).each |rptr_ty| { - debug!("rptr_ty=%s", ty_to_str(rcx.fcx.ccx.tcx, *rptr_ty)); - let r = ty::ty_region(*rptr_ty); + 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); } } @@ -608,6 +632,11 @@ mod guarantor { 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 { @@ -683,52 +712,76 @@ mod guarantor { debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr)); let _i = ::util::common::indenter(); - let tcx = rcx.fcx.ccx.tcx; - if rcx.fcx.ccx.method_map.contains_key(expr.id) { - debug!("method call"); - return id_categorization(rcx, None, expr.id); - } - - let expr_ty = match rcx.resolve_node_type(expr.id) { - None => { return id_categorization(rcx, None, expr.id); } - Some(t) => { t } - }; - let mut cat = ExprCategorization { - guarantor: guarantor(rcx, expr), - pointer: pointer_categorize(expr_ty) - }; - - debug!("before adjustments, cat=%?", cat); + 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); - for uint::range(0, adjustment.autoderefs) |_| { - cat.guarantor = guarantor_of_deref(&cat); - match ty::deref(tcx, expr_ty, true) { - Some(t) => { - cat.pointer = pointer_categorize(t.ty); - } - None => { - tcx.sess.span_bug( - expr.span, - fmt!("Autoderef but type not derefable: %s", - ty_to_str(tcx, expr_ty))); - } - } + expr_ct = apply_autoderefs( + rcx, expr, adjustment.autoderefs, expr_ct); - debug!("autoderef, cat=%?", cat); + for adjustment.autoref.each |autoref| { + expr_ct.cat.guarantor = None; + expr_ct.cat.pointer = BorrowedPointer(autoref.region); + debug!("autoref, cat=%?", expr_ct.cat); } + } - for adjustment.autoref.each |autoref| { - cat.guarantor = None; - cat.pointer = BorrowedPointer(autoref.region); - debug!("autoref, cat=%?", 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 } + } - debug!("result=%?", cat); - return cat; + 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 { @@ -752,19 +805,6 @@ mod guarantor { } } - fn id_categorization(rcx: @rcx, - guarantor: Option, - id: ast::node_id) -> ExprCategorization - { - let pointer = match rcx.resolve_node_type(id) { - None => NotPointer, - Some(t) => pointer_categorize(t) - }; - - ExprCategorization {guarantor: guarantor, - pointer: pointer} - } - fn guarantor_of_deref(cat: &ExprCategorization) -> Option { match cat.pointer { NotPointer => cat.guarantor, @@ -824,16 +864,18 @@ mod guarantor { link_ref_bindings_in_pat(rcx, p, guarantor) } ast::pat_region(p) => { - for rcx.resolve_node_type(pat.id).each |rptr_ty| { - let r = ty::ty_region(*rptr_ty); + 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) => { - for rcx.resolve_node_type(pat.id).each |vec_ty| { - let vstore = ty::ty_vstore(*vec_ty); + 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), From e9640061ba53f6eefd05a9559f3ec8a642d8532a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 07:01:17 -0800 Subject: [PATCH 06/14] Remove bounds from type decl of smallintmap --- src/libstd/smallintmap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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_) } From fa71997fe22453e85d8bf3c9bb2d574f90ca13d9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 07:01:43 -0800 Subject: [PATCH 07/14] Add to_str() definitions for some AST types --- src/libsyntax/ast.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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) From 2b28115b5dac09fd165ef407b38ff591ecb4ee5a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 07:02:09 -0800 Subject: [PATCH 08/14] Do not try to encode/decode inference types, should not be necessary --- src/librustc/metadata/tydecode.rs | 3 --- src/librustc/metadata/tyencode.rs | 15 ++------------- 2 files changed, 2 insertions(+), 16 deletions(-) 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'); From c43b72ce0e228e297ba11d1a555bbc880e514c27 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 07:02:40 -0800 Subject: [PATCH 09/14] Refactor to make inference code around unification more readable --- src/librustc/middle/ty.rs | 94 +++++----- .../middle/typeck/infer/assignment.rs | 8 +- src/librustc/middle/typeck/infer/combine.rs | 121 +++++++------ src/librustc/middle/typeck/infer/lattice.rs | 64 ++++--- src/librustc/middle/typeck/infer/mod.rs | 50 +++--- src/librustc/middle/typeck/infer/resolve.rs | 19 +- src/librustc/middle/typeck/infer/sub.rs | 51 +++--- src/librustc/middle/typeck/infer/to_str.rs | 48 +++--- src/librustc/middle/typeck/infer/unify.rs | 163 +++++++++++++----- 9 files changed, 335 insertions(+), 283 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 840726b804179..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; @@ -215,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; @@ -241,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 @@ -703,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 } @@ -740,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 { @@ -752,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; @@ -851,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 } } @@ -884,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) } } @@ -3575,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()) } } } @@ -4451,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/infer/assignment.rs b/src/librustc/middle/typeck/infer/assignment.rs index fef63cf9a42cc..cd4514f6a2c5d 100644 --- a/src/librustc/middle/typeck/infer/assignment.rs +++ b/src/librustc/middle/typeck/infer/assignment.rs @@ -103,8 +103,8 @@ impl Assign { } (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 nde_a = self.infcx.get(a_id); + let nde_b = self.infcx.get(b_id); let a_bounds = nde_a.possible_types; let b_bounds = nde_b.possible_types; @@ -114,7 +114,7 @@ impl Assign { } (ty::ty_infer(TyVar(a_id)), _) => { - let nde_a = self.infcx.get(&self.infcx.ty_var_bindings, a_id); + let nde_a = self.infcx.get(a_id); let a_bounds = nde_a.possible_types; let a_bnd = option::or(a_bounds.ub, a_bounds.lb); @@ -122,7 +122,7 @@ impl Assign { } (_, ty::ty_infer(TyVar(b_id))) => { - let nde_b = self.infcx.get(&self.infcx.ty_var_bindings, b_id); + let nde_b = self.infcx.get(b_id); let b_bounds = nde_b.possible_types; let b_bnd = option::or(b_bounds.lb, b_bounds.ub); 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; // "unify result" type fres = Result; // "fixup result" type ares = cres>; // "assignment result" -#[deriving_eq] -enum IntVarValue { - IntType(ast::int_ty), - UintType(ast::uint_ty), -} - struct InferCtxt { tcx: ty::ctxt, @@ -364,22 +359,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. @@ -582,6 +569,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 +595,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 +606,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 +656,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 +684,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 +693,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..01f4b86b48858 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}; @@ -219,7 +218,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 +242,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 +250,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 +265,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); + } +} + + From 7c18b01e34e26731f0b2a43d0aa83cab4b1e3ef8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 11:06:20 -0800 Subject: [PATCH 10/14] rename assign to coerce, remove some bad copies --- src/librustc/middle/typeck/check/demand.rs | 4 +- src/librustc/middle/typeck/check/mod.rs | 16 +-- .../infer/{assignment.rs => coercion.rs} | 116 +++++++++--------- src/librustc/middle/typeck/infer/mod.rs | 26 ++-- 4 files changed, 78 insertions(+), 84 deletions(-) rename src/librustc/middle/typeck/infer/{assignment.rs => coercion.rs} (75%) diff --git a/src/librustc/middle/typeck/check/demand.rs b/src/librustc/middle/typeck/check/demand.rs index e4b232500bbf7..c8aaf2ca61b49 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -49,8 +49,8 @@ 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/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 029cbfab494e5..85e61cdbecba6 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -812,7 +812,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)) => { @@ -823,7 +823,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, @@ -986,12 +986,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) } } @@ -1225,7 +1225,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); } @@ -1243,7 +1243,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; } @@ -1739,7 +1739,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); @@ -2552,7 +2552,7 @@ 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); + return check_expr_coercable_to_type(fcx, init, lty); } fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { diff --git a/src/librustc/middle/typeck/infer/assignment.rs b/src/librustc/middle/typeck/infer/coercion.rs similarity index 75% rename from src/librustc/middle/typeck/infer/assignment.rs rename to src/librustc/middle/typeck/infer/coercion.rs index cd4514f6a2c5d..84fe51b65ec51 100644 --- a/src/librustc/middle/typeck/infer/assignment.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -79,30 +79,23 @@ fn to_ares(+c: cres) -> ares { } } -// Note: Assign is not actually a combiner, in that it does not +// Note: Coerce is not actually a combiner, in that it does not // conform to the same interface, though it performs a similar // function. -enum Assign = CombineFields; +pub enum Coerce = CombineFields; -impl Assign { - fn tys(a: ty::t, b: ty::t) -> ares { - debug!("Assign.tys(%s => %s)", +impl Coerce { + fn tys(&self, a: ty::t, b: ty::t) -> ares { + debug!("Coerce.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, _) => { + let _indent = indenter(); + let r = match (&ty::get(a).sty, &ty::get(b).sty) { + (&ty::ty_bot, _) => { Ok(None) } - (ty::ty_infer(TyVar(a_id)), ty::ty_infer(TyVar(b_id))) => { + (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { let nde_a = self.infcx.get(a_id); let nde_b = self.infcx.get(b_id); let a_bounds = nde_a.possible_types; @@ -110,42 +103,45 @@ impl Assign { 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) + self.coerce_tys_or_sub(a, b, a_bnd, b_bnd) } - (ty::ty_infer(TyVar(a_id)), _) => { + (&ty::ty_infer(TyVar(a_id)), _) => { let nde_a = self.infcx.get(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)) + self.coerce_tys_or_sub(a, b, a_bnd, Some(b)) } - (_, ty::ty_infer(TyVar(b_id))) => { + (_, &ty::ty_infer(TyVar(b_id))) => { let nde_b = self.infcx.get(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.coerce_tys_or_sub(a, b, Some(a), b_bnd) } (_, _) => { - self.assign_tys_or_sub(a, b, Some(a), Some(b)) + self.coerce_tys_or_sub(a, b, Some(a), Some(b)) } }; - debug!("Assign.tys end"); + debug!("Coerce.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)", +impl Coerce { + fn coerce_tys_or_sub( + &self, + +a: ty::t, + +b: ty::t, + +a_bnd: Option, + +b_bnd: Option) -> ares + { + debug!("Coerce.coerce_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(); @@ -167,59 +163,58 @@ priv impl Assign { 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) { + match (&ty::get(a_bnd).sty, &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)) => { + // being coerceed 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, + self.try_coerce(1, ty::AutoPtr, a, nr_b, mt_b.mutbl, r_b) } - (ty::ty_uniq(_), ty::ty_rptr(r_b, mt_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, + self.try_coerce(1, ty::AutoPtr, a, nr_b, mt_b.mutbl, r_b) } - (ty::ty_estr(vs_a), - ty::ty_estr(ty::vstore_slice(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, + self.try_coerce(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))) + (&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, + self.try_coerce(0, ty::AutoBorrowVec, a, nr_b, mt_b.mutbl, r_b) } - (ty::ty_fn(ref a_f), ty::ty_fn(ref b_f)) + (&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, + self.try_coerce(0, ty::AutoBorrowFn, a, nr_b, m_imm, b_f.meta.region) } - (ty::ty_fn(ref a_f), ty::ty_fn(ref b_f)) + (&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, @@ -229,49 +224,50 @@ priv impl Assign { // 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)) + 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)) + // check for &T being coerced 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: + // otherwise, coercement follows normal subtype rules: _ => { - to_ares(Sub(*self).tys(a, b)) + to_ares(Sub(**self).tys(a, b)) } } } _ => { // if insufficient bounds were available, just follow // normal subtype rules: - to_ares(Sub(*self).tys(a, b)) + to_ares(Sub(**self).tys(a, b)) } } } - /// Given an assignment from a type like `@a` to `&r_b/m nr_b`, + /// Given an coercement 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 + /// coercement is permitted, so it constructs a fresh region + /// variable `r_a >= r_b` and returns a corresponding coercement /// record. See the discussion at the top of this file for more /// details. - fn try_assign(autoderefs: uint, + fn try_coerce(&self, + 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)", + r_b: ty::Region) -> ares + { + debug!("try_coerce(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); + 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| { diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index b702448fbf474..8ec7e68176a56 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -217,11 +217,11 @@ when possible but otherwise merge the variables" strategy. In other words, `GLB(A, B)` where `A` and `B` are variables will often result in `A` and `B` being merged and the result being `A`. -## Type assignment +## Type coercion We have a notion of assignability which differs somewhat from subtyping; in particular it may cause region borrowing to occur. See -the big comment later in this file on Type Assignment for specifics. +the big comment later in this file on Type Coercion for specifics. ### In conclusion @@ -254,7 +254,7 @@ use middle::ty::{ty_int, ty_uint, get, terr_fn, TyVar, IntVar, FloatVar}; use middle::ty::IntVarValue; use middle::ty; use middle::typeck::check::regionmanip::{replace_bound_regions_in_fn_sig}; -use middle::typeck::infer::assignment::Assign; +use middle::typeck::infer::coercion::Coerce; use middle::typeck::infer::combine::{CombineFields, eq_tys}; use middle::typeck::infer::glb::Glb; use middle::typeck::infer::lub::Lub; @@ -293,17 +293,15 @@ export new_infer_ctxt; export mk_subty, can_mk_subty; export mk_subr; export mk_eqty; -export mk_assignty, can_mk_assignty; +export mk_coercety, can_mk_coercety; export resolve_nested_tvar, resolve_rvar, resolve_ivar, resolve_all; export force_tvar, force_rvar, force_ivar, force_all; export resolve_and_force_all_but_regions, not_regions; export resolve_type, resolve_region; export resolve_borrowings; export cres, fres, fixup_err, fixup_err_to_str; -export assignment; export root, to_str; export int_ty_set_all; -export assignment; export combine; export glb; export integral; @@ -312,6 +310,7 @@ export lub; export region_inference; export resolve; export sub; +export coercion; export to_str; export unify; export uok; @@ -323,8 +322,7 @@ export infer_ctxt; export fixup_err; export IntVarValue, IntType, UintType; -#[legacy_exports] -mod assignment; +mod coercion; #[legacy_exports] mod combine; #[legacy_exports] @@ -458,22 +456,22 @@ fn mk_eqty(cx: @InferCtxt, a_is_expected: bool, span: span, }.to_ures() } -fn mk_assignty(cx: @InferCtxt, a_is_expected: bool, span: span, +fn mk_coercety(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)); + 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() } From 69a3b472168908ef989167b1f4d5c71d48ce31d9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 17:20:08 -0800 Subject: [PATCH 11/14] Rewrite the coercion code to be more readable, more sound, and to reborrow when needed. Regarding soundness: there was a subtle bug in how it was done before; see the compile-fail test for an example. Regarding reborrowing: reborrowing allows mut and const slices/borrowed-pointers to be used with pure fns that expect immutable data. --- src/librustc/middle/typeck/check/_match.rs | 8 +- src/librustc/middle/typeck/check/demand.rs | 5 +- src/librustc/middle/typeck/check/method.rs | 76 ++- src/librustc/middle/typeck/check/mod.rs | 84 ++- src/librustc/middle/typeck/check/writeback.rs | 5 +- src/librustc/middle/typeck/infer/coercion.rs | 555 +++++++++++------- src/librustc/middle/typeck/infer/mod.rs | 9 +- src/librustc/middle/typeck/infer/resolve.rs | 1 + .../borrowck-borrowed-uniq-rvalue.rs | 6 +- src/test/compile-fail/coerce-bad-variance.rs | 17 + src/test/compile-fail/issue-4500.rs | 3 +- .../kindck-owned-trait-contains.rs | 11 +- src/test/compile-fail/regions-scoping.rs | 3 +- .../run-pass/coerce-reborrow-imm-ptr-arg.rs | 17 + .../run-pass/coerce-reborrow-imm-ptr-rcvr.rs | 16 + .../run-pass/coerce-reborrow-imm-vec-arg.rs | 19 + .../run-pass/coerce-reborrow-imm-vec-rcvr.rs | 18 + .../run-pass/coerce-reborrow-mut-ptr-arg.rs | 22 + .../run-pass/coerce-reborrow-mut-ptr-rcvr.rs | 24 + .../run-pass/coerce-reborrow-mut-vec-arg.rs | 15 + .../run-pass/coerce-reborrow-mut-vec-rcvr.rs | 21 + src/test/run-pass/issue-3026.rs | 2 +- src/test/run-pass/let-assignability.rs | 8 - 23 files changed, 643 insertions(+), 302 deletions(-) create mode 100644 src/test/compile-fail/coerce-bad-variance.rs create mode 100644 src/test/run-pass/coerce-reborrow-imm-ptr-arg.rs create mode 100644 src/test/run-pass/coerce-reborrow-imm-ptr-rcvr.rs create mode 100644 src/test/run-pass/coerce-reborrow-imm-vec-arg.rs create mode 100644 src/test/run-pass/coerce-reborrow-imm-vec-rcvr.rs create mode 100644 src/test/run-pass/coerce-reborrow-mut-ptr-arg.rs create mode 100644 src/test/run-pass/coerce-reborrow-mut-ptr-rcvr.rs create mode 100644 src/test/run-pass/coerce-reborrow-mut-vec-arg.rs create mode 100644 src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs 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 c8aaf2ca61b49..c8a644fef101c 100644 --- a/src/librustc/middle/typeck/check/demand.rs +++ b/src/librustc/middle/typeck/check/demand.rs @@ -50,7 +50,10 @@ fn eqtype(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t) { } // Checks that the type `actual` can be coerced to `expected`. -fn coerce(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) { +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..238e4e8c3f772 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,82 @@ 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_r, self_mt) if self_mt.mutbl == m_mutbl => { + let region = fresh_region(self, self_r); + (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(self_r)) + if self_mt.mutbl == m_mutbl => { + let region = fresh_region(self, self_r); + (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 fresh_region(self: &LookupContext, + self_r: ty::Region) -> ty::Region { + let region = self.infcx().next_region_var(self.expr.span, + self.expr.id); + + // FIXME(#3148)---in principle this dependency should + // be done more generally as part of regionck + match infer::mk_subr(self.infcx(), true, self.expr.span, + region, self_r) { + Ok(_) => {} + Err(e) => { + self.tcx().sess.span_bug( + self.expr.span, + fmt!("Failed with error: %?", e)); + } + } + + return region; + } + } + fn search_for_autosliced_method( &self, self_ty: ty::t, @@ -729,6 +800,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 85e61cdbecba6..9b5cbbc4cc8f1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -139,7 +139,6 @@ export regionck; export demand; export method; export fn_ctxt; -export lookup_local; export impl_self_ty; export DerefArgs; export DontDerefArgs; @@ -189,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 @@ -376,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 @@ -414,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. @@ -445,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. @@ -466,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", + assign(local.node.id, o_ty); + debug!("Local variable %s is assigned type %s", fcx.pat_to_str(local.node.pat), - fcx.inh.locals.get(local.node.id).to_str()); + fcx.infcx().ty_to_str( + fcx.inh.locals.get(local.node.id))); visit::visit_local(local, e, v); }; @@ -478,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))); } _ => {} } @@ -694,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())) @@ -1359,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); @@ -2551,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_coercable_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 { @@ -2819,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) } @@ -2841,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/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/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 84fe51b65ec51..9c319bdc733ae 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -8,280 +8,389 @@ // 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. +/*! + +# 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; +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::{ares, cres}; +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; -fn to_ares(+c: cres) -> ares { - match c { - Ok(_) => Ok(None), - Err(ref e) => Err((*e)) - } -} - // 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) -> ares { + 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(); - let r = match (&ty::get(a).sty, &ty::get(b).sty) { - (&ty::ty_bot, _) => { - Ok(None) + + // 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_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { - let nde_a = self.infcx.get(a_id); - let nde_b = self.infcx.get(b_id); - let a_bounds = nde_a.possible_types; - let b_bounds = nde_b.possible_types; + ty::ty_estr(vstore_slice(_)) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_string(a, sty_a, b) + }; + } - let a_bnd = option::or(a_bounds.ub, a_bounds.lb); - let b_bnd = option::or(b_bounds.lb, b_bounds.ub); - self.coerce_tys_or_sub(a, b, a_bnd, b_bnd) + 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_infer(TyVar(a_id)), _) => { - let nde_a = self.infcx.get(a_id); - let a_bounds = nde_a.possible_types; + 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) + }; + } - let a_bnd = option::or(a_bounds.ub, a_bounds.lb); - self.coerce_tys_or_sub(a, b, a_bnd, Some(b)) + ty::ty_ptr(_) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_unsafe_ptr(a, sty_a, b) + }; } - (_, &ty::ty_infer(TyVar(b_id))) => { - let nde_b = self.infcx.get(b_id); - let b_bounds = nde_b.possible_types; + _ => {} + } - let b_bnd = option::or(b_bounds.lb, b_bounds.ub); - self.coerce_tys_or_sub(a, b, Some(a), b_bnd) + 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) + } + } - (_, _) => { - self.coerce_tys_or_sub(a, b, Some(a), Some(b)) + 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); - debug!("Coerce.tys end"); + // 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(r_a, mt_a) => { + // Ensure that the pointer we are borrowing from lives + // at least as long as the borrowed result. + // + // FIXME(#3148)---in principle this dependency should + // be done more generally + if_ok!(sub.contraregions(r_a, r_borrow)); + mt_a.ty + } + _ => { + return self.subtype(a, b); + } + }; - move r + 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 + }) + })) } -} -impl Coerce { - fn coerce_tys_or_sub( - &self, - +a: ty::t, - +b: ty::t, - +a_bnd: Option, - +b_bnd: Option) -> ares + fn coerce_borrowed_string(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) -> CoerceResult { - debug!("Coerce.coerce_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 + 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 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 + 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, vstore_box) => mt.ty, + ty::ty_evec(mt, vstore_uniq) => mt.ty, + ty::ty_evec(mt, vstore_fixed(_)) => mt.ty, + ty::ty_evec(mt, vstore_slice(r_a)) => { + // Ensure that the pointer we are borrowing from lives + // at least as long as the borrowed result. + // + // FIXME(#3148)---in principle this dependency should + // be done more generally + if_ok!(sub.contraregions(r_a, r_borrow)); + mt.ty } - } + _ => { + return self.subtype(a, b); + } + }; - match (a_bnd, b_bnd) { - (Some(a_bnd), Some(b_bnd)) => { - match (&ty::get(a_bnd).sty, &ty::get(b_bnd).sty) { - // check for a case where a non-region pointer (@, ~) is - // being coerceed 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_coerce(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_coerce(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_coerce(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_coerce(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_coerce(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 coerced to *T: - (&ty::ty_rptr(_, ref a_t), &ty::ty_ptr(ref b_t)) => { - to_ares(Sub(**self).mts(*a_t, *b_t)) - } - - // otherwise, coercement follows normal subtype rules: - _ => { - to_ares(Sub(**self).tys(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); } _ => { - // if insufficient bounds were available, just follow - // normal subtype rules: - to_ares(Sub(**self).tys(a, 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) } } - /// Given an coercement from a type like `@a` to `&r_b/m nr_b`, - /// this function checks that `a <: nr_b`. In that case, the - /// coercement is permitted, so it constructs a fresh region - /// variable `r_a >= r_b` and returns a corresponding coercement - /// record. See the discussion at the top of this file for more - /// details. - fn try_coerce(&self, - autoderefs: uint, - kind: ty::AutoRefKind, - a: ty::t, - nr_b: ty::t, - m: ast::mutability, - r_b: ty::Region) -> ares + 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!("try_coerce(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 - }) - })) - } + 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/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 8ec7e68176a56..cac5fa5feb1c1 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -317,12 +317,11 @@ export uok; export cyclic_ty, unresolved_ty, region_var_bound_by_region_var; export Bound, Bounds; export ures; -export ares; +export CoerceResult; export infer_ctxt; export fixup_err; export IntVarValue, IntType, UintType; -mod coercion; #[legacy_exports] mod combine; #[legacy_exports] @@ -341,6 +340,7 @@ mod sub; mod to_str; #[legacy_exports] mod unify; +mod coercion; type Bound = Option; type Bounds = {lb: Bound, ub: Bound}; @@ -348,7 +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" +type CoerceResult = cres>; struct InferCtxt { tcx: ty::ctxt, @@ -457,7 +457,8 @@ fn mk_eqty(cx: @InferCtxt, a_is_expected: bool, span: span, } fn mk_coercety(cx: @InferCtxt, a_is_expected: bool, span: span, - a: ty::t, b: ty::t) -> ares { + 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 { diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index 01f4b86b48858..b25c4db8a90b7 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -78,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; 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/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(); } From 929f06eb1c06cd8bd47118e736c35d8e908eb224 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Jan 2013 10:29:25 -0800 Subject: [PATCH 12/14] remove hacks now that #3148 is fixed --- src/librustc/middle/typeck/check/method.rs | 30 ++++---------------- src/librustc/middle/typeck/infer/coercion.rs | 23 ++------------- 2 files changed, 8 insertions(+), 45 deletions(-) diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 238e4e8c3f772..86f58edd6c8a8 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -739,8 +739,9 @@ impl LookupContext { let tcx = self.tcx(); return match ty::get(self_ty).sty { - ty::ty_rptr(self_r, self_mt) if self_mt.mutbl == m_mutbl => { - let region = fresh_region(self, self_r); + 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, @@ -748,9 +749,10 @@ impl LookupContext { region: region, mutbl: self_mt.mutbl})}) } - ty::ty_evec(self_mt, vstore_slice(self_r)) + ty::ty_evec(self_mt, vstore_slice(_)) if self_mt.mutbl == m_mutbl => { - let region = fresh_region(self, self_r); + 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, @@ -763,26 +765,6 @@ impl LookupContext { autoref: None}) } }; - - fn fresh_region(self: &LookupContext, - self_r: ty::Region) -> ty::Region { - let region = self.infcx().next_region_var(self.expr.span, - self.expr.id); - - // FIXME(#3148)---in principle this dependency should - // be done more generally as part of regionck - match infer::mk_subr(self.infcx(), true, self.expr.span, - region, self_r) { - Ok(_) => {} - Err(e) => { - self.tcx().sess.span_bug( - self.expr.span, - fmt!("Failed with error: %?", e)); - } - } - - return region; - } } fn search_for_autosliced_method( diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index 9c319bdc733ae..fac84281f7fe2 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -196,15 +196,7 @@ impl Coerce { 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(r_a, mt_a) => { - // Ensure that the pointer we are borrowing from lives - // at least as long as the borrowed result. - // - // FIXME(#3148)---in principle this dependency should - // be done more generally - if_ok!(sub.contraregions(r_a, r_borrow)); - mt_a.ty - } + ty::ty_rptr(r_a, mt_a) => mt_a.ty, _ => { return self.subtype(a, b); } @@ -267,18 +259,7 @@ impl Coerce { 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, vstore_box) => mt.ty, - ty::ty_evec(mt, vstore_uniq) => mt.ty, - ty::ty_evec(mt, vstore_fixed(_)) => mt.ty, - ty::ty_evec(mt, vstore_slice(r_a)) => { - // Ensure that the pointer we are borrowing from lives - // at least as long as the borrowed result. - // - // FIXME(#3148)---in principle this dependency should - // be done more generally - if_ok!(sub.contraregions(r_a, r_borrow)); - mt.ty - } + ty::ty_evec(mt, _) => mt.ty, _ => { return self.subtype(a, b); } From b919d476533d2dc69bb3fc64f3f2d9bdc5b0de72 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Jan 2013 19:02:03 -0800 Subject: [PATCH 13/14] remove unused variable --- src/librustc/middle/typeck/infer/coercion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index fac84281f7fe2..f0fe2deffdfa8 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -196,7 +196,7 @@ impl Coerce { 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(r_a, mt_a) => mt_a.ty, + ty::ty_rptr(_, mt_a) => mt_a.ty, _ => { return self.subtype(a, b); } From 8495ed4832c743b0e162c9684703360f9e66d385 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 25 Jan 2013 19:02:09 -0800 Subject: [PATCH 14/14] loan: Track whether or not the current path owns the data being lent. We can be more liberal with respect to the scope of the loan if we do not own the data being lent, which used to be impossible but can now occur with `&mut`. --- src/librustc/middle/borrowck/loan.rs | 184 +++++++++++++++++++-------- 1 file changed, 128 insertions(+), 56 deletions(-) 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)}); } } }