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:
Niko Matsakis 2013-01-24 16:24:45 -08:00
parent 83ca034d2e
commit d4fd30c6ac
11 changed files with 640 additions and 94 deletions

View File

@ -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

View File

@ -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
//

View File

@ -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,

View File

@ -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);

View File

@ -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,40 +250,36 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
// particular case. There is an extensive comment on the
// function check_cast_for_escaping_regions() in kind.rs
// explaining how it goes about doing that.
match rcx.resolve_node_type(expr.id) {
result::Err(_) => { return; /*typeck will fail anyhow*/ }
result::Ok(target_ty) => {
match ty::get(target_ty).sty {
ty::ty_trait(_, _, vstore_slice(trait_region)) => {
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, trait_region,
expr.span, source_ty);
}
_ => ()
for rcx.resolve_node_type(expr.id).each |target_ty| {
match ty::get(*target_ty).sty {
ty::ty_trait(_, _, vstore_slice(trait_region)) => {
let source_ty = rcx.fcx.expr_ty(source);
constrain_regions_in_type(rcx, trait_region,
expr.span, source_ty);
}
_ => ()
}
};
}
}
ast::expr_addr_of(*) => {
// FIXME(#3148) -- in some cases, we need to capture a
// dependency between the regions found in operand the
// resulting region type. See #3148 for more details.
ast::expr_addr_of(_, base) => {
guarantor::for_addr_of(rcx, expr, base);
}
ast::expr_match(discr, ref arms) => {
guarantor::for_match(rcx, discr, *arms);
}
ast::expr_fn(*) | ast::expr_fn_block(*) => {
match rcx.resolve_node_type(expr.id) {
result::Err(_) => return, // Typechecking will fail anyhow.
result::Ok(function_type) => {
match ty::get(function_type).sty {
ty::ty_fn(ref fn_ty) => {
if fn_ty.meta.proto == ast::ProtoBorrowed {
constrain_free_variables(
rcx, fn_ty.meta.region, expr);
}
for rcx.resolve_node_type(expr.id).each |function_type| {
match ty::get(*function_type).sty {
ty::ty_fn(ref fn_ty) => {
if fn_ty.meta.proto == ast::ProtoBorrowed {
constrain_free_variables(
rcx, fn_ty.meta.region, expr);
}
_ => ()
}
_ => ()
}
}
}
@ -406,8 +410,8 @@ fn constrain_regions_in_type_of_node(
// is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase.
let ty = match rcx.resolve_node_type(id) {
result::Err(_) => return true,
result::Ok(ty) => ty
None => { return true; }
Some(ty) => { ty }
};
debug!("constrain_regions_in_type_of_node(\
@ -477,3 +481,401 @@ fn constrain_regions_in_type(
}
}
}
mod guarantor {
/*!
*
* The routines in this module are aiming to deal with the case
* where the lifetime resulting from a borrow is linked to the
* lifetime of the thing being borrowed. Imagine you have a
* borrowed pointer `b` with lifetime L1 and you have an
* expression `&*b`. The result of this borrow will be another
* borrowed pointer with lifetime L2 (which is an inference
* variable). The borrow checker is going to enforce the
* constraint that L2 < L1, because otherwise you are re-borrowing
* data for a lifetime larger than the original loan. However,
* without the routines in this module, the region inferencer would
* not know of this dependency and thus it might infer the
* lifetime of L2 to be greater than L1 (issue #3148).
*
* There are a number of troublesome scenarios in the test
* `region-dependent-addr-of.rs`, but here is one example:
*
* struct Foo { i: int }
* struct Bar { foo: Foo }
* fn get_i(x: &a/Bar) -> &a/int {
* let foo = &x.foo; // Lifetime L1
* &foo.i // Lifetime L2
* }
*
* Note that this comes up either with `&` expressions, `ref`
* bindings, and `autorefs`, which are the three ways to introduce
* a borrow.
*
* The key point here is that when you are borrowing a value that
* is "guaranteed" by a borrowed pointer, you must link the
* lifetime of that borrowed pointer (L1, here) to the lifetime of
* the borrow itself (L2). What do I mean by "guaranteed" by a
* borrowed pointer? Well, I would say the data "owned" by the
* borrowed pointer, except that a borrowed pointer never owns its
* contents, but the relation is the same. That is, I mean any
* data that is reached by first derefencing a borrowed pointer
* and then either traversing interior offsets or owned pointers.
* We say that the guarantor of such data it the region of the borrowed
* pointer that was traversed.
*
* NB: I really wanted to use the `mem_categorization` code here
* but I cannot because final type resolution hasn't happened yet.
* So this is very similar logic to what you would find there,
* but more special purpose.
*/
pub fn for_addr_of(rcx: @rcx, expr: @ast::expr, base: @ast::expr) {
/*!
*
* Computes the guarantor for an expression `&base` and then
* ensures that the lifetime of the resulting pointer is linked.
*/
debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base));
let _i = ::util::common::indenter();
let guarantor = guarantor(rcx, base);
link(rcx, expr.span, expr.id, guarantor);
}
pub fn for_match(rcx: @rcx, discr: @ast::expr, arms: &[ast::arm]) {
/*!
*
* Computes the guarantors for any ref bindings in a match and
* then ensures that the lifetime of the resulting pointer is
* linked.
*/
let discr_guarantor = guarantor(rcx, discr);
for arms.each |arm| {
for arm.pats.each |pat| {
link_ref_bindings_in_pat(rcx, *pat, discr_guarantor);
}
}
}
fn link(
rcx: @rcx,
span: span,
id: ast::node_id,
guarantor: Option<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));
}
}
}

View File

@ -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, ", "))

View File

@ -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
)

View File

@ -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,

View File

@ -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 ]);

View 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;
}

View File

@ -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() {}