Auto merge of #32202 - arielb1:slice-patterns, r=nikomatsakis

Implement RFC495 semantics for slice patterns

non-MIR translation is still not supported for these and will happily ICE.

This is a [breaking-change] for many uses of slice_patterns.

[RFC 495 text](https://github.com/rust-lang/rfcs/blob/master/text/0495-array-pattern-changes.md)
This commit is contained in:
bors 2016-06-08 19:30:33 -07:00
commit bb4b3fb7f9
77 changed files with 940 additions and 738 deletions

View file

@ -106,6 +106,7 @@ pub mod mir {
pub mod tcx;
pub mod visit;
pub mod transform;
pub mod traversal;
pub mod mir_map;
}

View file

@ -993,40 +993,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
}
}
}
PatKind::Vec(_, Some(ref slice_pat), _) => {
// The `slice_pat` here creates a slice into
// the original vector. This is effectively a
// borrow of the elements of the vector being
// matched.
let (slice_cmt, slice_mutbl, slice_r) =
return_if_err!(mc.cat_slice_pattern(cmt_pat, &slice_pat));
// Note: We declare here that the borrow
// occurs upon entering the `[...]`
// pattern. This implies that something like
// `[a; b]` where `a` is a move is illegal,
// because the borrow is already in effect.
// In fact such a move would be safe-ish, but
// it effectively *requires* that we use the
// nulling out semantics to indicate when a
// value has been moved, which we are trying
// to move away from. Otherwise, how can we
// indicate that the first element in the
// vector has been moved? Eventually, we
// could perhaps modify this rule to permit
// `[..a, b]` where `b` is a move, because in
// that case we can adjust the length of the
// original vec accordingly, but we'd have to
// make trans do the right thing, and it would
// only work for `Box<[T]>`s. It seems simpler
// to just require that people call
// `vec.pop()` or `vec.unshift()`.
let slice_bk = ty::BorrowKind::from_mutbl(slice_mutbl);
delegate.borrow(pat.id, pat.span,
slice_cmt, slice_r,
slice_bk, RefBinding);
}
_ => {}
}
}));

View file

@ -228,10 +228,10 @@ fn deref_kind(t: Ty, context: DerefKindContext) -> McResult<deref_kind> {
Ok(deref_interior(InteriorField(PositionalField(0))))
}
ty::TyArray(_, _) | ty::TySlice(_) | ty::TyStr => {
ty::TyArray(_, _) | ty::TySlice(_) => {
// no deref of indexed content without supplying InteriorOffsetKind
if let Some(context) = context {
Ok(deref_interior(InteriorElement(context, element_kind(t))))
Ok(deref_interior(InteriorElement(context, ElementKind::VecElement)))
} else {
Err(())
}
@ -981,18 +981,19 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
let method_call = ty::MethodCall::expr(elt.id());
let method_ty = self.infcx.node_method_ty(method_call);
let element_ty = match method_ty {
let (element_ty, element_kind) = match method_ty {
Some(method_ty) => {
let ref_ty = self.overloaded_method_return_ty(method_ty);
base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
// FIXME(#20649) -- why are we using the `self_ty` as the element type...?
let self_ty = method_ty.fn_sig().input(0);
self.tcx().no_late_bound_regions(&self_ty).unwrap()
(self.tcx().no_late_bound_regions(&self_ty).unwrap(),
ElementKind::OtherElement)
}
None => {
match base_cmt.ty.builtin_index() {
Some(ty) => ty,
Some(ty) => (ty, ElementKind::VecElement),
None => {
return Err(());
}
@ -1000,102 +1001,11 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
}
};
let m = base_cmt.mutbl.inherit();
let ret = interior(elt, base_cmt.clone(), base_cmt.ty,
m, context, element_ty);
let interior_elem = InteriorElement(context, element_kind);
let ret =
self.cat_imm_interior(elt, base_cmt.clone(), element_ty, interior_elem);
debug!("cat_index ret {:?}", ret);
return Ok(ret);
fn interior<'tcx, N: ast_node>(elt: &N,
of_cmt: cmt<'tcx>,
vec_ty: Ty<'tcx>,
mutbl: MutabilityCategory,
context: InteriorOffsetKind,
element_ty: Ty<'tcx>) -> cmt<'tcx>
{
let interior_elem = InteriorElement(context, element_kind(vec_ty));
Rc::new(cmt_ {
id:elt.id(),
span:elt.span(),
cat:Categorization::Interior(of_cmt, interior_elem),
mutbl:mutbl,
ty:element_ty,
note: NoteNone
})
}
}
// Takes either a vec or a reference to a vec and returns the cmt for the
// underlying vec.
fn deref_vec<N:ast_node>(&self,
elt: &N,
base_cmt: cmt<'tcx>,
context: InteriorOffsetKind)
-> McResult<cmt<'tcx>>
{
let ret = match deref_kind(base_cmt.ty, Some(context))? {
deref_ptr(ptr) => {
// for unique ptrs, we inherit mutability from the
// owning reference.
let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr);
// the deref is explicit in the resulting cmt
Rc::new(cmt_ {
id:elt.id(),
span:elt.span(),
cat:Categorization::Deref(base_cmt.clone(), 0, ptr),
mutbl:m,
ty: match base_cmt.ty.builtin_deref(false, ty::NoPreference) {
Some(mt) => mt.ty,
None => bug!("Found non-derefable type")
},
note: NoteNone
})
}
deref_interior(_) => {
base_cmt
}
};
debug!("deref_vec ret {:?}", ret);
Ok(ret)
}
/// Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is the cmt for `P`, `slice_pat` is
/// the pattern `Q`, returns:
///
/// * a cmt for `Q`
/// * the mutability and region of the slice `Q`
///
/// These last two bits of info happen to be things that borrowck needs.
pub fn cat_slice_pattern(&self,
vec_cmt: cmt<'tcx>,
slice_pat: &hir::Pat)
-> McResult<(cmt<'tcx>, hir::Mutability, ty::Region)> {
let slice_ty = self.node_ty(slice_pat.id)?;
let (slice_mutbl, slice_r) = vec_slice_info(slice_pat, slice_ty);
let context = InteriorOffsetKind::Pattern;
let cmt_vec = self.deref_vec(slice_pat, vec_cmt, context)?;
let cmt_slice = self.cat_index(slice_pat, cmt_vec, context)?;
return Ok((cmt_slice, slice_mutbl, slice_r));
/// 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.
fn vec_slice_info(pat: &hir::Pat, slice_ty: Ty)
-> (hir::Mutability, ty::Region) {
match slice_ty.sty {
ty::TyRef(r, ref mt) => match mt.ty.sty {
ty::TySlice(_) => (mt.mutbl, *r),
_ => vec_slice_info(pat, mt.ty),
},
_ => {
span_bug!(pat.span,
"type of slice pattern is not a slice");
}
}
}
}
pub fn cat_imm_interior<N:ast_node>(&self,
@ -1319,15 +1229,12 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
PatKind::Vec(ref before, ref slice, ref after) => {
let context = InteriorOffsetKind::Pattern;
let vec_cmt = self.deref_vec(pat, cmt, context)?;
let elt_cmt = self.cat_index(pat, vec_cmt, context)?;
let elt_cmt = self.cat_index(pat, cmt, context)?;
for before_pat in before {
self.cat_pattern_(elt_cmt.clone(), &before_pat, op)?;
}
if let Some(ref slice_pat) = *slice {
let slice_ty = self.pat_ty(&slice_pat)?;
let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty);
self.cat_pattern_(slice_cmt, &slice_pat, op)?;
self.cat_pattern_(elt_cmt.clone(), &slice_pat, op)?;
}
for after_pat in after {
self.cat_pattern_(elt_cmt.clone(), &after_pat, op)?;
@ -1620,18 +1527,6 @@ impl fmt::Debug for InteriorKind {
}
}
fn element_kind(t: Ty) -> ElementKind {
match t.sty {
ty::TyRef(_, ty::TypeAndMut{ty, ..}) |
ty::TyBox(ty) => match ty.sty {
ty::TySlice(_) => VecElement,
_ => OtherElement
},
ty::TyArray(..) | ty::TySlice(_) => VecElement,
_ => OtherElement
}
}
impl fmt::Debug for Upvar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}/{:?}", self.id, self.kind)

View file

@ -674,6 +674,14 @@ pub enum ProjectionElem<'tcx, V> {
from_end: bool,
},
/// These indices are generated by slice patterns.
///
/// slice[from:-to] in Python terms.
Subslice {
from: u32,
to: u32,
},
/// "Downcast" to a variant of an ADT. Currently, we only introduce
/// this for ADTs with more than one variant. It may be better to
/// just introduce it always, or always for enums.
@ -753,6 +761,14 @@ impl<'tcx> Debug for Lvalue<'tcx> {
write!(fmt, "{:?}[{:?} of {:?}]", data.base, offset, min_length),
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
write!(fmt, "{:?}[-{:?} of {:?}]", data.base, offset, min_length),
ProjectionElem::Subslice { from, to } if to == 0 =>
write!(fmt, "{:?}[{:?}:", data.base, from),
ProjectionElem::Subslice { from, to } if from == 0 =>
write!(fmt, "{:?}[:-{:?}]", data.base, to),
ProjectionElem::Subslice { from, to } =>
write!(fmt, "{:?}[{:?}:-{:?}]", data.base,
from, to),
},
}
}
@ -856,17 +872,6 @@ pub enum Rvalue<'tcx> {
/// away after type-checking and before lowering.
Aggregate(AggregateKind<'tcx>, Vec<Operand<'tcx>>),
/// Generates a slice of the form `&input[from_start..L-from_end]`
/// where `L` is the length of the slice. This is only created by
/// slice pattern matching, so e.g. a pattern of the form `[x, y,
/// .., z]` might create a slice with `from_start=2` and
/// `from_end=1`.
Slice {
input: Lvalue<'tcx>,
from_start: usize,
from_end: usize,
},
InlineAsm {
asm: InlineAsm,
outputs: Vec<Lvalue<'tcx>>,
@ -972,8 +977,6 @@ impl<'tcx> Debug for Rvalue<'tcx> {
InlineAsm { ref asm, ref outputs, ref inputs } => {
write!(fmt, "asm!({:?} : {:?} : {:?})", asm, outputs, inputs)
}
Slice { ref input, from_start, from_end } =>
write!(fmt, "{:?}[{:?}..-{:?}]", input, from_start, from_end),
Ref(_, borrow_kind, ref lv) => {
let kind_str = match borrow_kind {

View file

@ -59,6 +59,20 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> {
LvalueTy::Ty {
ty: self.to_ty(tcx).builtin_index().unwrap()
},
ProjectionElem::Subslice { from, to } => {
let ty = self.to_ty(tcx);
LvalueTy::Ty {
ty: match ty.sty {
ty::TyArray(inner, size) => {
tcx.mk_array(inner, size-(from as usize)-(to as usize))
}
ty::TySlice(..) => ty,
_ => {
bug!("cannot subslice non-array type: `{:?}`", self)
}
}
}
}
ProjectionElem::Downcast(adt_def1, index) =>
match self.to_ty(tcx).sty {
ty::TyEnum(adt_def, substs) => {
@ -219,7 +233,6 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
}
}
}
Rvalue::Slice { .. } => None,
Rvalue::InlineAsm { .. } => None
}
}

View file

@ -0,0 +1,286 @@
// Copyright 2016 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.
use std::vec;
use rustc_data_structures::bitvec::BitVector;
use super::repr::*;
/// Preorder traversal of a graph.
///
/// Preorder traversal is when each node is visited before an of it's
/// successors
///
/// ```text
///
/// A
/// / \
/// / \
/// B C
/// \ /
/// \ /
/// D
/// ```
///
/// A preorder traversal of this graph is either `A B D C` or `A C D B`
#[derive(Clone)]
pub struct Preorder<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
visited: BitVector,
worklist: Vec<BasicBlock>,
}
impl<'a, 'tcx> Preorder<'a, 'tcx> {
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Preorder<'a, 'tcx> {
let worklist = vec![root];
Preorder {
mir: mir,
visited: BitVector::new(mir.basic_blocks.len()),
worklist: worklist
}
}
}
pub fn preorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Preorder<'a, 'tcx> {
Preorder::new(mir, START_BLOCK)
}
impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
while let Some(idx) = self.worklist.pop() {
if !self.visited.insert(idx.index()) {
continue;
}
let data = self.mir.basic_block_data(idx);
if let Some(ref term) = data.terminator {
for &succ in term.successors().iter() {
self.worklist.push(succ);
}
}
return Some((idx, data));
}
None
}
}
/// Postorder traversal of a graph.
///
/// Postorder traversal is when each node is visited after all of it's
/// successors, except when the successor is only reachable by a back-edge
///
///
/// ```text
///
/// A
/// / \
/// / \
/// B C
/// \ /
/// \ /
/// D
/// ```
///
/// A Postorder traversal of this graph is `D B C A` or `D C B A`
pub struct Postorder<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
visited: BitVector,
visit_stack: Vec<(BasicBlock, vec::IntoIter<BasicBlock>)>
}
impl<'a, 'tcx> Postorder<'a, 'tcx> {
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> {
let mut po = Postorder {
mir: mir,
visited: BitVector::new(mir.basic_blocks.len()),
visit_stack: Vec::new()
};
let data = po.mir.basic_block_data(root);
if let Some(ref term) = data.terminator {
po.visited.insert(root.index());
let succs = term.successors().into_owned().into_iter();
po.visit_stack.push((root, succs));
po.traverse_successor();
}
po
}
fn traverse_successor(&mut self) {
// This is quite a complex loop due to 1. the borrow checker not liking it much
// and 2. what exactly is going on is not clear
//
// It does the actual traversal of the graph, while the `next` method on the iterator
// just pops off of the stack. `visit_stack` is a stack containing pairs of nodes and
// iterators over the sucessors of those nodes. Each iteration attempts to get the next
// node from the top of the stack, then pushes that node and an iterator over the
// successors to the top of the stack. This loop only grows `visit_stack`, stopping when
// we reach a child that has no children that we haven't already visited.
//
// For a graph that looks like this:
//
// A
// / \
// / \
// B C
// | |
// | |
// D |
// \ /
// \ /
// E
//
// The state of the stack starts out with just the root node (`A` in this case);
// [(A, [B, C])]
//
// When the first call to `traverse_sucessor` happens, the following happens:
//
// [(B, [D]), // `B` taken from the successors of `A`, pushed to the
// // top of the stack along with the successors of `B`
// (A, [C])]
//
// [(D, [E]), // `D` taken from successors of `B`, pushed to stack
// (B, []),
// (A, [C])]
//
// [(E, []), // `E` taken from successors of `D`, pushed to stack
// (D, []),
// (B, []),
// (A, [C])]
//
// Now that the top of the stack has no successors we can traverse, each item will
// be popped off during iteration until we get back to `A`. This yeilds [E, D, B].
//
// When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
// since we've already visited `E`, that child isn't added to the stack. The last
// two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
loop {
let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
if let Some(bb) = iter.next() {
bb
} else {
break;
}
} else {
break;
};
if self.visited.insert(bb.index()) {
let data = self.mir.basic_block_data(bb);
if let Some(ref term) = data.terminator {
let succs = term.successors().into_owned().into_iter();
self.visit_stack.push((bb, succs));
}
}
}
}
}
pub fn postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> Postorder<'a, 'tcx> {
Postorder::new(mir, START_BLOCK)
}
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
let next = self.visit_stack.pop();
if next.is_some() {
self.traverse_successor();
}
next.map(|(bb, _)| {
let data = self.mir.basic_block_data(bb);
(bb, data)
})
}
}
/// Reverse postorder traversal of a graph
///
/// Reverse postorder is the reverse order of a postorder traversal.
/// This is different to a preorder traversal and represents a natural
/// linearisation of control-flow.
///
/// ```text
///
/// A
/// / \
/// / \
/// B C
/// \ /
/// \ /
/// D
/// ```
///
/// A reverse postorder traversal of this graph is either `A B C D` or `A C B D`
/// Note that for a graph containing no loops (i.e. A DAG), this is equivalent to
/// a topological sort.
///
/// Construction of a `ReversePostorder` traversal requires doing a full
/// postorder traversal of the graph, therefore this traversal should be
/// constructed as few times as possible. Use the `reset` method to be able
/// to re-use the traversal
#[derive(Clone)]
pub struct ReversePostorder<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>,
blocks: Vec<BasicBlock>,
idx: usize
}
impl<'a, 'tcx> ReversePostorder<'a, 'tcx> {
pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> {
let blocks : Vec<_> = Postorder::new(mir, root).map(|(bb, _)| bb).collect();
let len = blocks.len();
ReversePostorder {
mir: mir,
blocks: blocks,
idx: len
}
}
pub fn reset(&mut self) {
self.idx = self.blocks.len();
}
}
pub fn reverse_postorder<'a, 'tcx>(mir: &'a Mir<'tcx>) -> ReversePostorder<'a, 'tcx> {
ReversePostorder::new(mir, START_BLOCK)
}
impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
if self.idx == 0 { return None; }
self.idx -= 1;
self.blocks.get(self.idx).map(|&bb| {
let data = self.mir.basic_block_data(bb);
(bb, data)
})
}
}

View file

@ -533,15 +533,6 @@ macro_rules! make_mir_visitor {
}
}
Rvalue::Slice { ref $($mutability)* input,
from_start,
from_end } => {
self.visit_lvalue(input, LvalueContext::Slice {
from_start: from_start,
from_end: from_end,
});
}
Rvalue::InlineAsm { ref $($mutability)* outputs,
ref $($mutability)* inputs,
asm: _ } => {
@ -602,6 +593,8 @@ macro_rules! make_mir_visitor {
match *proj {
ProjectionElem::Deref => {
}
ProjectionElem::Subslice { from: _, to: _ } => {
}
ProjectionElem::Field(_field, ref $($mutability)* ty) => {
self.visit_ty(ty);
}

View file

@ -17,6 +17,8 @@ use session::Session;
use traits;
use ty::{self, Ty, TyCtxt, TypeFoldable};
use util::common::slice_pat;
use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::attr;
use syntax::codemap::DUMMY_SP;
@ -98,17 +100,17 @@ impl TargetDataLayout {
let mut dl = TargetDataLayout::default();
for spec in sess.target.target.data_layout.split("-") {
match &spec.split(":").collect::<Vec<_>>()[..] {
["e"] => dl.endian = Endian::Little,
["E"] => dl.endian = Endian::Big,
["a", a..] => dl.aggregate_align = align(a, "a"),
["f32", a..] => dl.f32_align = align(a, "f32"),
["f64", a..] => dl.f64_align = align(a, "f64"),
[p @ "p", s, a..] | [p @ "p0", s, a..] => {
match slice_pat(&&spec.split(":").collect::<Vec<_>>()[..]) {
&["e"] => dl.endian = Endian::Little,
&["E"] => dl.endian = Endian::Big,
&["a", ref a..] => dl.aggregate_align = align(a, "a"),
&["f32", ref a..] => dl.f32_align = align(a, "f32"),
&["f64", ref a..] => dl.f64_align = align(a, "f64"),
&[p @ "p", s, ref a..] | &[p @ "p0", s, ref a..] => {
dl.pointer_size = size(s, p);
dl.pointer_align = align(a, p);
}
[s, a..] if s.starts_with("i") => {
&[s, ref a..] if s.starts_with("i") => {
let ty_align = match s[1..].parse::<u64>() {
Ok(1) => &mut dl.i8_align,
Ok(8) => &mut dl.i8_align,
@ -123,7 +125,7 @@ impl TargetDataLayout {
};
*ty_align = align(a, s);
}
[s, a..] if s.starts_with("v") => {
&[s, ref a..] if s.starts_with("v") => {
let v_size = size(&s[1..], "v");
let a = align(a, s);
if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {

View file

@ -247,3 +247,15 @@ pub fn path2cstr(p: &Path) -> CString {
pub fn path2cstr(p: &Path) -> CString {
CString::new(p.to_str().unwrap()).unwrap()
}
// FIXME(stage0): remove this
// HACK: this is needed because the interpretation of slice
// patterns changed between stage0 and now.
#[cfg(stage0)]
pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'a &'b [T] {
t
}
#[cfg(not(stage0))]
pub fn slice_pat<'a, 'b, T>(t: &'a &'b [T]) -> &'b [T] {
*t
}