use rustc::hir::def_id::DefId;
use rustc::hir::RangeEnd;
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::ty::layout::{Integer, IntegerExt, VariantIdx};
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const};
+use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size};
use rustc::mir::Field;
-use rustc::mir::interpret::ConstValue;
+use rustc::mir::interpret::{ConstValue, Pointer, Scalar};
use rustc::util::common::ErrorReported;
use syntax::attr::{SignedInt, UnsignedInt};
pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pattern<'tcx>)
-> &'a Pattern<'tcx>
{
- cx.pattern_arena.alloc(LiteralExpander.fold_pattern(&pat))
+ cx.pattern_arena.alloc(LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat))
}
-struct LiteralExpander;
-impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
+struct LiteralExpander<'a, 'tcx> {
+ tcx: TyCtxt<'a, 'tcx, 'tcx>
+}
+
+impl<'a, 'tcx> LiteralExpander<'a, 'tcx> {
+ /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice
+ fn fold_const_value_deref(
+ &mut self,
+ val: ConstValue<'tcx>,
+ rty: Ty<'tcx>,
+ crty: Ty<'tcx>,
+ ) -> ConstValue<'tcx> {
+ match (val, &crty.sty, &rty.sty) {
+ // the easy case, deref a reference
+ (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => ConstValue::ByRef(
+ p.alloc_id,
+ self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
+ p.offset,
+ ),
+ // unsize array to slice if pattern is array but match value or other patterns are slice
+ (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
+ assert_eq!(t, u);
+ ConstValue::ScalarPair(
+ Scalar::Ptr(p),
+ n.val.try_to_scalar().unwrap(),
+ )
+ },
+ // fat pointers stay the same
+ (ConstValue::ScalarPair(..), _, _) => val,
+ // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
+ _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
+ }
+ }
+}
+
+impl<'a, 'tcx> PatternFolder<'tcx> for LiteralExpander<'a, 'tcx> {
fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> {
match (&pat.ty.sty, &*pat.kind) {
- (&ty::Ref(_, rty, _), &PatternKind::Constant { ref value }) => {
+ (
+ &ty::Ref(_, rty, _),
+ &PatternKind::Constant { value: Const {
+ val,
+ ty: ty::TyS { sty: ty::Ref(_, crty, _), .. },
+ } },
+ ) => {
Pattern {
ty: pat.ty,
span: pat.span,
subpattern: Pattern {
ty: rty,
span: pat.span,
- kind: box PatternKind::Constant { value: value.clone() },
+ kind: box PatternKind::Constant { value: Const::from_const_value(
+ self.tcx,
+ self.fold_const_value_deref(*val, rty, crty),
+ rty,
+ ) },
}
}
}
for row in patterns {
match *row.kind {
PatternKind::Constant { value } => {
- if let Some(ptr) = value.to_ptr() {
- let is_array_ptr = value.ty
- .builtin_deref(true)
- .and_then(|t| t.ty.builtin_index())
- .map_or(false, |t| t == cx.tcx.types.u8);
- if is_array_ptr {
- let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
- max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64);
- }
+ match (value.val, &value.ty.sty) {
+ (_, ty::Array(_, n)) => max_fixed_len = cmp::max(
+ max_fixed_len,
+ n.unwrap_usize(cx.tcx),
+ ),
+ (ConstValue::ScalarPair(_, n), ty::Slice(_)) => max_fixed_len = cmp::max(
+ max_fixed_len,
+ n.to_usize(&cx.tcx).unwrap(),
+ ),
+ _ => {},
}
}
PatternKind::Slice { ref prefix, slice: None, ref suffix } => {
) -> Result<bool, ErrorReported> {
let data: &[u8] = match *ctor {
ConstantValue(const_val) => {
- let val = match const_val.val {
- ConstValue::Unevaluated(..) |
- ConstValue::ByRef(..) => bug!("unexpected ConstValue: {:?}", const_val),
- ConstValue::Scalar(val) | ConstValue::ScalarPair(val, _) => val,
- };
- if let Ok(ptr) = val.to_ptr() {
- tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id).bytes.as_ref()
- } else {
- bug!("unexpected non-ptr ConstantValue")
+ match (const_val.val, &const_val.ty.sty) {
+ (ConstValue::ByRef(id, alloc, offset), ty::Array(t, n)) => {
+ if *t != tcx.types.u8 {
+ // FIXME(oli-obk): can't mix const patterns with slice patterns and get
+ // any sort of exhaustiveness/unreachable check yet
+ return Ok(false);
+ }
+ let ptr = Pointer::new(id, offset);
+ let n = n.assert_usize(tcx).unwrap();
+ alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
+ },
+ (ConstValue::ScalarPair(Scalar::Bits { .. }, n), ty::Slice(_)) => {
+ assert_eq!(n.to_usize(&tcx).unwrap(), 0);
+ &[]
+ },
+ (ConstValue::ScalarPair(Scalar::Ptr(ptr), n), ty::Slice(t)) => {
+ if *t != tcx.types.u8 {
+ // FIXME(oli-obk): can't mix const patterns with slice patterns and get
+ // any sort of exhaustiveness/unreachable check yet
+ return Ok(false);
+ }
+ let n = n.to_usize(&tcx).unwrap();
+ tcx.alloc_map
+ .lock()
+ .unwrap_memory(ptr.alloc_id)
+ .get_bytes(&tcx, ptr, Size::from_bytes(n))
+ .unwrap()
+ },
+ _ => bug!(
+ "slice_pat_covered_by_constructor: {:#?}, {:#?}, {:#?}, {:#?}",
+ ctor, prefix, slice, suffix,
+ ),
}
}
- _ => bug!()
+ _ => bug!(
+ "slice_pat_covered_by_constructor not ConstValue: {:#?}, {:#?}, {:#?}, {:#?}",
+ ctor, prefix, slice, suffix,
+ ),
};
let pat_len = prefix.len() + suffix.len();
// necessarily point to memory, they are usually just integers. The only time
// they should be pointing to memory is when they are subslices of nonzero
// slices
- let (opt_ptr, n, ty) = match value.ty.builtin_deref(false).unwrap().ty.sty {
- ty::TyKind::Array(t, n) => (value.to_ptr(), n.unwrap_usize(cx.tcx), t),
- ty::TyKind::Slice(t) => {
- match value.val {
- ConstValue::ScalarPair(ptr, n) => (
- ptr.to_ptr().ok(),
- n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
- t,
- ),
- _ => span_bug!(
- pat.span,
- "slice pattern constant must be scalar pair but is {:?}",
- value,
- ),
- }
- },
+ let (opt_ptr, n, ty) = match (value.val, &value.ty.sty) {
+ (ConstValue::ByRef(id, alloc, offset), ty::TyKind::Array(t, n)) => (
+ Some((
+ Pointer::new(id, offset),
+ alloc,
+ )),
+ n.unwrap_usize(cx.tcx),
+ t,
+ ),
+ (ConstValue::ScalarPair(ptr, n), ty::TyKind::Slice(t)) => (
+ ptr.to_ptr().ok().map(|ptr| (
+ ptr,
+ cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
+ )),
+ n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
+ t,
+ ),
_ => span_bug!(
pat.span,
"unexpected const-val {:?} with ctor {:?}",
// convert a constant slice/array pattern to a list of patterns.
match (n, opt_ptr) {
(0, _) => Some(SmallVec::new()),
- (_, Some(ptr)) => {
- let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
+ (_, Some((ptr, alloc))) => {
let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
(0..n).map(|i| {
let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;