Skip to content

Commit

Permalink
Don't depend on Allocation sizes for pattern length
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Dec 10, 2018
1 parent 3a75e80 commit a5a7fcb
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 68 deletions.
161 changes: 116 additions & 45 deletions src/librustc_mir/hair/pattern/_match.rs
Expand Up @@ -178,11 +178,11 @@ use super::{PatternFoldable, PatternFolder, compare_const_vals};

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};
Expand All @@ -200,22 +200,66 @@ use std::u128;
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,
kind: box PatternKind::Deref {
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,
) },
}
}
}
Expand Down Expand Up @@ -732,15 +776,16 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
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 } => {
Expand Down Expand Up @@ -1358,18 +1403,44 @@ fn slice_pat_covered_by_constructor<'tcx>(
) -> 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();
Expand Down Expand Up @@ -1675,22 +1746,23 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
// 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 {:?}",
Expand All @@ -1702,8 +1774,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
// 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()?;
Expand Down
44 changes: 21 additions & 23 deletions src/librustc_mir/hair/pattern/mod.rs
Expand Up @@ -1259,34 +1259,32 @@ pub fn compare_const_vals<'a, 'tcx>(
}
}

if let ty::Ref(_, rty, _) = ty.value.sty {
if let ty::Str = rty.sty {
match (a.val, b.val) {
(
ConstValue::ScalarPair(
Scalar::Ptr(ptr_a),
len_a,
),
ConstValue::ScalarPair(
Scalar::Ptr(ptr_b),
len_b,
),
) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => {
if let Ok(len_a) = len_a.to_bits(tcx.data_layout.pointer_size) {
if let Ok(len_b) = len_b.to_bits(tcx.data_layout.pointer_size) {
if len_a == len_b {
let map = tcx.alloc_map.lock();
let alloc_a = map.unwrap_memory(ptr_a.alloc_id);
let alloc_b = map.unwrap_memory(ptr_b.alloc_id);
if alloc_a.bytes.len() as u128 == len_a {
return from_bool(alloc_a == alloc_b);
}
if let ty::Str = ty.value.sty {
match (a.val, b.val) {
(
ConstValue::ScalarPair(
Scalar::Ptr(ptr_a),
len_a,
),
ConstValue::ScalarPair(
Scalar::Ptr(ptr_b),
len_b,
),
) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => {
if let Ok(len_a) = len_a.to_bits(tcx.data_layout.pointer_size) {
if let Ok(len_b) = len_b.to_bits(tcx.data_layout.pointer_size) {
if len_a == len_b {
let map = tcx.alloc_map.lock();
let alloc_a = map.unwrap_memory(ptr_a.alloc_id);
let alloc_b = map.unwrap_memory(ptr_b.alloc_id);
if alloc_a.bytes.len() as u128 == len_a {
return from_bool(alloc_a == alloc_b);
}
}
}
}
_ => (),
}
_ => (),
}
}

Expand Down

0 comments on commit a5a7fcb

Please sign in to comment.