Replace OffsetOf by an actual sum.

This commit is contained in:
Camille Gillot 2025-10-25 04:15:43 +00:00
parent 0df64c5784
commit 72444372ae
71 changed files with 475 additions and 646 deletions

View file

@ -1559,7 +1559,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
self.consume_operand(location, (operand2, span), state);
}
Rvalue::NullaryOp(_op, _ty) => {
Rvalue::NullaryOp(_op) => {
// nullary ops take no dynamic input; no borrowck effect.
}

View file

@ -314,7 +314,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
self.consume_operand(location, operand2);
}
Rvalue::NullaryOp(_op, _ty) => {}
Rvalue::NullaryOp(_op) => {}
Rvalue::Aggregate(_, operands) => {
for operand in operands {

View file

@ -1046,7 +1046,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
&Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => {}
&Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => {}
Rvalue::ShallowInitBox(_operand, ty) => {
let trait_ref =
@ -1633,8 +1633,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| Rvalue::BinaryOp(..)
| Rvalue::RawPtr(..)
| Rvalue::ThreadLocalRef(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {}
| Rvalue::Discriminant(..) => {}
}
}

View file

@ -853,31 +853,14 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
fx.bcx.ins().nop();
}
}
Rvalue::NullaryOp(ref null_op, ty) => {
Rvalue::NullaryOp(ref null_op) => {
assert!(lval.layout().ty.is_sized(fx.tcx, fx.typing_env()));
let layout = fx.layout_of(fx.monomorphize(ty));
let val = match null_op {
NullOp::OffsetOf(fields) => fx
.tcx
.offset_of_subfield(
ty::TypingEnv::fully_monomorphized(),
layout,
fields.iter(),
)
.bytes(),
NullOp::RuntimeChecks(kind) => {
let val = kind.value(fx.tcx.sess);
let val = CValue::by_val(
fx.bcx.ins().iconst(types::I8, i64::from(val)),
fx.layout_of(fx.tcx.types.bool),
);
lval.write_cvalue(fx, val);
return;
}
NullOp::RuntimeChecks(kind) => kind.value(fx.tcx.sess),
};
let val = CValue::by_val(
fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(val).unwrap()),
fx.layout_of(fx.tcx.types.usize),
fx.bcx.ins().iconst(types::I8, i64::from(val)),
fx.layout_of(fx.tcx.types.bool),
);
lval.write_cvalue(fx, val);
}

View file

@ -613,17 +613,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
mir::Rvalue::NullaryOp(ref null_op, ty) => {
let ty = self.monomorphize(ty);
let layout = bx.cx().layout_of(ty);
mir::Rvalue::NullaryOp(ref null_op) => {
let val = match null_op {
mir::NullOp::OffsetOf(fields) => {
let val = bx
.tcx()
.offset_of_subfield(bx.typing_env(), layout, fields.iter())
.bytes();
bx.cx().const_usize(val)
}
mir::NullOp::RuntimeChecks(kind) => {
let val = kind.value(bx.tcx().sess);
bx.cx().const_bool(val)

View file

@ -645,7 +645,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
Rvalue::Cast(_, _, _) => {}
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) => {}
Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => {}
Rvalue::ShallowInitBox(_, _) => {}
Rvalue::UnaryOp(op, operand) => {
@ -853,7 +853,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
// Intrinsics are language primitives, not regular calls, so treat them separately.
if let Some(intrinsic) = tcx.intrinsic(callee) {
if let Some(intrinsic) = tcx.intrinsic(callee)
&& intrinsic.name != sym::offset_of
{
if !tcx.is_const_fn(callee) {
// Non-const intrinsic.
self.check_op(ops::IntrinsicNonConst { name: intrinsic.name });

View file

@ -6,7 +6,7 @@ mod simd;
use std::assert_matches::assert_matches;
use rustc_abi::{FieldIdx, HasDataLayout, Size};
use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx};
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
@ -203,6 +203,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let val = layout.align.bytes();
self.write_scalar(Scalar::from_target_usize(val, self), dest)?;
}
sym::offset_of => {
let tp_ty = instance.args.type_at(0);
let u32_layout = self.layout_of(self.tcx.types.u32)?;
let variant = self.read_scalar(&args[0])?.to_bits(u32_layout.size)? as u32;
let field = self.read_scalar(&args[1])?.to_bits(u32_layout.size)? as usize;
let layout = self.layout_of(tp_ty)?;
let cx = ty::layout::LayoutCx::new(*self.tcx, self.typing_env);
let layout = layout.for_variant(&cx, VariantIdx::from_u32(variant));
let offset = layout.fields.offset(field).bytes();
self.write_scalar(Scalar::from_target_usize(offset, self), dest)?;
}
sym::variant_count => {
let tp_ty = instance.args.type_at(0);
let ty = match tp_ty.kind() {

View file

@ -4,7 +4,7 @@ use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir::NullOp;
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
use rustc_middle::ty::{self, FloatTy, ScalarInt};
use rustc_middle::{bug, mir, span_bug};
use rustc_span::sym;
use tracing::trace;
@ -506,22 +506,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
}
pub fn nullary_op(
&self,
null_op: NullOp<'tcx>,
arg_ty: Ty<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
pub fn nullary_op(&self, null_op: NullOp) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
use rustc_middle::mir::NullOp::*;
let layout = self.layout_of(arg_ty)?;
let usize_layout = || self.layout_of(self.tcx.types.usize).unwrap();
interp_ok(match null_op {
OffsetOf(fields) => {
let val =
self.tcx.offset_of_subfield(self.typing_env, layout, fields.iter()).bytes();
ImmTy::from_uint(val, usize_layout())
}
RuntimeChecks(r) => ImmTy::from_bool(M::runtime_checks(self, r)?, *self.tcx),
})
}

View file

@ -203,9 +203,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_immediate(*result, &dest)?;
}
NullaryOp(null_op, ty) => {
let ty = self.instantiate_from_current_frame_and_normalize_erasing_regions(ty)?;
let val = self.nullary_op(null_op, ty)?;
NullaryOp(null_op) => {
let val = self.nullary_op(null_op)?;
self.write_immediate(*val, &dest)?;
}

View file

@ -170,6 +170,7 @@ language_item_table! {
Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
AlignOf, sym::mem_align_const, align_const, Target::AssocConst, GenericRequirement::Exact(0);
SizeOf, sym::mem_size_const, size_const, Target::AssocConst, GenericRequirement::Exact(0);
OffsetOf, sym::offset_of, offset_of, Target::Fn, GenericRequirement::Exact(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);

View file

@ -163,6 +163,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::minnumf128
| sym::mul_with_overflow
| sym::needs_drop
| sym::offset_of
| sym::overflow_checks
| sym::powf16
| sym::powf32
@ -288,6 +289,7 @@ pub(crate) fn check_intrinsic_type(
sym::size_of_val | sym::align_of_val => {
(1, 0, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
}
sym::offset_of => (1, 0, vec![tcx.types.u32, tcx.types.u32], tcx.types.usize),
sym::rustc_peek => (1, 0, vec![param(0)], param(0)),
sym::caller_location => (0, 0, vec![], tcx.caller_location_ty()),
sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => {

View file

@ -3884,10 +3884,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fields: &[Ident],
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
let container = self.lower_ty(container).normalized;
let mut current_container = self.lower_ty(container).normalized;
let mut field_indices = Vec::with_capacity(fields.len());
let mut current_container = container;
let mut fields = fields.into_iter();
while let Some(&field) = fields.next() {
@ -3975,7 +3973,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Save the index of all fields regardless of their visibility in case
// of error recovery.
field_indices.push((index, subindex));
field_indices.push((current_container, index, subindex));
current_container = field_ty;
continue;
@ -4010,7 +4008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Save the index of all fields regardless of their visibility in case
// of error recovery.
field_indices.push((FIRST_VARIANT, index));
field_indices.push((current_container, FIRST_VARIANT, index));
current_container = field_ty;
continue;
@ -4031,7 +4029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
field_indices.push((FIRST_VARIANT, index.into()));
field_indices.push((current_container, FIRST_VARIANT, index.into()));
current_container = field_ty;
continue;
@ -4046,10 +4044,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
break;
}
self.typeck_results
.borrow_mut()
.offset_of_data_mut()
.insert(expr.hir_id, (container, field_indices));
self.typeck_results.borrow_mut().offset_of_data_mut().insert(expr.hir_id, field_indices);
self.tcx.types.usize
}

View file

@ -770,12 +770,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
for (local_id, &(container, ref indices)) in
fcx_typeck_results.offset_of_data().items_in_stable_order()
{
for (local_id, indices) in fcx_typeck_results.offset_of_data().items_in_stable_order() {
let hir_id = HirId { owner: common_hir_owner, local_id };
let container = self.resolve(container, &hir_id);
self.typeck_results.offset_of_data_mut().insert(hir_id, (container, indices.clone()));
let indices = indices
.iter()
.map(|&(ty, variant, field)| (self.resolve(ty, &hir_id), variant, field))
.collect();
self.typeck_results.offset_of_data_mut().insert(hir_id, indices);
}
}

View file

@ -179,6 +179,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
hir::ExprKind::AddrOf(..) => Some("borrow"),
hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
hir::ExprKind::Unary(..) => Some("unary operation"),
// The `offset_of` macro wraps its contents inside a `const` block.
hir::ExprKind::ConstBlock(block) => {
let body = cx.tcx.hir_body(block.body);
if let hir::ExprKind::Block(block, _) = body.value.kind
&& let Some(expr) = block.expr
&& let hir::ExprKind::OffsetOf(..) = expr.kind
{
Some("`offset_of` call")
} else {
None
}
}
_ => None,
};

View file

@ -649,7 +649,7 @@ impl<'tcx> Body<'tcx> {
}
match rvalue {
Rvalue::NullaryOp(NullOp::RuntimeChecks(kind), _) => {
Rvalue::NullaryOp(NullOp::RuntimeChecks(kind)) => {
Some((kind.value(tcx.sess) as u128, targets))
}
Rvalue::Use(Operand::Constant(constant)) => {

View file

@ -1097,19 +1097,15 @@ impl<'tcx> Debug for Rvalue<'tcx> {
BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
NullaryOp(ref op, ref t) => {
let t = with_no_trimmed_paths!(format!("{}", t));
match op {
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
NullOp::RuntimeChecks(RuntimeChecks::UbChecks) => write!(fmt, "UbChecks()"),
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
write!(fmt, "ContractChecks()")
}
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
write!(fmt, "OverflowChecks()")
}
NullaryOp(ref op) => match op {
NullOp::RuntimeChecks(RuntimeChecks::UbChecks) => write!(fmt, "UbChecks()"),
NullOp::RuntimeChecks(RuntimeChecks::ContractChecks) => {
write!(fmt, "ContractChecks()")
}
}
NullOp::RuntimeChecks(RuntimeChecks::OverflowChecks) => {
write!(fmt, "OverflowChecks()")
}
},
ThreadLocalRef(did) => ty::tls::with(|tcx| {
let muta = tcx.static_mutability(did).unwrap().prefix_str();
write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))

View file

@ -756,7 +756,7 @@ impl<'tcx> Rvalue<'tcx> {
_,
)
| Rvalue::BinaryOp(_, _)
| Rvalue::NullaryOp(_, _)
| Rvalue::NullaryOp(_)
| Rvalue::UnaryOp(_, _)
| Rvalue::Discriminant(_)
| Rvalue::Aggregate(_, _)
@ -794,8 +794,7 @@ impl<'tcx> Rvalue<'tcx> {
op.ty(tcx, arg_ty)
}
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => tcx.types.usize,
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => tcx.types.bool,
Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => tcx.types.bool,
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
AggregateKind::Tuple => {
@ -859,10 +858,9 @@ impl BorrowKind {
}
}
impl<'tcx> NullOp<'tcx> {
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
impl NullOp {
pub fn ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
NullOp::OffsetOf(_) => tcx.types.usize,
NullOp::RuntimeChecks(_) => tcx.types.bool,
}
}

View file

@ -1419,7 +1419,7 @@ pub enum Rvalue<'tcx> {
BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>),
/// Computes a value as described by the operation.
NullaryOp(NullOp<'tcx>, Ty<'tcx>),
NullaryOp(NullOp),
/// Exactly like `BinaryOp`, but less operands.
///
@ -1562,9 +1562,7 @@ pub enum AggregateKind<'tcx> {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum NullOp<'tcx> {
/// Returns the offset of a field
OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
pub enum NullOp {
/// Returns whether we should perform some checking at runtime.
RuntimeChecks(RuntimeChecks),
}

View file

@ -775,9 +775,7 @@ macro_rules! make_mir_visitor {
);
}
Rvalue::NullaryOp(_op, ty) => {
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
Rvalue::NullaryOp(_op) => {}
Rvalue::Aggregate(kind, operands) => {
let kind = &$($mutability)? **kind;

View file

@ -32,7 +32,7 @@ use crate::thir::visit::for_each_immediate_subpat;
use crate::ty::adjustment::PointerCoercion;
use crate::ty::layout::IntegerExt;
use crate::ty::{
self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, List, Ty,
self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, Ty,
TyCtxt, UpvarArgs,
};
@ -550,11 +550,6 @@ pub enum ExprKind<'tcx> {
},
/// Inline assembly, i.e. `asm!()`.
InlineAsm(Box<InlineAsmExpr<'tcx>>),
/// Field offset (`offset_of!`)
OffsetOf {
container: Ty<'tcx>,
fields: &'tcx List<(VariantIdx, FieldIdx)>,
},
/// An expression taking a reference to a thread local.
ThreadLocalRef(DefId),
/// A `yield` expression.

View file

@ -188,7 +188,6 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
}
}
}
OffsetOf { container: _, fields: _ } => {}
ThreadLocalRef(_) => {}
Yield { value } => visitor.visit_expr(&visitor.thir()[value]),
}

View file

@ -10,7 +10,7 @@ use std::hash::Hash;
use std::intrinsics;
use std::marker::{DiscriminantKind, PointeeSized};
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
use rustc_serialize::{Decodable, Encodable};
@ -489,15 +489,6 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for &'tcx ty::List<LocalDefId> {
}
}
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<(VariantIdx, FieldIdx)> {
fn decode(decoder: &mut D) -> &'tcx Self {
let len = decoder.read_usize();
decoder.interner().mk_offset_of_from_iter(
(0..len).map::<(VariantIdx, FieldIdx), _>(|_| Decodable::decode(decoder)),
)
}
}
impl_decodable_via_ref! {
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,

View file

@ -954,7 +954,6 @@ pub struct CtxtInterners<'tcx> {
fields: InternedSet<'tcx, List<FieldIdx>>,
local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>,
offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>,
valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>,
patterns: InternedSet<'tcx, List<ty::Pattern<'tcx>>>,
outlives: InternedSet<'tcx, List<ty::ArgOutlivesPredicate<'tcx>>>,
@ -992,7 +991,6 @@ impl<'tcx> CtxtInterners<'tcx> {
fields: InternedSet::with_capacity(N * 4),
local_def_ids: InternedSet::with_capacity(N),
captures: InternedSet::with_capacity(N),
offset_of: InternedSet::with_capacity(N),
valtree: InternedSet::with_capacity(N),
patterns: InternedSet::with_capacity(N),
outlives: InternedSet::with_capacity(N),
@ -2831,7 +2829,6 @@ slice_interners!(
fields: pub mk_fields(FieldIdx),
local_def_ids: intern_local_def_ids(LocalDefId),
captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
offset_of: pub mk_offset_of((VariantIdx, FieldIdx)),
patterns: pub mk_patterns(Pattern<'tcx>),
outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>),
predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)),
@ -3234,14 +3231,6 @@ impl<'tcx> TyCtxt<'tcx> {
T::collect_and_apply(iter, |xs| self.mk_fields(xs))
}
pub fn mk_offset_of_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<(VariantIdx, FieldIdx), &'tcx List<(VariantIdx, FieldIdx)>>,
{
T::collect_and_apply(iter, |xs| self.mk_offset_of(xs))
}
pub fn mk_args_trait(
self,
self_ty: Ty<'tcx>,

View file

@ -1392,37 +1392,3 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
}
impl<'tcx, C: FnAbiOfHelpers<'tcx>> FnAbiOf<'tcx> for C {}
impl<'tcx> TyCtxt<'tcx> {
pub fn offset_of_subfield<I>(
self,
typing_env: ty::TypingEnv<'tcx>,
mut layout: TyAndLayout<'tcx>,
indices: I,
) -> Size
where
I: Iterator<Item = (VariantIdx, FieldIdx)>,
{
let cx = LayoutCx::new(self, typing_env);
let mut offset = Size::ZERO;
for (variant, field) in indices {
layout = layout.for_variant(&cx, variant);
let index = field.index();
offset += layout.fields.offset(index);
layout = layout.field(&cx, index);
if !layout.is_sized() {
// If it is not sized, then the tail must still have at least a known static alignment.
let tail = self.struct_tail_for_codegen(layout.ty, typing_env);
if !matches!(tail.kind(), ty::Slice(..)) {
bug!(
"offset of not-statically-aligned field (type {:?}) cannot be computed statically",
layout.ty
);
}
}
}
offset
}
}

View file

@ -253,7 +253,7 @@ TrivialTypeTraversalImpls! {
crate::mir::FakeReadCause,
crate::mir::Local,
crate::mir::MirPhase,
crate::mir::NullOp<'tcx>,
crate::mir::NullOp,
crate::mir::Promoted,
crate::mir::RawPtrKind,
crate::mir::RetagKind,

View file

@ -221,7 +221,7 @@ pub struct TypeckResults<'tcx> {
pub transmutes_to_check: Vec<(Ty<'tcx>, Ty<'tcx>, HirId)>,
/// Container types and field indices of `offset_of!` expressions
offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>,
offset_of_data: ItemLocalMap<Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>>,
}
impl<'tcx> TypeckResults<'tcx> {
@ -553,15 +553,13 @@ impl<'tcx> TypeckResults<'tcx> {
&self.coercion_casts
}
pub fn offset_of_data(
&self,
) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> {
pub fn offset_of_data(&self) -> LocalTableInContext<'_, Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>> {
LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data }
}
pub fn offset_of_data_mut(
&mut self,
) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)> {
) -> LocalTableInContextMut<'_, Vec<(Ty<'tcx>, VariantIdx, FieldIdx)>> {
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data }
}
}

View file

@ -580,7 +580,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ConstBlock { .. }
| ExprKind::StaticRef { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::Yield { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::Call { .. }

View file

@ -411,10 +411,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}))))
}
ExprKind::OffsetOf { container, fields } => {
block.and(Rvalue::NullaryOp(NullOp::OffsetOf(fields), container))
}
ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }

View file

@ -71,7 +71,6 @@ impl Category {
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::OffsetOf { .. }
| ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::ConstBlock { .. }

View file

@ -784,7 +784,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ConstParam { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
// should be handled above

View file

@ -477,7 +477,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Box { .. }
| ExprKind::If { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::Use { .. } => {
// We don't need to save the old value and restore it

View file

@ -1,11 +1,11 @@
use itertools::Itertools;
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_abi::{FIRST_VARIANT, FieldIdx, Size, VariantIdx};
use rustc_ast::UnsafeBinderCastKind;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::find_attr;
use rustc_hir::{LangItem, find_attr};
use rustc_index::Idx;
use rustc_middle::hir::place::{
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
@ -336,6 +336,8 @@ impl<'tcx> ThirBuildCx<'tcx> {
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
let tcx = self.tcx;
let expr_ty = self.typeck_results.expr_ty(expr);
let mk_expr =
|kind, ty| Expr { temp_scope_id: expr.hir_id.local_id, span: expr.span, ty, kind };
let kind = match expr.kind {
// Here comes the interesting stuff:
@ -813,11 +815,48 @@ impl<'tcx> ThirBuildCx<'tcx> {
})),
hir::ExprKind::OffsetOf(_, _) => {
let data = self.typeck_results.offset_of_data();
let &(container, ref indices) = data.get(expr.hir_id).unwrap();
let fields = tcx.mk_offset_of_from_iter(indices.iter().copied());
let offset_of_intrinsic = tcx.require_lang_item(LangItem::OffsetOf, expr.span);
let mk_u32_kind = |val: u32| ExprKind::NonHirLiteral {
lit: ScalarInt::try_from_uint(val, Size::from_bits(32)).unwrap(),
user_ty: None,
};
let mk_call =
|thir: &mut Thir<'tcx>, ty: Ty<'tcx>, variant: VariantIdx, field: FieldIdx| {
let fun_ty =
Ty::new_fn_def(tcx, offset_of_intrinsic, [ty::GenericArg::from(ty)]);
let fun = thir
.exprs
.push(mk_expr(ExprKind::ZstLiteral { user_ty: None }, fun_ty));
let variant =
thir.exprs.push(mk_expr(mk_u32_kind(variant.as_u32()), tcx.types.u32));
let field =
thir.exprs.push(mk_expr(mk_u32_kind(field.as_u32()), tcx.types.u32));
let args = Box::new([variant, field]);
ExprKind::Call {
ty: fun_ty,
fun,
args,
from_hir_call: false,
fn_span: expr.span,
}
};
ExprKind::OffsetOf { container, fields }
let indices = self.typeck_results.offset_of_data().get(expr.hir_id).unwrap();
let mut expr = None::<ExprKind<'tcx>>;
for &(container, variant, field) in indices.iter() {
let next = mk_call(&mut self.thir, container, variant, field);
expr = Some(match expr.take() {
None => next,
Some(last) => {
let last = self.thir.exprs.push(mk_expr(last, tcx.types.usize));
let next = self.thir.exprs.push(mk_expr(next, tcx.types.usize));
ExprKind::Binary { op: BinOp::Add, lhs: last, rhs: next }
}
});
}
expr.unwrap_or(mk_u32_kind(0))
}
hir::ExprKind::ConstBlock(ref anon_const) => {
@ -1081,7 +1120,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"),
};
Expr { temp_scope_id: expr.hir_id.local_id, ty: expr_ty, span: expr.span, kind }
mk_expr(kind, expr_ty)
}
fn user_args_applied_to_res(

View file

@ -367,7 +367,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
| Match { .. }
| NamedConst { .. }
| NonHirLiteral { .. }
| OffsetOf { .. }
| Repeat { .. }
| StaticRef { .. }
| ThreadLocalRef { .. }

View file

@ -575,19 +575,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_inline_asm_expr(&**expr, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
OffsetOf { container, fields } => {
print_indented!(self, "OffsetOf {", depth_lvl);
print_indented!(self, format!("container: {:?}", container), depth_lvl + 1);
print_indented!(self, "fields: [", depth_lvl + 1);
for field in fields.iter() {
print_indented!(self, format!("{:?}", field), depth_lvl + 2);
print_indented!(self, ",", depth_lvl + 1);
}
print_indented!(self, "]", depth_lvl + 1);
print_indented!(self, "}", depth_lvl);
}
ThreadLocalRef(def_id) => {
print_indented!(self, "ThreadLocalRef {", depth_lvl);
print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);

View file

@ -451,7 +451,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
Rvalue::Ref(..)
| Rvalue::RawPtr(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(NullOp::OffsetOf(..) | NullOp::RuntimeChecks(_), _) => {}
| Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => {}
}
}

View file

@ -463,20 +463,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
FlatSet::Top => FlatSet::Top,
}
}
Rvalue::NullaryOp(null_op, ty) => {
let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
return ValueOrPlace::Value(FlatSet::Top);
};
let val = match null_op {
NullOp::OffsetOf(fields) => self
.ecx
.borrow()
.tcx
.offset_of_subfield(self.typing_env, layout, fields.iter())
.bytes(),
_ => return ValueOrPlace::Value(FlatSet::Top),
};
FlatSet::Elem(Scalar::from_target_usize(val, &self.tcx))
Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => {
return ValueOrPlace::TOP;
}
Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map),
Rvalue::Use(operand) => return self.handle_operand(operand, state),

View file

@ -248,7 +248,7 @@ enum Value<'a, 'tcx> {
Discriminant(VnIndex),
// Operations.
NullaryOp(NullOp<'tcx>, Ty<'tcx>),
NullaryOp(NullOp),
UnaryOp(UnOp, VnIndex),
BinaryOp(BinOp, VnIndex, VnIndex),
Cast {
@ -668,17 +668,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?;
discr_value.into()
}
NullaryOp(null_op, arg_ty) => {
let arg_layout = self.ecx.layout_of(arg_ty).ok()?;
let val = match null_op {
NullOp::OffsetOf(fields) => self
.tcx
.offset_of_subfield(self.typing_env(), arg_layout, fields.iter())
.bytes(),
NullOp::RuntimeChecks(_) => return None,
};
ImmTy::from_uint(val, ty).into()
}
NullaryOp(NullOp::RuntimeChecks(_)) => return None,
UnaryOp(un_op, operand) => {
let operand = self.eval_to_const(operand)?;
let operand = self.ecx.read_immediate(operand).discard_err()?;
@ -1031,7 +1021,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
let op = self.simplify_operand(op, location)?;
Value::Repeat(op, amount)
}
Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty),
Rvalue::NullaryOp(op) => Value::NullaryOp(op),
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
Rvalue::Ref(_, borrow_kind, ref mut place) => {
self.simplify_place_projection(place, location);

View file

@ -170,7 +170,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
fn simplify_ub_check(&self, rvalue: &mut Rvalue<'tcx>) {
// FIXME: Should we do the same for overflow checks?
let Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks), _) = *rvalue else {
let Rvalue::NullaryOp(NullOp::RuntimeChecks(RuntimeChecks::UbChecks)) = *rvalue else {
return;
};

View file

@ -605,17 +605,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
Ref(..) | RawPtr(..) => return None,
NullaryOp(ref null_op, ty) => {
let op_layout = self.ecx.layout_of(ty).ok()?;
let val = match null_op {
NullOp::OffsetOf(fields) => self
.tcx
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
.bytes(),
NullOp::RuntimeChecks(_) => return None,
};
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
}
NullaryOp(NullOp::RuntimeChecks(_)) => return None,
ShallowInitBox(..) => return None,

View file

@ -35,7 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
terminator.source_info,
StatementKind::Assign(Box::new((
*destination,
Rvalue::NullaryOp(NullOp::RuntimeChecks(op), tcx.types.bool),
Rvalue::NullaryOp(NullOp::RuntimeChecks(op)),
))),
));
terminator.kind = TerminatorKind::Goto { target };

View file

@ -449,8 +449,7 @@ impl<'tcx> Validator<'_, 'tcx> {
self.validate_operand(operand)?;
}
Rvalue::NullaryOp(op, _) => match op {
NullOp::OffsetOf(_) => {}
Rvalue::NullaryOp(op) => match op {
NullOp::RuntimeChecks(_) => {}
},

View file

@ -1436,53 +1436,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
}
Rvalue::NullaryOp(NullOp::OffsetOf(indices), container) => {
let fail_out_of_bounds = |this: &mut Self, location, field, ty| {
this.fail(location, format!("Out of bounds field {field:?} for {ty}"));
};
let mut current_ty = *container;
for (variant, field) in indices.iter() {
match current_ty.kind() {
ty::Tuple(fields) => {
if variant != FIRST_VARIANT {
self.fail(
location,
format!("tried to get variant {variant:?} of tuple"),
);
return;
}
let Some(&f_ty) = fields.get(field.as_usize()) else {
fail_out_of_bounds(self, location, field, current_ty);
return;
};
current_ty = self.tcx.normalize_erasing_regions(self.typing_env, f_ty);
}
ty::Adt(adt_def, args) => {
let Some(field) = adt_def.variant(variant).fields.get(field) else {
fail_out_of_bounds(self, location, field, current_ty);
return;
};
let f_ty = field.ty(self.tcx, args);
current_ty = self.tcx.normalize_erasing_regions(self.typing_env, f_ty);
}
_ => {
self.fail(
location,
format!("Cannot get offset ({variant:?}, {field:?}) from type {current_ty}"),
);
return;
}
}
}
}
Rvalue::Repeat(_, _)
| Rvalue::ThreadLocalRef(_)
| Rvalue::RawPtr(_, _)
| Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _)
| Rvalue::NullaryOp(NullOp::RuntimeChecks(_))
| Rvalue::Discriminant(_) => {}
Rvalue::WrapUnsafeBinder(op, ty) => {

View file

@ -298,31 +298,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
}
fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
let data = self.typeck_results().offset_of_data();
let &(container, ref indices) =
data.get(expr.hir_id).expect("no offset_of_data for offset_of");
let indices = self
.typeck_results()
.offset_of_data()
.get(expr.hir_id)
.expect("no offset_of_data for offset_of");
let body_did = self.typeck_results().hir_owner.to_def_id();
let typing_env = ty::TypingEnv::non_body_analysis(self.tcx, body_did);
let mut current_ty = container;
for &(variant, field) in indices {
for &(current_ty, variant, field) in indices {
match current_ty.kind() {
ty::Adt(def, args) => {
ty::Adt(def, _) => {
let field = &def.variant(variant).fields[field];
self.insert_def_id(field.did);
let field_ty = field.ty(self.tcx, args);
current_ty = self.tcx.normalize_erasing_regions(typing_env, field_ty);
}
// we don't need to mark tuple fields as live,
// but we may need to mark subfields
ty::Tuple(tys) => {
current_ty =
self.tcx.normalize_erasing_regions(typing_env, tys[field.as_usize()]);
}
ty::Tuple(_) => {}
_ => span_bug!(expr.span, "named field access on non-ADT"),
}
}

View file

@ -588,7 +588,7 @@ pub enum Rvalue {
ThreadLocalRef(crate::CrateItem),
/// Computes a value as described by the operation.
NullaryOp(NullOp, Ty),
NullaryOp(NullOp),
/// Exactly like `BinaryOp`, but less operands.
///
@ -641,8 +641,7 @@ impl Rvalue {
.discriminant_ty()
.ok_or_else(|| error!("Expected a `RigidTy` but found: {place_ty:?}"))
}
Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => Ok(Ty::usize_ty()),
Rvalue::NullaryOp(NullOp::RuntimeChecks(_), _) => Ok(Ty::bool_ty()),
Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) => Ok(Ty::bool_ty()),
Rvalue::Aggregate(ak, ops) => match *ak {
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
AggregateKind::Tuple => Ok(Ty::new_tuple(
@ -1021,8 +1020,6 @@ pub enum CastKind {
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize)]
pub enum NullOp {
/// Returns the offset of a field.
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
/// Codegen conditions for runtime checks.
RuntimeChecks(RuntimeChecks),
}

View file

@ -386,8 +386,8 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> {
Rvalue::ThreadLocalRef(item) => {
write!(writer, "thread_local_ref{item:?}")
}
Rvalue::NullaryOp(nul, ty) => {
write!(writer, "{nul:?}::<{ty}>() \" \"")
Rvalue::NullaryOp(nul) => {
write!(writer, "{nul:?}() \" \"")
}
Rvalue::UnaryOp(un, op) => {
write!(writer, "{:?}({})", un, pretty_operand(op))

View file

@ -282,9 +282,7 @@ macro_rules! make_mir_visitor {
self.visit_operand(op, location)
}
Rvalue::ThreadLocalRef(_) => {}
Rvalue::NullaryOp(_, ty) => {
self.visit_ty(ty, location);
}
Rvalue::NullaryOp(_) => {}
Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => {
self.visit_operand(op, location);
}

View file

@ -232,9 +232,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
)
}
}
NullaryOp(null_op, ty) => {
crate::mir::Rvalue::NullaryOp(null_op.stable(tables, cx), ty.stable(tables, cx))
}
NullaryOp(null_op) => crate::mir::Rvalue::NullaryOp(null_op.stable(tables, cx)),
UnaryOp(un_op, op) => {
crate::mir::Rvalue::UnaryOp(un_op.stable(tables, cx), op.stable(tables, cx))
}
@ -314,19 +312,16 @@ impl<'tcx> Stable<'tcx> for mir::FakeBorrowKind {
}
}
impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
impl<'tcx> Stable<'tcx> for mir::NullOp {
type T = crate::mir::NullOp;
fn stable<'cx>(
&self,
tables: &mut Tables<'cx, BridgeTys>,
cx: &CompilerCtxt<'cx, BridgeTys>,
_: &mut Tables<'cx, BridgeTys>,
_: &CompilerCtxt<'cx, BridgeTys>,
) -> Self::T {
use rustc_middle::mir::NullOp::*;
use rustc_middle::mir::RuntimeChecks::*;
match self {
OffsetOf(indices) => crate::mir::NullOp::OffsetOf(
indices.iter().map(|idx| idx.stable(tables, cx)).collect(),
),
RuntimeChecks(op) => crate::mir::NullOp::RuntimeChecks(match op {
UbChecks => crate::mir::RuntimeChecks::UbChecks,
ContractChecks => crate::mir::RuntimeChecks::ContractChecks,

View file

@ -264,7 +264,6 @@ fn recurse_build<'tcx>(
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. }
| ExprKind::ThreadLocalRef(_) => {
error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
}
@ -364,7 +363,6 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::ZstLiteral { .. }
| thir::ExprKind::StaticRef { .. }
| thir::ExprKind::InlineAsm(_)
| thir::ExprKind::OffsetOf { .. }
| thir::ExprKind::ThreadLocalRef(_)
| thir::ExprKind::Yield { .. } => false,
}

View file

@ -2791,6 +2791,23 @@ pub const fn size_of<T>() -> usize;
#[rustc_intrinsic]
pub const fn align_of<T>() -> usize;
/// The offset of a field inside a type.
///
/// Note that, unlike most intrinsics, this is safe to call;
/// it does not require an `unsafe` block.
/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is [`core::mem::offset_of`].
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_stable_indirect]
#[rustc_intrinsic_const_stable_indirect]
#[rustc_intrinsic]
#[lang = "offset_of"]
pub const fn offset_of<T: PointeeSized>(variant: u32, field: u32) -> usize;
/// Returns the number of variants of the type `T` cast to a `usize`;
/// if `T` has no variants, returns `0`. Uninhabited variants will be counted.
///

View file

@ -1424,10 +1424,10 @@ impl<T> SizedTypeProperties for T {}
/// [`offset_of_enum`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-enum.html
/// [`offset_of_slice`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-slice.html
#[stable(feature = "offset_of", since = "1.77.0")]
#[allow_internal_unstable(builtin_syntax)]
#[allow_internal_unstable(builtin_syntax, core_intrinsics)]
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
// The `{}` is for better error messages
{builtin # offset_of($Container, $($fields)+)}
const {builtin # offset_of($Container, $($fields)+)}
}
/// Create a fresh instance of the inhabited ZST type `T`.

View file

@ -194,7 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::RuntimeChecks(_)) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, cx.tcx);
if ty.is_integral() || ty.is_bool() {

View file

@ -1,5 +0,0 @@
//@ known-bug: #123959
#![feature(generic_const_exprs)]
fn foo<T>(_: [(); std::mem::offset_of!((T,), 0)]) {}
pub fn main() {}

View file

@ -1,15 +0,0 @@
//@ known-bug: rust-lang/rust#125680
//@ edition:2021
#![feature(generic_const_exprs)]
use core::fmt::Debug;
struct Inline<T>
where
[(); std::mem::offset_of!((T,), 0)]:,
{}
fn main() {
let dst = Inline::<dyn Debug>::new(0); // BANG!
}

View file

@ -1,6 +0,0 @@
//@ known-bug: rust-lang/rust#129425
//@compile-flags: --crate-type=lib
#![feature(generic_const_exprs)]
fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}

View file

@ -1,13 +0,0 @@
//@ known-bug: #136175
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
trait Trait {}
struct A<T>(T)
where
[(); std::mem::offset_of!((T,), 0)]:;
fn main() {
let x: A<dyn Trait>;
}

View file

@ -0,0 +1,115 @@
//@ compile-flags: -Zmir-opt-level=0
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
#![feature(offset_of_enum)]
use std::marker::PhantomData;
use std::mem::offset_of;
struct Alpha {
x: u8,
y: u16,
z: Beta,
}
struct Beta(u8, u8);
struct Gamma<T> {
x: u8,
y: u16,
_t: T,
}
#[repr(C)]
struct Delta<T> {
_phantom: PhantomData<T>,
x: u8,
y: u16,
}
enum Blah {
A,
B { x: u8, y: usize },
}
// CHECK-LABEL: fn concrete(
fn concrete() {
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: debug h => [[h:_.*]];
// CHECK: debug z0 => [[z0:_.*]];
// CHECK: debug z1 => [[z1:_.*]];
// CHECK: [[x]] = const concrete::[[const_x:.*]];
let x = offset_of!(Alpha, x);
// CHECK: [[y]] = const concrete::[[const_y:.*]];
let y = offset_of!(Alpha, y);
// CHECK: [[h]] = const concrete::[[const_h:.*]];
let h = offset_of!(Blah, B.y);
// CHECK: [[z0]] = const concrete::[[const_z0:.*]];
let z0 = offset_of!(Alpha, z.0);
// CHECK: [[z1]] = const concrete::[[const_z1:.*]];
let z1 = offset_of!(Alpha, z.1);
}
// CHECK: concrete::[[const_x]]: usize
// CHECK: _0 = offset_of::<Alpha>(const 0_u32, const 0_u32)
// CHECK: concrete::[[const_y]]: usize
// CHECK: _0 = offset_of::<Alpha>(const 0_u32, const 1_u32)
// CHECK: concrete::[[const_h]]: usize
// CHECK: _0 = offset_of::<Blah>(const 1_u32, const 1_u32)
// CHECK: concrete::[[const_z0]]: usize
// CHECK: [[z:_.*]] = offset_of::<Alpha>(const 0_u32, const 2_u32)
// CHECK: [[z0:_.*]] = offset_of::<Beta>(const 0_u32, const 0_u32)
// CHECK: [[sum:_.*]] = AddWithOverflow(copy [[z]], copy [[z0]]);
// CHECK: _0 = move ([[sum]].0: usize);
// CHECK: concrete::[[const_z1]]: usize
// CHECK: [[z:_.*]] = offset_of::<Alpha>(const 0_u32, const 2_u32)
// CHECK: [[z1:_.*]] = offset_of::<Beta>(const 0_u32, const 1_u32)
// CHECK: [[sum:_.*]] = AddWithOverflow(copy [[z]], copy [[z1]]);
// CHECK: _0 = move ([[sum]].0: usize);
// CHECK-LABEL: fn generic(
fn generic<T>() {
// CHECK: debug gx => [[gx:_.*]];
// CHECK: debug gy => [[gy:_.*]];
// CHECK: debug dx => [[dx:_.*]];
// CHECK: debug dy => [[dy:_.*]];
// CHECK: [[gx]] = const generic::<T>::[[const_gx:.*]];
let gx = offset_of!(Gamma<T>, x);
// CHECK: [[gy]] = const generic::<T>::[[const_gy:.*]];
let gy = offset_of!(Gamma<T>, y);
// CHECK: [[dx]] = const generic::<T>::[[const_dx:.*]];
let dx = offset_of!(Delta<T>, x);
// CHECK: [[dy]] = const generic::<T>::[[const_dy:.*]];
let dy = offset_of!(Delta<T>, y);
}
// CHECK: generic::[[const_gx]]: usize
// CHECK: _0 = offset_of::<Gamma<T>>(const 0_u32, const 0_u32)
// CHECK: generic::[[const_gy]]: usize
// CHECK: _0 = offset_of::<Gamma<T>>(const 0_u32, const 1_u32)
// CHECK: generic::[[const_dx]]: usize
// CHECK: _0 = offset_of::<Delta<T>>(const 0_u32, const 1_u32)
// CHECK: generic::[[const_dy]]: usize
// CHECK: _0 = offset_of::<Delta<T>>(const 0_u32, const 2_u32)
fn main() {
concrete();
generic::<()>();
}

View file

@ -34,25 +34,25 @@
bb0: {
StorageLive(_1);
- _1 = OffsetOf(Alpha, [(0, 0)]);
- _1 = const concrete::{constant#0};
+ _1 = const 4_usize;
StorageLive(_2);
- _2 = OffsetOf(Alpha, [(0, 1)]);
- _2 = const concrete::{constant#1};
+ _2 = const 0_usize;
StorageLive(_3);
- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
- _3 = const concrete::{constant#2};
+ _3 = const 2_usize;
StorageLive(_4);
- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
- _4 = const concrete::{constant#3};
+ _4 = const 3_usize;
StorageLive(_5);
- _5 = OffsetOf(Epsilon, [(0, 0)]);
- _5 = const concrete::{constant#4};
+ _5 = const 1_usize;
StorageLive(_6);
- _6 = OffsetOf(Epsilon, [(0, 1)]);
- _6 = const concrete::{constant#5};
+ _6 = const 2_usize;
StorageLive(_7);
- _7 = OffsetOf(Epsilon, [(2, 0)]);
- _7 = const concrete::{constant#6};
+ _7 = const 4_usize;
_0 = const ();
StorageDead(_7);

View file

@ -34,25 +34,25 @@
bb0: {
StorageLive(_1);
- _1 = OffsetOf(Alpha, [(0, 0)]);
- _1 = const concrete::{constant#0};
+ _1 = const 4_usize;
StorageLive(_2);
- _2 = OffsetOf(Alpha, [(0, 1)]);
- _2 = const concrete::{constant#1};
+ _2 = const 0_usize;
StorageLive(_3);
- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
- _3 = const concrete::{constant#2};
+ _3 = const 2_usize;
StorageLive(_4);
- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
- _4 = const concrete::{constant#3};
+ _4 = const 3_usize;
StorageLive(_5);
- _5 = OffsetOf(Epsilon, [(0, 0)]);
- _5 = const concrete::{constant#4};
+ _5 = const 1_usize;
StorageLive(_6);
- _6 = OffsetOf(Epsilon, [(0, 1)]);
- _6 = const concrete::{constant#5};
+ _6 = const 2_usize;
StorageLive(_7);
- _7 = OffsetOf(Epsilon, [(2, 0)]);
- _7 = const concrete::{constant#6};
+ _7 = const 4_usize;
_0 = const ();
StorageDead(_7);

View file

@ -34,21 +34,21 @@
bb0: {
StorageLive(_1);
_1 = OffsetOf(Gamma<T>, [(0, 0)]);
_1 = const generic::<T>::{constant#0};
StorageLive(_2);
_2 = OffsetOf(Gamma<T>, [(0, 1)]);
_2 = const generic::<T>::{constant#1};
StorageLive(_3);
- _3 = OffsetOf(Delta<T>, [(0, 1)]);
- _3 = const generic::<T>::{constant#2};
+ _3 = const 0_usize;
StorageLive(_4);
- _4 = OffsetOf(Delta<T>, [(0, 2)]);
- _4 = const generic::<T>::{constant#3};
+ _4 = const 2_usize;
StorageLive(_5);
_5 = OffsetOf(Zeta<T>, [(0, 0)]);
_5 = const generic::<T>::{constant#4};
StorageLive(_6);
_6 = OffsetOf(Zeta<T>, [(0, 1)]);
_6 = const generic::<T>::{constant#5};
StorageLive(_7);
_7 = OffsetOf(Zeta<T>, [(1, 0)]);
_7 = const generic::<T>::{constant#6};
_0 = const ();
StorageDead(_7);
StorageDead(_6);

View file

@ -34,21 +34,21 @@
bb0: {
StorageLive(_1);
_1 = OffsetOf(Gamma<T>, [(0, 0)]);
_1 = const generic::<T>::{constant#0};
StorageLive(_2);
_2 = OffsetOf(Gamma<T>, [(0, 1)]);
_2 = const generic::<T>::{constant#1};
StorageLive(_3);
- _3 = OffsetOf(Delta<T>, [(0, 1)]);
- _3 = const generic::<T>::{constant#2};
+ _3 = const 0_usize;
StorageLive(_4);
- _4 = OffsetOf(Delta<T>, [(0, 2)]);
- _4 = const generic::<T>::{constant#3};
+ _4 = const 2_usize;
StorageLive(_5);
_5 = OffsetOf(Zeta<T>, [(0, 0)]);
_5 = const generic::<T>::{constant#4};
StorageLive(_6);
_6 = OffsetOf(Zeta<T>, [(0, 1)]);
_6 = const generic::<T>::{constant#5};
StorageLive(_7);
_7 = OffsetOf(Zeta<T>, [(1, 0)]);
_7 = const generic::<T>::{constant#6};
_0 = const ();
StorageDead(_7);
StorageDead(_6);

View file

@ -1,44 +0,0 @@
- // MIR for `concrete` before DataflowConstProp
+ // MIR for `concrete` after DataflowConstProp
fn concrete() -> () {
let mut _0: ();
let _1: usize;
scope 1 {
debug x => _1;
let _2: usize;
scope 2 {
debug y => _2;
let _3: usize;
scope 3 {
debug z0 => _3;
let _4: usize;
scope 4 {
debug z1 => _4;
}
}
}
}
bb0: {
StorageLive(_1);
- _1 = OffsetOf(Alpha, [(0, 0)]);
+ _1 = const 4_usize;
StorageLive(_2);
- _2 = OffsetOf(Alpha, [(0, 1)]);
+ _2 = const 0_usize;
StorageLive(_3);
- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
+ _3 = const 2_usize;
StorageLive(_4);
- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
+ _4 = const 3_usize;
_0 = const ();
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
StorageDead(_1);
return;
}
}

View file

@ -1,44 +0,0 @@
- // MIR for `concrete` before DataflowConstProp
+ // MIR for `concrete` after DataflowConstProp
fn concrete() -> () {
let mut _0: ();
let _1: usize;
scope 1 {
debug x => _1;
let _2: usize;
scope 2 {
debug y => _2;
let _3: usize;
scope 3 {
debug z0 => _3;
let _4: usize;
scope 4 {
debug z1 => _4;
}
}
}
}
bb0: {
StorageLive(_1);
- _1 = OffsetOf(Alpha, [(0, 0)]);
+ _1 = const 4_usize;
StorageLive(_2);
- _2 = OffsetOf(Alpha, [(0, 1)]);
+ _2 = const 0_usize;
StorageLive(_3);
- _3 = OffsetOf(Alpha, [(0, 2), (0, 0)]);
+ _3 = const 2_usize;
StorageLive(_4);
- _4 = OffsetOf(Alpha, [(0, 2), (0, 1)]);
+ _4 = const 3_usize;
_0 = const ();
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
StorageDead(_1);
return;
}
}

View file

@ -1,42 +0,0 @@
- // MIR for `generic` before DataflowConstProp
+ // MIR for `generic` after DataflowConstProp
fn generic() -> () {
let mut _0: ();
let _1: usize;
scope 1 {
debug gx => _1;
let _2: usize;
scope 2 {
debug gy => _2;
let _3: usize;
scope 3 {
debug dx => _3;
let _4: usize;
scope 4 {
debug dy => _4;
}
}
}
}
bb0: {
StorageLive(_1);
_1 = OffsetOf(Gamma<T>, [(0, 0)]);
StorageLive(_2);
_2 = OffsetOf(Gamma<T>, [(0, 1)]);
StorageLive(_3);
- _3 = OffsetOf(Delta<T>, [(0, 1)]);
+ _3 = const 0_usize;
StorageLive(_4);
- _4 = OffsetOf(Delta<T>, [(0, 2)]);
+ _4 = const 2_usize;
_0 = const ();
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
StorageDead(_1);
return;
}
}

View file

@ -1,42 +0,0 @@
- // MIR for `generic` before DataflowConstProp
+ // MIR for `generic` after DataflowConstProp
fn generic() -> () {
let mut _0: ();
let _1: usize;
scope 1 {
debug gx => _1;
let _2: usize;
scope 2 {
debug gy => _2;
let _3: usize;
scope 3 {
debug dx => _3;
let _4: usize;
scope 4 {
debug dy => _4;
}
}
}
}
bb0: {
StorageLive(_1);
_1 = OffsetOf(Gamma<T>, [(0, 0)]);
StorageLive(_2);
_2 = OffsetOf(Gamma<T>, [(0, 1)]);
StorageLive(_3);
- _3 = OffsetOf(Delta<T>, [(0, 1)]);
+ _3 = const 0_usize;
StorageLive(_4);
- _4 = OffsetOf(Delta<T>, [(0, 2)]);
+ _4 = const 2_usize;
_0 = const ();
StorageDead(_4);
StorageDead(_3);
StorageDead(_2);
StorageDead(_1);
return;
}
}

View file

@ -1,75 +0,0 @@
//@ test-mir-pass: DataflowConstProp
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
use std::marker::PhantomData;
use std::mem::offset_of;
struct Alpha {
x: u8,
y: u16,
z: Beta,
}
struct Beta(u8, u8);
struct Gamma<T> {
x: u8,
y: u16,
_t: T,
}
#[repr(C)]
struct Delta<T> {
_phantom: PhantomData<T>,
x: u8,
y: u16,
}
// EMIT_MIR offset_of.concrete.DataflowConstProp.diff
// CHECK-LABEL: fn concrete(
fn concrete() {
// CHECK: debug x => [[x:_.*]];
// CHECK: debug y => [[y:_.*]];
// CHECK: debug z0 => [[z0:_.*]];
// CHECK: debug z1 => [[z1:_.*]];
// CHECK: [[x]] = const 4_usize
let x = offset_of!(Alpha, x);
// CHECK: [[y]] = const 0_usize
let y = offset_of!(Alpha, y);
// CHECK: [[z0]] = const 2_usize
let z0 = offset_of!(Alpha, z.0);
// CHECK: [[z1]] = const 3_usize
let z1 = offset_of!(Alpha, z.1);
}
// EMIT_MIR offset_of.generic.DataflowConstProp.diff
// CHECK-LABEL: fn generic(
fn generic<T>() {
// CHECK: debug gx => [[gx:_.*]];
// CHECK: debug gy => [[gy:_.*]];
// CHECK: debug dx => [[dx:_.*]];
// CHECK: debug dy => [[dy:_.*]];
// CHECK: [[gx]] = OffsetOf(Gamma<T>, [(0, 0)]);
let gx = offset_of!(Gamma<T>, x);
// CHECK: [[gy]] = OffsetOf(Gamma<T>, [(0, 1)]);
let gy = offset_of!(Gamma<T>, y);
// CHECK: [[dx]] = const 0_usize
let dx = offset_of!(Delta<T>, x);
// CHECK: [[dy]] = const 2_usize
let dy = offset_of!(Delta<T>, y);
}
fn main() {
concrete();
generic::<()>();
}

View file

@ -0,0 +1,23 @@
//! Regression test for ICEs #123959, #125680, #129425 and #136175.
//@ edition:2021
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
trait Trait {}
fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
//~^ ERROR overly complex generic constant
//~| ERROR cycle detected when evaluating type-level constant
struct Inline<T>
//~^ ERROR type parameter `T` is never used
where
[(); std::mem::offset_of!((T,), 0)]:,
//~^ ERROR overly complex generic constant
{}
fn main() {
let dst: Inline<dyn Trait>;
//~^ ERROR the size for values of type `dyn Trait` cannot be known at compilation time
}

View file

@ -0,0 +1,90 @@
error: overly complex generic constant
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
= note: this error originates in the macro `std::mem::offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0392]: type parameter `T` is never used
--> $DIR/inside-array-length.rs:13:15
|
LL | struct Inline<T>
| ^ unused type parameter
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
= help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
error: overly complex generic constant
--> $DIR/inside-array-length.rs:16:10
|
LL | [(); std::mem::offset_of!((T,), 0)]:,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
= note: this error originates in the macro `std::mem::offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0391]: cycle detected when evaluating type-level constant
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires const-evaluating + checking `foo::{constant#0}`...
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires caching mir of `foo::{constant#0}` for CTFE...
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires elaborating drops for `foo::{constant#0}`...
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires borrow-checking `foo::{constant#0}`...
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires normalizing `Binder { value: ConstEvaluatable(UnevaluatedConst { def: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}), args: ['^c_1, T/#1] }), bound_vars: [] }`...
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when normalizing `inside_array_length::::foo::{constant#0}`
--> $DIR/inside-array-length.rs:9:27
|
LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
= note: this error originates in the macro `std::mem::offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
--> $DIR/inside-array-length.rs:21:14
|
LL | let dst: Inline<dyn Trait>;
| ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Trait`
note: required by an implicit `Sized` bound in `Inline`
--> $DIR/inside-array-length.rs:13:15
|
LL | struct Inline<T>
| ^ required by the implicit `Sized` requirement on this type parameter in `Inline`
help: consider relaxing the implicit `Sized` restriction
|
LL | struct Inline<T: ?Sized>
| ++++++++
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0277, E0391, E0392.
For more information about an error, try `rustc --explain E0277`.

View file

@ -41,9 +41,6 @@ LL | let _: isize = offset_of!(S, v);
error[E0308]: mismatched types
--> $DIR/offset-of-output-type.rs:17:5
|
LL | fn main() {
| - expected `()` because of default return type
...
LL | offset_of!(S, v)
| ^^^^^^^^^^^^^^^^ expected `()`, found `usize`
|

View file

@ -2,13 +2,52 @@
// regression test for #112051, not in `offset-of-dst` as the issue is in codegen,
// and isn't triggered in the presence of typeck errors
#![feature(extern_types)]
use std::mem::offset_of;
#[repr(C)]
struct Alpha {
x: u8,
y: u16,
z: [u8],
}
trait Trait {}
#[repr(C)]
struct Beta {
x: u8,
y: u16,
z: dyn Trait,
}
unsafe extern "C" {
type Extern;
}
#[repr(C)]
struct Gamma {
x: u8,
y: u16,
z: Extern,
}
struct S<T: ?Sized> {
a: u64,
b: T,
}
trait Tr {}
fn main() {
let _a = core::mem::offset_of!(S<dyn Tr>, a);
let _b = core::mem::offset_of!((u64, dyn Tr), 0);
let _ = offset_of!(Alpha, x);
let _ = offset_of!(Alpha, y);
let _ = offset_of!(Beta, x);
let _ = offset_of!(Beta, y);
let _ = offset_of!(Gamma, x);
let _ = offset_of!(Gamma, y);
let _ = offset_of!(S<dyn Trait>, a);
let _ = offset_of!((u64, dyn Trait), 0);
}

View file

@ -312,7 +312,7 @@ mod expressions {
{ builtin # offset_of(T, field) };
const { builtin # offset_of(T, field) };
}
/// ExprKind::MacCall
fn expr_mac_call() { "..."; "..."; "..."; }

View file

@ -343,7 +343,7 @@ mod expressions {
{ offset_of!(T, field) };
const { offset_of!(T, field) };
}
/// ExprKind::MacCall
fn expr_mac_call() { "..."; "..."; "..."; }