From 2e10ea58c3c4954e2343363f392d65db1482ad90 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 24 Jan 2013 19:33:48 -0800 Subject: [PATCH] Integrate vec patterns into borrow checker. The tail portion of the pattern effectively borrows a vector, but the borrow checker knew nothing about this. r=catamorphism --- src/librustc/middle/borrowck/gather_loans.rs | 42 +++++++++++++ src/librustc/middle/borrowck/mod.rs | 8 ++- src/librustc/middle/mem_categorization.rs | 60 +++++++++++-------- src/librustc/middle/ty.rs | 4 +- src/librustc/middle/typeck/check/regionck.rs | 29 +++++---- ...s => borrowck-vec-pattern-element-loan.rs} | 6 +- .../borrowck-vec-pattern-loan-from-mut.rs | 12 ++++ .../borrowck-vec-pattern-nesting.rs | 21 +++++++ ...borrowck-vec-pattern-tail-element-loan.rs} | 4 +- src/test/run-pass/region-dependent-addr-of.rs | 3 + .../vec-matching-legal-tail-element-borrow.rs | 2 +- 11 files changed, 144 insertions(+), 47 deletions(-) rename src/test/compile-fail/{alt-vec-illegal-tail-loan.rs => borrowck-vec-pattern-element-loan.rs} (67%) create mode 100644 src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs create mode 100644 src/test/compile-fail/borrowck-vec-pattern-nesting.rs rename src/test/compile-fail/{alt-vec-illegal-tail-element-loan.rs => borrowck-vec-pattern-tail-element-loan.rs} (65%) diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index 967b1681b1bf..0fff0c66a384 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -591,11 +591,53 @@ impl gather_loan_ctxt { } } + ast::pat_vec(_, Some(tail_pat)) => { + // The `tail_pat` here creates a slice into the + // original vector. This is effectively a borrow of + // the elements of the vector being matched. + + let tail_ty = self.tcx().ty(tail_pat); + let (tail_mutbl, tail_r) = + self.vec_slice_info(tail_pat, tail_ty); + let mcx = self.bccx.mc_ctxt(); + let cmt_index = mcx.cat_index(tail_pat, cmt); + self.guarantee_valid(cmt_index, tail_mutbl, tail_r); + } + _ => {} } } } + fn vec_slice_info(&self, + pat: @ast::pat, + tail_ty: ty::t) -> (ast::mutability, ty::Region) + { + /*! + * + * In a pattern like [a, b, ..c], normally `c` has slice type, + * but if you have [a, b, ..ref c], then the type of `ref c` + * will be `&&[]`, so to extract the slice details we have + * to recurse through rptrs. + */ + + match ty::get(tail_ty).sty { + ty::ty_evec(tail_mt, ty::vstore_slice(tail_r)) => { + (tail_mt.mutbl, tail_r) + } + + ty::ty_rptr(_, ref mt) => { + self.vec_slice_info(pat, mt.ty) + } + + _ => { + self.tcx().sess.span_bug( + pat.span, + fmt!("Type of tail pattern is not a slice")); + } + } + } + fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool { pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat) } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 35c1c96befae..a01c04a8abf1 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -504,9 +504,13 @@ impl borrowck_ctxt { return @{cat:cat_discr(cmt, match_id),.. *cmt}; } + fn mc_ctxt() -> mem_categorization_ctxt { + mem_categorization_ctxt {tcx: self.tcx, + method_map: self.method_map} + } + fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { - let mc = &mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; + let mc = self.mc_ctxt(); mc.cat_pattern(cmt, pat, op); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 6cd14d332bf5..7c1e96e5c0e4 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -521,8 +521,8 @@ impl &mem_categorization_ctxt { ty: self.tcx.ty(arg)} } - fn cat_rvalue(expr: @ast::expr, expr_ty: ty::t) -> cmt { - @{id:expr.id, span:expr.span, + fn cat_rvalue(elt: N, expr_ty: ty::t) -> cmt { + @{id:elt.id(), span:elt.span(), cat:cat_rvalue, lp:None, mutbl:m_imm, ty:expr_ty} } @@ -643,12 +643,12 @@ impl &mem_categorization_ctxt { } } - fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt { + fn cat_index(elt: N, base_cmt: cmt) -> cmt { let mt = match ty::index(self.tcx, base_cmt.ty) { Some(mt) => mt, None => { self.tcx.sess.span_bug( - expr.span, + elt.span(), fmt!("Explicit index of non-index type `%s`", ty_to_str(self.tcx, base_cmt.ty))); } @@ -675,25 +675,27 @@ impl &mem_categorization_ctxt { }; // (c) the deref is explicit in the resulting cmt - let deref_cmt = @{id:expr.id, span:expr.span, + let deref_cmt = @{id:elt.id(), span:elt.span(), cat:cat_deref(base_cmt, 0u, ptr), lp:deref_lp, mutbl:m, ty:mt.ty}; - comp(expr, deref_cmt, base_cmt.ty, m, mt.ty) + comp(elt, deref_cmt, base_cmt.ty, m, mt.ty) } deref_comp(_) => { // fixed-length vectors have no deref let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl); - comp(expr, base_cmt, base_cmt.ty, m, mt.ty) + comp(elt, base_cmt, base_cmt.ty, m, mt.ty) } }; - fn comp(expr: @ast::expr, of_cmt: cmt, - vect: ty::t, mutbl: ast::mutability, ty: ty::t) -> cmt { + fn comp(elt: N, of_cmt: cmt, + vect: ty::t, mutbl: ast::mutability, + ty: ty::t) -> cmt + { let comp = comp_index(vect, mutbl); let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) ); - @{id:expr.id, span:expr.span, + @{id:elt.id(), span:elt.span(), cat:cat_comp(of_cmt, comp), lp:index_lp, mutbl:mutbl, ty:ty} } @@ -723,8 +725,6 @@ impl &mem_categorization_ctxt { fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) { - op(cmt, pat); - // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. // @@ -759,13 +759,15 @@ impl &mem_categorization_ctxt { // and the id of `local(x)->@->@` is the id of the `y` pattern. - let _i = indenter(); let tcx = self.tcx; debug!("cat_pattern: id=%d pat=%s cmt=%s", pat.id, pprust::pat_to_str(pat, tcx.sess.intr()), self.cmt_to_repr(cmt)); + let _i = indenter(); - match /*bad*/copy pat.node { + op(cmt, pat); + + match pat.node { ast::pat_wild => { // _ } @@ -773,7 +775,7 @@ impl &mem_categorization_ctxt { ast::pat_enum(_, None) => { // variant(*) } - ast::pat_enum(_, Some(subpats)) => { + ast::pat_enum(_, Some(ref subpats)) => { match self.tcx.def_map.find(pat.id) { Some(ast::def_variant(enum_did, _)) => { // variant(x, y, z) @@ -805,7 +807,8 @@ impl &mem_categorization_ctxt { // nullary variant or identifier: ignore } - ast::pat_rec(field_pats, _) => { + ast::pat_rec(ref field_pats, _) | + ast::pat_struct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for field_pats.each |fp| { let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id); @@ -813,15 +816,7 @@ impl &mem_categorization_ctxt { } } - ast::pat_struct(_, field_pats, _) => { - // {f1: p1, ..., fN: pN} - for field_pats.each |fp| { - let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id); - self.cat_pattern(cmt_field, fp.pat, op); - } - } - - ast::pat_tup(subpats) => { + ast::pat_tup(ref subpats) => { // (p1, ..., pN) for subpats.each |subpat| { let subcmt = self.cat_tuple_elt(*subpat, cmt); @@ -836,7 +831,20 @@ impl &mem_categorization_ctxt { self.cat_pattern(subcmt, subpat, op); } - ast::pat_vec(*) | ast::pat_lit(_) | ast::pat_range(_, _) => { + ast::pat_vec(ref pats, opt_tail_pat) => { + for pats.each |pat| { + let elt_cmt = self.cat_index(*pat, cmt); + self.cat_pattern(elt_cmt, *pat, op); + } + + for opt_tail_pat.each |tail_pat| { + let tail_ty = self.tcx.ty(*tail_pat); + let tail_cmt = self.cat_rvalue(*tail_pat, tail_ty); + self.cat_pattern(tail_cmt, *tail_pat, op); + } + } + + ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2e0d7026769e..840726b80417 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3017,7 +3017,9 @@ pure fn ty_vstore(ty: t) -> vstore { fn ty_region(ty: t) -> Region { match get(ty).sty { ty_rptr(r, _) => r, - ref s => fail fmt!("ty_region() invoked on non-rptr: %?", (*s)) + ty_evec(_, vstore_slice(r)) => r, + ty_estr(vstore_slice(r)) => r, + ref s => fail fmt!("ty_region() invoked on in appropriate ty: %?", (*s)) } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 419e6269957c..a4aa645da0b5 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -530,6 +530,13 @@ mod guarantor { * but more special purpose. */ + use core::prelude::*; + use middle::typeck::check::regionck::{rcx, infallibly_mk_subr}; + use middle::ty; + use syntax::ast; + use syntax::codemap::span; + use util::ppaux::{ty_to_str}; + pub fn for_addr_of(rcx: @rcx, expr: @ast::expr, base: @ast::expr) { /*! * @@ -726,17 +733,17 @@ mod guarantor { 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)) => { + ty::ty_rptr(r, _) | ty::ty_evec(_, ty::vstore_slice(r)) | + ty::ty_estr(ty::vstore_slice(r)) => { BorrowedPointer(r) } - ty::ty_uniq(*) | ty::ty_estr(vstore_uniq) | - ty::ty_evec(_, vstore_uniq) => { + ty::ty_uniq(*) | ty::ty_estr(ty::vstore_uniq) | + ty::ty_evec(_, ty::vstore_uniq) => { OwnedPointer } ty::ty_box(*) | ty::ty_ptr(*) | - ty::ty_evec(_, vstore_box) | - ty::ty_estr(vstore_box) => { + ty::ty_evec(_, ty::vstore_box) | + ty::ty_estr(ty::vstore_box) => { OtherPointer } _ => { @@ -828,9 +835,9 @@ mod guarantor { 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 + ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, + ty::vstore_slice(r) => Some(r), + ty::vstore_box => None }; link_ref_bindings_in_pats(rcx, ps, guarantor1); @@ -844,8 +851,8 @@ mod guarantor { } fn link_ref_bindings_in_pats(rcx: @rcx, - pats: &~[@ast::pat], - guarantor: Option) + pats: &~[@ast::pat], + guarantor: Option) { for pats.each |pat| { link_ref_bindings_in_pat(rcx, *pat, guarantor); diff --git a/src/test/compile-fail/alt-vec-illegal-tail-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs similarity index 67% rename from src/test/compile-fail/alt-vec-illegal-tail-loan.rs rename to src/test/compile-fail/borrowck-vec-pattern-element-loan.rs index 01f270772167..358917de85fb 100644 --- a/src/test/compile-fail/alt-vec-illegal-tail-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs @@ -1,9 +1,7 @@ -// xfail-test - fn a() -> &[int] { let vec = [1, 2, 3, 4]; - let tail = match vec { - [_a, ..tail] => tail, //~ ERROR illegal borrow + let tail = match vec { //~ ERROR illegal borrow + [_a, ..tail] => tail, _ => fail ~"foo" }; move tail diff --git a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs new file mode 100644 index 000000000000..27902100373a --- /dev/null +++ b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs @@ -0,0 +1,12 @@ +fn a() { + let mut v = ~[1, 2, 3]; + match v { + [_a, ..tail] => { + v.push(tail[0] + tail[1]); //~ ERROR conflicts with prior loan + } + _ => {} + }; +} + +fn main() {} + diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs new file mode 100644 index 000000000000..50feff707adf --- /dev/null +++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs @@ -0,0 +1,21 @@ +fn a() { + let mut vec = [~1, ~2, ~3]; + match vec { + [~ref _a] => { + vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + } + _ => fail ~"foo" + } +} + +fn b() { + let mut vec = [~1, ~2, ~3]; + match vec { + [.._b] => { + vec[0] = ~4; //~ ERROR prohibited due to outstanding loan + } + } +} + +fn main() {} + diff --git a/src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs similarity index 65% rename from src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs rename to src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs index 36d019058ae7..6477fd9fb2cf 100644 --- a/src/test/compile-fail/alt-vec-illegal-tail-element-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs @@ -1,7 +1,7 @@ fn a() -> &int { let vec = [1, 2, 3, 4]; - let tail = match vec { - [_a, ..tail] => &tail[0], //~ ERROR illegal borrow + let tail = match vec { //~ ERROR illegal borrow + [_a, ..tail] => &tail[0], _ => fail ~"foo" }; move tail diff --git a/src/test/run-pass/region-dependent-addr-of.rs b/src/test/run-pass/region-dependent-addr-of.rs index 594556fffed9..6765c1a11ae7 100644 --- a/src/test/run-pass/region-dependent-addr-of.rs +++ b/src/test/run-pass/region-dependent-addr-of.rs @@ -109,4 +109,7 @@ fn main() { let p = get_v6_c(&a, 1); assert *p == a.value.v6.get().f; + + let p = get_v5_ref(&a, 1); + assert *p == a.value.v5.f; } diff --git a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs index 6b005bf0e121..0d093a1b4b0f 100644 --- a/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs +++ b/src/test/run-pass/vec-matching-legal-tail-element-borrow.rs @@ -2,7 +2,7 @@ fn main() { let x = &[1, 2, 3, 4, 5]; if !x.is_empty() { let el = match x { - [1, ..ref tail] => &tail[0], + [1, ..ref tail] => &tail[0], _ => ::core::util::unreachable() }; io::println(fmt!("%d", *el));