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
This commit is contained in:
Niko Matsakis 2013-01-24 19:33:48 -08:00
parent d4fd30c6ac
commit 2e10ea58c3
11 changed files with 144 additions and 47 deletions

View File

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

View File

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

View File

@ -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<N: ast_node>(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<N: ast_node>(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<N: ast_node>(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*/
}
}

View File

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

View File

@ -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<ty::Region>)
pats: &~[@ast::pat],
guarantor: Option<ty::Region>)
{
for pats.each |pat| {
link_ref_bindings_in_pat(rcx, *pat, guarantor);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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