Skip to content

Commit 8f5f8f9

Browse files
committed
Auto merge of #67192 - oli-obk:const_zst_addr, r=RalfJung,varkor
Various const eval and pattern matching ICE fixes r? @RalfJung cc @spastorino This PR does not change existing behaviour anymore and just fixes a bunch of ICEs reachable from user code (sometimes even on stable via obscure union transmutes).
2 parents a04c789 + f65a91e commit 8f5f8f9

15 files changed

+192
-59
lines changed

src/librustc/mir/interpret/error.rs

+6
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ pub enum UnsupportedOpInfo<'tcx> {
432432
HeapAllocNonPowerOfTwoAlignment(u64),
433433
ReadFromReturnPointer,
434434
PathNotFound(Vec<String>),
435+
TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>),
435436
}
436437

437438
impl fmt::Debug for UnsupportedOpInfo<'tcx> {
@@ -460,6 +461,11 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
460461
passing data of type {:?}",
461462
callee_ty, caller_ty
462463
),
464+
TransmuteSizeDiff(from_ty, to_ty) => write!(
465+
f,
466+
"tried to transmute from {:?} to {:?}, but their sizes differed",
467+
from_ty, to_ty
468+
),
463469
FunctionRetMismatch(caller_ty, callee_ty) => write!(
464470
f,
465471
"tried to call a function with return type {:?} \

src/librustc/mir/interpret/value.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,9 @@ impl<'tcx, Tag> Scalar<Tag> {
367367
}
368368

369369
/// Do not call this method! Use either `assert_ptr` or `force_ptr`.
370+
/// This method is intentionally private, do not make it public.
370371
#[inline]
371-
pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
372+
fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
372373
match self {
373374
Scalar::Raw { data: 0, .. } => throw_unsup!(InvalidNullPointerUsage),
374375
Scalar::Raw { .. } => throw_unsup!(ReadBytesAsPointer),
@@ -544,12 +545,6 @@ impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
544545
}
545546
}
546547

547-
/// Do not call this method! Use either `assert_ptr` or `force_ptr`.
548-
#[inline(always)]
549-
pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
550-
self.not_undef()?.to_ptr()
551-
}
552-
553548
/// Do not call this method! Use either `assert_bits` or `force_bits`.
554549
#[inline(always)]
555550
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {

src/librustc/ty/relate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,8 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
537537
Ok(ConstValue::Scalar(a_val))
538538
} else if let ty::FnPtr(_) = a.ty.kind {
539539
let alloc_map = tcx.alloc_map.lock();
540-
let a_instance = alloc_map.unwrap_fn(a_val.to_ptr().unwrap().alloc_id);
541-
let b_instance = alloc_map.unwrap_fn(b_val.to_ptr().unwrap().alloc_id);
540+
let a_instance = alloc_map.unwrap_fn(a_val.assert_ptr().alloc_id);
541+
let b_instance = alloc_map.unwrap_fn(b_val.assert_ptr().alloc_id);
542542
if a_instance == b_instance {
543543
Ok(ConstValue::Scalar(a_val))
544544
} else {

src/librustc_mir/const_eval/eval_queries.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub(super) fn op_to_const<'tcx>(
119119
};
120120
let val = match immediate {
121121
Ok(mplace) => {
122-
let ptr = mplace.ptr.to_ptr().unwrap();
122+
let ptr = mplace.ptr.assert_ptr();
123123
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
124124
ConstValue::ByRef { alloc, offset: ptr.offset }
125125
}
@@ -133,7 +133,7 @@ pub(super) fn op_to_const<'tcx>(
133133
// comes from a constant so it can happen have `Undef`, because the indirect
134134
// memory that was read had undefined bytes.
135135
let mplace = op.assert_mem_place();
136-
let ptr = mplace.ptr.to_ptr().unwrap();
136+
let ptr = mplace.ptr.assert_ptr();
137137
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
138138
ConstValue::ByRef { alloc, offset: ptr.offset }
139139
}
@@ -176,7 +176,7 @@ fn validate_and_turn_into_const<'tcx>(
176176
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
177177
// whether they become immediates.
178178
if is_static || cid.promoted.is_some() {
179-
let ptr = mplace.ptr.to_ptr()?;
179+
let ptr = mplace.ptr.assert_ptr();
180180
Ok(tcx.mk_const(ty::Const {
181181
val: ty::ConstKind::Value(ConstValue::ByRef {
182182
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),

src/librustc_mir/hair/pattern/_match.rs

+40-10
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ use syntax_pos::{Span, DUMMY_SP};
252252
use arena::TypedArena;
253253

254254
use smallvec::{smallvec, SmallVec};
255+
use std::borrow::Cow;
255256
use std::cmp::{self, max, min, Ordering};
256257
use std::convert::TryInto;
257258
use std::fmt;
@@ -260,11 +261,12 @@ use std::ops::RangeInclusive;
260261
use std::u128;
261262

262263
pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
263-
LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)
264+
LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
264265
}
265266

266267
struct LiteralExpander<'tcx> {
267268
tcx: TyCtxt<'tcx>,
269+
param_env: ty::ParamEnv<'tcx>,
268270
}
269271

270272
impl LiteralExpander<'tcx> {
@@ -284,9 +286,23 @@ impl LiteralExpander<'tcx> {
284286
debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
285287
match (val, &crty.kind, &rty.kind) {
286288
// the easy case, deref a reference
287-
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
288-
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
289-
ConstValue::ByRef { alloc, offset: p.offset }
289+
(ConstValue::Scalar(p), x, y) if x == y => {
290+
match p {
291+
Scalar::Ptr(p) => {
292+
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
293+
ConstValue::ByRef { alloc, offset: p.offset }
294+
}
295+
Scalar::Raw { .. } => {
296+
let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
297+
if layout.is_zst() {
298+
// Deref of a reference to a ZST is a nop.
299+
ConstValue::Scalar(Scalar::zst())
300+
} else {
301+
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
302+
bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
303+
}
304+
}
305+
}
290306
}
291307
// unsize array to slice if pattern is array but match value or other patterns are slice
292308
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
@@ -2348,16 +2364,30 @@ fn specialize_one_pattern<'p, 'tcx>(
23482364
// just integers. The only time they should be pointing to memory
23492365
// is when they are subslices of nonzero slices.
23502366
let (alloc, offset, n, ty) = match value.ty.kind {
2351-
ty::Array(t, n) => match value.val {
2352-
ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
2353-
(alloc, offset, n.eval_usize(cx.tcx, cx.param_env), t)
2367+
ty::Array(t, n) => {
2368+
let n = n.eval_usize(cx.tcx, cx.param_env);
2369+
// Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
2370+
// the result would be exactly what we early return here.
2371+
if n == 0 {
2372+
if ctor_wild_subpatterns.len() as u64 == 0 {
2373+
return Some(PatStack::from_slice(&[]));
2374+
} else {
2375+
return None;
2376+
}
23542377
}
2355-
_ => span_bug!(pat.span, "array pattern is {:?}", value,),
2356-
},
2378+
match value.val {
2379+
ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
2380+
(Cow::Borrowed(alloc), offset, n, t)
2381+
}
2382+
_ => span_bug!(pat.span, "array pattern is {:?}", value,),
2383+
}
2384+
}
23572385
ty::Slice(t) => {
23582386
match value.val {
23592387
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
2360-
(data, Size::from_bytes(start as u64), (end - start) as u64, t)
2388+
let offset = Size::from_bytes(start as u64);
2389+
let n = (end - start) as u64;
2390+
(Cow::Borrowed(data), offset, n, t)
23612391
}
23622392
ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
23632393
// FIXME(oli-obk): implement `deref` for `ConstValue`

src/librustc_mir/hair/pattern/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,12 @@ pub fn compare_const_vals<'tcx>(
992992
return fallback();
993993
}
994994

995+
// Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
996+
// are just integer addresses).
997+
if a.val == b.val {
998+
return from_bool(true);
999+
}
1000+
9951001
let a_bits = a.try_eval_bits(tcx, param_env, ty);
9961002
let b_bits = b.try_eval_bits(tcx, param_env, ty);
9971003

src/librustc_mir/interpret/eval_context.rs

+29-7
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use rustc_macros::HashStable;
2020
use syntax::source_map::{self, Span, DUMMY_SP};
2121

2222
use super::{
23-
Immediate, MPlaceTy, Machine, MemPlace, Memory, Operand, Place, PlaceTy, ScalarMaybeUndef,
24-
StackPopInfo,
23+
Immediate, MPlaceTy, Machine, MemPlace, Memory, OpTy, Operand, Place, PlaceTy,
24+
ScalarMaybeUndef, StackPopInfo,
2525
};
2626

2727
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -118,7 +118,7 @@ pub struct LocalState<'tcx, Tag = (), Id = AllocId> {
118118
}
119119

120120
/// Current value of a local variable
121-
#[derive(Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
121+
#[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable)] // Miri debug-prints these
122122
pub enum LocalValue<Tag = (), Id = AllocId> {
123123
/// This local is not currently alive, and cannot be used at all.
124124
Dead,
@@ -743,7 +743,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
743743
// FIXME: should we tell the user that there was a local which was never written to?
744744
if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local {
745745
trace!("deallocating local");
746-
let ptr = ptr.to_ptr()?;
746+
// All locals have a backing allocation, even if the allocation is empty
747+
// due to the local having ZST type.
748+
let ptr = ptr.assert_ptr();
747749
if log_enabled!(::log::Level::Trace) {
748750
self.memory.dump_alloc(ptr.alloc_id);
749751
}
@@ -752,13 +754,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
752754
Ok(())
753755
}
754756

757+
pub(super) fn const_eval(
758+
&self,
759+
gid: GlobalId<'tcx>,
760+
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
761+
let val = if self.tcx.is_static(gid.instance.def_id()) {
762+
self.tcx.const_eval_poly(gid.instance.def_id())?
763+
} else if let Some(promoted) = gid.promoted {
764+
self.tcx.const_eval_promoted(gid.instance, promoted)?
765+
} else {
766+
self.tcx.const_eval_instance(self.param_env, gid.instance, Some(self.tcx.span))?
767+
};
768+
// Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a
769+
// recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
770+
// return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call
771+
// `ecx.const_eval`.
772+
self.eval_const_to_op(val, None)
773+
}
774+
755775
pub fn const_eval_raw(
756776
&self,
757777
gid: GlobalId<'tcx>,
758778
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
759-
// FIXME(oli-obk): make this check an assertion that it's not a static here
760-
// FIXME(RalfJ, oli-obk): document that `Place::Static` can never be anything but a static
761-
// and `ConstValue::Unevaluated` can never be a static
779+
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
780+
// and thus don't care about the parameter environment. While we could just use
781+
// `self.param_env`, that would mean we invoke the query to evaluate the static
782+
// with different parameter environments, thus causing the static to be evaluated
783+
// multiple times.
762784
let param_env = if self.tcx.is_static(gid.instance.def_id()) {
763785
ty::ParamEnv::reveal_all()
764786
} else {

src/librustc_mir/interpret/intern.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,21 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx
187187
if let ty::Ref(_, referenced_ty, mutability) = ty.kind {
188188
let value = self.ecx.read_immediate(mplace.into())?;
189189
let mplace = self.ecx.ref_to_mplace(value)?;
190-
// Handle trait object vtables
190+
// Handle trait object vtables.
191191
if let ty::Dynamic(..) =
192192
self.ecx.tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind
193193
{
194-
if let Ok(vtable) = mplace.meta.unwrap().to_ptr() {
195-
// explitly choose `Immutable` here, since vtables are immutable, even
196-
// if the reference of the fat pointer is mutable
194+
// Validation has already errored on an invalid vtable pointer so we can safely not
195+
// do anything if this is not a real pointer.
196+
if let Scalar::Ptr(vtable) = mplace.meta.unwrap() {
197+
// Explicitly choose `Immutable` here, since vtables are immutable, even
198+
// if the reference of the fat pointer is mutable.
197199
self.intern_shallow(vtable.alloc_id, Mutability::Not, None)?;
200+
} else {
201+
self.ecx().tcx.sess.delay_span_bug(
202+
syntax_pos::DUMMY_SP,
203+
"vtables pointers cannot be integer pointers",
204+
);
198205
}
199206
}
200207
// Check if we have encountered this pointer+layout combination before.
@@ -280,7 +287,9 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
280287
ecx,
281288
leftover_allocations,
282289
base_intern_mode,
283-
ret.ptr.to_ptr()?.alloc_id,
290+
// The outermost allocation must exist, because we allocated it with
291+
// `Memory::allocate`.
292+
ret.ptr.assert_ptr().alloc_id,
284293
base_mutability,
285294
Some(ret.layout.ty),
286295
)?;

src/librustc_mir/interpret/intrinsics.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use rustc::hir::def_id::DefId;
66
use rustc::mir::{
77
self,
8-
interpret::{ConstValue, InterpResult, Scalar},
8+
interpret::{ConstValue, GlobalId, InterpResult, Scalar},
99
BinOp,
1010
};
1111
use rustc::ty;
@@ -118,9 +118,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
118118
| sym::size_of
119119
| sym::type_id
120120
| sym::type_name => {
121-
let val =
122-
self.tcx.const_eval_instance(self.param_env, instance, Some(self.tcx.span))?;
123-
let val = self.eval_const_to_op(val, None)?;
121+
let gid = GlobalId { instance, promoted: None };
122+
let val = self.const_eval(gid)?;
124123
self.copy_op(val, dest)?;
125124
}
126125

src/librustc_mir/interpret/operand.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
578578
ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
579579
ty::ConstKind::Unevaluated(def_id, substs) => {
580580
let instance = self.resolve(def_id, substs)?;
581-
return Ok(OpTy::from(self.const_eval_raw(GlobalId { instance, promoted: None })?));
581+
// We use `const_eval` here and `const_eval_raw` elsewhere in mir interpretation.
582+
// The reason we use `const_eval_raw` everywhere else is to prevent cycles during
583+
// validation, because validation automatically reads through any references, thus
584+
// potentially requiring the current static to be evaluated again. This is not a
585+
// problem here, because we are building an operand which means an actual read is
586+
// happening.
587+
// FIXME(oli-obk): eliminate all the `const_eval_raw` usages when we get rid of
588+
// `StaticKind` once and for all.
589+
return self.const_eval(GlobalId { instance, promoted: None });
582590
}
583591
ty::ConstKind::Infer(..)
584592
| ty::ConstKind::Bound(..)

src/librustc_mir/interpret/place.rs

+11-20
Original file line numberDiff line numberDiff line change
@@ -923,12 +923,14 @@ where
923923
return self.copy_op(src, dest);
924924
}
925925
// We still require the sizes to match.
926-
assert!(
927-
src.layout.size == dest.layout.size,
928-
"Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}",
929-
src,
930-
dest
931-
);
926+
if src.layout.size != dest.layout.size {
927+
// FIXME: This should be an assert instead of an error, but if we transmute within an
928+
// array length computation, `typeck` may not have yet been run and errored out. In fact
929+
// most likey we *are* running `typeck` right now. Investigate whether we can bail out
930+
// on `typeck_tables().has_errors` at all const eval entry points.
931+
debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest);
932+
throw_unsup!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty));
933+
}
932934
// Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want
933935
// to avoid that here.
934936
assert!(
@@ -974,31 +976,20 @@ where
974976
let (mplace, size) = match place.place {
975977
Place::Local { frame, local } => {
976978
match self.stack[frame].locals[local].access_mut()? {
977-
Ok(local_val) => {
979+
Ok(&mut local_val) => {
978980
// We need to make an allocation.
979-
// FIXME: Consider not doing anything for a ZST, and just returning
980-
// a fake pointer? Are we even called for ZST?
981-
982-
// We cannot hold on to the reference `local_val` while allocating,
983-
// but we can hold on to the value in there.
984-
let old_val =
985-
if let LocalValue::Live(Operand::Immediate(value)) = *local_val {
986-
Some(value)
987-
} else {
988-
None
989-
};
990981

991982
// We need the layout of the local. We can NOT use the layout we got,
992983
// that might e.g., be an inner field of a struct with `Scalar` layout,
993984
// that has different alignment than the outer field.
994-
// We also need to support unsized types, and hence cannot use `allocate`.
995985
let local_layout = self.layout_of_local(&self.stack[frame], local, None)?;
986+
// We also need to support unsized types, and hence cannot use `allocate`.
996987
let (size, align) = self
997988
.size_and_align_of(meta, local_layout)?
998989
.expect("Cannot allocate for non-dyn-sized type");
999990
let ptr = self.memory.allocate(size, align, MemoryKind::Stack);
1000991
let mplace = MemPlace { ptr: ptr.into(), align, meta };
1001-
if let Some(value) = old_val {
992+
if let LocalValue::Live(Operand::Immediate(value)) = local_val {
1002993
// Preserve old value.
1003994
// We don't have to validate as we can assume the local
1004995
// was already valid for its type.

0 commit comments

Comments
 (0)