Link the lifetimes of regions resulting from borrows of the
contents of other borrowed pointers to the lifetimes of the borrowed value. Fixes #3148. r=catamorphism
This commit is contained in:
parent
83ca034d2e
commit
d4fd30c6ac
11 changed files with 640 additions and 94 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: <R0> */
|
||||
/// *b
|
||||
/// }
|
||||
///
|
||||
/// Here, the region of `b` will be `<R0>`. `<R0>` 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 `&<R0>.int` and then `*b` will require
|
||||
/// that `<R0>` be bigger than the let and the `*b` expression, so we
|
||||
/// will effectively resolve `<R0>` to be the block B.
|
||||
fn resolve_type(unresolved_ty: ty::t) -> fres<ty::t> {
|
||||
resolve_type(self.fcx.infcx(), unresolved_ty,
|
||||
resolve_and_force_all_but_regions)
|
||||
fn resolve_type(unresolved_ty: ty::t) -> Option<ty::t> {
|
||||
/*!
|
||||
* 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: <R0>
|
||||
* *b
|
||||
* }
|
||||
*
|
||||
* Here, the region of `b` will be `<R0>`. `<R0>` 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
|
||||
* `&<R0>.int` and then `*b` will require that `<R0>` be
|
||||
* bigger than the let and the `*b` expression, so we will
|
||||
* effectively resolve `<R0>` 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<ty::t> {
|
||||
fn resolve_node_type(id: ast::node_id) -> Option<ty::t> {
|
||||
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,10 +250,8 @@ 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 {
|
||||
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,
|
||||
|
@ -254,20 +260,19 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
|
|||
_ => ()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ast::expr_addr_of(*) => {
|
||||
// FIXME(#3148) -- in some cases, we need to capture a
|
||||
// dependency between the regions found in operand the
|
||||
// resulting region type. See #3148 for more details.
|
||||
ast::expr_addr_of(_, base) => {
|
||||
guarantor::for_addr_of(rcx, expr, base);
|
||||
}
|
||||
|
||||
ast::expr_match(discr, ref arms) => {
|
||||
guarantor::for_match(rcx, discr, *arms);
|
||||
}
|
||||
|
||||
ast::expr_fn(*) | ast::expr_fn_block(*) => {
|
||||
match rcx.resolve_node_type(expr.id) {
|
||||
result::Err(_) => return, // Typechecking will fail anyhow.
|
||||
result::Ok(function_type) => {
|
||||
match ty::get(function_type).sty {
|
||||
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(
|
||||
|
@ -278,7 +283,6 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => ()
|
||||
}
|
||||
|
@ -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<ty::Region>)
|
||||
{
|
||||
/*!
|
||||
*
|
||||
* 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<ty::Region>,
|
||||
pointer: PointerCat
|
||||
}
|
||||
|
||||
fn guarantor(rcx: @rcx, expr: @ast::expr) -> Option<ty::Region> {
|
||||
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<ty::Region>,
|
||||
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<ty::Region> {
|
||||
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<ty::Region>)
|
||||
{
|
||||
/*!
|
||||
*
|
||||
* 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<ty::Region>)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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, ", "))
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ]);
|
||||
|
|
112
src/test/run-pass/region-dependent-addr-of.rs
Normal file
112
src/test/run-pass/region-dependent-addr-of.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<C>
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// xfail-test (#3148)
|
||||
|
||||
struct cell<T> {
|
||||
value: T;
|
||||
}
|
||||
|
||||
struct cells<T> {
|
||||
vals: ~[Option<cell<T>>];
|
||||
}
|
||||
|
||||
impl<T> &cells<T> {
|
||||
fn get(idx: uint) -> &self/T {
|
||||
match self.vals[idx] {
|
||||
Some(ref v) => &v.value,
|
||||
None => fail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue