Don't ICE on the use of integer addresses for ZST constants in pattern matching

This commit is contained in:
Oliver Scherer 2019-12-14 00:01:12 +01:00
parent 3e0a1c0910
commit 1e40681f50
4 changed files with 63 additions and 12 deletions

View File

@ -127,6 +127,10 @@ impl<Tag> Allocation<Tag> {
extra: (),
}
}
pub fn zst(align: Align) -> Self {
Self::undef(Size::ZERO, align)
}
}
impl Allocation<(), ()> {

View File

@ -237,11 +237,11 @@ use super::{FieldPat, Pat, PatKind, PatRange};
use rustc::hir::def_id::DefId;
use rustc::hir::{HirId, RangeEnd};
use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx};
use rustc::ty::layout::{Align, Integer, IntegerExt, Size, VariantIdx};
use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef};
use rustc::lint;
use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
use rustc::mir::interpret::{truncate, AllocId, Allocation, ConstValue, Pointer, Scalar};
use rustc::mir::Field;
use rustc::util::captures::Captures;
use rustc::util::common::ErrorReported;
@ -252,6 +252,7 @@ use syntax_pos::{Span, DUMMY_SP};
use arena::TypedArena;
use smallvec::{smallvec, SmallVec};
use std::borrow::Cow;
use std::cmp::{self, max, min, Ordering};
use std::convert::TryInto;
use std::fmt;
@ -260,11 +261,12 @@ use std::ops::RangeInclusive;
use std::u128;
pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)
LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
}
struct LiteralExpander<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl LiteralExpander<'tcx> {
@ -284,9 +286,23 @@ impl LiteralExpander<'tcx> {
debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
match (val, &crty.kind, &rty.kind) {
// the easy case, deref a reference
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
ConstValue::ByRef { alloc, offset: p.offset }
(ConstValue::Scalar(p), x, y) if x == y => {
match p {
Scalar::Ptr(p) => {
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
ConstValue::ByRef { alloc, offset: p.offset }
}
Scalar::Raw { .. } => {
let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
if layout.is_zst() {
// Deref of a reference to a ZST is a nop.
ConstValue::Scalar(Scalar::zst())
} else {
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
}
}
}
}
// 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)) => {
@ -2348,16 +2364,28 @@ fn specialize_one_pattern<'p, 'tcx>(
// just integers. The only time they should be pointing to memory
// is when they are subslices of nonzero slices.
let (alloc, offset, n, ty) = match value.ty.kind {
ty::Array(t, n) => match value.val {
ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
(alloc, offset, n.eval_usize(cx.tcx, cx.param_env), t)
ty::Array(t, n) => {
let n = n.eval_usize(cx.tcx, cx.param_env);
match value.val {
ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
(Cow::Borrowed(alloc), offset, n, t)
}
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. }))
if n == 0 =>
{
let align = Align::from_bytes(data as u64).unwrap();
// empty array
(Cow::Owned(Allocation::zst(align)), Size::ZERO, 0, t)
}
_ => span_bug!(pat.span, "array pattern is {:?}", value,),
}
_ => span_bug!(pat.span, "array pattern is {:?}", value,),
},
}
ty::Slice(t) => {
match value.val {
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
(data, Size::from_bytes(start as u64), (end - start) as u64, t)
let offset = Size::from_bytes(start as u64);
let n = (end - start) as u64;
(Cow::Borrowed(data), offset, n, t)
}
ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
// FIXME(oli-obk): implement `deref` for `ConstValue`

View File

@ -993,6 +993,12 @@ pub fn compare_const_vals<'tcx>(
return fallback();
}
// Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
// are just integer addresses).
if a.val == b.val {
return from_bool(true);
}
let a_bits = a.try_eval_bits(tcx, param_env, ty);
let b_bits = b.try_eval_bits(tcx, param_env, ty);

View File

@ -1,7 +1,10 @@
// run-pass
#![feature(const_transmute)]
const FOO: isize = 10;
const BAR: isize = 3;
const ZST: &() = unsafe { std::mem::transmute(1usize) };
const ZST_ARR: &[u8; 0] = unsafe { std::mem::transmute(1usize) };
const fn foo() -> isize { 4 }
const BOO: isize = foo();
@ -15,4 +18,14 @@ pub fn main() {
_ => 3
};
assert_eq!(y, 2);
let z = match &() {
ZST => 9,
// FIXME: this should not be required
_ => 42,
};
assert_eq!(z, 9);
let z = match b"" {
ZST_ARR => 10,
};
assert_eq!(z, 10);
}