Skip to content

Commit 351dfa2

Browse files
committed
Miri/CTFE engine: do not treat unions as scalar values
1 parent 9f8f0a6 commit 351dfa2

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ pub(super) fn op_to_const<'tcx>(
118118
// instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
119119
// the usual cases of extracting e.g. a `usize`, without there being a real use case for the
120120
// `Undef` situation.
121+
// FIXME: This is all a horrible hack and should go away once we are properly using valtrees.
121122
let try_as_immediate = match op.layout.abi {
122-
Abi::Scalar(..) => true,
123+
Abi::Scalar(..) if crate::interpret::type_is_scalar(ecx.tcx.tcx, op.layout.ty) => true,
123124
Abi::ScalarPair(..) => match op.layout.ty.kind() {
124125
ty::Ref(_, inner, _) => match *inner.kind() {
125126
ty::Slice(elem) => elem == ecx.tcx.types.u8,

compiler/rustc_const_eval/src/interpret/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ pub use self::validity::{CtfeValidationMode, RefTracking};
3030
pub use self::visitor::{MutValueVisitor, ValueVisitor};
3131

3232
crate use self::intrinsics::eval_nullary_intrinsic;
33+
crate use self::operand::type_is_scalar;
3334
use eval_context::{from_known_layout, mir_assign_valid_types};

compiler/rustc_const_eval/src/interpret/operand.rs

+60-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_hir::def::Namespace;
99
use rustc_macros::HashStable;
1010
use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
1111
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer};
12-
use rustc_middle::ty::{ConstInt, Ty};
12+
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
1313
use rustc_middle::{mir, ty};
1414
use rustc_target::abi::{Abi, HasDataLayout, Size, TagEncoding};
1515
use rustc_target::abi::{VariantIdx, Variants};
@@ -246,6 +246,57 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
246246
}
247247
}
248248

249+
/// See `try_read_immediate_from_mplace` for what this function is about.
250+
/// Exposed to the rest of the crate since some hacky code in `eval_queries.rs` (that shouldn't
251+
/// exist) also needs it.
252+
pub(crate) fn type_is_scalar<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
253+
match ty.kind() {
254+
// Primitive types. We *have to* handle these since the primitive operations that
255+
// act on them require it.
256+
ty::Bool
257+
| ty::Char
258+
| ty::Float(_)
259+
| ty::Int(_)
260+
| ty::Uint(_)
261+
| ty::RawPtr(..)
262+
| ty::Ref(..)
263+
| ty::FnPtr(..)
264+
| ty::FnDef(..)
265+
| ty::Never => true,
266+
// Compound types. We have to exclude unions here.
267+
ty::Adt(adt_def, substs) => {
268+
if adt_def.is_union() {
269+
// Unions are explicitly allowed to be partially initialized, so we have to
270+
// exclude them here.
271+
false
272+
} else {
273+
// Check all fields of all variants, to make sure there is no union hiding here.
274+
adt_def.all_fields().all(|field| type_is_scalar(tcx, field.ty(tcx, substs)))
275+
}
276+
}
277+
ty::Tuple(..) => {
278+
// If all fields are scalar, we are good.
279+
ty.tuple_fields().iter().all(|field| type_is_scalar(tcx, field))
280+
}
281+
// FIXME: can these ever have Scalar ABI?
282+
ty::Closure(..) | ty::Generator(..) => false,
283+
// Types that don't have scalar layout to begin with.
284+
ty::Array(..) | ty::Slice(..) | ty::Str | ty::Dynamic(..) | ty::Foreign(..) => {
285+
false
286+
}
287+
// Types we should not uusally see here, but when called from CTFE op_to_const these can
288+
// actually happen.
289+
ty::Error(_)
290+
| ty::Infer(..)
291+
| ty::Placeholder(..)
292+
| ty::Bound(..)
293+
| ty::Param(..)
294+
| ty::Opaque(..)
295+
| ty::Projection(..)
296+
| ty::GeneratorWitness(..) => false,
297+
}
298+
}
299+
249300
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
250301
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
251302
/// Returns `None` if the layout does not permit loading this as a value.
@@ -266,12 +317,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
266317
}));
267318
};
268319

320+
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
321+
// However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
322+
// and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
323+
// case where some of the bytes are initialized and others are not. So, we need an extra
324+
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
325+
// like a `Scalar` (or `ScalarPair`).
269326
match mplace.layout.abi {
270-
Abi::Scalar(..) => {
327+
Abi::Scalar(..) if type_is_scalar(self.tcx.tcx, mplace.layout.ty) => {
271328
let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
272329
Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
273330
}
274-
Abi::ScalarPair(a, b) => {
331+
Abi::ScalarPair(a, b) if type_is_scalar(self.tcx.tcx, mplace.layout.ty) => {
275332
// We checked `ptr_align` above, so all fields will have the alignment they need.
276333
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
277334
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.

0 commit comments

Comments
 (0)