Skip to content

Conversation

@oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Sep 23, 2025

I am opening this PR for discussion about the general design we should start out with, as there are various options (that are not too hard to transition between each other, so we should totally just pick one and go with it and reiterate later)

r? @scottmcm and @joshtriplett

project goal issue: rust-lang/rust-project-goals#406
tracking issue: #146922

The design currently implemented by this PR is

  • TypeId::info (method, usually used as id.info() returns a Type struct
  • the Type struct has fields that contain information about the type
  • the most notable field is kind, which is a non-exhaustive enum over all possible type kinds and their specific information. So it has a Tuple(Tuple) variant, where the only field is a Tuple struct type that contains more information (The list of type ids that make up the tuple).
  • To get nested type information (like the type of fields) you need to call TypeId::info again.
  • There is only one language intrinsic to go from TypeId to Type, and it does all the work

An alternative design could be

  • Lots of small methods (each backed by an intrinsic) on TypeId that return all the individual information pieces (size, align, number of fields, number of variants, ...)
  • This is how C++ does it (see https://lemire.me/blog/2025/06/22/c26-will-include-compile-time-reflection-why-should-you-care/ and https://isocpp.org/files/papers/P2996R13.html#member-queries)
  • Advantage: you only get the information you ask for, so it's probably cheaper if you get just one piece of information for lots of types (e.g. reimplementing size_of in terms of TypeId::info is likely expensive and wasteful)
  • Disadvantage: lots of method calling (and Option return types, or "general" methods like num_fields returning 0 for primitives) instead of matching and field accesses
  • a crates.io crate could implement TypeId::info in terms of this design

The backing implementation is modular enough that switching from one to the other is probably not an issue, and the alternative design could be easier for the CTFE engine's implementation, just not as nice to use for end users (without crates wrapping the logic)

One wart of this design that I'm fixing in separate branches is that TypeId::info will panic if used at runtime, while it should be uncallable

@rustbot
Copy link
Collaborator

rustbot commented Sep 23, 2025

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

@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 Sep 23, 2025
@oli-obk oli-obk force-pushed the comptime-reflect branch 2 times, most recently from aab0141 to 4234855 Compare September 23, 2025 08:12
@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

/// It can only be called at compile time.
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
pub const fn info(self) -> Type {
Copy link
Member

Choose a reason for hiding this comment

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

This currently has zero regards for semver, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, but it also only supports tuples, which was an explicit choice so we can handle semver related things when we support Adts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unless you mean the fact that it allows inspecting a generic param and now knowing it's a tuple. This can look through opaque types and stuff

Copy link
Member

Choose a reason for hiding this comment

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

Oh right it also breaks parametricity (though specialization also does that).

Not super relevant right now, but I hope "discuss semver considerations" is a major item somewhere on the agenda for this feature. ;) (The tracking issue is still rather barebones at the moment.)

Choose a reason for hiding this comment

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

Maybe controversial, but could Type be limited to viewing types within the current crate, and anything outside it simply be Opaque/Foreign/etc.? Libraries that want the Type of one of their types to be part of the public API could then expose it via a public constant or function returning said constant.

@theemathas
Copy link
Contributor

It seems like... trying to obtain a Type of a struct causes an ICE?

@oli-obk
Copy link
Contributor Author

oli-obk commented Sep 23, 2025

It seems like... trying to obtain a Type of a struct causes an ICE?

Oh whoops, that shouldn't happen. I'll add some more tests

/// Primitives
Leaf,
/// FIXME(#146922): add all the common types
Unimplemented,
Copy link
Contributor

Choose a reason for hiding this comment

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

@addiesh

This comment has been minimized.

@theemathas

This comment has been minimized.

@RalfJung
Copy link
Member

RalfJung commented Sep 24, 2025

This is an MVP. Please do not flood this PR with all your wildest reflection dreams. Anything that suggests to extend the scope of this PR is off-topic.

@theemathas
Copy link
Contributor

Currently, this implementation says that, in the type (u8, dyn Send), the offset of the dyn Send field is 1. Is this correct? I believe that there will be padding bytes before the dyn Send field to make the data inside aligned.

Copy link
Contributor

@onkoe onkoe left a comment

Choose a reason for hiding this comment

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

newer contributor here. basically, I just got some nits you can ignore :)

thank you so much for working on this!!! :D

View changes since this review

Comment on lines 33 to +41

#[unstable(feature = "type_info", issue = "146922")]
pub mod type_info;
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be on purpose, but did you intend to use the same re-export pattern as above?

Otherwise, wouldn't this make a guarantee that everything in this module would become unstable/stable at the same time? (and prevent making changes to those internals..?)

if other MVPs tend to ignore this stuff, please ignore this comment :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if I make the module stable, its internals would not become stable. The module may also stay unstable forever. For easier usage while entirely unstable I'm just making the module publicly available so all internals can be used and played with

@rustbot rustbot assigned BoxyUwU and unassigned wesleywiser Dec 16, 2025
Copy link
Member

@BoxyUwU BoxyUwU left a comment

Choose a reason for hiding this comment

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

neat!

I'm a bit nervous about exposing a Type type- or more specifically I'm nervous about exposing a TyKind equivalent 🤔 Doesn't really matter for experimenting with stuff though, can figure out what we're confident in not being a problem for future evolution of the type system some other time before stabilization.

It's kinda difficult to read the type_info.rs code because it relies so heavily on the type definitions in core but you can't really see them side-by-side with the code you're reading (they're in a whole other file). I guess there's not really anything to do about this? Just a bit unfortunate

View changes since this review

) -> InterpResult<'tcx> {
// project into the `type_info::Tuple::fields` field
let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?;
// get the `type_info::Field` type from `fields: &[Field]`
Copy link
Member

Choose a reason for hiding this comment

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

use a lang item instead? it seems morally weird:tm: to place arbitrary compiler expectations about whatever type happens to be used as the type of this field, rather than having Field be a lang item (clearly indicating it has requirements) and then asserting this field is of type &[Field]

Copy link
Member

Choose a reason for hiding this comment

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

I guess generally it's kinda funny that you wind up not needing to make everything in the libs module be a lang item even though everything in there is morally a lang item with compiler magic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea... I like this while everything is up in the air. Otherwise I need to make all variants lang items, too and that will just pollute the lang item space

Comment on lines +34 to +37
help: consider importing this struct
|
LL + use std::mem::type_info::Type;
|
Copy link
Member

Choose a reason for hiding this comment

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

lol thats quite unfortunate. it would be nice if nameres diagnostics didnt tell people to import unstable stuff

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it does so on stable, as we explicitly filter unstable things.

|
LL | fn a<F: Fn<usize>>(f: F) {}
| ^^^^^^^^^ the trait `Tuple` is not implemented for `usize`
| ^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `usize`
Copy link
Member

Choose a reason for hiding this comment

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

what's going on here? 🤔 two things with the name Tuple in core now so it explicitly specifies which one?

Copy link
Contributor Author

@oli-obk oli-obk Dec 30, 2025

Choose a reason for hiding this comment

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

Yea, there's also the unstable Tuple trait

@BoxyUwU BoxyUwU 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 Dec 30, 2025
@rustbot
Copy link
Collaborator

rustbot commented Jan 3, 2026

triagebot.toml has been modified, there may have been changes to the review queue.

cc @davidtwco, @wesleywiser

@rustbot rustbot added the A-meta Area: Issues & PRs about the rust-lang/rust repository itself label Jan 3, 2026
@rustbot
Copy link
Collaborator

rustbot commented Jan 3, 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.

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 3, 2026

@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 Jan 3, 2026
@BoxyUwU
Copy link
Member

BoxyUwU commented Jan 3, 2026

r=me if CI passes and you notice before i do :>

@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 3, 2026

@bors r=BoxyUwU

@bors
Copy link
Collaborator

bors commented Jan 3, 2026

📌 Commit 34f9cff has been approved by BoxyUwU

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 3, 2026
@JonathanBrouwer
Copy link
Contributor

@bors rollup=iffy
Since this is a large PR with potential for regressions

rem-labels = [
"regression-from-stable-to-beta",
"regression-from-stable-to-nightly",
]
Copy link
Contributor

@JonathanBrouwer JonathanBrouwer Jan 3, 2026

Choose a reason for hiding this comment

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

@oli-obk Is there a reason this PR reformats triagebot.toml?
Is this accidental? If not, can this be in a seperate PR in the future?

Copy link
Member

@fmease fmease Jan 3, 2026

Choose a reason for hiding this comment

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

Likely autoformatted by the editor which uses whatever fmter+cfg it so happens to have + unintentionally committed.

Ideally we'd fixate the fmting via ig .gitattributes, .editorconfig or sth. else? Tho those are likely not recognized by the majority of editors anyway?

@bors
Copy link
Collaborator

bors commented Jan 3, 2026

⌛ Testing commit 34f9cff with merge 6b547bd...

bors added a commit that referenced this pull request Jan 3, 2026
Reflection MVP

I am opening this PR for discussion about the general design we should start out with, as there are various options (that are not too hard to transition between each other, so we should totally just pick one and go with it and reiterate later)

r? `@scottmcm` and `@joshtriplett`

project goal issue: rust-lang/rust-project-goals#406
tracking issue: #146922

The design currently implemented by this PR is

* `TypeId::info` (method, usually used as `id.info()` returns a `Type` struct
* the `Type` struct has fields that contain information about the type
* the most notable field is `kind`, which is a non-exhaustive enum over all possible type kinds and their specific information. So it has a `Tuple(Tuple)` variant, where the only field is a `Tuple` struct type that contains more information (The list of type ids that make up the tuple).
* To get nested type information (like the type of fields) you need to call `TypeId::info` again.
* There is only one language intrinsic to go from `TypeId` to `Type`, and it does all the work

An alternative design could be

* Lots of small methods (each backed by an intrinsic) on `TypeId` that return all the individual information pieces (size, align, number of fields, number of variants, ...)
* This is how C++ does it (see https://lemire.me/blog/2025/06/22/c26-will-include-compile-time-reflection-why-should-you-care/ and https://isocpp.org/files/papers/P2996R13.html#member-queries)
* Advantage: you only get the information you ask for, so it's probably cheaper if you get just one piece of information for lots of types (e.g. reimplementing size_of in terms of `TypeId::info` is likely expensive and wasteful)
* Disadvantage: lots of method calling (and `Option` return types, or "general" methods like `num_fields` returning 0 for primitives) instead of matching and field accesses
* a crates.io crate could implement `TypeId::info` in terms of this design

The backing implementation is modular enough that switching from one to the other is probably not an issue, and the alternative design could be easier for the CTFE engine's implementation, just not as nice to use for end users (without crates wrapping the logic)

One wart of this design that I'm fixing in separate branches is that `TypeId::info` will panic if used at runtime, while it should be uncallable
@rust-log-analyzer
Copy link
Collaborator

The job dist-i586-gnu-i586-i686-musl failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
Type {
    kind: Tuple(
        Tuple {
            fields: [
                Field {
                    ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
                    offset: 0,
                },
                Field {
                    ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb),
                    offset: 1,
                },
                Field {
                    ty: TypeId(0x41223169ff28813ba79b7268a2a968d9),
                    offset: 2,
                },
            ],
        },
    ),
    size: Some(

@bors
Copy link
Collaborator

bors commented Jan 3, 2026

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jan 3, 2026
@BoxyUwU
Copy link
Member

BoxyUwU commented Jan 3, 2026

ah we should not format the entire triagebot.toml :>

@bors r-

@bors bors 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 Jan 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-meta Area: Issues & PRs about the rust-lang/rust repository itself 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. 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.