about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_abi/src/lib.rs33
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs33
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0795.md28
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs60
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs4
-rw-r--r--compiler/rustc_middle/src/thir.rs5
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs12
-rw-r--r--compiler/rustc_middle/src/ty/context.rs13
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs2
-rw-r--r--compiler/rustc_passes/src/dead.rs29
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs12
-rw-r--r--compiler/rustc_target/src/abi/mod.rs24
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff56
-rw-r--r--tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff56
-rw-r--r--tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff50
-rw-r--r--tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff50
-rw-r--r--tests/mir-opt/const_prop/offset_of.rs17
-rw-r--r--tests/ui/offset-of/offset-of-enum.rs3
-rw-r--r--tests/ui/offset-of/offset-of-enum.stderr8
-rw-r--r--tests/ui/offset-of/offset-of-private.rs11
-rw-r--r--tests/ui/offset-of/offset-of-private.stderr22
27 files changed, 472 insertions, 75 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 8e7aa59ee34..0654bfd11c5 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1107,6 +1107,39 @@ impl Scalar {
 }
 
 // NOTE: This struct is generic over the FieldIdx for rust-analyzer usage.
+rustc_index::newtype_index! {
+    /// The *source-order* index of a field in a variant.
+    ///
+    /// This is how most code after type checking refers to fields, rather than
+    /// using names (as names have hygiene complications and more complex lookup).
+    ///
+    /// Particularly for `repr(Rust)` types, this may not be the same as *layout* order.
+    /// (It is for `repr(C)` `struct`s, however.)
+    ///
+    /// For example, in the following types,
+    /// ```rust
+    /// # enum Never {}
+    /// # #[repr(u16)]
+    /// enum Demo1 {
+    ///    Variant0 { a: Never, b: i32 } = 100,
+    ///    Variant1 { c: u8, d: u64 } = 10,
+    /// }
+    /// struct Demo2 { e: u8, f: u16, g: u8 }
+    /// ```
+    /// `b` is `FieldIdx(1)` in `VariantIdx(0)`,
+    /// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and
+    /// `f` is `FieldIdx(1)` in `VariantIdx(0)`.
+    #[derive(HashStable_Generic)]
+    pub struct FieldIdx {}
+}
+
+/// `offset_of` can traverse fields and enum variants and should keep track of which is which.
+#[derive(Copy, Clone, Debug, Eq, Hash, HashStable_Generic, PartialEq, Encodable, Decodable)]
+pub enum OffsetOfIdx {
+    Field(FieldIdx),
+    Variant(VariantIdx),
+}
+
 /// Describes how the fields of a type are located in memory.
 #[derive(PartialEq, Eq, Hash, Clone, Debug)]
 #[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 80e7c5bd9ed..91b1547cb6e 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -766,7 +766,7 @@ fn codegen_stmt<'tcx>(
                         NullOp::SizeOf => layout.size.bytes(),
                         NullOp::AlignOf => layout.align.abi.bytes(),
                         NullOp::OffsetOf(fields) => {
-                            layout.offset_of_subfield(fx, fields.iter().map(|f| f.index())).bytes()
+                            layout.offset_of_subfield(fx, fields.iter()).bytes()
                         }
                     };
                     let val = CValue::by_val(
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index f591afaaaf4..b0f757898e3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -680,7 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         layout.align.abi.bytes()
                     }
                     mir::NullOp::OffsetOf(fields) => {
-                        layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
+                        layout.offset_of_subfield(bx.cx(), fields.iter()).bytes()
                     }
                 };
                 let val = bx.cx().const_usize(val);
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 8c34d05042b..b6993d939c5 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -275,7 +275,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     mir::NullOp::SizeOf => layout.size.bytes(),
                     mir::NullOp::AlignOf => layout.align.abi.bytes(),
                     mir::NullOp::OffsetOf(fields) => {
-                        layout.offset_of_subfield(self, fields.iter().map(|f| f.index())).bytes()
+                        layout.offset_of_subfield(self, fields.iter()).bytes()
                     }
                 };
                 self.write_scalar(Scalar::from_target_usize(val, self), &dest)?;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index b70a342aa15..bdccd9b0edb 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -1056,16 +1056,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => {
+            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;
+                let mut indices = indices.into_iter();
 
-                for field in fields.iter() {
-                    match current_ty.kind() {
-                        ty::Tuple(fields) => {
+                use rustc_target::abi::OffsetOfIdx::*;
+
+                while let Some(index) = indices.next() {
+                    match (current_ty.kind(), index) {
+                        (ty::Tuple(fields), Field(field)) => {
                             let Some(&f_ty) = fields.get(field.as_usize()) else {
                                 fail_out_of_bounds(self, location, field, current_ty);
                                 return;
@@ -1073,16 +1076,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
 
                             current_ty = self.tcx.normalize_erasing_regions(self.param_env, f_ty);
                         }
-                        ty::Adt(adt_def, args) => {
-                            if adt_def.is_enum() {
+                        (ty::Adt(adt_def, args), Field(field)) if !adt_def.is_enum() => {
+                            let Some(field) = adt_def.non_enum_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.param_env, f_ty);
+                        }
+                        (ty::Adt(adt_def, args), Variant(variant)) if adt_def.is_enum() => {
+                            let Some(Field(field)) = indices.next() else {
                                 self.fail(
                                     location,
-                                    format!("Cannot get field offset from enum {current_ty:?}"),
+                                    format!("enum variant must be followed by field index in offset_of; in {current_ty:?}"),
                                 );
                                 return;
-                            }
-
-                            let Some(field) = adt_def.non_enum_variant().fields.get(field) else {
+                            };
+                            let Some(field) = adt_def.variant(variant).fields.get(field) else {
                                 fail_out_of_bounds(self, location, field, current_ty);
                                 return;
                             };
@@ -1093,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         _ => {
                             self.fail(
                                 location,
-                                format!("Cannot get field offset from non-adt type {current_ty:?}"),
+                                format!("Cannot get offset {index:?} from type {current_ty:?}"),
                             );
                             return;
                         }
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 89c44c6ec8a..6680e8875c3 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -514,6 +514,7 @@ E0791: include_str!("./error_codes/E0791.md"),
 E0792: include_str!("./error_codes/E0792.md"),
 E0793: include_str!("./error_codes/E0793.md"),
 E0794: include_str!("./error_codes/E0794.md"),
+E0795: include_str!("./error_codes/E0795.md"),
 }
 
 // Undocumented removed error codes. Note that many removed error codes are kept in the list above
diff --git a/compiler/rustc_error_codes/src/error_codes/E0795.md b/compiler/rustc_error_codes/src/error_codes/E0795.md
new file mode 100644
index 00000000000..8b4b2dc87fd
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0795.md
@@ -0,0 +1,28 @@
+Invalid argument for the `offset_of!` macro.
+
+Erroneous code example:
+
+```compile_fail,E0795
+#![feature(offset_of)]
+
+let x = std::mem::offset_of!(Option<u8>, Some);
+```
+
+The `offset_of!` macro gives the offset of a field within a type. It can
+navigate through enum variants, but the final component of its second argument
+must be a field and not a variant.
+
+The offset of the contained `u8` in the `Option<u8>` can be found by specifying
+the field name `0`:
+
+```
+#![feature(offset_of)]
+
+let x: usize = std::mem::offset_of!(Option<u8>, Some.0);
+```
+
+The discriminant of an enumeration may be read with `core::mem::discriminant`,
+but this is not always a value physically present within the enum.
+
+Further information about enum layout may be found at
+https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html.
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index b1d6e4f0523..394a9033765 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -3103,16 +3103,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &[Ident],
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
+        use rustc_target::abi::OffsetOfIdx::*;
+
         let container = self.to_ty(container).normalized;
 
         let mut field_indices = Vec::with_capacity(fields.len());
         let mut current_container = container;
+        let mut fields = fields.into_iter();
 
-        for &field in fields {
+        while let Some(&field) = fields.next() {
             let container = self.structurally_resolve_type(expr.span, current_container);
 
             match container.kind() {
-                ty::Adt(container_def, args) if !container_def.is_enum() => {
+                ty::Adt(container_def, args) if container_def.is_enum() => {
+                    let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+                    let (ident, _def_scope) =
+                        self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
+
+                    if let Some((index, variant)) = container_def.variants()
+                        .iter_enumerated()
+                        .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident)
+                    {
+                        let Some(&subfield) = fields.next() else {
+                            let mut err = type_error_struct!(
+                                self.tcx().sess,
+                                ident.span,
+                                container,
+                                E0795,
+                                "`{ident}` is an enum variant; expected field at end of `offset_of`",
+                                );
+                            err.span_label(field.span, "enum variant");
+                            err.emit();
+                            break;
+                        };
+                        let (subident, sub_def_scope) =
+                            self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block);
+
+                        if let Some((subindex, field)) = variant.fields
+                            .iter_enumerated()
+                            .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident)
+                        {
+                            let field_ty = self.field_ty(expr.span, field, args);
+
+                            // FIXME: DSTs with static alignment should be allowed
+                            self.require_type_is_sized(field_ty, expr.span, traits::MiscObligation);
+
+                            if field.vis.is_accessible_from(sub_def_scope, self.tcx) {
+                                self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None);
+                            } else {
+                                self.private_field_err(ident, container_def.did()).emit();
+                            }
+
+                            // Save the index of all fields regardless of their visibility in case
+                            // of error recovery.
+                            field_indices.push(Variant(index));
+                            field_indices.push(Field(subindex));
+                            current_container = field_ty;
+
+                            continue;
+                        }
+                    }
+                }
+                ty::Adt(container_def, args) => {
                     let block = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
                     let (ident, def_scope) =
                         self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
@@ -3135,7 +3187,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);
+                        field_indices.push(Field(index));
                         current_container = field_ty;
 
                         continue;
@@ -3149,7 +3201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.require_type_is_sized(ty, expr.span, traits::MiscObligation);
                         }
                         if let Some(&field_ty) = tys.get(index) {
-                            field_indices.push(index.into());
+                            field_indices.push(Field(index.into()));
                             current_container = field_ty;
 
                             continue;
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index b89c7bc512e..d0094a81634 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -17,7 +17,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir};
 use rustc_hir::{self, CoroutineKind};
 use rustc_index::IndexVec;
-use rustc_target::abi::{FieldIdx, VariantIdx};
+use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx};
 
 use rustc_ast::Mutability;
 use rustc_span::def_id::LocalDefId;
@@ -1354,7 +1354,7 @@ pub enum NullOp<'tcx> {
     /// Returns the minimum alignment of a type
     AlignOf,
     /// Returns the offset of a field
-    OffsetOf(&'tcx List<FieldIdx>),
+    OffsetOf(&'tcx List<OffsetOfIdx>),
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index f1747356139..2ee726c76bf 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -25,7 +25,8 @@ use rustc_middle::ty::{
 };
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP};
-use rustc_target::abi::{FieldIdx, VariantIdx};
+use rustc_span::{sym, Span, Symbol, DUMMY_SP};
+use rustc_target::abi::{FieldIdx, OffsetOfIdx, VariantIdx};
 use rustc_target::asm::InlineAsmRegOrRegClass;
 use std::fmt;
 use std::ops::Index;
@@ -490,7 +491,7 @@ pub enum ExprKind<'tcx> {
     /// Field offset (`offset_of!`)
     OffsetOf {
         container: Ty<'tcx>,
-        fields: &'tcx List<FieldIdx>,
+        fields: &'tcx List<OffsetOfIdx>,
     },
     /// An expression taking a reference to a thread local.
     ThreadLocalRef(DefId),
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index d52a717b6b0..ce5198ee3ba 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -19,7 +19,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty::TyCtxt;
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::Span;
-use rustc_target::abi::FieldIdx;
+use rustc_target::abi::{FieldIdx, OffsetOfIdx};
 pub use rustc_type_ir::{TyDecoder, TyEncoder};
 use std::hash::Hash;
 use std::intrinsics;
@@ -414,6 +414,15 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<Fi
     }
 }
 
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<OffsetOfIdx> {
+    fn decode(decoder: &mut D) -> &'tcx Self {
+        let len = decoder.read_usize();
+        decoder
+            .interner()
+            .mk_offset_of_from_iter((0..len).map::<OffsetOfIdx, _>(|_| Decodable::decode(decoder)))
+    }
+}
+
 impl_decodable_via_ref! {
     &'tcx ty::TypeckResults<'tcx>,
     &'tcx ty::List<Ty<'tcx>>,
@@ -426,6 +435,7 @@ impl_decodable_via_ref! {
     &'tcx ty::List<ty::BoundVariableKind>,
     &'tcx ty::List<ty::Clause<'tcx>>,
     &'tcx ty::List<FieldIdx>,
+    &'tcx ty::List<OffsetOfIdx>,
 }
 
 #[macro_export]
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 0b3b6700cec..c89ae1e661e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -63,7 +63,7 @@ use rustc_session::{Limit, MetadataKind, Session};
 use rustc_span::def_id::{DefPathHash, StableCrateId};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
-use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
+use rustc_target::abi::{FieldIdx, Layout, LayoutS, OffsetOfIdx, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::WithCachedTypeInfo;
@@ -163,6 +163,7 @@ pub struct CtxtInterners<'tcx> {
     predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
     fields: InternedSet<'tcx, List<FieldIdx>>,
     local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
+    offset_of: InternedSet<'tcx, List<OffsetOfIdx>>,
 }
 
 impl<'tcx> CtxtInterners<'tcx> {
@@ -189,6 +190,7 @@ impl<'tcx> CtxtInterners<'tcx> {
             predefined_opaques_in_body: Default::default(),
             fields: Default::default(),
             local_def_ids: Default::default(),
+            offset_of: Default::default(),
         }
     }
 
@@ -1587,6 +1589,7 @@ slice_interners!(
     bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
     fields: pub mk_fields(FieldIdx),
     local_def_ids: intern_local_def_ids(LocalDefId),
+    offset_of: pub mk_offset_of(OffsetOfIdx),
 );
 
 impl<'tcx> TyCtxt<'tcx> {
@@ -1914,6 +1917,14 @@ 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<OffsetOfIdx, &'tcx List<OffsetOfIdx>>,
+    {
+        T::collect_and_apply(iter, |xs| self.mk_offset_of(xs))
+    }
+
     pub fn mk_args_trait(
         self,
         self_ty: Ty<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 58ad1eb900f..623d8d61d2d 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -24,7 +24,7 @@ use rustc_macros::HashStable;
 use rustc_middle::mir::FakeReadCause;
 use rustc_session::Session;
 use rustc_span::Span;
-use rustc_target::abi::FieldIdx;
+use rustc_target::abi::{FieldIdx, OffsetOfIdx};
 use std::{collections::hash_map::Entry, hash::Hash, iter};
 
 use super::RvalueScopes;
@@ -205,7 +205,7 @@ pub struct TypeckResults<'tcx> {
     pub closure_size_eval: LocalDefIdMap<ClosureSizeProfileData<'tcx>>,
 
     /// Container types and field indices of `offset_of!` expressions
-    offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<FieldIdx>)>,
+    offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<OffsetOfIdx>)>,
 }
 
 impl<'tcx> TypeckResults<'tcx> {
@@ -464,11 +464,13 @@ impl<'tcx> TypeckResults<'tcx> {
         &self.coercion_casts
     }
 
-    pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
+    pub fn offset_of_data(&self) -> LocalTableInContext<'_, (Ty<'tcx>, Vec<OffsetOfIdx>)> {
         LocalTableInContext { hir_owner: self.hir_owner, data: &self.offset_of_data }
     }
 
-    pub fn offset_of_data_mut(&mut self) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<FieldIdx>)> {
+    pub fn offset_of_data_mut(
+        &mut self,
+    ) -> LocalTableInContextMut<'_, (Ty<'tcx>, Vec<OffsetOfIdx>)> {
         LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.offset_of_data }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 6c3564a20f6..dfd39b512e2 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -670,7 +670,7 @@ impl<'tcx> Cx<'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_fields_from_iter(indices.iter().copied());
+                let fields = tcx.mk_offset_of_from_iter(indices.iter().copied());
 
                 ExprKind::OffsetOf { container, fields }
             }
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index fd067cb234b..ca10dc48eb8 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -287,7 +287,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                     NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
                     NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
                     NullOp::OffsetOf(fields) => layout
-                        .offset_of_subfield(&self.ecx, fields.iter().map(|f| f.index()))
+                        .offset_of_subfield(&self.ecx, fields.iter())
                         .bytes(),
                     _ => return ValueOrPlace::Value(FlatSet::Top),
                 };
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 87d4850b475..3b4f6a618c8 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -248,6 +248,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     }
 
     fn handle_offset_of(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        use rustc_target::abi::OffsetOfIdx::*;
+
         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");
@@ -256,11 +258,23 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         let param_env = self.tcx.param_env(body_did);
 
         let mut current_ty = container;
+        let mut indices = indices.into_iter();
+
+        while let Some(&index) = indices.next() {
+            match (current_ty.kind(), index) {
+                (ty::Adt(def, subst), Field(field)) if !def.is_enum() => {
+                    let field = &def.non_enum_variant().fields[field];
 
-        for &index in indices {
-            match current_ty.kind() {
-                ty::Adt(def, subst) => {
-                    let field = &def.non_enum_variant().fields[index];
+                    self.insert_def_id(field.did);
+                    let field_ty = field.ty(self.tcx, subst);
+
+                    current_ty = self.tcx.normalize_erasing_regions(param_env, field_ty);
+                }
+                (ty::Adt(def, subst), Variant(variant)) if def.is_enum() => {
+                    let Some(&Field(field)) = indices.next() else {
+                        span_bug!(expr.span, "variant must be followed by field in offset_of")
+                    };
+                    let field = &def.variant(variant).fields[field];
 
                     self.insert_def_id(field.did);
                     let field_ty = field.ty(self.tcx, subst);
@@ -269,11 +283,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 }
                 // we don't need to mark tuple fields as live,
                 // but we may need to mark subfields
-                ty::Tuple(tys) => {
+                (ty::Tuple(tys), Field(field)) => {
                     current_ty =
-                        self.tcx.normalize_erasing_regions(param_env, tys[index.as_usize()]);
+                        self.tcx.normalize_erasing_regions(param_env, tys[field.as_usize()]);
                 }
-                _ => span_bug!(expr.span, "named field access on non-ADT"),
+                (_, Field(_)) => span_bug!(expr.span, "named field access on non-ADT"),
+                (_, Variant(_)) => span_bug!(expr.span, "enum variant access on non-enum"),
             }
         }
     }
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 5ab5a048ffa..e64afa5f8d9 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -16,7 +16,7 @@ use rustc_middle::mir::interpret::{alloc_range, AllocId};
 use rustc_middle::mir::mono::MonoItem;
 use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, Variance};
 use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc_target::abi::FieldIdx;
+use rustc_target::abi::{FieldIdx, OffsetOfIdx};
 use stable_mir::mir::mono::InstanceDef;
 use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
 use stable_mir::ty::{
@@ -643,6 +643,16 @@ impl<'tcx> Stable<'tcx> for FieldIdx {
     }
 }
 
+impl<'tcx> Stable<'tcx> for OffsetOfIdx {
+    type T = usize;
+    fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
+        match self {
+            OffsetOfIdx::Field(f) => f.as_usize(),
+            OffsetOfIdx::Variant(v) => v.as_usize(),
+        }
+    }
+}
+
 impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
     type T = stable_mir::mir::Operand;
     fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index f6f8b53d130..a37a5bb2689 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -250,7 +250,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         Ty::is_transparent(self)
     }
 
-    pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
+    pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = OffsetOfIdx>) -> Size
     where
         Ty: TyAbiInterface<'a, C>,
     {
@@ -258,13 +258,21 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         let mut offset = Size::ZERO;
 
         for index in indices {
-            offset += layout.fields.offset(index);
-            layout = layout.field(cx, index);
-            assert!(
-                layout.is_sized(),
-                "offset of unsized field (type {:?}) cannot be computed statically",
-                layout.ty
-            );
+            match index {
+                OffsetOfIdx::Field(field) => {
+                    let index = field.index();
+                    offset += layout.fields.offset(index);
+                    layout = layout.field(cx, index);
+                    assert!(
+                        layout.is_sized(),
+                        "offset of unsized field (type {:?}) cannot be computed statically",
+                        layout.ty
+                    );
+                }
+                OffsetOfIdx::Variant(variant) => {
+                    layout = layout.for_variant(cx, variant);
+                }
+            }
         }
 
         offset
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
index c73d217aeec..e460c08f9bb 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-abort.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug x => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug z1 => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug eA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug eA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug eC => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [Field(0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
@@ -37,7 +52,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [Field(1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
@@ -47,7 +62,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [Field(2), Field(0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
@@ -57,7 +72,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [Field(2), Field(1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
@@ -65,7 +80,40 @@
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+-         _10 = OffsetOf(Epsilon, [Variant(0), Field(0)]);
+-         _9 = must_use::<usize>(move _10) -> [return: bb5, unwind unreachable];
++         _10 = const 1_usize;
++         _9 = must_use::<usize>(const 1_usize) -> [return: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+-         _12 = OffsetOf(Epsilon, [Variant(0), Field(1)]);
+-         _11 = must_use::<usize>(move _12) -> [return: bb6, unwind unreachable];
++         _12 = const 2_usize;
++         _11 = must_use::<usize>(const 2_usize) -> [return: bb6, unwind unreachable];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+-         _14 = OffsetOf(Epsilon, [Variant(2), Field(0)]);
+-         _13 = must_use::<usize>(move _14) -> [return: bb7, unwind unreachable];
++         _14 = const 4_usize;
++         _13 = must_use::<usize>(const 4_usize) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
index 913ffca4ae9..3b8d0d75101 100644
--- a/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/offset_of.concrete.ConstProp.panic-unwind.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug x => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug z1 => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug eA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug eA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug eC => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
--         _2 = OffsetOf(Alpha, [0]);
+-         _2 = OffsetOf(Alpha, [Field(0)]);
 -         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
 +         _2 = const 4_usize;
 +         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
@@ -37,7 +52,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
--         _4 = OffsetOf(Alpha, [1]);
+-         _4 = OffsetOf(Alpha, [Field(1)]);
 -         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
 +         _4 = const 0_usize;
 +         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
@@ -47,7 +62,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = OffsetOf(Alpha, [2, 0]);
+-         _6 = OffsetOf(Alpha, [Field(2), Field(0)]);
 -         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
 +         _6 = const 2_usize;
 +         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
@@ -57,7 +72,7 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
--         _8 = OffsetOf(Alpha, [2, 1]);
+-         _8 = OffsetOf(Alpha, [Field(2), Field(1)]);
 -         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
 +         _8 = const 3_usize;
 +         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
@@ -65,7 +80,40 @@
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+-         _10 = OffsetOf(Epsilon, [Variant(0), Field(0)]);
+-         _9 = must_use::<usize>(move _10) -> [return: bb5, unwind continue];
++         _10 = const 1_usize;
++         _9 = must_use::<usize>(const 1_usize) -> [return: bb5, unwind continue];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+-         _12 = OffsetOf(Epsilon, [Variant(0), Field(1)]);
+-         _11 = must_use::<usize>(move _12) -> [return: bb6, unwind continue];
++         _12 = const 2_usize;
++         _11 = must_use::<usize>(const 2_usize) -> [return: bb6, unwind continue];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+-         _14 = OffsetOf(Epsilon, [Variant(2), Field(0)]);
+-         _13 = must_use::<usize>(move _14) -> [return: bb7, unwind continue];
++         _14 = const 4_usize;
++         _13 = must_use::<usize>(const 4_usize) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
index 7519331f6d7..f26b104e065 100644
--- a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
+++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-abort.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug gx => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug dy => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug zA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug zA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug zB => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [Field(0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
       }
   
@@ -35,7 +50,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [Field(1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
       }
   
@@ -43,7 +58,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = OffsetOf(Delta<T>, [1]);
+          _6 = OffsetOf(Delta<T>, [Field(1)]);
           _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
       }
   
@@ -51,13 +66,40 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
-          _8 = OffsetOf(Delta<T>, [2]);
+          _8 = OffsetOf(Delta<T>, [Field(2)]);
           _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = OffsetOf(Zeta<T>, [Variant(0), Field(0)]);
+          _9 = must_use::<usize>(move _10) -> [return: bb5, unwind unreachable];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+          _12 = OffsetOf(Zeta<T>, [Variant(0), Field(1)]);
+          _11 = must_use::<usize>(move _12) -> [return: bb6, unwind unreachable];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = OffsetOf(Zeta<T>, [Variant(1), Field(0)]);
+          _13 = must_use::<usize>(move _14) -> [return: bb7, unwind unreachable];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
index fd5206e460c..0f8750fce46 100644
--- a/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/offset_of.generic.ConstProp.panic-unwind.diff
@@ -8,6 +8,9 @@
       let mut _4: usize;
       let mut _6: usize;
       let mut _8: usize;
+      let mut _10: usize;
+      let mut _12: usize;
+      let mut _14: usize;
       scope 1 {
           debug gx => _1;
           let _3: usize;
@@ -19,6 +22,18 @@
                   let _7: usize;
                   scope 4 {
                       debug dy => _7;
+                      let _9: usize;
+                      scope 5 {
+                          debug zA0 => _9;
+                          let _11: usize;
+                          scope 6 {
+                              debug zA1 => _11;
+                              let _13: usize;
+                              scope 7 {
+                                  debug zB => _13;
+                              }
+                          }
+                      }
                   }
               }
           }
@@ -27,7 +42,7 @@
       bb0: {
           StorageLive(_1);
           StorageLive(_2);
-          _2 = OffsetOf(Gamma<T>, [0]);
+          _2 = OffsetOf(Gamma<T>, [Field(0)]);
           _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
       }
   
@@ -35,7 +50,7 @@
           StorageDead(_2);
           StorageLive(_3);
           StorageLive(_4);
-          _4 = OffsetOf(Gamma<T>, [1]);
+          _4 = OffsetOf(Gamma<T>, [Field(1)]);
           _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
       }
   
@@ -43,7 +58,7 @@
           StorageDead(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = OffsetOf(Delta<T>, [1]);
+          _6 = OffsetOf(Delta<T>, [Field(1)]);
           _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
       }
   
@@ -51,13 +66,40 @@
           StorageDead(_6);
           StorageLive(_7);
           StorageLive(_8);
-          _8 = OffsetOf(Delta<T>, [2]);
+          _8 = OffsetOf(Delta<T>, [Field(2)]);
           _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
       }
   
       bb4: {
           StorageDead(_8);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = OffsetOf(Zeta<T>, [Variant(0), Field(0)]);
+          _9 = must_use::<usize>(move _10) -> [return: bb5, unwind continue];
+      }
+  
+      bb5: {
+          StorageDead(_10);
+          StorageLive(_11);
+          StorageLive(_12);
+          _12 = OffsetOf(Zeta<T>, [Variant(0), Field(1)]);
+          _11 = must_use::<usize>(move _12) -> [return: bb6, unwind continue];
+      }
+  
+      bb6: {
+          StorageDead(_12);
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = OffsetOf(Zeta<T>, [Variant(1), Field(0)]);
+          _13 = must_use::<usize>(move _14) -> [return: bb7, unwind continue];
+      }
+  
+      bb7: {
+          StorageDead(_14);
           _0 = const ();
+          StorageDead(_13);
+          StorageDead(_11);
+          StorageDead(_9);
           StorageDead(_7);
           StorageDead(_5);
           StorageDead(_3);
diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs
index 21911f8dbb5..8a5289d5899 100644
--- a/tests/mir-opt/const_prop/offset_of.rs
+++ b/tests/mir-opt/const_prop/offset_of.rs
@@ -28,12 +28,26 @@ struct Delta<T> {
     y: u16,
 }
 
+enum Epsilon {
+    A(u8, u16),
+    B,
+    C { c: u32 }
+}
+
+enum Zeta<T> {
+    A(T, bool),
+    B(char),
+}
+
 // EMIT_MIR offset_of.concrete.ConstProp.diff
 fn concrete() {
     let x = offset_of!(Alpha, x);
     let y = offset_of!(Alpha, y);
     let z0 = offset_of!(Alpha, z.0);
     let z1 = offset_of!(Alpha, z.1);
+    let eA0 = offset_of!(Epsilon, A.0);
+    let eA1 = offset_of!(Epsilon, A.1);
+    let eC = offset_of!(Epsilon, C.c);
 }
 
 // EMIT_MIR offset_of.generic.ConstProp.diff
@@ -42,6 +56,9 @@ fn generic<T>() {
     let gy = offset_of!(Gamma<T>, y);
     let dx = offset_of!(Delta<T>, x);
     let dy = offset_of!(Delta<T>, y);
+    let zA0 = offset_of!(Zeta<T>, A.0);
+    let zA1 = offset_of!(Zeta<T>, A.1);
+    let zB = offset_of!(Zeta<T>, B.0);
 }
 
 fn main() {
diff --git a/tests/ui/offset-of/offset-of-enum.rs b/tests/ui/offset-of/offset-of-enum.rs
index d73505821ff..92d34e67c71 100644
--- a/tests/ui/offset-of/offset-of-enum.rs
+++ b/tests/ui/offset-of/offset-of-enum.rs
@@ -9,5 +9,6 @@ enum Alpha {
 
 fn main() {
     offset_of!(Alpha::One, 0); //~ ERROR expected type, found variant `Alpha::One`
-    offset_of!(Alpha, Two.0); //~ ERROR no field `Two` on type `Alpha`
+    offset_of!(Alpha, One); //~ ERROR `One` is an enum variant; expected field at end of `offset_of`
+    offset_of!(Alpha, Two.0);
 }
diff --git a/tests/ui/offset-of/offset-of-enum.stderr b/tests/ui/offset-of/offset-of-enum.stderr
index 6958d199fbd..1d849e682e6 100644
--- a/tests/ui/offset-of/offset-of-enum.stderr
+++ b/tests/ui/offset-of/offset-of-enum.stderr
@@ -7,13 +7,13 @@ LL |     offset_of!(Alpha::One, 0);
    |                not a type
    |                help: try using the variant's enum: `Alpha`
 
-error[E0609]: no field `Two` on type `Alpha`
+error[E0795]: `One` is an enum variant; expected field at end of `offset_of`
   --> $DIR/offset-of-enum.rs:12:23
    |
-LL |     offset_of!(Alpha, Two.0);
-   |                       ^^^
+LL |     offset_of!(Alpha, One);
+   |                       ^^^ enum variant
 
 error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0573, E0609.
+Some errors have detailed explanations: E0573, E0795.
 For more information about an error, try `rustc --explain E0573`.
diff --git a/tests/ui/offset-of/offset-of-private.rs b/tests/ui/offset-of/offset-of-private.rs
index 6b1a16ba62b..6fa30d63fb8 100644
--- a/tests/ui/offset-of/offset-of-private.rs
+++ b/tests/ui/offset-of/offset-of-private.rs
@@ -8,13 +8,20 @@ mod m {
         pub public: u8,
         private: u8,
     }
+
     #[repr(C)]
     pub struct FooTuple(pub u8, u8);
+
     #[repr(C)]
     struct Bar {
         pub public: u8,
         private: u8,
     }
+
+    pub enum Baz {
+        Var1(Foo),
+        Var2(u64),
+    }
 }
 
 fn main() {
@@ -25,4 +32,8 @@ fn main() {
     offset_of!(m::Bar, public); //~ ERROR struct `Bar` is private
     offset_of!(m::Bar, private); //~ ERROR struct `Bar` is private
     //~| ERROR field `private` of struct `Bar` is private
+
+    offset_of!(m::Baz, Var1.0.public);
+    offset_of!(m::Baz, Var1.0.private); //~ ERROR field `private` of struct `Foo` is private
+    offset_of!(m::Baz, Var2.0);
 }
diff --git a/tests/ui/offset-of/offset-of-private.stderr b/tests/ui/offset-of/offset-of-private.stderr
index 0674b58f860..930e30e6390 100644
--- a/tests/ui/offset-of/offset-of-private.stderr
+++ b/tests/ui/offset-of/offset-of-private.stderr
@@ -1,46 +1,52 @@
 error[E0603]: struct `Bar` is private
-  --> $DIR/offset-of-private.rs:25:19
+  --> $DIR/offset-of-private.rs:32:19
    |
 LL |     offset_of!(m::Bar, public);
    |                   ^^^ private struct
    |
 note: the struct `Bar` is defined here
-  --> $DIR/offset-of-private.rs:14:5
+  --> $DIR/offset-of-private.rs:16:5
    |
 LL |     struct Bar {
    |     ^^^^^^^^^^
 
 error[E0603]: struct `Bar` is private
-  --> $DIR/offset-of-private.rs:26:19
+  --> $DIR/offset-of-private.rs:33:19
    |
 LL |     offset_of!(m::Bar, private);
    |                   ^^^ private struct
    |
 note: the struct `Bar` is defined here
-  --> $DIR/offset-of-private.rs:14:5
+  --> $DIR/offset-of-private.rs:16:5
    |
 LL |     struct Bar {
    |     ^^^^^^^^^^
 
 error[E0616]: field `private` of struct `Foo` is private
-  --> $DIR/offset-of-private.rs:22:24
+  --> $DIR/offset-of-private.rs:29:24
    |
 LL |     offset_of!(m::Foo, private);
    |                        ^^^^^^^ private field
 
 error[E0616]: field `1` of struct `FooTuple` is private
-  --> $DIR/offset-of-private.rs:24:29
+  --> $DIR/offset-of-private.rs:31:29
    |
 LL |     offset_of!(m::FooTuple, 1);
    |                             ^ private field
 
 error[E0616]: field `private` of struct `Bar` is private
-  --> $DIR/offset-of-private.rs:26:24
+  --> $DIR/offset-of-private.rs:33:24
    |
 LL |     offset_of!(m::Bar, private);
    |                        ^^^^^^^ private field
 
-error: aborting due to 5 previous errors
+error[E0616]: field `private` of struct `Foo` is private
+  --> $DIR/offset-of-private.rs:37:31
+   |
+LL |     offset_of!(m::Baz, Var1.0.private);
+   |                               ^^^^^^^ private field
+
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0603, E0616.
 For more information about an error, try `rustc --explain E0603`.