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

@rust-bors rust-bors bot 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 9, 2026
@oli-obk
Copy link
Contributor Author

oli-obk commented Jan 9, 2026

@bors r=BoxyUwU

@rust-bors rust-bors bot 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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jan 9, 2026
@rust-bors
Copy link
Contributor

rust-bors bot commented Jan 9, 2026

📌 Commit 82028b0 has been approved by BoxyUwU

It is now in the queue for this repository.

Zalathar added a commit to Zalathar/rust that referenced this pull request Jan 10, 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: rust-lang#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
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jan 10, 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: rust-lang#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
@matthiaskrgr
Copy link
Member

@bors p=6
since this is at the bottom now due to ordering bug rust-lang/bors#605

@rust-bors

This comment has been minimized.

@rust-bors rust-bors bot added the merged-by-bors This PR was explicitly merged by bors. label Jan 10, 2026
@rust-bors
Copy link
Contributor

rust-bors bot commented Jan 10, 2026

☀️ Test successful - CI
Approved by: BoxyUwU
Pushing f57eac1 to main...

@rust-bors rust-bors bot merged commit f57eac1 into rust-lang:main Jan 10, 2026
12 checks passed
@rustbot rustbot added this to the 1.94.0 milestone Jan 10, 2026
@github-actions
Copy link
Contributor

What is this? This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.

Comparing 1b9ae9e (parent) -> f57eac1 (this PR)

Test differences

Show 172 test diffs

Stage 1

  • [ui] tests/ui/reflection/dump.rs: [missing] -> pass (J1)
  • [ui] tests/ui/reflection/feature_gate.rs: [missing] -> pass (J1)
  • [ui] tests/ui/reflection/tuples.rs: [missing] -> pass (J1)

Stage 2

  • [ui] tests/ui/reflection/dump.rs: [missing] -> pass (J0)
  • [ui] tests/ui/reflection/feature_gate.rs: [missing] -> pass (J0)
  • [ui] tests/ui/reflection/tuples.rs: [missing] -> pass (J0)

Additionally, 166 doctest diffs were found. These are ignored, as they are noisy.

Job group index

Test dashboard

Run

cargo run --manifest-path src/ci/citool/Cargo.toml -- \
    test-dashboard f57eac1bf98cb5d578e3364b64365ec398c137df --output-dir test-dashboard

And then open test-dashboard/index.html in your browser to see an overview of all executed tests.

Job duration changes

  1. dist-apple-various: 4445.5s -> 3152.6s (-29.1%)
  2. dist-x86_64-apple: 9025.2s -> 6876.6s (-23.8%)
  3. pr-check-1: 1668.6s -> 2041.7s (+22.4%)
  4. i686-gnu-2: 5277.0s -> 6131.2s (+16.2%)
  5. test-various: 6612.2s -> 7617.9s (+15.2%)
  6. x86_64-gnu-llvm-21-1: 3912.6s -> 4478.4s (+14.5%)
  7. x86_64-gnu-llvm-20-2: 5098.1s -> 5804.9s (+13.9%)
  8. x86_64-gnu-llvm-20-1: 3821.5s -> 4337.6s (+13.5%)
  9. armhf-gnu: 4942.1s -> 5585.1s (+13.0%)
  10. aarch64-gnu-debug: 3933.2s -> 4383.9s (+11.5%)
How to interpret the job duration changes?

Job durations can vary a lot, based on the actual runner instance
that executed the job, system noise, invalidated caches, etc. The table above is provided
mostly for t-infra members, for simpler debugging of potential CI slow-downs.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (f57eac1): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results (primary -1.9%, secondary 0.7%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.7% [0.5%, 2.9%] 9
Improvements ✅
(primary)
-1.9% [-1.9%, -1.9%] 1
Improvements ✅
(secondary)
-3.6% [-4.6%, -2.6%] 2
All ❌✅ (primary) -1.9% [-1.9%, -1.9%] 1

Cycles

Results (primary -2.4%, secondary -0.8%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.8% [2.8%, 2.8%] 1
Improvements ✅
(primary)
-2.4% [-3.1%, -2.0%] 4
Improvements ✅
(secondary)
-2.5% [-2.6%, -2.5%] 2
All ❌✅ (primary) -2.4% [-3.1%, -2.0%] 4

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 472.721s -> 473.812s (0.23%)
Artifact size: 391.33 MiB -> 391.34 MiB (0.00%)

@traviscross traviscross added the I-lang-radar Items that are on lang's radar and will need eventual work or consideration. label Jan 11, 2026
@SpriteOvO SpriteOvO added the F-type_info #![feature(type_info)] label Jan 14, 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 F-type_info #![feature(type_info)] I-lang-radar Items that are on lang's radar and will need eventual work or consideration. merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. 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.