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
This commit is contained in:
parent
c07ae16de1
commit
2b67d88809
|
@ -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);
|
||||
|
|
|
@ -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 */ }
|
||||
|
|
|
@ -700,6 +700,8 @@ impl LookupContext {
|
|||
autoderefs: uint)
|
||||
-> Option<method_map_entry>
|
||||
{
|
||||
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(
|
||||
|
|
|
@ -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<ast::node_id, TyVid>,
|
||||
locals: HashMap<ast::node_id, ty::t>,
|
||||
node_types: HashMap<ast::node_id, ty::t>,
|
||||
node_type_substs: HashMap<ast::node_id, ty::substs>,
|
||||
adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
|
||||
|
@ -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<self_info>) {
|
||||
let tcx = fcx.ccx.tcx;
|
||||
|
||||
let assign = fn@(span: span, nid: ast::node_id,
|
||||
ty_opt: Option<ty::t>) {
|
||||
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<ty::t>) {
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: 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<T>(+c: cres<T>) -> 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<ty::t>,
|
||||
+b_bnd: Option<ty::t>) -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T> = Option<T>;
|
||||
type Bounds<T> = {lb: Bound<T>, ub: Bound<T>};
|
||||
|
@ -348,7 +348,7 @@ type Bounds<T> = {lb: Bound<T>, ub: Bound<T>};
|
|||
type cres<T> = Result<T,ty::type_err>; // "combine result"
|
||||
type ures = cres<()>; // "unify result"
|
||||
type fres<T> = Result<T, fixup_err>; // "fixup result"
|
||||
type ares = cres<Option<@ty::AutoAdjustment>>; // "assignment result"
|
||||
type CoerceResult = cres<Option<@ty::AutoAdjustment>>;
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ use std::map;
|
|||
fn main() {
|
||||
let buggy_map :HashMap<uint, &uint> =
|
||||
HashMap::<uint, &uint>();
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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() {}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -20,11 +20,12 @@ fn repeater<A:Copy>(v: @A) -> repeat<A> {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -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| {
|
||||
|
||||
|
|
|
@ -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() {}
|
|
@ -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;
|
||||
}
|
|
@ -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() {}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -17,5 +17,5 @@ use std::map;
|
|||
fn main() {
|
||||
let buggy_map :HashMap<uint, &uint> = HashMap::<uint, &uint>();
|
||||
let x = ~1;
|
||||
buggy_map.insert(42, x);
|
||||
buggy_map.insert(42, &*x);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue