Skip to content

Don't depend on Allocation sizes for pattern length #56540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 15, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 138 additions & 54 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -200,22 +200,72 @@ 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.
///
/// `crty` and `rty` can differ because you can use array constants in the presence of slice
/// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
/// the array to a slice in that case.
fn fold_const_value_deref(
&mut self,
val: ConstValue<'tcx>,
// the pattern's pointee type
rty: Ty<'tcx>,
// the constant's pointee type
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in this case in the existing code? A bug again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this is an ICE: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=76fe5897409eefe240b587fb5ba59f6e

It will stay an ICE, but this PR is prepping the road for making it saner, because it's an explicit choice to ICE instead of just a random assert triggering.

_ => 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,
) },
}
}
}
@@ -732,15 +782,17 @@ 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);
}
// extract the length of an array/slice from a constant
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 } => {
@@ -1348,28 +1400,62 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
}
}

fn slice_pat_covered_by_constructor<'tcx>(
// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
// meaning all other types will compare unequal and thus equal patterns often do not cause the
// second pattern to lint about unreachable match arms.
fn slice_pat_covered_by_const<'tcx>(
tcx: TyCtxt<'_, 'tcx, '_>,
_span: Span,
ctor: &Constructor,
const_val: &ty::Const<'tcx>,
prefix: &[Pattern<'tcx>],
slice: &Option<Pattern<'tcx>>,
suffix: &[Pattern<'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")
let data: &[u8] = 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
// This solely means that we don't lint about unreachable patterns, even if some
// are definitely unreachable.
return Ok(false);
}
}
_ => bug!()
let ptr = Pointer::new(id, offset);
let n = n.assert_usize(tcx).unwrap();
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
},
// a slice fat pointer to a zero length slice
(ConstValue::ScalarPair(Scalar::Bits { .. }, 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
// This solely means that we don't lint about unreachable patterns, even if some
// are definitely unreachable.
return Ok(false);
}
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
// This solely means that we don't lint about unreachable patterns, even if some
// are definitely unreachable.
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_const: {:#?}, {:#?}, {:#?}, {:#?}",
const_val, prefix, slice, suffix,
),
};

let pat_len = prefix.len() + suffix.len();
@@ -1675,22 +1761,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 {:?}",
@@ -1702,8 +1789,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()?;
@@ -1766,10 +1852,8 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
None
}
}
ConstantValue(..) => {
match slice_pat_covered_by_constructor(
cx.tcx, pat.span, constructor, prefix, slice, suffix
) {
ConstantValue(cv) => {
match slice_pat_covered_by_const(cx.tcx, pat.span, cv, prefix, slice, suffix) {
Ok(true) => Some(smallvec![]),
Ok(false) => None,
Err(ErrorReported) => None
44 changes: 21 additions & 23 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
_ => (),
}
_ => (),
}
}

1 change: 1 addition & 0 deletions src/test/run-pass/ctfe/references.rs
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ fn main() {
_ => panic!("c"),
}

#[allow(unreachable_patterns)]
match &43 {
&42 => panic!(),
BOO => panic!(),
13 changes: 13 additions & 0 deletions src/test/ui/pattern/const-pat-ice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// failure-status: 101

// This is a repro test for an ICE in our pattern handling of constants.

const FOO: &&&u32 = &&&42;

fn main() {
match unimplemented!() {
&&&42 => {},
FOO => {},
_ => {},
}
}
15 changes: 11 additions & 4 deletions src/test/ui/pattern/slice-pattern-const-2.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
// compile-pass
#![deny(unreachable_patterns)]

fn main() {
let s = &[0x00; 4][..]; //Slice of any value
const MAGIC_TEST: &[u32] = &[4, 5, 6, 7]; //Const slice to pattern match with
match s {
MAGIC_TEST => (),
[0x00, 0x00, 0x00, 0x00] => (),
[4, 5, 6, 7] => (), // this should warn
[4, 5, 6, 7] => (), //~ ERROR unreachable pattern
_ => (),
}
match s {
[0x00, 0x00, 0x00, 0x00] => (),
MAGIC_TEST => (),
[4, 5, 6, 7] => (), // this should warn
[4, 5, 6, 7] => (), //~ ERROR unreachable pattern
_ => (),
}
match s {
[0x00, 0x00, 0x00, 0x00] => (),
[4, 5, 6, 7] => (),
MAGIC_TEST => (), // this should warn
MAGIC_TEST => (), // FIXME(oli-obk): this should warn, but currently does not
_ => (),
}
const FOO: [u32; 1] = [4];
match [99] {
[0x00] => (),
[4] => (),
FOO => (), //~ ERROR unreachable pattern
_ => (),
}
}
26 changes: 26 additions & 0 deletions src/test/ui/pattern/slice-pattern-const-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error: unreachable pattern
--> $DIR/slice-pattern-const-2.rs:9:9
|
LL | [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
| ^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/slice-pattern-const-2.rs:1:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/slice-pattern-const-2.rs:15:9
|
LL | [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
| ^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/slice-pattern-const-2.rs:28:9
|
LL | FOO => (), //~ ERROR unreachable pattern
| ^^^

error: aborting due to 3 previous errors

15 changes: 11 additions & 4 deletions src/test/ui/pattern/slice-pattern-const-3.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
// compile-pass
#![deny(unreachable_patterns)]

fn main() {
let s = &["0x00"; 4][..]; //Slice of any value
const MAGIC_TEST: &[&str] = &["4", "5", "6", "7"]; //Const slice to pattern match with
match s {
MAGIC_TEST => (),
["0x00", "0x00", "0x00", "0x00"] => (),
["4", "5", "6", "7"] => (), // this should warn
["4", "5", "6", "7"] => (), // FIXME(oli-obk): this should warn, but currently does not
_ => (),
}
match s {
["0x00", "0x00", "0x00", "0x00"] => (),
MAGIC_TEST => (),
["4", "5", "6", "7"] => (), // this should warn
["4", "5", "6", "7"] => (), // FIXME(oli-obk): this should warn, but currently does not
_ => (),
}
match s {
["0x00", "0x00", "0x00", "0x00"] => (),
["4", "5", "6", "7"] => (),
MAGIC_TEST => (), // this should warn
MAGIC_TEST => (), // FIXME(oli-obk): this should warn, but currently does not
_ => (),
}
const FOO: [&str; 1] = ["boo"];
match ["baa"] {
["0x00"] => (),
["boo"] => (),
FOO => (), //~ ERROR unreachable pattern
_ => (),
}
}
14 changes: 14 additions & 0 deletions src/test/ui/pattern/slice-pattern-const-3.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unreachable pattern
--> $DIR/slice-pattern-const-3.rs:28:9
|
LL | FOO => (), //~ ERROR unreachable pattern
| ^^^
|
note: lint level defined here
--> $DIR/slice-pattern-const-3.rs:1:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

31 changes: 27 additions & 4 deletions src/test/ui/pattern/slice-pattern-const.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,47 @@
//compile-pass
#![deny(unreachable_patterns)]

fn main() {
let s = &[0x00; 4][..]; //Slice of any value
const MAGIC_TEST: &[u8] = b"TEST"; //Const slice to pattern match with
match s {
MAGIC_TEST => (),
[0x00, 0x00, 0x00, 0x00] => (),
[84, 69, 83, 84] => (), // this should warn
[84, 69, 83, 84] => (), //~ ERROR unreachable pattern
_ => (),
}
match s {
[0x00, 0x00, 0x00, 0x00] => (),
MAGIC_TEST => (),
[84, 69, 83, 84] => (), // this should warn
[84, 69, 83, 84] => (), //~ ERROR unreachable pattern
_ => (),
}
match s {
[0x00, 0x00, 0x00, 0x00] => (),
[84, 69, 83, 84] => (),
MAGIC_TEST => (), // this should warn
MAGIC_TEST => (), //~ ERROR unreachable pattern
_ => (),
}
const FOO: [u8; 1] = [4];
match [99] {
[0x00] => (),
[4] => (),
FOO => (), //~ ERROR unreachable pattern
_ => (),
}
const BAR: &[u8; 1] = &[4];
match &[99] {
[0x00] => (),
[4] => (),
BAR => (), //~ ERROR unreachable pattern
b"a" => (),
_ => (),
}

const BOO: &[u8; 0] = &[];
match &[] {
[] => (),
BOO => (), //~ ERROR unreachable pattern
b"" => (), //~ ERROR unreachable pattern
_ => (), //~ ERROR unreachable pattern
}
}
56 changes: 56 additions & 0 deletions src/test/ui/pattern/slice-pattern-const.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
error: unreachable pattern
--> $DIR/slice-pattern-const.rs:9:9
|
LL | [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
| ^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/slice-pattern-const.rs:1:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:15:9
|
LL | [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
| ^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:21:9
|
LL | MAGIC_TEST => (), //~ ERROR unreachable pattern
| ^^^^^^^^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:28:9
|
LL | FOO => (), //~ ERROR unreachable pattern
| ^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:35:9
|
LL | BAR => (), //~ ERROR unreachable pattern
| ^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:43:9
|
LL | BOO => (), //~ ERROR unreachable pattern
| ^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:44:9
|
LL | b"" => (), //~ ERROR unreachable pattern
| ^^^

error: unreachable pattern
--> $DIR/slice-pattern-const.rs:45:9
|
LL | _ => (), //~ ERROR unreachable pattern
| ^

error: aborting due to 8 previous errors