Skip to content

Commit a593de4

Browse files
committed
interpret: support projecting into Place::Local without force_allocation
1 parent 42f5419 commit a593de4

File tree

12 files changed

+410
-258
lines changed

12 files changed

+410
-258
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,6 @@ const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but e
423423
const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer
424424
const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference
425425
const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str`
426-
const_eval_uninit_unsized_local =
427-
unsized local is used while uninitialized
428426
const_eval_unreachable = entering unreachable code
429427
const_eval_unreachable_unwind =
430428
unwinding past a stack frame that does not allow unwinding

compiler/rustc_const_eval/src/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
835835
rustc_middle::error::middle_adjust_for_foreign_abi_error
836836
}
837837
InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized,
838-
InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local,
838+
InvalidProgramInfo::ConstPropNonsense => panic!("We had const-prop nonsense, this should never be printed"),
839839
}
840840
}
841841
fn add_args<G: EmissionGuarantee>(
@@ -846,7 +846,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
846846
match self {
847847
InvalidProgramInfo::TooGeneric
848848
| InvalidProgramInfo::AlreadyReported(_)
849-
| InvalidProgramInfo::UninitUnsizedLocal => {}
849+
| InvalidProgramInfo::ConstPropNonsense => {}
850850
InvalidProgramInfo::Layout(e) => {
851851
let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
852852
for (name, val) in diag.args() {

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,9 +1014,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
10141014
{
10151015
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10161016
match self.place {
1017-
Place::Local { frame, local } => {
1017+
Place::Local { frame, local, offset } => {
10181018
let mut allocs = Vec::new();
10191019
write!(fmt, "{:?}", local)?;
1020+
if let Some(offset) = offset {
1021+
write!(fmt, "+{:#x}", offset.bytes())?;
1022+
}
10201023
if frame != self.ecx.frame_idx() {
10211024
write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?;
10221025
}

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
1313

1414
use super::{
1515
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
16-
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
16+
InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer,
1717
Provenance, Scalar,
1818
};
1919

@@ -240,37 +240,69 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
240240
let int = self.to_scalar().assert_int();
241241
ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral())
242242
}
243+
244+
/// Compute the "sub-immediate" that is located within the `base` at the given offset with the
245+
/// given layout.
246+
pub(super) fn offset(
247+
&self,
248+
offset: Size,
249+
layout: TyAndLayout<'tcx>,
250+
cx: &impl HasDataLayout,
251+
) -> Self {
252+
// This makes several assumptions about what layouts we will encounter; we match what
253+
// codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
254+
let inner_val: Immediate<_> = match (**self, self.layout.abi) {
255+
// if the entire value is uninit, then so is the field (can happen in ConstProp)
256+
(Immediate::Uninit, _) => Immediate::Uninit,
257+
// the field contains no information, can be left uninit
258+
_ if layout.is_zst() => Immediate::Uninit,
259+
// the field covers the entire type
260+
_ if layout.size == self.layout.size => {
261+
assert!(match (self.layout.abi, layout.abi) {
262+
(Abi::Scalar(..), Abi::Scalar(..)) => true,
263+
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
264+
_ => false,
265+
});
266+
assert!(offset.bytes() == 0);
267+
**self
268+
}
269+
// extract fields from types with `ScalarPair` ABI
270+
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
271+
assert!(matches!(layout.abi, Abi::Scalar(..)));
272+
Immediate::from(if offset.bytes() == 0 {
273+
debug_assert_eq!(layout.size, a.size(cx));
274+
a_val
275+
} else {
276+
debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
277+
debug_assert_eq!(layout.size, b.size(cx));
278+
b_val
279+
})
280+
}
281+
// everything else is a bug
282+
_ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout),
283+
};
284+
285+
ImmTy::from_immediate(inner_val, layout)
286+
}
243287
}
244288

245289
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
246-
pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
247-
if self.layout.is_unsized() {
248-
if matches!(self.op, Operand::Immediate(Immediate::Uninit)) {
249-
// Uninit unsized places shouldn't occur. In the interpreter we have them
250-
// temporarily for unsized arguments before their value is put in; in ConstProp they
251-
// remain uninit and this code can actually be reached.
252-
throw_inval!(UninitUnsizedLocal);
290+
pub(super) fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta<Prov>> {
291+
Ok(if self.layout.is_unsized() {
292+
if matches!(self.op, Operand::Immediate(_)) {
293+
// Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing.
294+
// However, ConstProp doesn't do that, so we can run into this nonsense situation.
295+
throw_inval!(ConstPropNonsense);
253296
}
254297
// There are no unsized immediates.
255-
self.assert_mem_place().len(cx)
298+
self.assert_mem_place().meta
256299
} else {
257-
match self.layout.fields {
258-
abi::FieldsShape::Array { count, .. } => Ok(count),
259-
_ => bug!("len not supported on sized type {:?}", self.layout.ty),
260-
}
261-
}
300+
MemPlaceMeta::None
301+
})
262302
}
263303

264-
/// Replace the layout of this operand. There's basically no sanity check that this makes sense,
265-
/// you better know what you are doing! If this is an immediate, applying the wrong layout can
266-
/// not just lead to invalid data, it can actually *shift the data around* since the offsets of
267-
/// a ScalarPair are entirely determined by the layout, not the data.
268-
pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self {
269-
assert_eq!(
270-
self.layout.size, layout.size,
271-
"transmuting with a size change, that doesn't seem right"
272-
);
273-
OpTy { layout, ..*self }
304+
pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
305+
self.meta()?.len(self.layout, cx)
274306
}
275307

276308
/// Offset the operand in memory (if possible) and change its metadata.
@@ -286,13 +318,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
286318
match self.as_mplace_or_imm() {
287319
Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()),
288320
Right(imm) => {
289-
assert!(
290-
matches!(*imm, Immediate::Uninit),
291-
"Scalar/ScalarPair cannot be offset into"
292-
);
293321
assert!(!meta.has_meta()); // no place to store metadata here
294322
// Every part of an uninit is uninit.
295-
Ok(ImmTy::uninit(layout).into())
323+
Ok(imm.offset(offset, layout, cx).into())
296324
}
297325
}
298326
}
@@ -502,13 +530,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
502530
&self,
503531
place: &PlaceTy<'tcx, M::Provenance>,
504532
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
505-
let op = match **place {
506-
Place::Ptr(mplace) => Operand::Indirect(mplace),
507-
Place::Local { frame, local } => {
508-
*self.local_to_op(&self.stack()[frame], local, None)?
533+
match place.as_mplace_or_local() {
534+
Left(mplace) => Ok(mplace.into()),
535+
Right((frame, local, offset)) => {
536+
let base = self.local_to_op(&self.stack()[frame], local, None)?;
537+
let mut field = if let Some(offset) = offset {
538+
// This got offset. We can be sure that the field is sized.
539+
base.offset(offset, place.layout, self)?
540+
} else {
541+
assert_eq!(place.layout, base.layout);
542+
// Unsized cases are possible here since an unsized local will be a
543+
// `Place::Local` until the first projection calls `place_to_op` to extract the
544+
// underlying mplace.
545+
base
546+
};
547+
field.align = Some(place.align);
548+
Ok(field)
509549
}
510-
};
511-
Ok(OpTy { op, layout: place.layout, align: Some(place.align) })
550+
}
512551
}
513552

514553
/// Evaluate a place with the goal of reading from it. This lets us sometimes

0 commit comments

Comments
 (0)