diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 909d1f95fde4b..6cd14d332bf55 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -111,6 +111,8 @@ enum special_kind { // a complete categorization of a value indicating where it originated // and how it is located, as well as the mutability of the memory in // which the value is stored. +// +// note: cmt stands for "categorized mutable type". type cmt_ = {id: ast::node_id, // id of expr/pat producing this value span: span, // span of same expr/pat cat: categorization, // categorization of expr diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 68198ae3f3fc9..60ed64b1c1d5e 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -25,6 +25,7 @@ use core::str; use core::vec; use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm}; use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk, capture_clause}; +use syntax::ast::{bind_by_value, bind_infer, bind_by_ref, bind_by_move}; use syntax::ast::{crate, crate_num, decl_item, def, def_arg, def_binding}; use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label}; use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self}; @@ -4521,6 +4522,10 @@ impl Resolver { struct or enum variant", self.session.str_of(ident)); + self.enforce_default_binding_mode( + pattern, + binding_mode, + "an enum variant"); self.record_def(pattern.id, def); } FoundStructOrEnumVariant(_) => { @@ -4537,6 +4542,10 @@ impl Resolver { constant", self.session.str_of(ident)); + self.enforce_default_binding_mode( + pattern, + binding_mode, + "a constant"); self.record_def(pattern.id, def); } FoundConst(_) => { @@ -5371,6 +5380,32 @@ impl Resolver { self.def_map.insert(node_id, def); } + fn enforce_default_binding_mode(pat: @pat, + pat_binding_mode: binding_mode, + descr: &str) { + match pat_binding_mode { + bind_infer => {} + bind_by_value => { + self.session.span_err( + pat.span, + fmt!("cannot use `copy` binding mode with %s", + descr)); + } + bind_by_move => { + self.session.span_err( + pat.span, + fmt!("cannot use `move` binding mode with %s", + descr)); + } + bind_by_ref(*) => { + self.session.span_err( + pat.span, + fmt!("cannot use `ref` binding mode with %s", + descr)); + } + } + } + // // main function checking // diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c82825795869a..2e0d7026769ef 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -119,6 +119,7 @@ export ty_opaque_box, mk_opaque_box; export ty_float, mk_float, mk_mach_float, type_is_fp; export ty_fn, FnTy, FnTyBase, FnMeta, FnSig, mk_fn; export ty_fn_proto, ty_fn_purity, ty_fn_ret, tys_in_fn_sig; +export ty_vstore; export replace_fn_return_type; export ty_int, mk_int, mk_mach_int, mk_char; export mk_i8, mk_u8, mk_i16, mk_u16, mk_i32, mk_u32, mk_i64, mk_u64; @@ -3005,6 +3006,14 @@ fn is_fn_ty(fty: t) -> bool { } } +pure fn ty_vstore(ty: t) -> vstore { + match get(ty).sty { + ty_evec(_, vstore) => vstore, + ty_estr(vstore) => vstore, + ref s => fail fmt!("ty_vstore() called on invalid sty: %?", s) + } +} + fn ty_region(ty: t) -> Region { match get(ty).sty { ty_rptr(r, _) => r, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 43f609dd2f0a3..be6559640d724 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -105,7 +105,7 @@ use middle::typeck::{isr_alist, lookup_def_ccx, method_map_entry}; use middle::typeck::{method_origin, method_self, method_trait, no_params}; use middle::typeck::{require_same_types}; use util::common::{block_query, indenter, loop_query}; -use util::ppaux::{bound_region_to_str, expr_repr}; +use util::ppaux::{bound_region_to_str, expr_repr, pat_repr}; use util::ppaux; use core::either; @@ -127,7 +127,6 @@ use syntax::ast_util; use syntax::codemap::span; use syntax::codemap; use syntax::parse::token::special_idents; -use syntax::print::pprust::{expr_to_str, pat_to_str}; use syntax::print::pprust; use syntax::visit; use syntax; @@ -469,7 +468,7 @@ fn check_fn(ccx: @crate_ctxt, }; assign(local.span, local.node.id, o_ty); debug!("Local variable %s is assigned to %s", - pat_to_str(local.node.pat, tcx.sess.intr()), + fcx.pat_to_str(local.node.pat), fcx.inh.locals.get(local.node.id).to_str()); visit::visit_local(local, e, v); }; @@ -756,6 +755,10 @@ impl @fn_ctxt { expr_repr(self.tcx(), expr) } + fn pat_to_str(pat: @ast::pat) -> ~str { + pat_repr(self.tcx(), pat) + } + fn expr_ty(ex: @ast::expr) -> ty::t { match self.inh.node_types.find(ex.id) { Some(t) => t, @@ -1600,7 +1603,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, let fty = ty::mk_fn(tcx, copy fn_ty); debug!("check_expr_fn_with_unifier %s fty=%s", - expr_to_str(expr, tcx.sess.intr()), + fcx.expr_to_str(expr), fcx.infcx().ty_to_str(fty)); fcx.write_ty(expr.id, fty); @@ -2713,7 +2716,7 @@ fn check_enum_variants(ccx: @crate_ctxt, do v.node.disr_expr.iter |e_ref| { let e = *e_ref; debug!("disr expr, checking %s", - expr_to_str(e, ccx.tcx.sess.intr())); + pprust::expr_to_str(e, ccx.tcx.sess.intr())); let declty = ty::mk_int(ccx.tcx); let fcx = blank_fn_ctxt(ccx, rty, e.id); check_const_with_ty(fcx, e.span, e, declty); diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 05f23bddb0860..419e6269957cf 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -30,7 +30,7 @@ this point a bit better. use core::prelude::*; use middle::freevars::get_freevars; -use middle::pat_util::pat_bindings; +use middle::pat_util::{pat_bindings, pat_is_binding}; use middle::ty::{encl_region, re_scope}; use middle::ty::{ty_fn_proto, vstore_box, vstore_fixed, vstore_slice}; use middle::ty::{vstore_uniq}; @@ -73,35 +73,44 @@ fn encl_region_of_def(fcx: @fn_ctxt, def: ast::def) -> ty::Region { } impl @rcx { - /// Try to resolve the type for the given node. - /// - /// Note one important point: we do not attempt to resolve *region - /// variables* here. This is because regionck is essentially adding - /// constraints to those region variables and so may yet influence - /// how they are resolved. - /// - /// Consider this silly example: - /// - /// fn borrow(x: &int) -> &int {x} - /// fn foo(x: @int) -> int { /* block: B */ - /// let b = borrow(x); /* region: */ - /// *b - /// } - /// - /// Here, the region of `b` will be ``. `` is constrainted - /// to be some subregion of the block B and some superregion of - /// the call. If we forced it now, we'd choose the smaller region - /// (the call). But that would make the *b illegal. Since we don't - /// resolve, the type of b will be `&.int` and then `*b` will require - /// that `` be bigger than the let and the `*b` expression, so we - /// will effectively resolve `` to be the block B. - fn resolve_type(unresolved_ty: ty::t) -> fres { - resolve_type(self.fcx.infcx(), unresolved_ty, - resolve_and_force_all_but_regions) + fn resolve_type(unresolved_ty: ty::t) -> Option { + /*! + * Try to resolve the type for the given node, returning + * None if an error results. Note that we never care + * about the details of the error, the same error will be + * detected and reported in the writeback phase. + * + * Note one important point: we do not attempt to resolve + * *region variables* here. This is because regionck is + * essentially adding constraints to those region variables + * and so may yet influence how they are resolved. + * + * Consider this silly example: + * + * fn borrow(x: &int) -> &int {x} + * fn foo(x: @int) -> int { // block: B + * let b = borrow(x); // region: + * *b + * } + * + * Here, the region of `b` will be ``. `` is + * constrainted to be some subregion of the block B and some + * superregion of the call. If we forced it now, we'd choose + * the smaller region (the call). But that would make the *b + * illegal. Since we don't resolve, the type of b will be + * `&.int` and then `*b` will require that `` be + * bigger than the let and the `*b` expression, so we will + * effectively resolve `` to be the block B. + */ + match resolve_type(self.fcx.infcx(), unresolved_ty, + resolve_and_force_all_but_regions) { + Ok(t) => Some(t), + Err(_) => None + } } /// Try to resolve the type for the given node. - fn resolve_node_type(id: ast::node_id) -> fres { + fn resolve_node_type(id: ast::node_id) -> Option { self.resolve_type(self.fcx.node_ty(id)) } } @@ -170,8 +179,7 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) { } fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { - debug!("visit_expr(e=%s)", - pprust::expr_to_str(expr, rcx.fcx.tcx().sess.intr())); + debug!("visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); match /*bad*/copy expr.node { ast::expr_path(*) => { @@ -242,40 +250,36 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) { // particular case. There is an extensive comment on the // function check_cast_for_escaping_regions() in kind.rs // explaining how it goes about doing that. - match rcx.resolve_node_type(expr.id) { - result::Err(_) => { return; /*typeck will fail anyhow*/ } - result::Ok(target_ty) => { - match ty::get(target_ty).sty { - ty::ty_trait(_, _, vstore_slice(trait_region)) => { - let source_ty = rcx.fcx.expr_ty(source); - constrain_regions_in_type(rcx, trait_region, - expr.span, source_ty); - } - _ => () + for rcx.resolve_node_type(expr.id).each |target_ty| { + match ty::get(*target_ty).sty { + ty::ty_trait(_, _, vstore_slice(trait_region)) => { + let source_ty = rcx.fcx.expr_ty(source); + constrain_regions_in_type(rcx, trait_region, + expr.span, source_ty); } + _ => () } - }; + } + } + + ast::expr_addr_of(_, base) => { + guarantor::for_addr_of(rcx, expr, base); } - ast::expr_addr_of(*) => { - // FIXME(#3148) -- in some cases, we need to capture a - // dependency between the regions found in operand the - // resulting region type. See #3148 for more details. + ast::expr_match(discr, ref arms) => { + guarantor::for_match(rcx, discr, *arms); } ast::expr_fn(*) | ast::expr_fn_block(*) => { - match rcx.resolve_node_type(expr.id) { - result::Err(_) => return, // Typechecking will fail anyhow. - result::Ok(function_type) => { - match ty::get(function_type).sty { - ty::ty_fn(ref fn_ty) => { - if fn_ty.meta.proto == ast::ProtoBorrowed { - constrain_free_variables( - rcx, fn_ty.meta.region, expr); - } + for rcx.resolve_node_type(expr.id).each |function_type| { + match ty::get(*function_type).sty { + ty::ty_fn(ref fn_ty) => { + if fn_ty.meta.proto == ast::ProtoBorrowed { + constrain_free_variables( + rcx, fn_ty.meta.region, expr); } - _ => () } + _ => () } } } @@ -406,8 +410,8 @@ fn constrain_regions_in_type_of_node( // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. let ty = match rcx.resolve_node_type(id) { - result::Err(_) => return true, - result::Ok(ty) => ty + None => { return true; } + Some(ty) => { ty } }; debug!("constrain_regions_in_type_of_node(\ @@ -477,3 +481,401 @@ fn constrain_regions_in_type( } } } + +mod guarantor { + /*! + * + * The routines in this module are aiming to deal with the case + * where the lifetime resulting from a borrow is linked to the + * lifetime of the thing being borrowed. Imagine you have a + * borrowed pointer `b` with lifetime L1 and you have an + * expression `&*b`. The result of this borrow will be another + * borrowed pointer with lifetime L2 (which is an inference + * variable). The borrow checker is going to enforce the + * constraint that L2 < L1, because otherwise you are re-borrowing + * data for a lifetime larger than the original loan. However, + * without the routines in this module, the region inferencer would + * not know of this dependency and thus it might infer the + * lifetime of L2 to be greater than L1 (issue #3148). + * + * There are a number of troublesome scenarios in the test + * `region-dependent-addr-of.rs`, but here is one example: + * + * struct Foo { i: int } + * struct Bar { foo: Foo } + * fn get_i(x: &a/Bar) -> &a/int { + * let foo = &x.foo; // Lifetime L1 + * &foo.i // Lifetime L2 + * } + * + * Note that this comes up either with `&` expressions, `ref` + * bindings, and `autorefs`, which are the three ways to introduce + * a borrow. + * + * The key point here is that when you are borrowing a value that + * is "guaranteed" by a borrowed pointer, you must link the + * lifetime of that borrowed pointer (L1, here) to the lifetime of + * the borrow itself (L2). What do I mean by "guaranteed" by a + * borrowed pointer? Well, I would say the data "owned" by the + * borrowed pointer, except that a borrowed pointer never owns its + * contents, but the relation is the same. That is, I mean any + * data that is reached by first derefencing a borrowed pointer + * and then either traversing interior offsets or owned pointers. + * We say that the guarantor of such data it the region of the borrowed + * pointer that was traversed. + * + * NB: I really wanted to use the `mem_categorization` code here + * but I cannot because final type resolution hasn't happened yet. + * So this is very similar logic to what you would find there, + * but more special purpose. + */ + + pub fn for_addr_of(rcx: @rcx, expr: @ast::expr, base: @ast::expr) { + /*! + * + * Computes the guarantor for an expression `&base` and then + * ensures that the lifetime of the resulting pointer is linked. + */ + + debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base)); + let _i = ::util::common::indenter(); + + let guarantor = guarantor(rcx, base); + link(rcx, expr.span, expr.id, guarantor); + } + + pub fn for_match(rcx: @rcx, discr: @ast::expr, arms: &[ast::arm]) { + /*! + * + * Computes the guarantors for any ref bindings in a match and + * then ensures that the lifetime of the resulting pointer is + * linked. + */ + + let discr_guarantor = guarantor(rcx, discr); + for arms.each |arm| { + for arm.pats.each |pat| { + link_ref_bindings_in_pat(rcx, *pat, discr_guarantor); + } + } + } + + fn link( + rcx: @rcx, + span: span, + id: ast::node_id, + guarantor: Option) + { + /*! + * + * Links the lifetime of the borrowed pointer resulting from a borrow + * to the lifetime of its guarantor. + */ + + debug!("opt_constrain_region(id=%?, guarantor=%?)", id, guarantor); + + let bound = match guarantor { + None => { return; } + Some(r) => { r } + }; + + // this routine is used for the result of ref bindings and & + // expressions, both of which always yield a region variable, so + // mk_subr should never fail. + for rcx.resolve_node_type(id).each |rptr_ty| { + debug!("rptr_ty=%s", ty_to_str(rcx.fcx.ccx.tcx, *rptr_ty)); + let r = ty::ty_region(*rptr_ty); + infallibly_mk_subr(rcx, true, span, r, bound); + } + } + + enum PointerCat { + NotPointer, + OwnedPointer, + BorrowedPointer(ty::Region), + OtherPointer + } + + struct ExprCategorization { + guarantor: Option, + pointer: PointerCat + } + + fn guarantor(rcx: @rcx, expr: @ast::expr) -> Option { + debug!("guarantor(expr=%s)", rcx.fcx.expr_to_str(expr)); + match expr.node { + ast::expr_unary(ast::deref, b) => { + let cat = categorize(rcx, b); + guarantor_of_deref(&cat) + } + ast::expr_field(b, _, _) => { + categorize(rcx, b).guarantor + } + ast::expr_index(b, _) => { + let cat = categorize(rcx, b); + guarantor_of_deref(&cat) + } + + ast::expr_paren(e) => { + guarantor(rcx, e) + } + + ast::expr_path(*) => { + // Either a variable or constant and hence resides + // in constant memory or on the stack frame. Either way, + // not guaranteed by a region pointer. + None + } + + // All of these expressions are rvalues and hence their + // value is not guaranteed by a region pointer. + ast::expr_mac(*) | + ast::expr_lit(_) | + ast::expr_unary(*) | + ast::expr_addr_of(*) | + ast::expr_binary(*) | + ast::expr_vstore(*) | + ast::expr_break(*) | + ast::expr_again(*) | + ast::expr_ret(*) | + ast::expr_log(*) | + ast::expr_fail(*) | + ast::expr_assert(*) | + ast::expr_while(*) | + ast::expr_loop(*) | + ast::expr_assign(*) | + ast::expr_swap(*) | + ast::expr_assign_op(*) | + ast::expr_cast(*) | + ast::expr_call(*) | + ast::expr_method_call(*) | + ast::expr_rec(*) | + ast::expr_struct(*) | + ast::expr_tup(*) | + ast::expr_if(*) | + ast::expr_match(*) | + ast::expr_fn(*) | + ast::expr_fn_block(*) | + ast::expr_loop_body(*) | + ast::expr_do_body(*) | + ast::expr_block(*) | + ast::expr_copy(*) | + ast::expr_unary_move(*) | + ast::expr_repeat(*) | + ast::expr_vec(*) => { + assert !ty::expr_is_lval( + rcx.fcx.tcx(), rcx.fcx.ccx.method_map, expr); + None + } + } + } + + fn categorize(rcx: @rcx, + expr: @ast::expr) -> ExprCategorization + { + debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr)); + let _i = ::util::common::indenter(); + + let tcx = rcx.fcx.ccx.tcx; + if rcx.fcx.ccx.method_map.contains_key(expr.id) { + debug!("method call"); + return id_categorization(rcx, None, expr.id); + } + + let expr_ty = match rcx.resolve_node_type(expr.id) { + None => { return id_categorization(rcx, None, expr.id); } + Some(t) => { t } + }; + let mut cat = ExprCategorization { + guarantor: guarantor(rcx, expr), + pointer: pointer_categorize(expr_ty) + }; + + debug!("before adjustments, cat=%?", cat); + + for rcx.fcx.inh.adjustments.find(expr.id).each |adjustment| { + debug!("adjustment=%?", adjustment); + for uint::range(0, adjustment.autoderefs) |_| { + cat.guarantor = guarantor_of_deref(&cat); + + match ty::deref(tcx, expr_ty, true) { + Some(t) => { + cat.pointer = pointer_categorize(t.ty); + } + None => { + tcx.sess.span_bug( + expr.span, + fmt!("Autoderef but type not derefable: %s", + ty_to_str(tcx, expr_ty))); + } + } + + debug!("autoderef, cat=%?", cat); + } + + for adjustment.autoref.each |autoref| { + cat.guarantor = None; + cat.pointer = BorrowedPointer(autoref.region); + debug!("autoref, cat=%?", cat); + } + } + + debug!("result=%?", cat); + return cat; + } + + fn pointer_categorize(ty: ty::t) -> PointerCat { + match ty::get(ty).sty { + ty::ty_rptr(r, _) | ty::ty_evec(_, vstore_slice(r)) | + ty::ty_estr(vstore_slice(r)) => { + BorrowedPointer(r) + } + ty::ty_uniq(*) | ty::ty_estr(vstore_uniq) | + ty::ty_evec(_, vstore_uniq) => { + OwnedPointer + } + ty::ty_box(*) | ty::ty_ptr(*) | + ty::ty_evec(_, vstore_box) | + ty::ty_estr(vstore_box) => { + OtherPointer + } + _ => { + NotPointer + } + } + } + + fn id_categorization(rcx: @rcx, + guarantor: Option, + id: ast::node_id) -> ExprCategorization + { + let pointer = match rcx.resolve_node_type(id) { + None => NotPointer, + Some(t) => pointer_categorize(t) + }; + + ExprCategorization {guarantor: guarantor, + pointer: pointer} + } + + fn guarantor_of_deref(cat: &ExprCategorization) -> Option { + match cat.pointer { + NotPointer => cat.guarantor, + BorrowedPointer(r) => Some(r), + OwnedPointer => cat.guarantor, + OtherPointer => None + } + } + + fn link_ref_bindings_in_pat( + rcx: @rcx, + pat: @ast::pat, + guarantor: Option) + { + /*! + * + * Descends through the pattern, tracking the guarantor + * of the value being matched. When a ref binding is encountered, + * links the lifetime of that ref binding to the lifetime of + * the guarantor. We begin with the guarantor of the + * discriminant but of course as we go we may pass through + * other pointers. + */ + + debug!("link_ref_bindings_in_pat(pat=%s, guarantor=%?)", + rcx.fcx.pat_to_str(pat), guarantor); + let _i = ::util::common::indenter(); + + match pat.node { + ast::pat_wild => {} + ast::pat_ident(ast::bind_by_ref(_), _, opt_p) => { + link(rcx, pat.span, pat.id, guarantor); + + for opt_p.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + ast::pat_ident(_, _, opt_p) => { + for opt_p.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + ast::pat_enum(*) => {} + ast::pat_rec(ref fpats, _) | + ast::pat_struct(_, ref fpats, _) => { + for fpats.each |fpat| { + link_ref_bindings_in_pat(rcx, fpat.pat, guarantor); + } + } + ast::pat_tup(ref ps) => { + link_ref_bindings_in_pats(rcx, ps, guarantor) + } + ast::pat_box(p) => { + link_ref_bindings_in_pat(rcx, p, None) + } + ast::pat_uniq(p) => { + link_ref_bindings_in_pat(rcx, p, guarantor) + } + ast::pat_region(p) => { + for rcx.resolve_node_type(pat.id).each |rptr_ty| { + let r = ty::ty_region(*rptr_ty); + link_ref_bindings_in_pat(rcx, p, Some(r)); + } + } + ast::pat_lit(*) => {} + ast::pat_range(*) => {} + ast::pat_vec(ref ps, ref opt_tail_pat) => { + for rcx.resolve_node_type(pat.id).each |vec_ty| { + let vstore = ty::ty_vstore(*vec_ty); + let guarantor1 = match vstore { + vstore_fixed(_) | vstore_uniq => guarantor, + vstore_slice(r) => Some(r), + vstore_box => None + }; + + link_ref_bindings_in_pats(rcx, ps, guarantor1); + + for opt_tail_pat.each |p| { + link_ref_bindings_in_pat(rcx, *p, guarantor); + } + } + } + } + } + + fn link_ref_bindings_in_pats(rcx: @rcx, + pats: &~[@ast::pat], + guarantor: Option) + { + for pats.each |pat| { + link_ref_bindings_in_pat(rcx, *pat, guarantor); + } + } + +} + +fn infallibly_mk_subr(rcx: @rcx, + a_is_expected: bool, + span: span, + a: ty::Region, + b: ty::Region) +{ + /*! + * + * Constraints `a` to be a subregion of `b`. In many cases, we + * know that this can never yield an error due to the way that + * region inferencing works. Therefore just report a bug if we + * ever see `Err(_)`. + */ + + match rcx.fcx.mk_subr(a_is_expected, span, a, b) { + result::Ok(()) => {} + result::Err(e) => { + rcx.fcx.ccx.tcx.sess.span_bug( + span, + fmt!("Supposedly infallible attempt to \ + make %? < %? failed: %?", + a, b, e)); + } + } +} \ No newline at end of file diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6b6912915fd25..ac079f8f04450 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -270,6 +270,12 @@ fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str { pprust::expr_to_str(expr, cx.sess.intr())) } +fn pat_repr(cx: ctxt, pat: @ast::pat) -> ~str { + fmt!("pat(%d: %s)", + pat.id, + pprust::pat_to_str(pat, cx.sess.intr())) +} + fn tys_to_str(cx: ctxt, ts: &[t]) -> ~str { let tstrs = ts.map(|t| ty_to_str(cx, *t)); fmt!("(%s)", str::connect(tstrs, ", ")) diff --git a/src/libsyntax/ext/auto_encode.rs b/src/libsyntax/ext/auto_encode.rs index 77f230311358e..1bb516e831fd5 100644 --- a/src/libsyntax/ext/auto_encode.rs +++ b/src/libsyntax/ext/auto_encode.rs @@ -879,7 +879,7 @@ fn ser_variant( let pat_node = if pats.is_empty() { ast::pat_ident( - ast::bind_by_ref(ast::m_imm), + ast::bind_infer, cx.path(span, ~[v_name]), None ) diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 5a8a1d8753dee..5ff6953960619 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -267,8 +267,14 @@ fn mk_pat(cx: ext_ctxt, span: span, +pat: ast::pat_) -> @ast::pat { @ast::pat { id: cx.next_id(), node: pat, span: span } } fn mk_pat_ident(cx: ext_ctxt, span: span, ident: ast::ident) -> @ast::pat { + mk_pat_ident_with_binding_mode(cx, span, ident, ast::bind_by_value) +} +fn mk_pat_ident_with_binding_mode(cx: ext_ctxt, + span: span, + ident: ast::ident, + bm: ast::binding_mode) -> @ast::pat { let path = mk_raw_path(span, ~[ ident ]); - let pat = ast::pat_ident(ast::bind_by_value, path, None); + let pat = ast::pat_ident(bm, path, None); mk_pat(cx, span, move pat) } fn mk_pat_enum(cx: ext_ctxt, diff --git a/src/libsyntax/ext/deriving.rs b/src/libsyntax/ext/deriving.rs index d7059fc197783..d542b104e541e 100644 --- a/src/libsyntax/ext/deriving.rs +++ b/src/libsyntax/ext/deriving.rs @@ -363,7 +363,8 @@ fn create_enum_variant_pattern(cx: ext_ctxt, match variant.node.kind { tuple_variant_kind(ref variant_args) => { if variant_args.len() == 0 { - return build::mk_pat_ident(cx, span, variant_ident); + return build::mk_pat_ident_with_binding_mode( + cx, span, variant_ident, ast::bind_infer); } let matching_path = build::mk_raw_path(span, ~[ variant_ident ]); diff --git a/src/test/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/region-dependent-addr-of.rs new file mode 100644 index 0000000000000..594556fffed95 --- /dev/null +++ b/src/test/run-pass/region-dependent-addr-of.rs @@ -0,0 +1,112 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { + value: B +} + +struct B { + v1: int, + v2: [int * 3], + v3: ~[int], + v4: C, + v5: ~C, + v6: Option +} + +struct C { + f: int +} + +fn get_v1(a: &v/A) -> &v/int { + // Region inferencer must deduce that &v < L2 < L1 + let foo = &a.value; // L1 + &foo.v1 // L2 +} + +fn get_v2(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v2[i] +} + +fn get_v3(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v3[i] +} + +fn get_v4(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v4.f +} + +fn get_v5(a: &v/A, i: uint) -> &v/int { + let foo = &a.value; + &foo.v5.f +} + +fn get_v6_a(a: &v/A, i: uint) -> &v/int { + match a.value.v6 { + Some(ref v) => &v.f, + None => fail + } +} + +fn get_v6_b(a: &v/A, i: uint) -> &v/int { + match *a { + A { value: B { v6: Some(ref v), _ } } => &v.f, + _ => fail + } +} + +fn get_v6_c(a: &v/A, i: uint) -> &v/int { + match a { + &A { value: B { v6: Some(ref v), _ } } => &v.f, + _ => fail + } +} + +fn get_v5_ref(a: &v/A, i: uint) -> &v/int { + match &a.value { + &B {v5: ~C {f: ref v}, _} => v + } +} + +fn main() { + let a = A {value: B {v1: 22, + v2: [23, 24, 25], + v3: ~[26, 27, 28], + v4: C { f: 29 }, + v5: ~C { f: 30 }, + v6: Some(C { f: 31 })}}; + + let p = get_v1(&a); + assert *p == a.value.v1; + + let p = get_v2(&a, 1); + assert *p == a.value.v2[1]; + + let p = get_v3(&a, 1); + assert *p == a.value.v3[1]; + + let p = get_v4(&a, 1); + assert *p == a.value.v4.f; + + let p = get_v5(&a, 1); + assert *p == a.value.v5.f; + + let p = get_v6_a(&a, 1); + assert *p == a.value.v6.get().f; + + let p = get_v6_b(&a, 1); + assert *p == a.value.v6.get().f; + + let p = get_v6_c(&a, 1); + assert *p == a.value.v6.get().f; +} diff --git a/src/test/run-pass/region-return-interior-of-option-in-self.rs b/src/test/run-pass/region-return-interior-of-option-in-self.rs deleted file mode 100644 index 8ed85b957ee73..0000000000000 --- a/src/test/run-pass/region-return-interior-of-option-in-self.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-test (#3148) - -struct cell { - value: T; -} - -struct cells { - vals: ~[Option>]; -} - -impl &cells { - fn get(idx: uint) -> &self/T { - match self.vals[idx] { - Some(ref v) => &v.value, - None => fail - } - } -} - -fn main() {}