Skip to content

Conversation

BennoLossin
Copy link
Contributor

@BennoLossin BennoLossin commented Sep 7, 2025

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 that uses the same syntax as the offset_of! macro. They 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.

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)+ $(,)?);

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 (using the same syntax as offset_of!). 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.

FRTs should act as though they were defined as enum 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. 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).

Implementation Details

  • Reuse offset_of! type checking machinery in the FnCtxt case,
    • Create a new type called FieldPath for using them in both field_of! and offset_of!,
    • Add lower_field_path to HirTyLowerer,
  • Use an intrinsic to add the offset information into the associated OFFSET constant of the UnalignedField trait,

Decisions

During the development several desirable properties of FRTs were discovered. Either due to simplifying the implementation or because of language design reasons. These decisions are:

  • FRTs are uninhabited and have the same layout as !

    • They only contain type information via the [Unaligned]Field traits, so no need to make them useful as values.
    • This simplifies const-eval.
    • They are thus also Freeze
  • FRTs are considered local by the orphan check if and only if:

    • The outer-most container is a local struct,
    • All intermediate container types that are structs must be local (we don't consider the type of the field to be an intermediate base)

    So field_of!(Foo, bar.baz) is local if Foo and Bar are local (Baz is allowed to be foreign). Except for tuples, fundamental types are not handled specially. Tuples are allowed as intermediate bases, as long as the first one isn't a tuple. So field_of!(Foo, bar.0.baz) is considered local if Foo is local & bar has type (Bar0, Bar1, ..., BarN) with Bar0 also being local.

    The reasoning for these two rules is the following: we don't want to allow foreign crates control over FRTs, since they will often encode field invariants (such as the offset in the UnalignedField case, or that the field is aligned (Field)).

    Tuples are an exception to this, but only if the user controls the outer-most container.

    In the future, the first rule could be lifted to:

    • local structs are valid outer-most containers,
    • tuples where every type is a valid outer-most container are also valid outer-most containers.

    This would allow users to implement traits on field_of!((LocalTy, LocalTy), 0). But still prevent field_of!((usize, usize), 0).

  • traits may be implemented for FRTs in the usual way (so if the FRT or the trait is local). There is an exception:

    • Drop: since FRTs are uninhabited, having a Drop impl doesn't really make sense. It also would have required duplicating a lot of logic from ADTs and as such it simplified the implementation.
  • The Field trait is not user-implementable

    • this also can be lifted in the future, but for now only FRTs will implement it.

TODOs

There are several FIXME(field_projections) scattered around the code. These are either because I & Ding didn't 100% know what to do in that case. Note that not all are comments, some are also contained as bug!("FIXME(field_projections): ...").

  • symbol mangling for FTRs.
    • compiler/rustc_symbol_mangling/src/v0.rs
    • compiler/rustc_symbol_mangling/src/export.rs
    • compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
    • compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
  • rustdoc support for FTRs
    • src/librustdoc/passes/collect_intra_doc_links.rs
    • src/librustdoc/json/conversions.rs
    • src/librustdoc/clean/mod.rs
  • check that FRTs support tuples, unions and structs
  • investigate how complex enum support would be
    decided against enum support in this PR
  • what about DSTs? can we really support the OFFSET constant for those, or do we need different traits?
    decided against ?Sized types in this PR

@rustbot
Copy link
Collaborator

rustbot commented Sep 7, 2025

r? @lcnr

rustbot has assigned @lcnr.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-rustdoc-json Area: Rustdoc JSON backend PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. 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 Relevant to the rustdoc 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) labels Sep 7, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 8, 2025

changes to the core type system

cc @compiler-errors, @lcnr

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred in diagnostic error codes

cc @GuillaumeGomez

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred in compiler/rustc_sanitizers

cc @rcvalle

HIR ty lowering was modified

cc @fmease

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in exhaustiveness checking

cc @Nadrieril

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @vakaras

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

This PR changes rustc_public

cc @oli-obk, @celinval, @ouz-a

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

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

@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

Copy link
Contributor Author

@BennoLossin BennoLossin left a comment

Choose a reason for hiding this comment

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

There are some changes to symbol mangling & constant names that I'm not sure how they resulted from my changes. Anybody has any idea?

View changes since this review

@rustbot
Copy link
Collaborator

rustbot commented Sep 9, 2025

Some changes occurred to constck

cc @fee1-dead

@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Sep 9, 2025

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@rust-log-analyzer

This comment has been minimized.

@tmandry tmandry added the F-field_projections `#![feature(field_projections)]` label Sep 9, 2025
Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

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

Could you ensure you have a test that is generic over the field and that queries the offset? That would hit all these points I just mentioned here (or at least some of them), and anyway seems like a very non-trivial case to test.

Or... wait the intrinsic is only used to define that one associated const? I don't know exactly when and how we normalize those but it may be that we indeed never see that MIR in its generic form then...

The test may have to invoke the intrinsic directly to trigger the case I am thinking of.

View changes since this review

@rust-log-analyzer
Copy link
Collaborator

The job aarch64-gnu-llvm-20-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
   Compiling rustc_middle v0.0.0 (/checkout/compiler/rustc_middle)
error[E0599]: no variant or associated item named `UnalignedFieldOFFSET` found for enum `rustc_hir::LangItem` in the current scope
    --> compiler/rustc_middle/src/ty/context.rs:3483:75
     |
3483 |                 if self.is_lang_item(def_id.into_query_param(), LangItem::UnalignedFieldOFFSET) => {
     |                                                                           ^^^^^^^^^^^^^^^^^^^^ variant or associated item not found in `rustc_hir::LangItem`

For more information about this error, try `rustc --explain E0599`.
[RUSTC-TIMING] rustc_middle test:false 9.848
error: could not compile `rustc_middle` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-rustdoc-json Area: Rustdoc JSON backend F-field_projections `#![feature(field_projections)]` I-lang-radar Items that are on lang's radar and will need eventual work or consideration. PG-exploit-mitigations Project group: Exploit mitigations 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 Relevant to the rustdoc 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.

9 participants