Skip to content

Comments

add field representing types#152730

Open
BennoLossin wants to merge 2 commits intorust-lang:mainfrom
BennoLossin:field-projections-lang-item
Open

add field representing types#152730
BennoLossin wants to merge 2 commits intorust-lang:mainfrom
BennoLossin:field-projections-lang-item

Conversation

@BennoLossin
Copy link
Contributor

@BennoLossin BennoLossin commented Feb 16, 2026

Note

This is a rewrite of #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 between the two PRs


Add Field Representing Types (FRTs)

This PR implements the first step of the field projection lang experiment (Tracking Issue: #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.

API added to core::field

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:

#[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 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

@rustbot
Copy link
Collaborator

rustbot commented Feb 16, 2026

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

HIR ty lowering was modified

cc @fmease

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Feb 16, 2026
@rustbot
Copy link
Collaborator

rustbot commented Feb 16, 2026

oli-obk is not on the review rotation at the moment.
They may take a while to respond.

@rust-log-analyzer

This comment has been minimized.

@BennoLossin BennoLossin force-pushed the field-projections-lang-item branch from fe3fa8b to f5f42d1 Compare February 16, 2026 20:53
@rustbot
Copy link
Collaborator

rustbot commented Feb 16, 2026

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rustbot rustbot added A-rustdoc-json Area: Rustdoc JSON backend T-clippy Relevant to the Clippy team. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. labels Feb 16, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-bors

This comment has been minimized.

@oli-obk oli-obk added the I-types-nominated Nominated for discussion during a types team meeting. label Feb 17, 2026
@BennoLossin BennoLossin force-pushed the field-projections-lang-item branch from 72f9813 to 2c83a72 Compare February 17, 2026 12:14
@rustbot
Copy link
Collaborator

rustbot commented Feb 17, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

@BennoLossin BennoLossin force-pushed the field-projections-lang-item branch from edbf7d1 to a94a2f8 Compare February 20, 2026 15:14
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 20, 2026
@rustbot
Copy link
Collaborator

rustbot commented Feb 20, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rust-log-analyzer

This comment has been minimized.

@BennoLossin
Copy link
Contributor Author

I had to move the indexes, since mod ty in layout is nightly-only. Also had to add #[gate_rustc_only] and make the HashStable_Generic conditional on nightly too. Hope all of that is correct.

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 20, 2026
@oli-obk
Copy link
Contributor

oli-obk commented Feb 20, 2026

Yep, that looks correct.

Please squash the new commits into the appropriate main ones. Then I'll approve it

@BennoLossin BennoLossin force-pushed the field-projections-lang-item branch from 503326e to e7bae32 Compare February 20, 2026 20:12
@theemathas
Copy link
Contributor

theemathas commented Feb 21, 2026

If we do go with this design with a new #[fundamental] type, we would have to make sure before stabilization that we've implemented absolutely every trait we need on it, since adding a new impl later is a breaking change.

#[unstable(feature = "field_projections", issue = "145383")]
#[rustc_deny_explicit_impl]
#[rustc_dyn_incompatible_trait]
pub unsafe trait Field: Send + Sync + Copy + Sized {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this Field trait requires Send and Sync, I assume that the FieldRepresentingType type needs to have unconditional Send and Sync impls, as opposed to inheriting the auto traits via the PhantomData field.

@theemathas
Copy link
Contributor

theemathas commented Feb 21, 2026

This code prints 4 with this PR, ignoring the possibility that the DST field might need extra padding for alignment:

#![feature(field_projections)]
#![expect(incomplete_features, dead_code)]

use std::field::{Field, field_of};

struct Thing(i32, dyn Send + Sync);

fn main() {
    let x = <field_of!(Thing, 1) as Field>::OFFSET;
    println!("{x}");
}

In contrast, offset_of!() requires the relevant field to be Sized:

#![expect(dead_code)]

use std::mem::offset_of;

struct Thing(i32, dyn Send + Sync);

fn main() {
    let x = offset_of!(Thing, 1);
    println!("{x}");
}
error[E0277]: the size for values of type `(dyn Send + Sync + 'static)` cannot be known at compilation time
 --> src\main.rs:8:13
  |
8 |     let x = offset_of!(Thing, 1);
  |             ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `(dyn Send + Sync + 'static)`
  = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.

@theemathas
Copy link
Contributor

theemathas commented Feb 21, 2026

The following code ICEs with this PR, due to the FRT implementing the Field trait with Type = dyn Send + Sync, despite the fact that the Field trait implicitly requires the Type associated type to be Sized:

#![feature(field_projections)]
#![expect(incomplete_features, dead_code)]

use std::field::{Field, field_of};

struct Thing(dyn Send + Sync);

fn main() {
    foo::<field_of!(Thing, 0)>();
}

fn foo<F: Field>() {
    let _ = Box::<<F as Field>::Type>::new_uninit();
}
Error output
error: internal compiler error: compiler\rustc_monomorphize\src\collector.rs:719:50: collection encountered polymorphic constant: Unevaluated(UnevaluatedConst { def: DefId(2:2571 ~ core[80cb]::mem::SizedTypeProperties::SIZE), args: [dyn [Binder { value: AutoTrait(DefId(2:4054 ~ core[80cb]::marker::Sync)), bound_vars: [] }, Binder { value: AutoTrait(DefId(2:39350 ~ core[80cb]::marker::Send)), bound_vars: [] }] + '{erased}], promoted: None }, usize)
 --> library\alloc\src\boxed.rs:324:16


thread 'rustc' (39488) panicked at compiler\rustc_monomorphize\src\collector.rs:719:50:
Box<dyn Any>
stack backtrace:
   0: std::panicking::begin_panic::<rustc_errors::ExplicitBug>
   1: <rustc_hir_pretty::State as rustc_ast_pretty::pprust::state::PrintState>::print_generic_args
   2: <rustc_errors::diagnostic::BugAbort as rustc_errors::diagnostic::EmissionGuarantee>::emit_producing_guarantee
   3: <rustc_errors::diagnostic::Diag<rustc_errors::diagnostic::BugAbort>>::emit
   4: <rustc_errors::DiagCtxtHandle>::span_bug::<rustc_span::span_encoding::Span, alloc::string::String>
   5: rustc_middle::util::bug::opt_span_bug_fmt::<rustc_span::span_encoding::Span>
   6: rustc_middle::util::bug::opt_span_bug_fmt::<rustc_span::span_encoding::Span>
   7: rustc_middle::util::bug::opt_span_bug_fmt::<rustc_span::span_encoding::Span>
   8: rustc_middle::util::bug::opt_span_bug_fmt::<rustc_span::span_encoding::Span>
   9: rustc_middle::util::bug::opt_span_bug_fmt::<rustc_span::span_encoding::Span>
  10: rustc_middle::util::bug::span_bug_fmt::<rustc_span::span_encoding::Span>
  11: <rustc_monomorphize::collector::UsageMap>::get_user_items    
  12: <rustc_monomorphize::collector::MirUsedCollector as rustc_middle::mir::visit::Visitor>::visit_const_operand
  13: <rustc_monomorphize::collector::MirUsedCollector as rustc_middle::mir::visit::Visitor>::super_terminator
  14: <rustc_monomorphize::collector::MirUsedCollector as rustc_middle::mir::visit::Visitor>::visit_terminator
  15: rustc_monomorphize::collector::collect_roots
  16: rustc_query_impl::query_impl::limits::alloc_self_profile_query_strings
  17: <std::thread::local::LocalKey<rustc_data_structures::sync::worker_local::ThreadData>>::with::<<rustc_data_structures::sync::worker_local::RegistryId>::verify::{closure#0}, (rustc_data_structures::sync::worker_local::RegistryId, usize)>
  18: RINvMs2_NtNtCsjk4If7qqH0b_3std6thread5localINtB6_8LocalKeyINtNtCs3ss6HBggOLS_4core4cell4CellPuEE4withNCINvNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context3tls13enter_contextNCNCINvMs1_NtNtB1M_9dep_graph5graphNtB2T_12DepGraphData9with_taskTNtNtCsb06bXvORIT1_16      
  19: RINvNtCslGuD12ebP0h_12rustc_middle9dep_graph9with_depsNCNCINvMs1_NtB2_5graphNtB11_12DepGraphData9with_taskTNtNtCsb06bXvORIT1_16rustc_query_impl8plumbing9QueryCtxtINtB1L_26SemiDynamicQueryDispatcherINtNtNtB4_5query6caches12DefaultCacheTNtNtNtB4_2ty8instance      
  20: RINvMs2_NtNtCsjk4If7qqH0b_3std6thread5localINtB6_8LocalKeyINtNtCs3ss6HBggOLS_4core4cell4CellPuEE4withNCINvNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context3tls13enter_contextNCINvNtCsb06bXvORIT1_16rustc_query_impl9execution16execute_job_incrINtNtNtB1M_5query6c      
  21: RINvNtCsb06bXvORIT1_16rustc_query_impl9execution17try_execute_queryINtNtNtCslGuD12ebP0h_12rustc_middle5query6caches12DefaultCacheTNtNtNtB1a_2ty8instance8InstanceNtNtNtB1a_3mir4mono14CollectionModeEINtNtB18_5erase10ErasedDataAhj20_EEKVNtB4_10QueryFlagsS7is_      
  22: rustc_monomorphize::collector::collect_roots
  23: rustc_monomorphize::collector::collect_roots
  24: rustc_monomorphize::collector::collect_roots
  25: rustc_monomorphize::collector::collect_items_root
  26: RINvMNtNtCshA4wA9hNRNN_21rustc_data_structures4sync8parallelNtB3_13ParallelGuard3runuNCNCNCINvB3_15par_for_each_inNtNtNtCslGuD12ebP0h_12rustc_middle3mir4mono8MonoItemINtNtCs9dkY0A6RMMU_5alloc3vec3VecB1O_ENCNCNvNtCsaQMz2UQdcge_18rustc_monomorphize9collector      
  27: RINvXs4_NtNtCs9dkY0A6RMMU_5alloc3vec9into_iterINtB6_8IntoIterNtNtNtCslGuD12ebP0h_12rustc_middle3mir4mono8MonoItemENtNtNtNtCs3ss6HBggOLS_4core4iter6traits8iterator8Iterator4folduNCINvNvB1O_8for_each4callBX_NCNCINvNtNtCshA4wA9hNRNN_21rustc_data_structures4sy      
  28: rustc_data_structures::sync::parallel::par_for_each_in::<rustc_middle::mir::mono::MonoItem, alloc::vec::Vec<rustc_middle::mir::mono::MonoItem>, rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}::{closure#0}>
  29: <rustc_session::session::Session>::time::<(), rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}>
  30: rustc_monomorphize::collector::collect_crate_mono_items      
  31: rustc_monomorphize::partitioning::compute_codegen_unit_name 
  32: <std::thread::local::LocalKey<rustc_data_structures::sync::worker_local::ThreadData>>::with::<<rustc_data_structures::sync::worker_local::RegistryId>::verify::{closure#0}, (rustc_data_structures::sync::worker_local::RegistryId, usize)>
  33: RINvMs2_NtNtCsjk4If7qqH0b_3std6thread5localINtB6_8LocalKeyINtNtCs3ss6HBggOLS_4core4cell4CellPuEE4withNCINvNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context3tls13enter_contextNCNCINvMs1_NtNtB1M_9dep_graph5graphNtB2T_12DepGraphData9with_taskTNtNtCsb06bXvORIT1_16      
  34: RINvNtCslGuD12ebP0h_12rustc_middle9dep_graph9with_depsNCNCINvMs1_NtB2_5graphNtB11_12DepGraphData9with_taskTNtNtCsb06bXvORIT1_16rustc_query_impl8plumbing9QueryCtxtINtB1L_26SemiDynamicQueryDispatcherINtNtNtB4_5query6caches11SingleCacheINtNtB3e_5erase10Erased      
  35: RINvMs2_NtNtCsjk4If7qqH0b_3std6thread5localINtB6_8LocalKeyINtNtCs3ss6HBggOLS_4core4cell4CellPuEE4withNCINvNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context3tls13enter_contextNCINvNtCsb06bXvORIT1_16rustc_query_impl9execution16execute_job_incrINtNtNtB1M_5query6c      
  36: rustc_query_impl::execution::try_execute_query::<rustc_middle::query::caches::SingleCache<rustc_middle::query::erase::ErasedData<[u8; 24]>>, {rustc_query_impl::QueryFlags { is_anon: false, is_depth_limit: false, is_feedable: false }}, true>
  37: rustc_codegen_ssa::base::codegen_crate::<rustc_codegen_llvm::LlvmCodegenBackend>
  38: <rustc_codegen_llvm::LlvmCodegenBackend as rustc_codegen_ssa::traits::backend::CodegenBackend>::codegen_crate
  39: <rustc_session::session::Session>::time::<alloc::boxed::Box<dyn core::any::Any>, rustc_interface::passes::start_codegen::{closure#0}>
  40: rustc_interface::passes::start_codegen
  41: <rustc_interface::queries::Linker>::codegen_and_build_linker 
  42: RINvMs2_NtNtCsjk4If7qqH0b_3std6thread5localINtB6_8LocalKeyINtNtCs3ss6HBggOLS_4core4cell4CellPuEE4withNCINvNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context3tls13enter_contextNCINvMsh_B1I_NtB1I_10GlobalCtxt5enterNCNCINvNtCsgDCgBVpkTu4_15rustc_interface6passes28      
  43: RINvMsl_NtNtCslGuD12ebP0h_12rustc_middle2ty7contextNtB6_6TyCtxt18create_global_ctxtINtNtCs3ss6HBggOLS_4core6option6OptionNtNtCsgDCgBVpkTu4_15rustc_interface7queries6LinkerENCNCINvNtB1Z_6passes28create_and_enter_global_ctxtB1j_NCNCNvCslx5e3yw6wJm_17rustc_dr      
  44: rustc_interface::passes::create_and_enter_global_ctxt::<(), rustc_driver_impl::run_compiler::{closure#0}::{closure#1}>
  45: RNvXst_NtCs9dkY0A6RMMU_5alloc5boxedINtB5_3BoxDG_INtNtNtCs3ss6HBggOLS_4core3ops8function6FnOnceTRL0_NtNtCsefPgroWLkb7_13rustc_session7session7SessionNtNtNtCslGuD12ebP0h_12rustc_middle2ty7context10CurrentGcxINtNtB7_4sync3ArcNtNtCshA4wA9hNRNN_21rustc_data_str      
  46: rustc_interface::passes::create_and_enter_global_ctxt::<core::option::Option<rustc_interface::queries::Linker>, rustc_driver_impl::run_compiler::{closure#0}::{closure#2}>
  47: RINvMs_Csl9ikBPEYqOJ_10scoped_tlsINtB5_9ScopedKeyNtCslMKAykjEFI3_10rustc_span14SessionGlobalsE3setNCNCNCINvNtCsgDCgBVpkTu4_15rustc_interface4util26run_in_thread_with_globalsNCINvB1H_31run_in_thread_pool_with_globalsNCINvNtB1J_9interface12run_compileruNCNvC      
  48: RINvCslMKAykjEFI3_10rustc_span27create_session_globals_thenuNCNCNCINvNtCsgDCgBVpkTu4_15rustc_interface4util26run_in_thread_with_globalsNCINvB15_31run_in_thread_pool_with_globalsNCINvNtB17_9interface12run_compileruNCNvCslx5e3yw6wJm_17rustc_driver_impl12run_      
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: please make sure that you have updated to the latest nightly 

note: please attach the file at `C:\Users\theem\Documents\foo\rustc-ice-2026-02-21T05_04_36-36980.txt` to your bug report

note: rustc 1.95.0-dev running on x86_64-pc-windows-msvc

note: compiler flags: --crate-type bin -C embed-bitcode=no -C debuginfo=2 -C incremental=[REDACTED]

note: some of the compiler flags provided by cargo are hidden      

query stack during panic:
#0 [items_of_instance] collecting items used by `alloc::boxed::Box::<dyn core::marker::Send + core::marker::Sync>::new_uninit`        
#1 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack
error: could not compile `foo` (bin "foo")

@theemathas theemathas added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 21, 2026
@rust-bors
Copy link
Contributor

rust-bors bot commented Feb 21, 2026

☔ The latest upstream changes (presumably #152624) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-rustdoc-json Area: Rustdoc JSON backend I-types-nominated Nominated for discussion during a types team meeting. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-rustdoc-frontend Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants