Skip to content

Commit 6dd6840

Browse files
committed
Auto merge of #96220 - RalfJung:scalar-no-padding, r=oli-obk
tighten sanity checks around Scalar and ScalarPair While investigating #96185 I noticed codegen has tighter sanity checks here than Miri does, so I added some more assertions. Strangely, some of them fail, so I also needed to add a HACK... that is probably worth looking into. This does not fix that issue, but it changes the ICE messages, making it quite clear that we have a scalar whose size is not the same as that of the surrounding layout. r? `@oli-obk`
2 parents 08b4f1b + 14f6daf commit 6dd6840

File tree

3 files changed

+53
-53
lines changed

3 files changed

+53
-53
lines changed

compiler/rustc_const_eval/src/interpret/operand.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
284284
Abi::Scalar(s) if force => Some(s.primitive()),
285285
_ => None,
286286
};
287-
if let Some(_) = scalar_layout {
288-
let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
287+
if let Some(_s) = scalar_layout {
288+
//FIXME(#96185): let size = s.size(self);
289+
//FIXME(#96185): assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
290+
let size = mplace.layout.size; //FIXME(#96185): remove this line
291+
let scalar = alloc.read_scalar(alloc_range(Size::ZERO, size))?;
289292
return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
290293
}
291294
let scalar_pair_layout = match mplace.layout.abi {
@@ -302,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
302305
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
303306
let (a_size, b_size) = (a.size(self), b.size(self));
304307
let b_offset = a_size.align_to(b.align(self).abi);
305-
assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
308+
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
306309
let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
307310
let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
308311
return Ok(Some(ImmTy {
@@ -394,28 +397,44 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
394397
Err(value) => value,
395398
};
396399

397-
let field_layout = op.layout.field(self, field);
398-
if field_layout.is_zst() {
399-
let immediate = Scalar::ZST.into();
400-
return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout });
401-
}
402-
let offset = op.layout.fields.offset(field);
403-
let immediate = match *base {
400+
let field_layout = base.layout.field(self, field);
401+
let offset = base.layout.fields.offset(field);
402+
// This makes several assumptions about what layouts we will encounter; we match what
403+
// codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
404+
let field_val: Immediate<_> = match (*base, base.layout.abi) {
405+
// the field contains no information
406+
_ if field_layout.is_zst() => Scalar::ZST.into(),
404407
// the field covers the entire type
405-
_ if offset.bytes() == 0 && field_layout.size == op.layout.size => *base,
408+
_ if field_layout.size == base.layout.size => {
409+
assert!(match (base.layout.abi, field_layout.abi) {
410+
(Abi::Scalar(..), Abi::Scalar(..)) => true,
411+
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
412+
_ => false,
413+
});
414+
assert!(offset.bytes() == 0);
415+
*base
416+
}
406417
// extract fields from types with `ScalarPair` ABI
407-
Immediate::ScalarPair(a, b) => {
408-
let val = if offset.bytes() == 0 { a } else { b };
409-
Immediate::from(val)
418+
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
419+
assert!(matches!(field_layout.abi, Abi::Scalar(..)));
420+
Immediate::from(if offset.bytes() == 0 {
421+
debug_assert_eq!(field_layout.size, a.size(self));
422+
a_val
423+
} else {
424+
debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
425+
debug_assert_eq!(field_layout.size, b.size(self));
426+
b_val
427+
})
410428
}
411-
Immediate::Scalar(val) => span_bug!(
429+
_ => span_bug!(
412430
self.cur_span(),
413-
"field access on non aggregate {:#?}, {:#?}",
414-
val,
415-
op.layout
431+
"invalid field access on immediate {}, layout {:#?}",
432+
base,
433+
base.layout
416434
),
417435
};
418-
Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout })
436+
437+
Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout })
419438
}
420439

421440
pub fn operand_index(

compiler/rustc_const_eval/src/interpret/place.rs

+10-30
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_target::abi::{HasDataLayout, Size, VariantIdx, Variants};
1616
use super::{
1717
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
1818
ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy,
19-
Operand, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
19+
Operand, Pointer, Provenance, Scalar, ScalarMaybeUninit,
2020
};
2121

2222
#[derive(Copy, Clone, Hash, PartialEq, Eq, HashStable, Debug)]
@@ -700,24 +700,7 @@ where
700700
src: Immediate<M::PointerTag>,
701701
dest: &PlaceTy<'tcx, M::PointerTag>,
702702
) -> InterpResult<'tcx> {
703-
if cfg!(debug_assertions) {
704-
// This is a very common path, avoid some checks in release mode
705-
assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
706-
match src {
707-
Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Ptr(..))) => assert_eq!(
708-
self.pointer_size(),
709-
dest.layout.size,
710-
"Size mismatch when writing pointer"
711-
),
712-
Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Int(int))) => {
713-
assert_eq!(int.size(), dest.layout.size, "Size mismatch when writing bits")
714-
}
715-
Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size
716-
Immediate::ScalarPair(_, _) => {
717-
// FIXME: Can we check anything here?
718-
}
719-
}
720-
}
703+
assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
721704
trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
722705

723706
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
@@ -753,31 +736,27 @@ where
753736
dest: &MPlaceTy<'tcx, M::PointerTag>,
754737
) -> InterpResult<'tcx> {
755738
// Note that it is really important that the type here is the right one, and matches the
756-
// type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
739+
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
757740
// to handle padding properly, which is only correct if we never look at this data with the
758741
// wrong type.
759742

760-
// Invalid places are a thing: the return place of a diverging function
761743
let tcx = *self.tcx;
762744
let Some(mut alloc) = self.get_place_alloc_mut(dest)? else {
763745
// zero-sized access
764746
return Ok(());
765747
};
766748

767-
// FIXME: We should check that there are dest.layout.size many bytes available in
768-
// memory. The code below is not sufficient, with enough padding it might not
769-
// cover all the bytes!
770749
match value {
771750
Immediate::Scalar(scalar) => {
772-
match dest.layout.abi {
773-
Abi::Scalar(_) => {} // fine
774-
_ => span_bug!(
751+
let Abi::Scalar(s) = dest.layout.abi else { span_bug!(
775752
self.cur_span(),
776753
"write_immediate_to_mplace: invalid Scalar layout: {:#?}",
777754
dest.layout
778-
),
779-
}
780-
alloc.write_scalar(alloc_range(Size::ZERO, dest.layout.size), scalar)
755+
)
756+
};
757+
let size = s.size(&tcx);
758+
//FIXME(#96185): assert_eq!(dest.layout.size, size, "abi::Scalar size does not match layout size");
759+
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
781760
}
782761
Immediate::ScalarPair(a_val, b_val) => {
783762
// We checked `ptr_align` above, so all fields will have the alignment they need.
@@ -791,6 +770,7 @@ where
791770
};
792771
let (a_size, b_size) = (a.size(&tcx), b.size(&tcx));
793772
let b_offset = a_size.align_to(b.align(&tcx).abi);
773+
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
794774

795775
// It is tempting to verify `b_offset` against `layout.fields.offset(1)`,
796776
// but that does not work: We could be a newtype around a pair, then the

compiler/rustc_const_eval/src/interpret/validity.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -645,17 +645,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
645645
// i.e. that we go over the `check_init` below.
646646
let size = scalar_layout.size(self.ecx);
647647
let is_full_range = match scalar_layout {
648-
ScalarAbi::Initialized { valid_range, .. } => {
648+
ScalarAbi::Initialized { .. } => {
649649
if M::enforce_number_validity(self.ecx) {
650650
false // not "full" since uninit is not accepted
651651
} else {
652-
valid_range.is_full_for(size)
652+
scalar_layout.is_always_valid(self.ecx)
653653
}
654654
}
655655
ScalarAbi::Union { .. } => true,
656656
};
657657
if is_full_range {
658-
// Nothing to check
658+
// Nothing to check. Cruciall we don't even `read_scalar` until here, since that would
659+
// fail for `Union` scalars!
659660
return Ok(());
660661
}
661662
// We have something to check: it must at least be initialized.
@@ -688,7 +689,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
688689
} else {
689690
return Ok(());
690691
}
691-
} else if scalar_layout.valid_range(self.ecx).is_full_for(size) {
692+
} else if scalar_layout.is_always_valid(self.ecx) {
692693
// Easy. (This is reachable if `enforce_number_validity` is set.)
693694
return Ok(());
694695
} else {

0 commit comments

Comments
 (0)