about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Scherer <github35764891676564198441@oli-obk.de>2019-12-14 00:01:12 +0100
committerOliver Scherer <github35764891676564198441@oli-obk.de>2019-12-26 22:46:22 +0100
commit1e40681f50bf820844480b375ab0379aef4ec429 (patch)
tree53d2a0d1bd49536982ff52d3248d5711faf4ca29
parent3e0a1c09108b52e41113520c7fa516480a8b67f9 (diff)
downloadrust-1e40681f50bf820844480b375ab0379aef4ec429.tar.gz
rust-1e40681f50bf820844480b375ab0379aef4ec429.zip
Don't ICE on the use of integer addresses for ZST constants in pattern matching
-rw-r--r--src/librustc/mir/interpret/allocation.rs4
-rw-r--r--src/librustc_mir/hair/pattern/_match.rs52
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs6
-rw-r--r--src/test/ui/consts/consts-in-patterns.rs13
4 files changed, 63 insertions, 12 deletions
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs
index 67f1c8072d6..957a028a59e 100644
--- a/src/librustc/mir/interpret/allocation.rs
+++ b/src/librustc/mir/interpret/allocation.rs
@@ -127,6 +127,10 @@ impl<Tag> Allocation<Tag> {
             extra: (),
         }
     }
+
+    pub fn zst(align: Align) -> Self {
+        Self::undef(Size::ZERO, align)
+    }
 }
 
 impl Allocation<(), ()> {
diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index f267be812c3..9851f246897 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -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`
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index 869aeeba418..a68ee3308bc 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -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);
 
diff --git a/src/test/ui/consts/consts-in-patterns.rs b/src/test/ui/consts/consts-in-patterns.rs
index ac6c5a51506..ee1e3cc22f7 100644
--- a/src/test/ui/consts/consts-in-patterns.rs
+++ b/src/test/ui/consts/consts-in-patterns.rs
@@ -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);
 }