Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4749,6 +4749,7 @@ dependencies = [
"ena",
"indexmap",
"rustc-hash 2.1.1",
"rustc_abi",
"rustc_ast_ir",
"rustc_data_structures",
"rustc_error_messages",
Expand Down
59 changes: 58 additions & 1 deletion compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,64 @@ mod simple;
mod ty;

#[cfg(feature = "nightly")]
pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
pub use ty::{Layout, TyAbiInterface, TyAndLayout};

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)`.
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
#[encodable]
#[orderable]
#[gate_rustc_only]
pub struct FieldIdx {}
}

impl FieldIdx {
/// The second field, at index 1.
///
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
}

rustc_index::newtype_index! {
/// The *source-order* index of a variant in a type.
///
/// For enums, these are always `0..variant_count`, regardless of any
/// custom discriminants that may have been defined, and including any
/// variants that may end up uninhabited due to field types. (Some of the
/// variants may not be present in a monomorphized ABI [`Variants`], but
/// those skipped variants are always counted when determining the *index*.)
///
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
/// with variant index zero, aka [`FIRST_VARIANT`].
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
#[encodable]
#[orderable]
#[gate_rustc_only]
pub struct VariantIdx {
/// Equivalent to `VariantIdx(0)`.
const FIRST_VARIANT = 0;
}
}

// A variant is absent if it's uninhabited and only has ZST fields.
// Present uninhabited variants only require space for their fields,
Expand Down
55 changes: 1 addition & 54 deletions compiler/rustc_abi/src/layout/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,14 @@ use std::ops::Deref;
use rustc_data_structures::intern::Interned;
use rustc_macros::HashStable_Generic;

use crate::layout::{FieldIdx, VariantIdx};
use crate::{
AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
PointeeInfo, Primitive, Size, Variants,
};

// Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.

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)]
#[encodable]
#[orderable]
pub struct FieldIdx {}
}

impl FieldIdx {
/// The second field, at index 1.
///
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
}

rustc_index::newtype_index! {
/// The *source-order* index of a variant in a type.
///
/// For enums, these are always `0..variant_count`, regardless of any
/// custom discriminants that may have been defined, and including any
/// variants that may end up uninhabited due to field types. (Some of the
/// variants may not be present in a monomorphized ABI [`Variants`], but
/// those skipped variants are always counted when determining the *index*.)
///
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
/// with variant index zero, aka [`FIRST_VARIANT`].
#[derive(HashStable_Generic)]
#[encodable]
#[orderable]
pub struct VariantIdx {
/// Equivalent to `VariantIdx(0)`.
const FIRST_VARIANT = 0;
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
#[rustc_pass_by_value]
pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
#[cfg(feature = "nightly")]
pub use extern_abi::CVariadicStatus;
pub use extern_abi::{ExternAbi, all_names};
pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx};
#[cfg(feature = "nightly")]
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
pub use layout::{LayoutCalculator, LayoutCalculatorError};
pub use layout::{Layout, TyAbiInterface, TyAndLayout};

/// Requirements for a `StableHashingContext` to be used in this crate.
/// This is a hack to allow using the `HashStable_Generic` derive macro
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,11 @@ pub enum TyKind {
/// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero<u32>`,
/// just as part of the type system.
Pat(Box<Ty>, Box<TyPat>),
/// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`).
///
/// Usually not written directly in user code but indirectly via the macro
/// `core::field::field_of!(...)`.
FieldOf(Box<Ty>, Option<Ident>, Ident),
/// Sometimes we need a dummy value when no error has occurred.
Dummy,
/// Placeholder for a kind that has failed to be defined.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
| ast::TyKind::ImplicitSelf
| ast::TyKind::CVarArgs
| ast::TyKind::Pat(..)
| ast::TyKind::FieldOf(..)
| ast::TyKind::Dummy
| ast::TyKind::Err(..) => break None,
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span))
}
TyKind::FieldOf(ty, variant, field) => hir::TyKind::FieldOf(
self.lower_ty_alloc(ty, itctx),
self.arena.alloc(hir::TyFieldPath {
variant: variant.map(|variant| self.lower_ident(variant)),
field: self.lower_ident(*field),
}),
),
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
}
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,23 @@ impl<'a> State<'a> {
self.word(" is ");
self.print_ty_pat(pat);
}
ast::TyKind::FieldOf(ty, variant, field) => {
self.word("builtin # field_of");
self.popen();
let ib = self.ibox(0);
self.print_type(ty);
self.word(",");
self.space();

if let Some(variant) = variant {
self.print_ident(*variant);
self.word(".");
}
self.print_ident(*field);

self.end(ib);
self.pclose();
}
}
self.end(ib);
}
Expand Down
24 changes: 23 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{
FieldInfo, FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeVisitableExt,
};
use rustc_middle::{bug, span_bug, ty};
use rustc_span::{Symbol, sym};
use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
Expand Down Expand Up @@ -227,6 +229,26 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {

self.write_scalar(Scalar::from_target_usize(offset, self), dest)?;
}
sym::field_offset => {
let frt_ty = instance.args.type_at(0);
ensure_monomorphic_enough(tcx, frt_ty)?;

let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind()
&& let Some(FieldInfo { base, variant_idx, field_idx, .. }) =
def.field_representing_type_info(tcx, args)
{
(base, variant_idx, field_idx)
} else {
span_bug!(self.cur_span(), "expected field representing type, got {frt_ty}")
};
let layout = self.layout_of(ty)?;
let cx = ty::layout::LayoutCx::new(*self.tcx, self.typing_env);

let layout = layout.for_variant(&cx, variant);
let offset = layout.fields.offset(field.index()).bytes();

self.write_scalar(Scalar::from_target_usize(offset, self), dest)?;
}
sym::vtable_for => {
let tp_ty = instance.args.type_at(0);
let result_ty = instance.args.type_at(1);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ declare_features! (
(internal, custom_mir, "1.65.0", None),
/// Implementation details of externally implementable items
(internal, eii_internals, "1.94.0", None),
/// Implementation details of field representing types.
(internal, field_representing_type_raw, "CURRENT_RUSTC_VERSION", None),
/// Outputs useful `assert!` messages
(unstable, generic_assert, "1.63.0", None),
/// Allows using the #[rustc_intrinsic] attribute.
Expand Down Expand Up @@ -492,6 +494,8 @@ declare_features! (
(unstable, ffi_const, "1.45.0", Some(58328)),
/// Allows the use of `#[ffi_pure]` on foreign functions.
(unstable, ffi_pure, "1.45.0", Some(58329)),
/// Experimental field projections.
(incomplete, field_projections, "CURRENT_RUSTC_VERSION", Some(145383)),
/// Allows marking trait functions as `final` to prevent overriding impls
(unstable, final_associated_functions, "CURRENT_RUSTC_VERSION", Some(1)),
/// Controlling the behavior of fmt::Debug
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,12 @@ impl<'hir> Block<'hir> {
}
}

#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TyFieldPath {
pub variant: Option<Ident>,
pub field: Ident,
}

#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TyPat<'hir> {
#[stable_hasher(ignore)]
Expand Down Expand Up @@ -3800,6 +3806,10 @@ pub enum TyKind<'hir, Unambig = ()> {
Err(rustc_span::ErrorGuaranteed),
/// Pattern types (`pattern_type!(u32 is 1..)`)
Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>),
/// Field representing type (`field_of!(Struct, field)`).
///
/// The optional ident is the variant when an enum is passed `field_of!(Enum, Variant.field)`.
FieldOf(&'hir Ty<'hir>, &'hir TyFieldPath),
/// `TyKind::Infer` means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
///
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -
try_visit!(visitor.visit_ty_unambig(ty));
try_visit!(visitor.visit_pattern_type_pattern(pat));
}
TyKind::FieldOf(ty, TyFieldPath { variant, field }) => {
try_visit!(visitor.visit_ty_unambig(ty));
visit_opt!(visitor, visit_ident, *variant);
try_visit!(visitor.visit_ident(*field));
}
}
V::Result::output()
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,13 @@ language_item_table! {
// Reborrowing related lang-items
Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0);
CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0);

// Field representing types.
FieldRepresentingType, sym::field_representing_type, field_representing_type, Target::Struct, GenericRequirement::Exact(3);
Field, sym::field, field, Target::Trait, GenericRequirement::Exact(0);
FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0);
FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0);
FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0);
}

/// The requirement imposed on the generics of a lang item
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::fabsf128
| sym::fadd_algebraic
| sym::fdiv_algebraic
| sym::field_offset
| sym::floorf16
| sym::floorf32
| sym::floorf64
Expand Down Expand Up @@ -297,6 +298,7 @@ pub(crate) fn check_intrinsic_type(
(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::field_offset => (1, 0, vec![], 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 => {
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,15 @@ pub struct NoVariantNamed<'tcx> {
pub ty: Ty<'tcx>,
}

#[derive(Diagnostic)]
#[diag("no field `{$field}` on type `{$ty}`", code = E0609)]
pub struct NoFieldOnType<'tcx> {
#[primary_span]
pub span: Span,
pub ty: Ty<'tcx>,
pub field: Ident,
}

// FIXME(fmease): Deduplicate:

#[derive(Diagnostic)]
Expand Down
Loading
Loading