Skip to content

Commit 91857a3

Browse files
committed
Auto merge of #56540 - oli-obk:less_const_hackery, r=varkor
Don't depend on `Allocation` sizes for pattern length And generally be more explicit about shortcomings of the implementation cc @RalfJung
2 parents bcf920f + 1c2a29e commit 91857a3

10 files changed

+318
-89
lines changed

src/librustc_mir/hair/pattern/_match.rs

+138-54
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ use super::{PatternFoldable, PatternFolder, compare_const_vals};
178178

179179
use rustc::hir::def_id::DefId;
180180
use rustc::hir::RangeEnd;
181-
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
182-
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx};
181+
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const};
182+
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size};
183183

184184
use rustc::mir::Field;
185-
use rustc::mir::interpret::ConstValue;
185+
use rustc::mir::interpret::{ConstValue, Pointer, Scalar};
186186
use rustc::util::common::ErrorReported;
187187

188188
use syntax::attr::{SignedInt, UnsignedInt};
@@ -200,22 +200,72 @@ use std::u128;
200200
pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pattern<'tcx>)
201201
-> &'a Pattern<'tcx>
202202
{
203-
cx.pattern_arena.alloc(LiteralExpander.fold_pattern(&pat))
203+
cx.pattern_arena.alloc(LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat))
204204
}
205205

206-
struct LiteralExpander;
207-
impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
206+
struct LiteralExpander<'a, 'tcx> {
207+
tcx: TyCtxt<'a, 'tcx, 'tcx>
208+
}
209+
210+
impl<'a, 'tcx> LiteralExpander<'a, 'tcx> {
211+
/// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice.
212+
///
213+
/// `crty` and `rty` can differ because you can use array constants in the presence of slice
214+
/// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
215+
/// the array to a slice in that case.
216+
fn fold_const_value_deref(
217+
&mut self,
218+
val: ConstValue<'tcx>,
219+
// the pattern's pointee type
220+
rty: Ty<'tcx>,
221+
// the constant's pointee type
222+
crty: Ty<'tcx>,
223+
) -> ConstValue<'tcx> {
224+
match (val, &crty.sty, &rty.sty) {
225+
// the easy case, deref a reference
226+
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => ConstValue::ByRef(
227+
p.alloc_id,
228+
self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
229+
p.offset,
230+
),
231+
// unsize array to slice if pattern is array but match value or other patterns are slice
232+
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
233+
assert_eq!(t, u);
234+
ConstValue::ScalarPair(
235+
Scalar::Ptr(p),
236+
n.val.try_to_scalar().unwrap(),
237+
)
238+
},
239+
// fat pointers stay the same
240+
(ConstValue::ScalarPair(..), _, _) => val,
241+
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
242+
_ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
243+
}
244+
}
245+
}
246+
247+
impl<'a, 'tcx> PatternFolder<'tcx> for LiteralExpander<'a, 'tcx> {
208248
fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> {
209249
match (&pat.ty.sty, &*pat.kind) {
210-
(&ty::Ref(_, rty, _), &PatternKind::Constant { ref value }) => {
250+
(
251+
&ty::Ref(_, rty, _),
252+
&PatternKind::Constant { value: Const {
253+
val,
254+
ty: ty::TyS { sty: ty::Ref(_, crty, _), .. },
255+
} },
256+
) => {
211257
Pattern {
212258
ty: pat.ty,
213259
span: pat.span,
214260
kind: box PatternKind::Deref {
215261
subpattern: Pattern {
216262
ty: rty,
217263
span: pat.span,
218-
kind: box PatternKind::Constant { value: value.clone() },
264+
kind: box PatternKind::Constant { value: Const::from_const_value(
265+
self.tcx,
266+
self.fold_const_value_deref(*val, rty, crty),
267+
rty,
268+
) },
219269
}
220270
}
221271
}
@@ -732,15 +782,17 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
732782
for row in patterns {
733783
match *row.kind {
734784
PatternKind::Constant { value } => {
735-
if let Some(ptr) = value.to_ptr() {
736-
let is_array_ptr = value.ty
737-
.builtin_deref(true)
738-
.and_then(|t| t.ty.builtin_index())
739-
.map_or(false, |t| t == cx.tcx.types.u8);
740-
if is_array_ptr {
741-
let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
742-
max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64);
743-
}
785+
// extract the length of an array/slice from a constant
786+
match (value.val, &value.ty.sty) {
787+
(_, ty::Array(_, n)) => max_fixed_len = cmp::max(
788+
max_fixed_len,
789+
n.unwrap_usize(cx.tcx),
790+
),
791+
(ConstValue::ScalarPair(_, n), ty::Slice(_)) => max_fixed_len = cmp::max(
792+
max_fixed_len,
793+
n.to_usize(&cx.tcx).unwrap(),
794+
),
795+
_ => {},
744796
}
745797
}
746798
PatternKind::Slice { ref prefix, slice: None, ref suffix } => {
@@ -1348,28 +1400,62 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
13481400
}
13491401
}
13501402

1351-
fn slice_pat_covered_by_constructor<'tcx>(
1403+
// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
1404+
// meaning all other types will compare unequal and thus equal patterns often do not cause the
1405+
// second pattern to lint about unreachable match arms.
1406+
fn slice_pat_covered_by_const<'tcx>(
13521407
tcx: TyCtxt<'_, 'tcx, '_>,
13531408
_span: Span,
1354-
ctor: &Constructor,
1409+
const_val: &ty::Const<'tcx>,
13551410
prefix: &[Pattern<'tcx>],
13561411
slice: &Option<Pattern<'tcx>>,
13571412
suffix: &[Pattern<'tcx>]
13581413
) -> Result<bool, ErrorReported> {
1359-
let data: &[u8] = match *ctor {
1360-
ConstantValue(const_val) => {
1361-
let val = match const_val.val {
1362-
ConstValue::Unevaluated(..) |
1363-
ConstValue::ByRef(..) => bug!("unexpected ConstValue: {:?}", const_val),
1364-
ConstValue::Scalar(val) | ConstValue::ScalarPair(val, _) => val,
1365-
};
1366-
if let Ok(ptr) = val.to_ptr() {
1367-
tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id).bytes.as_ref()
1368-
} else {
1369-
bug!("unexpected non-ptr ConstantValue")
1414+
let data: &[u8] = match (const_val.val, &const_val.ty.sty) {
1415+
(ConstValue::ByRef(id, alloc, offset), ty::Array(t, n)) => {
1416+
if *t != tcx.types.u8 {
1417+
// FIXME(oli-obk): can't mix const patterns with slice patterns and get
1418+
// any sort of exhaustiveness/unreachable check yet
1419+
// This solely means that we don't lint about unreachable patterns, even if some
1420+
// are definitely unreachable.
1421+
return Ok(false);
13701422
}
1371-
}
1372-
_ => bug!()
1423+
let ptr = Pointer::new(id, offset);
1424+
let n = n.assert_usize(tcx).unwrap();
1425+
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
1426+
},
1427+
// a slice fat pointer to a zero length slice
1428+
(ConstValue::ScalarPair(Scalar::Bits { .. }, n), ty::Slice(t)) => {
1429+
if *t != tcx.types.u8 {
1430+
// FIXME(oli-obk): can't mix const patterns with slice patterns and get
1431+
// any sort of exhaustiveness/unreachable check yet
1432+
// This solely means that we don't lint about unreachable patterns, even if some
1433+
// are definitely unreachable.
1434+
return Ok(false);
1435+
}
1436+
assert_eq!(n.to_usize(&tcx).unwrap(), 0);
1437+
&[]
1438+
},
1439+
//
1440+
(ConstValue::ScalarPair(Scalar::Ptr(ptr), n), ty::Slice(t)) => {
1441+
if *t != tcx.types.u8 {
1442+
// FIXME(oli-obk): can't mix const patterns with slice patterns and get
1443+
// any sort of exhaustiveness/unreachable check yet
1444+
// This solely means that we don't lint about unreachable patterns, even if some
1445+
// are definitely unreachable.
1446+
return Ok(false);
1447+
}
1448+
let n = n.to_usize(&tcx).unwrap();
1449+
tcx.alloc_map
1450+
.lock()
1451+
.unwrap_memory(ptr.alloc_id)
1452+
.get_bytes(&tcx, ptr, Size::from_bytes(n))
1453+
.unwrap()
1454+
},
1455+
_ => bug!(
1456+
"slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
1457+
const_val, prefix, slice, suffix,
1458+
),
13731459
};
13741460

13751461
let pat_len = prefix.len() + suffix.len();
@@ -1675,22 +1761,23 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
16751761
// necessarily point to memory, they are usually just integers. The only time
16761762
// they should be pointing to memory is when they are subslices of nonzero
16771763
// slices
1678-
let (opt_ptr, n, ty) = match value.ty.builtin_deref(false).unwrap().ty.sty {
1679-
ty::TyKind::Array(t, n) => (value.to_ptr(), n.unwrap_usize(cx.tcx), t),
1680-
ty::TyKind::Slice(t) => {
1681-
match value.val {
1682-
ConstValue::ScalarPair(ptr, n) => (
1683-
ptr.to_ptr().ok(),
1684-
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
1685-
t,
1686-
),
1687-
_ => span_bug!(
1688-
pat.span,
1689-
"slice pattern constant must be scalar pair but is {:?}",
1690-
value,
1691-
),
1692-
}
1693-
},
1764+
let (opt_ptr, n, ty) = match (value.val, &value.ty.sty) {
1765+
(ConstValue::ByRef(id, alloc, offset), ty::TyKind::Array(t, n)) => (
1766+
Some((
1767+
Pointer::new(id, offset),
1768+
alloc,
1769+
)),
1770+
n.unwrap_usize(cx.tcx),
1771+
t,
1772+
),
1773+
(ConstValue::ScalarPair(ptr, n), ty::TyKind::Slice(t)) => (
1774+
ptr.to_ptr().ok().map(|ptr| (
1775+
ptr,
1776+
cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
1777+
)),
1778+
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64,
1779+
t,
1780+
),
16941781
_ => span_bug!(
16951782
pat.span,
16961783
"unexpected const-val {:?} with ctor {:?}",
@@ -1702,8 +1789,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
17021789
// convert a constant slice/array pattern to a list of patterns.
17031790
match (n, opt_ptr) {
17041791
(0, _) => Some(SmallVec::new()),
1705-
(_, Some(ptr)) => {
1706-
let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
1792+
(_, Some((ptr, alloc))) => {
17071793
let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
17081794
(0..n).map(|i| {
17091795
let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
@@ -1766,10 +1852,8 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
17661852
None
17671853
}
17681854
}
1769-
ConstantValue(..) => {
1770-
match slice_pat_covered_by_constructor(
1771-
cx.tcx, pat.span, constructor, prefix, slice, suffix
1772-
) {
1855+
ConstantValue(cv) => {
1856+
match slice_pat_covered_by_const(cx.tcx, pat.span, cv, prefix, slice, suffix) {
17731857
Ok(true) => Some(smallvec![]),
17741858
Ok(false) => None,
17751859
Err(ErrorReported) => None

src/librustc_mir/hair/pattern/mod.rs

+21-23
Original file line numberDiff line numberDiff line change
@@ -1259,34 +1259,32 @@ pub fn compare_const_vals<'a, 'tcx>(
12591259
}
12601260
}
12611261

1262-
if let ty::Ref(_, rty, _) = ty.value.sty {
1263-
if let ty::Str = rty.sty {
1264-
match (a.val, b.val) {
1265-
(
1266-
ConstValue::ScalarPair(
1267-
Scalar::Ptr(ptr_a),
1268-
len_a,
1269-
),
1270-
ConstValue::ScalarPair(
1271-
Scalar::Ptr(ptr_b),
1272-
len_b,
1273-
),
1274-
) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => {
1275-
if let Ok(len_a) = len_a.to_bits(tcx.data_layout.pointer_size) {
1276-
if let Ok(len_b) = len_b.to_bits(tcx.data_layout.pointer_size) {
1277-
if len_a == len_b {
1278-
let map = tcx.alloc_map.lock();
1279-
let alloc_a = map.unwrap_memory(ptr_a.alloc_id);
1280-
let alloc_b = map.unwrap_memory(ptr_b.alloc_id);
1281-
if alloc_a.bytes.len() as u128 == len_a {
1282-
return from_bool(alloc_a == alloc_b);
1283-
}
1262+
if let ty::Str = ty.value.sty {
1263+
match (a.val, b.val) {
1264+
(
1265+
ConstValue::ScalarPair(
1266+
Scalar::Ptr(ptr_a),
1267+
len_a,
1268+
),
1269+
ConstValue::ScalarPair(
1270+
Scalar::Ptr(ptr_b),
1271+
len_b,
1272+
),
1273+
) if ptr_a.offset.bytes() == 0 && ptr_b.offset.bytes() == 0 => {
1274+
if let Ok(len_a) = len_a.to_bits(tcx.data_layout.pointer_size) {
1275+
if let Ok(len_b) = len_b.to_bits(tcx.data_layout.pointer_size) {
1276+
if len_a == len_b {
1277+
let map = tcx.alloc_map.lock();
1278+
let alloc_a = map.unwrap_memory(ptr_a.alloc_id);
1279+
let alloc_b = map.unwrap_memory(ptr_b.alloc_id);
1280+
if alloc_a.bytes.len() as u128 == len_a {
1281+
return from_bool(alloc_a == alloc_b);
12841282
}
12851283
}
12861284
}
12871285
}
1288-
_ => (),
12891286
}
1287+
_ => (),
12901288
}
12911289
}
12921290

src/test/run-pass/ctfe/references.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ fn main() {
2828
_ => panic!("c"),
2929
}
3030

31+
#[allow(unreachable_patterns)]
3132
match &43 {
3233
&42 => panic!(),
3334
BOO => panic!(),

src/test/ui/pattern/const-pat-ice.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// failure-status: 101
2+
3+
// This is a repro test for an ICE in our pattern handling of constants.
4+
5+
const FOO: &&&u32 = &&&42;
6+
7+
fn main() {
8+
match unimplemented!() {
9+
&&&42 => {},
10+
FOO => {},
11+
_ => {},
12+
}
13+
}
+11-4
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
1-
// compile-pass
1+
#![deny(unreachable_patterns)]
22

33
fn main() {
44
let s = &[0x00; 4][..]; //Slice of any value
55
const MAGIC_TEST: &[u32] = &[4, 5, 6, 7]; //Const slice to pattern match with
66
match s {
77
MAGIC_TEST => (),
88
[0x00, 0x00, 0x00, 0x00] => (),
9-
[4, 5, 6, 7] => (), // this should warn
9+
[4, 5, 6, 7] => (), //~ ERROR unreachable pattern
1010
_ => (),
1111
}
1212
match s {
1313
[0x00, 0x00, 0x00, 0x00] => (),
1414
MAGIC_TEST => (),
15-
[4, 5, 6, 7] => (), // this should warn
15+
[4, 5, 6, 7] => (), //~ ERROR unreachable pattern
1616
_ => (),
1717
}
1818
match s {
1919
[0x00, 0x00, 0x00, 0x00] => (),
2020
[4, 5, 6, 7] => (),
21-
MAGIC_TEST => (), // this should warn
21+
MAGIC_TEST => (), // FIXME(oli-obk): this should warn, but currently does not
22+
_ => (),
23+
}
24+
const FOO: [u32; 1] = [4];
25+
match [99] {
26+
[0x00] => (),
27+
[4] => (),
28+
FOO => (), //~ ERROR unreachable pattern
2229
_ => (),
2330
}
2431
}

0 commit comments

Comments
 (0)