Skip to content

Commit 46e366f

Browse files
Rollup merge of rust-lang#152730 - BennoLossin:field-projections-lang-item, r=oli-obk
add field representing types *[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/152730)* > [!NOTE] > This is a rewrite of rust-lang#146307 by using a lang item instead of a custom `TyKind`. We still need a `hir::TyKind::FieldOf` variant, because resolving the field name cannot be done before HIR construction. The advantage of doing it this way is that we don't need to make any changes to types after HIR (including symbol mangling). At the very beginning of this feature implementation, I tried to do it using a lang item, but then quickly abandoned the approach, because at that time I was still intending to support nested fields. Here is a [range-diff](https://triagebot.infra.rust-lang.org/gh-range-diff/rust-lang/rust/605f49b27444a738ea4032cb77e3bdc4eb811bab..d15f5052095b3549111854a2555dd7026b0a729e/605f49b27444a738ea4032cb77e3bdc4eb811bab..f5f42d1e03495dbaa23671c46b15fccddeb3492f) between the two PRs --- # Add Field Representing Types (FRTs) This PR implements the first step of the field projection lang experiment (Tracking Issue: rust-lang#145383). Field representing types (FRTs) are a new kind of type. They can be named through the use of the `field_of!` macro with the first argument being the type and the second the name of the field (or variant and field in the case of an enum). No nested fields are supported. FRTs natively implement the `Field` trait that's also added in this PR. It exposes information about the field such as the type of the field, the type of the base (i.e. the type that contains the field) and the offset within that base type. Only fields of non-packed structs are supported, fields of enums an unions have unique types for each field, but those do not implement the `Field` trait. This PR was created in collaboration with @dingxiangfei2009, it wouldn't have been possible without him, so huge thanks for mentoring me! I updated my library solution for field projections to use the FRTs from `core` instead of creating my own using the hash of the name of the field. See the [Rust-for-Linux/field-projection `lang-experiment` branch](https://github.com/Rust-for-Linux/field-projection/tree/lang-experiment). ## API added to `core::field` ```rust pub unsafe trait Field { type Base; type Type; const OFFSET: usize; } pub macro field_of($Container:ty, $($fields:expr)+ $(,)?); ``` Along with a perma-unstable type that the compiler uses in the expansion of the macro: ```rust #[unstable(feature = "field_representing_type_raw", issue = "none")] pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32> { _phantom: PhantomData<T>, } ``` ## Explanation of Field Representing Types (FRTs) FRTs are used for compile-time & trait-level reflection for fields of structs & tuples. Each struct & tuple has a unique compiler-generated type nameable through the `field_of!` macro. This type natively contains information about the field such as the outermost container, type of the field and its offset. Users may implement additional traits on these types in order to record custom information (for example a crate may define a [`PinnableField` trait](https://github.com/Rust-for-Linux/field-projection/blob/lang-experiment/src/marker.rs#L9-L23) that records whether the field is structurally pinned). They are the foundation of field projections, a general operation that's generic over the fields of a struct. This genericism needs to be expressible in the trait system. FRTs make this possible, since an operation generic over fields can just be a function with a generic parameter `F: Field`. > [!NOTE] > The approach of field projections has changed considerably since this PR was opened. In the end we might not need FRTs, so this API is highly experimental. FRTs should act as though they were defined as `struct MyStruct_my_field<StructGenerics>;` next to the struct. So it should be local to the crate defining the struct so that one can implement any trait for the FRT from that crate. The `Field` traits should be implemented by the compiler & populated with correct information (`unsafe` code needs to be able to rely on them being correct). ## TODOs There are some `FIXME(FRTs)` scattered around the code: - Diagnostics for `field_of!` can be improved - `tests/ui/field_representing_types/nonexistent.rs` - `tests/ui/field_representing_types/non-struct.rs` - `tests/ui/field_representing_types/offset.rs` - `tests/ui/field_representing_types/not-field-if-packed.rs` - `tests/ui/field_representing_types/invalid.rs` - Simple type alias already seem to work, but might need some extra work in `compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs` r? @oli-obk
2 parents 5cb42db + 7b42859 commit 46e366f

File tree

89 files changed

+2166
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2166
-89
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4706,6 +4706,7 @@ dependencies = [
47064706
"ena",
47074707
"indexmap",
47084708
"rustc-hash 2.1.1",
4709+
"rustc_abi",
47094710
"rustc_ast_ir",
47104711
"rustc_data_structures",
47114712
"rustc_error_messages",

compiler/rustc_abi/src/layout.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,64 @@ mod simple;
2121
mod ty;
2222

2323
#[cfg(feature = "nightly")]
24-
pub use ty::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
24+
pub use ty::{Layout, TyAbiInterface, TyAndLayout};
25+
26+
rustc_index::newtype_index! {
27+
/// The *source-order* index of a field in a variant.
28+
///
29+
/// This is how most code after type checking refers to fields, rather than
30+
/// using names (as names have hygiene complications and more complex lookup).
31+
///
32+
/// Particularly for `repr(Rust)` types, this may not be the same as *layout* order.
33+
/// (It is for `repr(C)` `struct`s, however.)
34+
///
35+
/// For example, in the following types,
36+
/// ```rust
37+
/// # enum Never {}
38+
/// # #[repr(u16)]
39+
/// enum Demo1 {
40+
/// Variant0 { a: Never, b: i32 } = 100,
41+
/// Variant1 { c: u8, d: u64 } = 10,
42+
/// }
43+
/// struct Demo2 { e: u8, f: u16, g: u8 }
44+
/// ```
45+
/// `b` is `FieldIdx(1)` in `VariantIdx(0)`,
46+
/// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and
47+
/// `f` is `FieldIdx(1)` in `VariantIdx(0)`.
48+
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
49+
#[encodable]
50+
#[orderable]
51+
#[gate_rustc_only]
52+
pub struct FieldIdx {}
53+
}
54+
55+
impl FieldIdx {
56+
/// The second field, at index 1.
57+
///
58+
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
59+
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
60+
}
61+
62+
rustc_index::newtype_index! {
63+
/// The *source-order* index of a variant in a type.
64+
///
65+
/// For enums, these are always `0..variant_count`, regardless of any
66+
/// custom discriminants that may have been defined, and including any
67+
/// variants that may end up uninhabited due to field types. (Some of the
68+
/// variants may not be present in a monomorphized ABI [`Variants`], but
69+
/// those skipped variants are always counted when determining the *index*.)
70+
///
71+
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
72+
/// with variant index zero, aka [`FIRST_VARIANT`].
73+
#[cfg_attr(feature = "nightly", derive(rustc_macros::HashStable_Generic))]
74+
#[encodable]
75+
#[orderable]
76+
#[gate_rustc_only]
77+
pub struct VariantIdx {
78+
/// Equivalent to `VariantIdx(0)`.
79+
const FIRST_VARIANT = 0;
80+
}
81+
}
2582

2683
// A variant is absent if it's uninhabited and only has ZST fields.
2784
// Present uninhabited variants only require space for their fields,

compiler/rustc_abi/src/layout/ty.rs

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,14 @@ use std::ops::Deref;
44
use rustc_data_structures::intern::Interned;
55
use rustc_macros::HashStable_Generic;
66

7+
use crate::layout::{FieldIdx, VariantIdx};
78
use crate::{
89
AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
910
PointeeInfo, Primitive, Size, Variants,
1011
};
1112

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

14-
rustc_index::newtype_index! {
15-
/// The *source-order* index of a field in a variant.
16-
///
17-
/// This is how most code after type checking refers to fields, rather than
18-
/// using names (as names have hygiene complications and more complex lookup).
19-
///
20-
/// Particularly for `repr(Rust)` types, this may not be the same as *layout* order.
21-
/// (It is for `repr(C)` `struct`s, however.)
22-
///
23-
/// For example, in the following types,
24-
/// ```rust
25-
/// # enum Never {}
26-
/// # #[repr(u16)]
27-
/// enum Demo1 {
28-
/// Variant0 { a: Never, b: i32 } = 100,
29-
/// Variant1 { c: u8, d: u64 } = 10,
30-
/// }
31-
/// struct Demo2 { e: u8, f: u16, g: u8 }
32-
/// ```
33-
/// `b` is `FieldIdx(1)` in `VariantIdx(0)`,
34-
/// `d` is `FieldIdx(1)` in `VariantIdx(1)`, and
35-
/// `f` is `FieldIdx(1)` in `VariantIdx(0)`.
36-
#[derive(HashStable_Generic)]
37-
#[encodable]
38-
#[orderable]
39-
pub struct FieldIdx {}
40-
}
41-
42-
impl FieldIdx {
43-
/// The second field, at index 1.
44-
///
45-
/// For use alongside [`FieldIdx::ZERO`], particularly with scalar pairs.
46-
pub const ONE: FieldIdx = FieldIdx::from_u32(1);
47-
}
48-
49-
rustc_index::newtype_index! {
50-
/// The *source-order* index of a variant in a type.
51-
///
52-
/// For enums, these are always `0..variant_count`, regardless of any
53-
/// custom discriminants that may have been defined, and including any
54-
/// variants that may end up uninhabited due to field types. (Some of the
55-
/// variants may not be present in a monomorphized ABI [`Variants`], but
56-
/// those skipped variants are always counted when determining the *index*.)
57-
///
58-
/// `struct`s, `tuples`, and `unions`s are considered to have a single variant
59-
/// with variant index zero, aka [`FIRST_VARIANT`].
60-
#[derive(HashStable_Generic)]
61-
#[encodable]
62-
#[orderable]
63-
pub struct VariantIdx {
64-
/// Equivalent to `VariantIdx(0)`.
65-
const FIRST_VARIANT = 0;
66-
}
67-
}
6815
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
6916
#[rustc_pass_by_value]
7017
pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);

compiler/rustc_abi/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
6464
#[cfg(feature = "nightly")]
6565
pub use extern_abi::CVariadicStatus;
6666
pub use extern_abi::{ExternAbi, all_names};
67+
pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorError, VariantIdx};
6768
#[cfg(feature = "nightly")]
68-
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
69-
pub use layout::{LayoutCalculator, LayoutCalculatorError};
69+
pub use layout::{Layout, TyAbiInterface, TyAndLayout};
7070

7171
/// Requirements for a `StableHashingContext` to be used in this crate.
7272
/// This is a hack to allow using the `HashStable_Generic` derive macro

compiler/rustc_ast/src/ast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,11 @@ pub enum TyKind {
25532553
/// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero<u32>`,
25542554
/// just as part of the type system.
25552555
Pat(Box<Ty>, Box<TyPat>),
2556+
/// A `field_of` expression (e.g., `builtin # field_of(Struct, field)`).
2557+
///
2558+
/// Usually not written directly in user code but indirectly via the macro
2559+
/// `core::field::field_of!(...)`.
2560+
FieldOf(Box<Ty>, Option<Ident>, Ident),
25562561
/// Sometimes we need a dummy value when no error has occurred.
25572562
Dummy,
25582563
/// Placeholder for a kind that has failed to be defined.

compiler/rustc_ast/src/util/classify.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
298298
| ast::TyKind::ImplicitSelf
299299
| ast::TyKind::CVarArgs
300300
| ast::TyKind::Pat(..)
301+
| ast::TyKind::FieldOf(..)
301302
| ast::TyKind::Dummy
302303
| ast::TyKind::Err(..) => break None,
303304
}

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
14961496
TyKind::Pat(ty, pat) => {
14971497
hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span))
14981498
}
1499+
TyKind::FieldOf(ty, variant, field) => hir::TyKind::FieldOf(
1500+
self.lower_ty_alloc(ty, itctx),
1501+
self.arena.alloc(hir::TyFieldPath {
1502+
variant: variant.map(|variant| self.lower_ident(variant)),
1503+
field: self.lower_ident(*field),
1504+
}),
1505+
),
14991506
TyKind::MacCall(_) => {
15001507
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
15011508
}

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,23 @@ impl<'a> State<'a> {
13771377
self.word(" is ");
13781378
self.print_ty_pat(pat);
13791379
}
1380+
ast::TyKind::FieldOf(ty, variant, field) => {
1381+
self.word("builtin # field_of");
1382+
self.popen();
1383+
let ib = self.ibox(0);
1384+
self.print_type(ty);
1385+
self.word(",");
1386+
self.space();
1387+
1388+
if let Some(variant) = variant {
1389+
self.print_ident(*variant);
1390+
self.word(".");
1391+
}
1392+
self.print_ident(*field);
1393+
1394+
self.end(ib);
1395+
self.pclose();
1396+
}
13801397
}
13811398
self.end(ib);
13821399
}

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use rustc_middle::mir::AssertMessage;
1212
use rustc_middle::mir::interpret::ReportedErrorInfo;
1313
use rustc_middle::query::TyCtxtAt;
1414
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
15-
use rustc_middle::ty::{self, Ty, TyCtxt};
16-
use rustc_middle::{bug, mir};
15+
use rustc_middle::ty::{self, FieldInfo, Ty, TyCtxt};
16+
use rustc_middle::{bug, mir, span_bug};
1717
use rustc_span::{Span, Symbol, sym};
1818
use rustc_target::callconv::FnAbi;
1919
use tracing::debug;
@@ -23,8 +23,9 @@ use crate::errors::{LongRunning, LongRunningWarn};
2323
use crate::interpret::{
2424
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
2525
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
26-
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
27-
throw_ub_custom, throw_unsup, throw_unsup_format, type_implements_dyn_trait,
26+
compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, throw_exhaust,
27+
throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
28+
type_implements_dyn_trait,
2829
};
2930

3031
/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -619,6 +620,27 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
619620
ecx.write_type_info(ty, dest)?;
620621
}
621622

623+
sym::field_offset => {
624+
let frt_ty = instance.args.type_at(0);
625+
ensure_monomorphic_enough(ecx.tcx.tcx, frt_ty)?;
626+
627+
let (ty, variant, field) = if let ty::Adt(def, args) = frt_ty.kind()
628+
&& let Some(FieldInfo { base, variant_idx, field_idx, .. }) =
629+
def.field_representing_type_info(ecx.tcx.tcx, args)
630+
{
631+
(base, variant_idx, field_idx)
632+
} else {
633+
span_bug!(ecx.cur_span(), "expected field representing type, got {frt_ty}")
634+
};
635+
let layout = ecx.layout_of(ty)?;
636+
let cx = ty::layout::LayoutCx::new(ecx.tcx.tcx, ecx.typing_env());
637+
638+
let layout = layout.for_variant(&cx, variant);
639+
let offset = layout.fields.offset(field.index()).bytes();
640+
641+
ecx.write_scalar(Scalar::from_target_usize(offset, ecx), dest)?;
642+
}
643+
622644
_ => {
623645
// We haven't handled the intrinsic, let's see if we can use a fallback body.
624646
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ use self::place::{MemPlace, Place};
3838
pub use self::projection::{OffsetMode, Projectable};
3939
pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation};
4040
pub use self::util::EnteredTraceSpan;
41-
pub(crate) use self::util::{create_static_alloc, type_implements_dyn_trait};
41+
pub(crate) use self::util::{
42+
create_static_alloc, ensure_monomorphic_enough, type_implements_dyn_trait,
43+
};
4244
pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking};
4345
pub use self::visitor::ValueVisitor;

0 commit comments

Comments
 (0)