Update InterpCx::project_field to take FieldIdx

As suggested by Ralf in 142005.
This commit is contained in:
Scott McMurray
2025-06-04 01:44:40 -07:00
parent ccf3198de3
commit 8bce2255e8
26 changed files with 102 additions and 80 deletions

View File

@@ -39,6 +39,13 @@ rustc_index::newtype_index! {
pub struct FieldIdx {} pub struct FieldIdx {}
} }
impl FieldIdx {
/// The second field.
///
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
}
rustc_index::newtype_index! { rustc_index::newtype_index! {
/// The *source-order* index of a variant in a type. /// The *source-order* index of a variant in a type.
/// ///
@@ -274,7 +281,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// Finds the one field that is not a 1-ZST. /// Finds the one field that is not a 1-ZST.
/// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields. /// Returns `None` if there are multiple non-1-ZST fields or only 1-ZST-fields.
pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(usize, Self)> pub fn non_1zst_field<C>(&self, cx: &C) -> Option<(FieldIdx, Self)>
where where
Ty: TyAbiInterface<'a, C> + Copy, Ty: TyAbiInterface<'a, C> + Copy,
{ {
@@ -288,7 +295,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
// More than one non-1-ZST field. // More than one non-1-ZST field.
return None; return None;
} }
found = Some((field_idx, field)); found = Some((FieldIdx::from_usize(field_idx), field));
} }
found found
} }

View File

@@ -850,7 +850,7 @@ fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option<ty
// Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151 // Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151
ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => { ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
let fields = &adt.non_enum_variant().fields; let fields = &adt.non_enum_variant().fields;
let ty = fields[FieldIdx::from_u32(1)].ty(fx.tcx, args); let ty = fields[FieldIdx::ONE].ty(fx.tcx, args);
let ty::Adt(ty, args) = ty.kind() else { let ty::Adt(ty, args) = ty.kind() else {
unreachable!("expected first field of `MaybeUninit` to be an ADT") unreachable!("expected first field of `MaybeUninit` to be an ADT")
}; };

View File

@@ -53,7 +53,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
.layout() .layout()
.non_1zst_field(fx) .non_1zst_field(fx)
.expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type"); .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type");
arg = arg.value_field(fx, FieldIdx::new(idx)); arg = arg.value_field(fx, idx);
} }
} }
@@ -62,8 +62,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>(
let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap()); let inner_layout = fx.layout_of(arg.layout().ty.builtin_deref(true).unwrap());
let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout); let dyn_star = CPlace::for_ptr(Pointer::new(arg.load_scalar(fx)), inner_layout);
let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr(); let ptr = dyn_star.place_field(fx, FieldIdx::ZERO).to_ptr();
let vtable = let vtable = dyn_star.place_field(fx, FieldIdx::ONE).to_cvalue(fx).load_scalar(fx);
dyn_star.place_field(fx, FieldIdx::new(1)).to_cvalue(fx).load_scalar(fx);
break 'block (ptr, vtable); break 'block (ptr, vtable);
} }
} }

View File

@@ -1077,7 +1077,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (idx, _) = op.layout.non_1zst_field(bx).expect( let (idx, _) = op.layout.non_1zst_field(bx).expect(
"not exactly one non-1-ZST field in a `DispatchFromDyn` type", "not exactly one non-1-ZST field in a `DispatchFromDyn` type",
); );
op = op.extract_field(self, bx, idx); op = op.extract_field(self, bx, idx.as_usize());
} }
// Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its // Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
@@ -1109,7 +1109,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (idx, _) = op.layout.non_1zst_field(bx).expect( let (idx, _) = op.layout.non_1zst_field(bx).expect(
"not exactly one non-1-ZST field in a `DispatchFromDyn` type", "not exactly one non-1-ZST field in a `DispatchFromDyn` type",
); );
op = op.extract_field(self, bx, idx); op = op.extract_field(self, bx, idx.as_usize());
} }
// Make sure that we've actually unwrapped the rcvr down // Make sure that we've actually unwrapped the rcvr down

View File

@@ -1,6 +1,6 @@
// Not in interpret to make sure we do not use private implementation details // Not in interpret to make sure we do not use private implementation details
use rustc_abi::VariantIdx; use rustc_abi::{FieldIdx, VariantIdx};
use rustc_middle::query::Key; use rustc_middle::query::Key;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -60,7 +60,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
let fields_iter = (0..field_count) let fields_iter = (0..field_count)
.map(|i| { .map(|i| {
let field_op = ecx.project_field(&down, i).discard_err()?; let field_op = ecx.project_field(&down, FieldIdx::from_usize(i)).discard_err()?;
let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true); let val = op_to_const(&ecx, &field_op, /* for diagnostics */ true);
Some((val, field_op.layout.ty)) Some((val, field_op.layout.ty))
}) })

View File

@@ -1,4 +1,4 @@
use rustc_abi::{BackendRepr, VariantIdx}; use rustc_abi::{BackendRepr, FieldIdx, VariantIdx};
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo}; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo};
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
@@ -40,7 +40,7 @@ fn branches<'tcx>(
} }
for i in 0..field_count { for i in 0..field_count {
let field = ecx.project_field(&place, i).unwrap(); let field = ecx.project_field(&place, FieldIdx::from_usize(i)).unwrap();
let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?;
branches.push(valtree); branches.push(valtree);
} }
@@ -437,7 +437,7 @@ fn valtree_into_mplace<'tcx>(
ty::Str | ty::Slice(_) | ty::Array(..) => { ty::Str | ty::Slice(_) | ty::Array(..) => {
ecx.project_index(place, i as u64).unwrap() ecx.project_index(place, i as u64).unwrap()
} }
_ => ecx.project_field(&place_adjusted, i).unwrap(), _ => ecx.project_field(&place_adjusted, FieldIdx::from_usize(i)).unwrap(),
}; };
debug!(?place_inner); debug!(?place_inner);

View File

@@ -62,7 +62,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
pub(super) fn fn_arg_field( pub(super) fn fn_arg_field(
&self, &self,
arg: &FnArg<'tcx, M::Provenance>, arg: &FnArg<'tcx, M::Provenance>,
field: usize, field: FieldIdx,
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
interp_ok(match arg { interp_ok(match arg {
FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?), FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?),
@@ -600,10 +600,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Cow::from( Cow::from(
args.iter() args.iter()
.map(|a| interp_ok(a.clone())) .map(|a| interp_ok(a.clone()))
.chain( .chain((0..untuple_arg.layout().fields.count()).map(|i| {
(0..untuple_arg.layout().fields.count()) self.fn_arg_field(untuple_arg, FieldIdx::from_usize(i))
.map(|i| self.fn_arg_field(untuple_arg, i)), }))
)
.collect::<InterpResult<'_, Vec<_>>>()?, .collect::<InterpResult<'_, Vec<_>>>()?,
) )
} else { } else {

View File

@@ -1,6 +1,6 @@
use std::assert_matches::assert_matches; use std::assert_matches::assert_matches;
use rustc_abi::Integer; use rustc_abi::{FieldIdx, Integer};
use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_apfloat::{Float, FloatConvert}; use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir::CastKind; use rustc_middle::mir::CastKind;
@@ -484,6 +484,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let mut found_cast_field = false; let mut found_cast_field = false;
for i in 0..src.layout.fields.count() { for i in 0..src.layout.fields.count() {
let cast_ty_field = cast_ty.field(self, i); let cast_ty_field = cast_ty.field(self, i);
let i = FieldIdx::from_usize(i);
let src_field = self.project_field(src, i)?; let src_field = self.project_field(src, i)?;
let dst_field = self.project_field(dest, i)?; let dst_field = self.project_field(dest, i)?;
if src_field.layout.is_1zst() && cast_ty_field.is_1zst() { if src_field.layout.is_1zst() && cast_ty_field.is_1zst() {

View File

@@ -26,7 +26,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// No need to validate that the discriminant here because the // No need to validate that the discriminant here because the
// `TyAndLayout::for_variant()` call earlier already checks the // `TyAndLayout::for_variant()` call earlier already checks the
// variant is valid. // variant is valid.
let tag_dest = self.project_field(dest, tag_field.as_usize())?; let tag_dest = self.project_field(dest, tag_field)?;
self.write_scalar(tag, &tag_dest) self.write_scalar(tag, &tag_dest)
} }
None => { None => {
@@ -96,7 +96,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?; let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
// Read tag and sanity-check `tag_layout`. // Read tag and sanity-check `tag_layout`.
let tag_val = self.read_immediate(&self.project_field(op, tag_field.as_usize())?)?; let tag_val = self.read_immediate(&self.project_field(op, tag_field)?)?;
assert_eq!(tag_layout.size, tag_val.layout.size); assert_eq!(tag_layout.size, tag_val.layout.size);
assert_eq!(tag_layout.backend_repr.is_signed(), tag_val.layout.backend_repr.is_signed()); assert_eq!(tag_layout.backend_repr.is_signed(), tag_val.layout.backend_repr.is_signed());
trace!("tag value: {}", tag_val); trace!("tag value: {}", tag_val);

View File

@@ -10,7 +10,7 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Range; use std::ops::Range;
use rustc_abi::{self as abi, Size, VariantIdx}; use rustc_abi::{self as abi, FieldIdx, Size, VariantIdx};
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::{bug, mir, span_bug, ty}; use rustc_middle::{bug, mir, span_bug, ty};
@@ -144,22 +144,22 @@ where
/// always possible without allocating, so it can take `&self`. Also return the field's layout. /// always possible without allocating, so it can take `&self`. Also return the field's layout.
/// This supports both struct and array fields, but not slices! /// This supports both struct and array fields, but not slices!
/// ///
/// This also works for arrays, but then the `usize` index type is restricting. /// This also works for arrays, but then the `FieldIdx` index type is restricting.
/// For indexing into arrays, use `mplace_index`. /// For indexing into arrays, use [`Self::project_index`].
pub fn project_field<P: Projectable<'tcx, M::Provenance>>( pub fn project_field<P: Projectable<'tcx, M::Provenance>>(
&self, &self,
base: &P, base: &P,
field: usize, field: FieldIdx,
) -> InterpResult<'tcx, P> { ) -> InterpResult<'tcx, P> {
// Slices nominally have length 0, so they will panic somewhere in `fields.offset`. // Slices nominally have length 0, so they will panic somewhere in `fields.offset`.
debug_assert!( debug_assert!(
!matches!(base.layout().ty.kind(), ty::Slice(..)), !matches!(base.layout().ty.kind(), ty::Slice(..)),
"`field` projection called on a slice -- call `index` projection instead" "`field` projection called on a slice -- call `index` projection instead"
); );
let offset = base.layout().fields.offset(field); let offset = base.layout().fields.offset(field.as_usize());
// Computing the layout does normalization, so we get a normalized type out of this // Computing the layout does normalization, so we get a normalized type out of this
// even if the field type is non-normalized (possible e.g. via associated types). // even if the field type is non-normalized (possible e.g. via associated types).
let field_layout = base.layout().field(self, field); let field_layout = base.layout().field(self, field.as_usize());
// Offset may need adjustment for unsized fields. // Offset may need adjustment for unsized fields.
let (meta, offset) = if field_layout.is_unsized() { let (meta, offset) = if field_layout.is_unsized() {
@@ -244,7 +244,7 @@ where
} }
_ => span_bug!( _ => span_bug!(
self.cur_span(), self.cur_span(),
"`mplace_index` called on non-array type {:?}", "`project_index` called on non-array type {:?}",
base.layout().ty base.layout().ty
), ),
}; };
@@ -260,7 +260,7 @@ where
) -> InterpResult<'tcx, (P, u64)> { ) -> InterpResult<'tcx, (P, u64)> {
assert!(base.layout().ty.ty_adt_def().unwrap().repr().simd()); assert!(base.layout().ty.ty_adt_def().unwrap().repr().simd());
// SIMD types must be newtypes around arrays, so all we have to do is project to their only field. // SIMD types must be newtypes around arrays, so all we have to do is project to their only field.
let array = self.project_field(base, 0)?; let array = self.project_field(base, FieldIdx::ZERO)?;
let len = array.len(self)?; let len = array.len(self)?;
interp_ok((array, len)) interp_ok((array, len))
} }
@@ -384,7 +384,7 @@ where
UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?, UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?,
// We don't want anything happening here, this is here as a dummy. // We don't want anything happening here, this is here as a dummy.
Subtype(_) => base.transmute(base.layout(), self)?, Subtype(_) => base.transmute(base.layout(), self)?,
Field(field, _) => self.project_field(base, field.index())?, Field(field, _) => self.project_field(base, field)?,
Downcast(_, variant) => self.project_downcast(base, variant)?, Downcast(_, variant) => self.project_downcast(base, variant)?,
Deref => self.deref_pointer(&base.to_op(self)?)?.into(), Deref => self.deref_pointer(&base.to_op(self)?)?.into(),
Index(local) => { Index(local) => {

View File

@@ -333,7 +333,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
} }
for (field_index, operand) in operands.iter_enumerated() { for (field_index, operand) in operands.iter_enumerated() {
let field_index = active_field_index.unwrap_or(field_index); let field_index = active_field_index.unwrap_or(field_index);
let field_dest = self.project_field(&variant_dest, field_index.as_usize())?; let field_dest = self.project_field(&variant_dest, field_index)?;
let op = self.eval_operand(operand, Some(field_dest.layout))?; let op = self.eval_operand(operand, Some(field_dest.layout))?;
self.copy_op(&op, &field_dest)?; self.copy_op(&op, &field_dest)?;
} }

View File

@@ -1,4 +1,4 @@
use rustc_abi::{Align, Size}; use rustc_abi::{Align, FieldIdx, Size};
use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::mir::interpret::{InterpResult, Pointer};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry}; use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry};
@@ -137,8 +137,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
"`unpack_dyn_star` only makes sense on `dyn*` types" "`unpack_dyn_star` only makes sense on `dyn*` types"
); );
let data = self.project_field(val, 0)?; let data = self.project_field(val, FieldIdx::ZERO)?;
let vtable = self.project_field(val, 1)?; let vtable = self.project_field(val, FieldIdx::ONE)?;
let vtable = self.read_pointer(&vtable.to_op(self)?)?; let vtable = self.read_pointer(&vtable.to_op(self)?)?;
let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?;
// `data` is already the right thing but has the wrong type. So we transmute it. // `data` is already the right thing but has the wrong type. So we transmute it.

View File

@@ -112,8 +112,10 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
// So we transmute it to a raw pointer. // So we transmute it to a raw pointer.
let raw_ptr_ty = Ty::new_mut_ptr(*self.ecx().tcx, self.ecx().tcx.types.unit); let raw_ptr_ty = Ty::new_mut_ptr(*self.ecx().tcx, self.ecx().tcx.types.unit);
let raw_ptr_ty = self.ecx().layout_of(raw_ptr_ty)?; let raw_ptr_ty = self.ecx().layout_of(raw_ptr_ty)?;
let vtable_field = let vtable_field = self
self.ecx().project_field(v, 1)?.transmute(raw_ptr_ty, self.ecx())?; .ecx()
.project_field(v, FieldIdx::ONE)?
.transmute(raw_ptr_ty, self.ecx())?;
self.visit_field(v, 1, &vtable_field)?; self.visit_field(v, 1, &vtable_field)?;
// Then unpack the first field, and continue. // Then unpack the first field, and continue.
@@ -140,14 +142,16 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
// `Box` has two fields: the pointer we care about, and the allocator. // `Box` has two fields: the pointer we care about, and the allocator.
assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields");
let (unique_ptr, alloc) = let (unique_ptr, alloc) = (
(self.ecx().project_field(v, 0)?, self.ecx().project_field(v, 1)?); self.ecx().project_field(v, FieldIdx::ZERO)?,
self.ecx().project_field(v, FieldIdx::ONE)?,
);
// Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
// (which means another 2 fields, the second of which is a `PhantomData`) // (which means another 2 fields, the second of which is a `PhantomData`)
assert_eq!(unique_ptr.layout().fields.count(), 2); assert_eq!(unique_ptr.layout().fields.count(), 2);
let (nonnull_ptr, phantom) = ( let (nonnull_ptr, phantom) = (
self.ecx().project_field(&unique_ptr, 0)?, self.ecx().project_field(&unique_ptr, FieldIdx::ZERO)?,
self.ecx().project_field(&unique_ptr, 1)?, self.ecx().project_field(&unique_ptr, FieldIdx::ONE)?,
); );
assert!( assert!(
phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
@@ -156,7 +160,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
); );
// ... that contains a `NonNull`... (gladly, only a single field here) // ... that contains a `NonNull`... (gladly, only a single field here)
assert_eq!(nonnull_ptr.layout().fields.count(), 1); assert_eq!(nonnull_ptr.layout().fields.count(), 1);
let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr let raw_ptr = self.ecx().project_field(&nonnull_ptr, FieldIdx::ZERO)?; // the actual raw ptr
// ... whose only field finally is a raw ptr we can dereference. // ... whose only field finally is a raw ptr we can dereference.
self.visit_box(ty, &raw_ptr)?; self.visit_box(ty, &raw_ptr)?;
@@ -188,9 +192,8 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
} }
FieldsShape::Arbitrary { memory_index, .. } => { FieldsShape::Arbitrary { memory_index, .. } => {
for idx in Self::aggregate_field_iter(memory_index) { for idx in Self::aggregate_field_iter(memory_index) {
let idx = idx.as_usize();
let field = self.ecx().project_field(v, idx)?; let field = self.ecx().project_field(v, idx)?;
self.visit_field(v, idx, &field)?; self.visit_field(v, idx.as_usize(), &field)?;
} }
} }
FieldsShape::Array { .. } => { FieldsShape::Array { .. } => {

View File

@@ -1,3 +1,4 @@
use rustc_abi::FieldIdx;
use rustc_hir::LangItem; use rustc_hir::LangItem;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
@@ -41,11 +42,14 @@ fn alloc_caller_location<'tcx>(
let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
// Initialize fields. // Initialize fields.
ecx.write_immediate(file_wide_ptr, &ecx.project_field(&location, 0).unwrap()) ecx.write_immediate(
file_wide_ptr,
&ecx.project_field(&location, FieldIdx::from_u32(0)).unwrap(),
)
.expect("writing to memory we just allocated cannot fail"); .expect("writing to memory we just allocated cannot fail");
ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap()) ecx.write_scalar(line, &ecx.project_field(&location, FieldIdx::from_u32(1)).unwrap())
.expect("writing to memory we just allocated cannot fail"); .expect("writing to memory we just allocated cannot fail");
ecx.write_scalar(col, &ecx.project_field(&location, 2).unwrap()) ecx.write_scalar(col, &ecx.project_field(&location, FieldIdx::from_u32(2)).unwrap())
.expect("writing to memory we just allocated cannot fail"); .expect("writing to memory we just allocated cannot fail");
location location

View File

@@ -1100,7 +1100,7 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
return; return;
}; };
if let Some(second_field) = fields.get(FieldIdx::from_u32(1)) { if let Some(second_field) = fields.get(FieldIdx::ONE) {
struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot have multiple fields") struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot have multiple fields")
.with_span_label(tcx.def_span(second_field.did), "excess field") .with_span_label(tcx.def_span(second_field.did), "excess field")
.emit(); .emit();

View File

@@ -171,7 +171,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
_ if ty.references_error() => return None, _ if ty.references_error() => return None,
ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => { ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => {
let fields = &adt.non_enum_variant().fields; let fields = &adt.non_enum_variant().fields;
let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx(), args); let ty = fields[FieldIdx::ONE].ty(self.tcx(), args);
// FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`? // FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
// If so, just get it from the args. // If so, just get it from the args.
let ty::Adt(ty, args) = ty.kind() else { let ty::Adt(ty, args) = ty.kind() else {

View File

@@ -616,7 +616,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
place, place,
operand, operand,
&mut |elem, op| match elem { &mut |elem, op| match elem {
TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), TrackElem::Field(idx) => self.ecx.project_field(op, idx).discard_err(),
TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(),
TrackElem::Discriminant => { TrackElem::Discriminant => {
let variant = self.ecx.read_discriminant(op).discard_err()?; let variant = self.ecx.read_discriminant(op).discard_err()?;
@@ -890,7 +890,8 @@ fn try_write_constant<'tcx>(
ty::Tuple(elem_tys) => { ty::Tuple(elem_tys) => {
for (i, elem) in elem_tys.iter().enumerate() { for (i, elem) in elem_tys.iter().enumerate() {
let Some(field) = map.apply(place, TrackElem::Field(FieldIdx::from_usize(i))) else { let i = FieldIdx::from_usize(i);
let Some(field) = map.apply(place, TrackElem::Field(i)) else {
throw_machine_stop_str!("missing field in tuple") throw_machine_stop_str!("missing field in tuple")
}; };
let field_dest = ecx.project_field(dest, i)?; let field_dest = ecx.project_field(dest, i)?;
@@ -928,7 +929,7 @@ fn try_write_constant<'tcx>(
let Some(field) = map.apply(variant_place, TrackElem::Field(i)) else { let Some(field) = map.apply(variant_place, TrackElem::Field(i)) else {
throw_machine_stop_str!("missing field in ADT") throw_machine_stop_str!("missing field in ADT")
}; };
let field_dest = ecx.project_field(&variant_dest, i.as_usize())?; let field_dest = ecx.project_field(&variant_dest, i)?;
try_write_constant(ecx, &field_dest, field, ty, state, map)?; try_write_constant(ecx, &field_dest, field, ty, state, map)?;
} }
ecx.write_discriminant(variant_idx, dest)?; ecx.write_discriminant(variant_idx, dest)?;

View File

@@ -438,8 +438,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
dest.clone() dest.clone()
}; };
for (field_index, op) in fields.into_iter().enumerate() { for (field_index, op) in fields.into_iter().enumerate() {
let field_dest = let field_dest = self
self.ecx.project_field(&variant_dest, field_index).discard_err()?; .ecx
.project_field(&variant_dest, FieldIdx::from_usize(field_index))
.discard_err()?;
self.ecx.copy_op(op, &field_dest).discard_err()?; self.ecx.copy_op(op, &field_dest).discard_err()?;
} }
self.ecx self.ecx
@@ -1583,7 +1585,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// We needed to check the variant to avoid trying to read the tag // We needed to check the variant to avoid trying to read the tag
// field from an enum where no fields have variants, since that tag // field from an enum where no fields have variants, since that tag
// field isn't in the `Aggregate` from which we're getting values. // field isn't in the `Aggregate` from which we're getting values.
Some((FieldIdx::from_usize(field_idx), field_layout.ty)) Some((field_idx, field_layout.ty))
} else if let ty::Adt(adt, args) = ty.kind() } else if let ty::Adt(adt, args) = ty.kind()
&& adt.is_struct() && adt.is_struct()
&& adt.repr().transparent() && adt.repr().transparent()

View File

@@ -388,7 +388,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
lhs, lhs,
constant, constant,
&mut |elem, op| match elem { &mut |elem, op| match elem {
TrackElem::Field(idx) => self.ecx.project_field(op, idx.as_usize()).discard_err(), TrackElem::Field(idx) => self.ecx.project_field(op, idx).discard_err(),
TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(), TrackElem::Variant(idx) => self.ecx.project_downcast(op, idx).discard_err(),
TrackElem::Discriminant => { TrackElem::Discriminant => {
let variant = self.ecx.read_discriminant(op).discard_err()?; let variant = self.ecx.read_discriminant(op).discard_err()?;

View File

@@ -359,8 +359,8 @@ pub fn create_ecx<'tcx>(
let argvs_layout = let argvs_layout =
ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?; ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?;
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?;
for (idx, arg) in argvs.into_iter().enumerate() { for (arg, idx) in argvs.into_iter().zip(0..) {
let place = ecx.project_field(&argvs_place, idx)?; let place = ecx.project_index(&argvs_place, idx)?;
ecx.write_immediate(arg, &place)?; ecx.write_immediate(arg, &place)?;
} }
ecx.mark_immutable(&argvs_place); ecx.mark_immutable(&argvs_place);
@@ -389,8 +389,8 @@ pub fn create_ecx<'tcx>(
ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?; ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?;
ecx.machine.cmd_line = Some(cmd_place.ptr()); ecx.machine.cmd_line = Some(cmd_place.ptr());
// Store the UTF-16 string. We just allocated so we know the bounds are fine. // Store the UTF-16 string. We just allocated so we know the bounds are fine.
for (idx, &c) in cmd_utf16.iter().enumerate() { for (&c, idx) in cmd_utf16.iter().zip(0..) {
let place = ecx.project_field(&cmd_place, idx)?; let place = ecx.project_index(&cmd_place, idx)?;
ecx.write_scalar(Scalar::from_u16(c), &place)?; ecx.write_scalar(Scalar::from_u16(c), &place)?;
} }
ecx.mark_immutable(&cmd_place); ecx.mark_immutable(&cmd_place);

View File

@@ -326,7 +326,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx, Option<P>> { ) -> InterpResult<'tcx, Option<P>> {
let this = self.eval_context_ref(); let this = self.eval_context_ref();
let adt = base.layout().ty.ty_adt_def().unwrap(); let adt = base.layout().ty.ty_adt_def().unwrap();
for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() { for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
if field.name.as_str() == name { if field.name.as_str() == name {
return interp_ok(Some(this.project_field(base, idx)?)); return interp_ok(Some(this.project_field(base, idx)?));
} }
@@ -376,6 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
for (idx, &val) in values.iter().enumerate() { for (idx, &val) in values.iter().enumerate() {
let idx = FieldIdx::from_usize(idx);
let field = this.project_field(dest, idx)?; let field = this.project_field(dest, idx)?;
this.write_int(val, &field)?; this.write_int(val, &field)?;
} }
@@ -763,10 +764,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// `EINVAL` in this case. /// `EINVAL` in this case.
fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> { fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let seconds_place = this.project_field(tp, 0)?; let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
let seconds_scalar = this.read_scalar(&seconds_place)?; let seconds_scalar = this.read_scalar(&seconds_place)?;
let seconds = seconds_scalar.to_target_isize(this)?; let seconds = seconds_scalar.to_target_isize(this)?;
let nanoseconds_place = this.project_field(tp, 1)?; let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?; let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;

View File

@@ -1,4 +1,4 @@
use rustc_abi::{CanonAbi, Size}; use rustc_abi::{CanonAbi, FieldIdx, Size};
use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::{self, Instance, Ty};
use rustc_span::{BytePos, Loc, Symbol, hygiene}; use rustc_span::{BytePos, Loc, Symbol, hygiene};
@@ -159,23 +159,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1 => { 1 => {
this.write_scalar( this.write_scalar(
Scalar::from_target_usize(name.len().to_u64(), this), Scalar::from_target_usize(name.len().to_u64(), this),
&this.project_field(dest, 0)?, &this.project_field(dest, FieldIdx::from_u32(0))?,
)?; )?;
this.write_scalar( this.write_scalar(
Scalar::from_target_usize(filename.len().to_u64(), this), Scalar::from_target_usize(filename.len().to_u64(), this),
&this.project_field(dest, 1)?, &this.project_field(dest, FieldIdx::from_u32(1))?,
)?; )?;
} }
_ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags), _ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags),
} }
this.write_scalar(Scalar::from_u32(lineno), &this.project_field(dest, 2)?)?; this.write_scalar(Scalar::from_u32(lineno), &this.project_field(dest, FieldIdx::from_u32(2))?)?;
this.write_scalar(Scalar::from_u32(colno), &this.project_field(dest, 3)?)?; this.write_scalar(Scalar::from_u32(colno), &this.project_field(dest, FieldIdx::from_u32(3))?)?;
// Support a 4-field struct for now - this is deprecated // Support a 4-field struct for now - this is deprecated
// and slated for removal. // and slated for removal.
if num_fields == 5 { if num_fields == 5 {
this.write_pointer(fn_ptr, &this.project_field(dest, 4)?)?; this.write_pointer(fn_ptr, &this.project_field(dest, FieldIdx::from_u32(4))?)?;
} }
interp_ok(()) interp_ok(())

View File

@@ -2,8 +2,9 @@ use std::ffi::{OsStr, OsString};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::{env, mem}; use std::{env, mem};
use rustc_abi::Size; use rustc_abi::{FieldIdx, Size};
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_index::IndexVec;
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
@@ -118,7 +119,7 @@ fn alloc_env_var<'tcx>(
/// Allocates an `environ` block with the given list of pointers. /// Allocates an `environ` block with the given list of pointers.
fn alloc_environ_block<'tcx>( fn alloc_environ_block<'tcx>(
ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>, ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
mut vars: Vec<Pointer>, mut vars: IndexVec<FieldIdx, Pointer>,
) -> InterpResult<'tcx, Pointer> { ) -> InterpResult<'tcx, Pointer> {
// Add trailing null. // Add trailing null.
vars.push(Pointer::null()); vars.push(Pointer::null());
@@ -129,7 +130,7 @@ fn alloc_environ_block<'tcx>(
u64::try_from(vars.len()).unwrap(), u64::try_from(vars.len()).unwrap(),
))?; ))?;
let vars_place = ecx.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; let vars_place = ecx.allocate(vars_layout, MiriMemoryKind::Runtime.into())?;
for (idx, var) in vars.into_iter().enumerate() { for (idx, var) in vars.into_iter_enumerated() {
let place = ecx.project_field(&vars_place, idx)?; let place = ecx.project_field(&vars_place, idx)?;
ecx.write_pointer(var, &place)?; ecx.write_pointer(var, &place)?;
} }

View File

@@ -2,6 +2,8 @@
use core::time::Duration; use core::time::Duration;
use rustc_abi::FieldIdx;
use crate::concurrency::sync::FutexRef; use crate::concurrency::sync::FutexRef;
use crate::*; use crate::*;
@@ -214,18 +216,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Only flag allowed is UMTX_ABSTIME. // Only flag allowed is UMTX_ABSTIME.
let abs_time = this.eval_libc_u32("UMTX_ABSTIME"); let abs_time = this.eval_libc_u32("UMTX_ABSTIME");
let timespec_place = this.project_field(ut, 0)?; let timespec_place = this.project_field(ut, FieldIdx::from_u32(0))?;
// Inner `timespec` must still be valid. // Inner `timespec` must still be valid.
let duration = match this.read_timespec(&timespec_place)? { let duration = match this.read_timespec(&timespec_place)? {
Some(dur) => dur, Some(dur) => dur,
None => return interp_ok(None), None => return interp_ok(None),
}; };
let flags_place = this.project_field(ut, 1)?; let flags_place = this.project_field(ut, FieldIdx::from_u32(1))?;
let flags = this.read_scalar(&flags_place)?.to_u32()?; let flags = this.read_scalar(&flags_place)?.to_u32()?;
let abs_time_flag = flags == abs_time; let abs_time_flag = flags == abs_time;
let clock_id_place = this.project_field(ut, 2)?; let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?;
let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?; let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?;
let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?; let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?;

View File

@@ -4,6 +4,8 @@ use std::io;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::time::Duration; use std::time::Duration;
use rustc_abi::FieldIdx;
use crate::concurrency::VClock; use crate::concurrency::VClock;
use crate::shims::files::{ use crate::shims::files::{
DynFileDescriptionRef, FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef, DynFileDescriptionRef, FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
@@ -284,8 +286,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if op == epoll_ctl_add || op == epoll_ctl_mod { if op == epoll_ctl_add || op == epoll_ctl_mod {
// Read event bitmask and data from epoll_event passed by caller. // Read event bitmask and data from epoll_event passed by caller.
let mut events = this.read_scalar(&this.project_field(&event, 0)?)?.to_u32()?; let mut events = this.read_scalar(&this.project_field(&event, FieldIdx::ZERO)?)?.to_u32()?;
let data = this.read_scalar(&this.project_field(&event, 1)?)?.to_u64()?; let data = this.read_scalar(&this.project_field(&event, FieldIdx::ONE)?)?.to_u64()?;
// Unset the flag we support to discover if any unsupported flags are used. // Unset the flag we support to discover if any unsupported flags are used.
let mut flags = events; let mut flags = events;

View File

@@ -1,4 +1,4 @@
use rustc_abi::{CanonAbi, Size}; use rustc_abi::{CanonAbi, FieldIdx, Size};
use rustc_apfloat::Float; use rustc_apfloat::Float;
use rustc_apfloat::ieee::Single; use rustc_apfloat::ieee::Single;
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
@@ -54,8 +54,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}; };
let (sum, cb_out) = carrying_add(this, cb_in, a, b, op)?; let (sum, cb_out) = carrying_add(this, cb_in, a, b, op)?;
this.write_scalar(cb_out, &this.project_field(dest, 0)?)?; this.write_scalar(cb_out, &this.project_field(dest, FieldIdx::ZERO)?)?;
this.write_immediate(*sum, &this.project_field(dest, 1)?)?; this.write_immediate(*sum, &this.project_field(dest, FieldIdx::ONE)?)?;
} }
// Used to implement the `_addcarryx_u{32, 64}` functions. They are semantically identical with the `_addcarry_u{32, 64}` functions, // Used to implement the `_addcarryx_u{32, 64}` functions. They are semantically identical with the `_addcarry_u{32, 64}` functions,