From 2b67d88809d9f6ddc4686ee514cb78200db1d737 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 Jan 2013 17:20:08 -0800 Subject: [PATCH] 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. r=brson --- 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 | 557 +++++++++++------- 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, 644 insertions(+), 303 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 2e82d531e155..ebfce27a4c8b 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 c8aaf2ca61b4..c8a644fef101 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 7ef6ae598803..238e4e8c3f77 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 2d823ff02dd9..cf13fcb86e81 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 1a665aa75661..e5dc91b7f179 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 84fe51b65ec5..9c319bdc733a 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; - - 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_estr(vstore_slice(_)) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_string(a, sty_a, b) + }; } - (&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.coerce_tys_or_sub(a, b, a_bnd, Some(b)) + ty::ty_evec(mt_b, vstore_slice(_)) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_borrowed_vector(a, sty_a, b, mt_b) + }; } - (_, &ty::ty_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) + 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) + }; } - (_, _) => { - self.coerce_tys_or_sub(a, b, Some(a), Some(b)) + ty::ty_ptr(_) => { + return do self.unpack_actual_value(a) |sty_a| { + self.coerce_unsafe_ptr(a, sty_a, b) + }; + } + + _ => {} + } + + do self.unpack_actual_value(a) |sty_a| { + match *sty_a { + ty::ty_fn(ref a_f) if a_f.meta.proto == ast::ProtoBare => { + // Bare functions are coercable to any closure type. + // + // FIXME(#3320) this should go away and be + // replaced with proper inference, got a patch + // underway - ndm + self.coerce_from_bare_fn(a, a_f, b) + } + _ => { + // Otherwise, just use subtyping rules. + self.subtype(a, b) + } + } + } + } + + fn subtype(&self, a: ty::t, b: ty::t) -> CoerceResult { + match Sub(**self).tys(a, b) { + Ok(_) => Ok(None), // No coercion required. + Err(ref e) => Err(*e) + } + } + + fn unpack_actual_value(&self, + a: ty::t, + f: &fn(&ty::sty) -> CoerceResult) -> CoerceResult + { + match resolve_type(self.infcx, a, try_resolve_tvar_shallow) { + Ok(t) => { + f(&ty::get(t).sty) + } + Err(e) => { + self.infcx.tcx.sess.span_bug( + self.span, + fmt!("Failed to resolve even without \ + any force options: %?", e)); + } + } + } + + fn coerce_borrowed_pointer(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t, + mt_b: ty::mt) -> CoerceResult + { + debug!("coerce_borrowed_pointer(a=%s, sty_a=%?, b=%s, mt_b=%?)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx), mt_b); + + // If we have a parameter of type `&M T_a` and the value + // provided is `expr`, we will be adding an implicit borrow, + // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, + // to type check, we will construct the type that `&M*expr` would + // yield. + + let sub = Sub(**self); + let r_borrow = self.infcx.next_region_var_nb(self.span); + + let inner_ty = match *sty_a { + ty::ty_box(mt_a) => mt_a.ty, + ty::ty_uniq(mt_a) => mt_a.ty, + ty::ty_rptr(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); } }; - debug!("Coerce.tys end"); - - 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(); + debug!("coerce_borrowed_string(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); - fn is_borrowable(v: ty::vstore) -> bool { - match v { - ty::vstore_fixed(_) | ty::vstore_uniq | ty::vstore_box => true, - ty::vstore_slice(_) => false + match *sty_a { + ty::ty_estr(vstore_box) | + ty::ty_estr(vstore_uniq) => {} + _ => { + return self.subtype(a, b); } - } + }; - 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 - } - } + 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 + }) + })) + } - 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) - } + 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)); - (&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 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 } _ => { - // if insufficient bounds were available, just follow - // normal subtype rules: - to_ares(Sub(**self).tys(a, b)) + return self.subtype(a, b); } + }; + + let a_borrowed = ty::mk_evec(self.infcx.tcx, + mt {ty: ty_inner, mutbl: mt_b.mutbl}, + vstore_slice(r_borrow)); + if_ok!(sub.tys(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 0, + autoref: Some(AutoRef { + kind: AutoBorrowVec, + region: r_borrow, + mutbl: mt_b.mutbl + }) + })) + } + + fn coerce_borrowed_fn(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t) -> CoerceResult + { + debug!("coerce_borrowed_fn(a=%s, sty_a=%?, b=%s)", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + let fn_ty = match *sty_a { + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBox => {f} + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoUniq => {f} + ty::ty_fn(ref f) if f.meta.proto == ast::ProtoBare => { + return self.coerce_from_bare_fn(a, f, b); + } + _ => { + return self.subtype(a, b); + } + }; + + let r_borrow = self.infcx.next_region_var_nb(self.span); + let meta = FnMeta {proto: ast::ProtoBorrowed, + region: r_borrow, + ..fn_ty.meta}; + let a_borrowed = ty::mk_fn(self.infcx.tcx, + FnTyBase {meta: meta, + sig: copy fn_ty.sig}); + + if_ok!(self.subtype(a_borrowed, b)); + Ok(Some(@AutoAdjustment { + autoderefs: 0, + autoref: Some(AutoRef { + kind: AutoBorrowFn, + region: r_borrow, + mutbl: m_imm + }) + })) + } + + fn coerce_from_bare_fn(&self, + a: ty::t, + fn_ty_a: &ty::FnTy, + b: ty::t) -> CoerceResult + { + do self.unpack_actual_value(b) |sty_b| { + self.coerce_from_bare_fn_post_unpack(a, fn_ty_a, b, sty_b) } } - /// 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)); + debug!("coerce_from_bare_fn(a=%s, b=%s)", + a.inf_str(self.infcx), 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 - }) - })) - } + 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 8ec7e68176a5..cac5fa5feb1c 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 01f4b86b4885..b25c4db8a90b 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 839d4137ba92..19cdfe784d2b 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 000000000000..c4cdbcb67e2b --- /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 83938293d754..356a64498219 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 c7c6aec9f1df..69f07e3e7749 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 1f59e9a8128c..f99924297334 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 000000000000..3c9748f29d98 --- /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 000000000000..0d8f40677f8b --- /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 000000000000..54a6b35b8baa --- /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 000000000000..24fb5cbd8830 --- /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 000000000000..4579907dfbd4 --- /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 000000000000..c915c01416e8 --- /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 000000000000..0cce52e7dc8d --- /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 000000000000..9fb748f049fd --- /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 04932676f3d6..8a7ebb8d129e 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 297858567452..453d556b13c9 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(); }