Skip to content

Commit 9cb052a

Browse files
committed
Auto merge of #58351 - oli-obk:double_check_const_eval, r=RalfJung
Refactor interning to properly mark memory as mutable or immutable r? @RalfJung This implementation is incomplete out of multiple reasons * [ ] add `-Zunleash_the_miri_inside_of_you` tests * [ ] report an error if there's an `UnsafeCell` behind a reference in a constant * [ ] make validity checks actually test whether the mutability of their allocations match what they see in the type
2 parents a6cbf2d + cd290c7 commit 9cb052a

30 files changed

+758
-197
lines changed

src/librustc/mir/interpret/value.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt;
22
use rustc_macros::HashStable;
33
use rustc_apfloat::{Float, ieee::{Double, Single}};
44

5-
use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef};
5+
use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size, Align}, subst::SubstsRef};
66
use crate::ty::PlaceholderConst;
77
use crate::hir::def_id::DefId;
88

@@ -45,7 +45,12 @@ pub enum ConstValue<'tcx> {
4545

4646
/// An allocation together with a pointer into the allocation.
4747
/// Invariant: the pointer's `AllocId` resolves to the allocation.
48-
ByRef(Pointer, &'tcx Allocation),
48+
/// The alignment exists to allow `const_field` to have `ByRef` access to nonprimitive fields
49+
/// of `repr(packed)` structs. The alignment may be lower than the type of this constant.
50+
/// This permits reads with lower alignment than what the type would normally require.
51+
/// FIXME(RalfJ,oli-obk): The alignment checks are part of miri, but const eval doesn't really
52+
/// need them. Disabling them may be too hard though.
53+
ByRef(Pointer, Align, &'tcx Allocation),
4954

5055
/// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
5156
/// variants when the code is monomorphic enough for that.

src/librustc/ty/structural_impls.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
13351335
impl<'tcx> TypeFoldable<'tcx> for ConstValue<'tcx> {
13361336
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
13371337
match *self {
1338-
ConstValue::ByRef(ptr, alloc) => ConstValue::ByRef(ptr, alloc),
1338+
ConstValue::ByRef(ptr, align, alloc) => ConstValue::ByRef(ptr, align, alloc),
13391339
ConstValue::Infer(ic) => ConstValue::Infer(ic.fold_with(folder)),
13401340
ConstValue::Param(p) => ConstValue::Param(p.fold_with(folder)),
13411341
ConstValue::Placeholder(p) => ConstValue::Placeholder(p),

src/librustc_codegen_llvm/common.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::value::Value;
1111
use rustc_codegen_ssa::traits::*;
1212

1313
use crate::consts::const_alloc_to_llvm;
14-
use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size};
14+
use rustc::ty::layout::{HasDataLayout, LayoutOf, self, TyLayout, Size, Align};
1515
use rustc::mir::interpret::{Scalar, GlobalAlloc, Allocation};
1616
use rustc_codegen_ssa::mir::place::PlaceRef;
1717

@@ -344,19 +344,20 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
344344
fn from_const_alloc(
345345
&self,
346346
layout: TyLayout<'tcx>,
347+
align: Align,
347348
alloc: &Allocation,
348349
offset: Size,
349350
) -> PlaceRef<'tcx, &'ll Value> {
350351
let init = const_alloc_to_llvm(self, alloc);
351-
let base_addr = self.static_addr_of(init, layout.align.abi, None);
352+
let base_addr = self.static_addr_of(init, align, None);
352353

353354
let llval = unsafe { llvm::LLVMConstInBoundsGEP(
354355
self.const_bitcast(base_addr, self.type_i8p()),
355356
&self.const_usize(offset.bytes()),
356357
1,
357358
)};
358359
let llval = self.const_bitcast(llval, self.type_ptr_to(layout.llvm_type(self)));
359-
PlaceRef::new_sized(llval, layout, alloc.align)
360+
PlaceRef::new_sized(llval, layout, align)
360361
}
361362

362363
fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {

src/librustc_codegen_llvm/consts.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ pub fn codegen_static_initializer(
7171
let static_ = cx.tcx.const_eval(param_env.and(cid))?;
7272

7373
let alloc = match static_.val {
74-
ConstValue::ByRef(ptr, alloc) if ptr.offset.bytes() == 0 => alloc,
74+
ConstValue::ByRef(ptr, align, alloc) if ptr.offset.bytes() == 0 && align == alloc.align => {
75+
alloc
76+
},
7577
_ => bug!("static const eval returned {:#?}", static_),
7678
};
7779
Ok((const_alloc_to_llvm(cx, alloc), alloc))

src/librustc_codegen_ssa/mir/operand.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
109109
let b_llval = bx.const_usize((end - start) as u64);
110110
OperandValue::Pair(a_llval, b_llval)
111111
},
112-
ConstValue::ByRef(ptr, alloc) => {
113-
return bx.load_operand(bx.from_const_alloc(layout, alloc, ptr.offset));
112+
ConstValue::ByRef(ptr, align, alloc) => {
113+
return bx.load_operand(bx.from_const_alloc(layout, align, alloc, ptr.offset));
114114
},
115115
};
116116

src/librustc_codegen_ssa/mir/place.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
424424
let layout = cx.layout_of(self.monomorphize(&ty));
425425
match bx.tcx().const_eval(param_env.and(cid)) {
426426
Ok(val) => match val.val {
427-
mir::interpret::ConstValue::ByRef(ptr, alloc) => {
428-
bx.cx().from_const_alloc(layout, alloc, ptr.offset)
427+
mir::interpret::ConstValue::ByRef(ptr, align, alloc) => {
428+
bx.cx().from_const_alloc(layout, align, alloc, ptr.offset)
429429
}
430430
_ => bug!("promoteds should have an allocation: {:?}", val),
431431
},

src/librustc_codegen_ssa/traits/consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
3434
fn from_const_alloc(
3535
&self,
3636
layout: layout::TyLayout<'tcx>,
37+
align: layout::Align,
3738
alloc: &Allocation,
3839
offset: layout::Size,
3940
) -> PlaceRef<'tcx, Self::Value>;

src/librustc_mir/const_eval.rs

+59-44
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::convert::TryInto;
99

1010
use rustc::hir::def::DefKind;
1111
use rustc::hir::def_id::DefId;
12-
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
12+
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
1313
use rustc::mir;
1414
use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
1515
use rustc::ty::layout::{self, LayoutOf, VariantIdx};
@@ -18,15 +18,14 @@ use rustc::traits::Reveal;
1818
use rustc::util::common::ErrorReported;
1919
use rustc_data_structures::fx::FxHashMap;
2020

21-
use syntax::ast::Mutability;
2221
use syntax::source_map::{Span, DUMMY_SP};
2322

2423
use crate::interpret::{self,
25-
PlaceTy, MPlaceTy, MemPlace, OpTy, ImmTy, Immediate, Scalar,
24+
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar,
2625
RawConst, ConstValue,
2726
InterpResult, InterpErrorInfo, InterpError, GlobalId, InterpretCx, StackPopCleanup,
2827
Allocation, AllocId, MemoryKind,
29-
snapshot, RefTracking,
28+
snapshot, RefTracking, intern_const_alloc_recursive,
3029
};
3130

3231
/// Number of steps until the detector even starts doing anything.
@@ -63,33 +62,19 @@ pub(crate) fn eval_promoted<'mir, 'tcx>(
6362
eval_body_using_ecx(&mut ecx, cid, body, param_env)
6463
}
6564

66-
fn mplace_to_const<'tcx>(
67-
ecx: &CompileTimeEvalContext<'_, 'tcx>,
68-
mplace: MPlaceTy<'tcx>,
69-
) -> &'tcx ty::Const<'tcx> {
70-
let MemPlace { ptr, align, meta } = *mplace;
71-
// extract alloc-offset pair
72-
assert!(meta.is_none());
73-
let ptr = ptr.to_ptr().unwrap();
74-
let alloc = ecx.memory.get(ptr.alloc_id).unwrap();
75-
assert!(alloc.align >= align);
76-
assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= mplace.layout.size.bytes());
77-
let mut alloc = alloc.clone();
78-
alloc.align = align;
79-
// FIXME shouldn't it be the case that `mark_static_initialized` has already
80-
// interned this? I thought that is the entire point of that `FinishStatic` stuff?
81-
let alloc = ecx.tcx.intern_const_alloc(alloc);
82-
let val = ConstValue::ByRef(ptr, alloc);
83-
ecx.tcx.mk_const(ty::Const { val, ty: mplace.layout.ty })
84-
}
85-
8665
fn op_to_const<'tcx>(
8766
ecx: &CompileTimeEvalContext<'_, 'tcx>,
8867
op: OpTy<'tcx>,
8968
) -> &'tcx ty::Const<'tcx> {
90-
// We do not normalize just any data. Only non-union scalars and slices.
91-
let normalize = match op.layout.abi {
92-
layout::Abi::Scalar(..) => op.layout.ty.ty_adt_def().map_or(true, |adt| !adt.is_union()),
69+
// We do not have value optmizations for everything.
70+
// Only scalars and slices, since they are very common.
71+
// Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
72+
// from scalar unions that are initialized with one of their zero sized variants. We could
73+
// instead allow `ConstValue::Scalar` to store `ScalarMaybeUndef`, but that would affect all
74+
// the usual cases of extracting e.g. a `usize`, without there being a real use case for the
75+
// `Undef` situation.
76+
let try_as_immediate = match op.layout.abi {
77+
layout::Abi::Scalar(..) => true,
9378
layout::Abi::ScalarPair(..) => match op.layout.ty.sty {
9479
ty::Ref(_, inner, _) => match inner.sty {
9580
ty::Slice(elem) => elem == ecx.tcx.types.u8,
@@ -100,16 +85,38 @@ fn op_to_const<'tcx>(
10085
},
10186
_ => false,
10287
};
103-
let normalized_op = if normalize {
104-
Err(*ecx.read_immediate(op).expect("normalization works on validated constants"))
88+
let immediate = if try_as_immediate {
89+
Err(ecx.read_immediate(op).expect("normalization works on validated constants"))
10590
} else {
91+
// It is guaranteed that any non-slice scalar pair is actually ByRef here.
92+
// When we come back from raw const eval, we are always by-ref. The only way our op here is
93+
// by-val is if we are in const_field, i.e., if this is (a field of) something that we
94+
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
95+
// structs containing such.
10696
op.try_as_mplace()
10797
};
108-
let val = match normalized_op {
109-
Ok(mplace) => return mplace_to_const(ecx, mplace),
110-
Err(Immediate::Scalar(x)) =>
111-
ConstValue::Scalar(x.not_undef().unwrap()),
112-
Err(Immediate::ScalarPair(a, b)) => {
98+
let val = match immediate {
99+
Ok(mplace) => {
100+
let ptr = mplace.ptr.to_ptr().unwrap();
101+
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
102+
ConstValue::ByRef(ptr, mplace.align, alloc)
103+
},
104+
// see comment on `let try_as_immediate` above
105+
Err(ImmTy { imm: Immediate::Scalar(x), .. }) => match x {
106+
ScalarMaybeUndef::Scalar(s) => ConstValue::Scalar(s),
107+
ScalarMaybeUndef::Undef => {
108+
// When coming out of "normal CTFE", we'll always have an `Indirect` operand as
109+
// argument and we will not need this. The only way we can already have an
110+
// `Immediate` is when we are called from `const_field`, and that `Immediate`
111+
// comes from a constant so it can happen have `Undef`, because the indirect
112+
// memory that was read had undefined bytes.
113+
let mplace = op.to_mem_place();
114+
let ptr = mplace.ptr.to_ptr().unwrap();
115+
let alloc = ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id);
116+
ConstValue::ByRef(ptr, mplace.align, alloc)
117+
},
118+
},
119+
Err(ImmTy { imm: Immediate::ScalarPair(a, b), .. }) => {
113120
let (data, start) = match a.not_undef().unwrap() {
114121
Scalar::Ptr(ptr) => (
115122
ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
@@ -164,13 +171,12 @@ fn eval_body_using_ecx<'mir, 'tcx>(
164171
ecx.run()?;
165172

166173
// Intern the result
167-
let mutability = if tcx.is_mutable_static(cid.instance.def_id()) ||
168-
!layout.ty.is_freeze(tcx, param_env, body.span) {
169-
Mutability::Mutable
170-
} else {
171-
Mutability::Immutable
172-
};
173-
ecx.memory.intern_static(ret.ptr.to_ptr()?.alloc_id, mutability)?;
174+
intern_const_alloc_recursive(
175+
ecx,
176+
cid.instance.def_id(),
177+
ret,
178+
param_env,
179+
)?;
174180

175181
debug!("eval_body_using_ecx done: {:?}", *ret);
176182
Ok(ret)
@@ -297,7 +303,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
297303
}
298304
}
299305

300-
type CompileTimeEvalContext<'mir, 'tcx> =
306+
crate type CompileTimeEvalContext<'mir, 'tcx> =
301307
InterpretCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
302308

303309
impl interpret::MayLeak for ! {
@@ -526,13 +532,22 @@ fn validate_and_turn_into_const<'tcx>(
526532
mplace.into(),
527533
path,
528534
Some(&mut ref_tracking),
529-
true, // const mode
530535
)?;
531536
}
532537
// Now that we validated, turn this into a proper constant.
538+
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
539+
// whether they become immediates.
533540
let def_id = cid.instance.def.def_id();
534541
if tcx.is_static(def_id) || cid.promoted.is_some() {
535-
Ok(mplace_to_const(&ecx, mplace))
542+
let ptr = mplace.ptr.to_ptr()?;
543+
Ok(tcx.mk_const(ty::Const {
544+
val: ConstValue::ByRef(
545+
ptr,
546+
mplace.align,
547+
ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
548+
),
549+
ty: mplace.layout.ty,
550+
}))
536551
} else {
537552
Ok(op_to_const(&ecx, mplace.into()))
538553
}

src/librustc_mir/hair/pattern/_match.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,15 @@ impl LiteralExpander<'tcx> {
215215
debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
216216
match (val, &crty.sty, &rty.sty) {
217217
// the easy case, deref a reference
218-
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => ConstValue::ByRef(
219-
p,
220-
self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id),
221-
),
218+
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => {
219+
let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id);
220+
ConstValue::ByRef(
221+
p,
222+
// FIXME(oli-obk): this should be the type's layout
223+
alloc.align,
224+
alloc,
225+
)
226+
},
222227
// unsize array to slice if pattern is array but match value or other patterns are slice
223228
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
224229
assert_eq!(t, u);
@@ -1431,7 +1436,7 @@ fn slice_pat_covered_by_const<'tcx>(
14311436
suffix: &[Pattern<'tcx>],
14321437
) -> Result<bool, ErrorReported> {
14331438
let data: &[u8] = match (const_val.val, &const_val.ty.sty) {
1434-
(ConstValue::ByRef(ptr, alloc), ty::Array(t, n)) => {
1439+
(ConstValue::ByRef(ptr, _, alloc), ty::Array(t, n)) => {
14351440
assert_eq!(*t, tcx.types.u8);
14361441
let n = n.assert_usize(tcx).unwrap();
14371442
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
@@ -1753,7 +1758,7 @@ fn specialize<'p, 'a: 'p, 'tcx>(
17531758
let (alloc, offset, n, ty) = match value.ty.sty {
17541759
ty::Array(t, n) => {
17551760
match value.val {
1756-
ConstValue::ByRef(ptr, alloc) => (
1761+
ConstValue::ByRef(ptr, _, alloc) => (
17571762
alloc,
17581763
ptr.offset,
17591764
n.unwrap_usize(cx.tcx),

src/librustc_mir/interpret/eval_context.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
576576
self.place_to_op(return_place)?,
577577
vec![],
578578
None,
579-
/*const_mode*/false,
580579
)?;
581580
}
582581
} else {
@@ -641,6 +640,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpretCx<'mir, 'tcx, M> {
641640
&self,
642641
gid: GlobalId<'tcx>,
643642
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
643+
// FIXME(oli-obk): make this check an assertion that it's not a static here
644+
// FIXME(RalfJ, oli-obk): document that `Place::Static` can never be anything but a static
645+
// and `ConstValue::Unevaluated` can never be a static
644646
let param_env = if self.tcx.is_static(gid.instance.def_id()) {
645647
ty::ParamEnv::reveal_all()
646648
} else {

0 commit comments

Comments
 (0)