Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Stabilize const TypeId::of#144133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
oli-obk wants to merge1 commit intorust-lang:master
base:master
Choose a base branch
Loading
fromoli-obk:stabilize-const-type-id

Conversation

oli-obk
Copy link
Contributor

@oli-obkoli-obk commentedJul 18, 2025
edited
Loading

fixes#77125

Stabilization report forconst_type_id

General design

What is the RFC for this feature and what changes have occurred to the user-facing design since the RFC was finalized?

N/A the constness was never RFCed

What behavior are we committing to that has been controversial? Summarize the major arguments pro/con.

const_type_id was kept unstable because we are currently unable to stabilize thePartialEq impl for it (in const contexts), so we feared people would transmute the type id to an integer and compare that integer.

Are there extensions to this feature that remain unstable? How do we know that we are not accidentally committing to those?

TypeId::eq is not const at this time, and will only become const once const traits are stable.

Has a Call for Testing period been conducted? If so, what feedback was received?

This feature has been unstable for a long time, and most people just worked around it on stable by storing a pointer toTypeId::of and calling that at "runtime" (usually LLVM devirtualized the function pointer and inlined the call so there was no real performance difference).

A lot of people seem to be using theconst_type_id feature gate (600 results for the feature gate on github:https://github.com/search?q=%22%23%21%5Bfeature%28const_type_id%29%5D%22&type=code)

We have had very little feedback except desire for stabilization being expressed.

Implementation quality

Until these three PRs

there was no difference between the const eval feature and the runtime feature except that we prevented you from usingTypeId::of at compile-time. These three recent PRs have hardened the internals ofTypeId:

  • it now contains an array of pointers instead of integers
  • these pointers at compile-time (and in miri) contain provenance that makes them unique and prevents inspection. Both miri and CTFE will in fact error if you mess with the bits or the provenance of the pointers in any way and then try to use theTypeId for an equality check. This also guards against creating values of typeTypeId by any means other thanTypeId::of

Summarize the major parts of the implementation and provide links into the code (or to PRs)

N/A see above

Summarize existing test coverage of this feature

Since we are not stabilizing any operations onTypeId except for creatingTypeIds, the test coverage of the runtime implementation ofTypeId covers all the interesting use cases not in the list below

Hardening against transmutes

TypeId::eq is still unstable

What outstanding bugs in the issue tracker involve this feature? Are they stabilization-blocking?

#129014 is still unresolved, but it affects more the runtime version ofTypeId than the compile-time.

What FIXMEs are still in the code for that feature and why is it ok to leave them there?

none

Summarize contributors to the feature by name for recognition and assuredness that people involved in the feature agree with stabilization

Which tools need to be adjusted to support this feature. Has this work been done?

N/A

Type system and execution rules

What compilation-time checks are done that are needed to prevent undefined behavior?

Already covered above. Transmuting types with private fields to expose those fields has always been library UB, but for the specific case ofTypeId CTFE and Miri will detect it if that is done in any way other than for reconstructing the exact sameTypeId in another location.

Does the feature's implementation need checks to prevent UB or is it sound by default and needs opt in in places to perform the dangerous/unsafe operations? If it is not sound by default, what is the rationale?

N/A

Can users use this feature to introduce undefined behavior, or use this feature to break the abstraction of Rust and expose the underlying assembly-level implementation? (Describe.)

N/A

What updates are needed to the reference/specification? (link to PRs when they exist)

Nothing more than what needs to exist forTypeId already.

Common interactions

Does this feature introduce new expressions and can they produce temporaries? What are the lifetimes of those temporaries?

N/A

What other unstable features may be exposed by this feature?

N/A

bjoernager, kanerogers, alice-i-cecile, fee1-dead, joseluis, and Firestar99 reacted with heart emoji
@oli-obkoli-obk added T-langRelevant to the language team A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...) I-lang-nominatedNominated for discussion during a lang team meeting. labelsJul 18, 2025
@rustbot
Copy link
Collaborator

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.

User? to explicitly pick a reviewer

@rustbotrustbot added the S-waiting-on-reviewStatus: Awaiting review from the assignee but also interested parties. labelJul 18, 2025
@rustbot
Copy link
Collaborator

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@oli-obkoli-obk mentioned this pull requestJul 18, 2025
4 tasks
@rust-log-analyzer

This comment has been minimized.

@oli-obkoli-obkforce-pushed thestabilize-const-type-id branch fromd3b3f08 tod94f60fCompareJuly 18, 2025 13:57
@RalfJung

This comment was marked as resolved.

@oli-obk

This comment was marked as resolved.

@oli-obkoli-obk changed the titleStabilize const TypeId::of andtype_nameStabilize const TypeId::ofJul 18, 2025
@oli-obkoli-obkforce-pushed thestabilize-const-type-id branch fromd94f60f to55cc0baCompareJuly 18, 2025 14:14
@RalfJung
Copy link
Member

RalfJung commentedJul 18, 2025
edited
Loading

OTOH type_name is so clearly documented as being not injective that I am totally fine with stabilizing it if there's a good usecase. Every API relying on type_name comparison is so obviously unsound, I'm not worried about this happening accidentally.

But there's no reason to stabilize it together with TypeId::of.

oli-obk reacted with thumbs up emoji

@rust-log-analyzer
Copy link
Collaborator

The jobx86_64-gnu-llvm-19 failed! Check out the build log:(web)(plain enhanced)(plain)

Click to see the possible cause of the failure (guessed by this bot)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 48) - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 51) - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 66)##[endgroup]Setting extra environment values for docker:  --env ENABLE_GCC_CODEGEN=1 --env GCC_EXEC_PREFIX=/usr/lib/gcc/[CI_JOB_NAME=x86_64-gnu-llvm-19][CI_JOB_NAME=x86_64-gnu-llvm-19]debug: `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` configured.---sccache: Listening on address 127.0.0.1:4226##[group]Configure the buildconfigure: processing command lineconfigure: configure: build.configure-args := ['--build=x86_64-unknown-linux-gnu', '--llvm-root=/usr/lib/llvm-19', '--enable-llvm-link-shared', '--set', 'rust.randomize-layout=true', '--set', 'rust.thin-lto-import-instr-limit=10', '--set', 'build.print-step-timings', '--enable-verbose-tests', '--set', 'build.metrics', '--enable-verbose-configure', '--enable-sccache', '--disable-manage-submodules', '--enable-locked-deps', '--enable-cargo-native-static', '--set', 'rust.codegen-units-std=1', '--set', 'dist.compression-profile=balanced', '--dist-compression-formats=xz', '--disable-dist-src', '--release-channel=nightly', '--enable-debug-assertions', '--enable-overflow-checks', '--enable-llvm-assertions', '--set', 'rust.verify-llvm-ir', '--set', 'rust.codegen-backends=llvm,cranelift,gcc', '--set', 'llvm.static-libstdcpp', '--set', 'gcc.download-ci-gcc=true', '--enable-new-symbol-mangling']configure: build.build          := x86_64-unknown-linux-gnuconfigure: target.x86_64-unknown-linux-gnu.llvm-config := /usr/lib/llvm-19/bin/llvm-configconfigure: llvm.link-shared     := Trueconfigure: rust.randomize-layout := Trueconfigure: rust.thin-lto-import-instr-limit := 10------- [mir-opt] tests/mir-opt/gvn_const_eval_polymorphic.rs stdout ----error: compilation failed!status: exit status: 1command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/mir-opt/gvn_const_eval_polymorphic.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=i686-unknown-linux-gnu" "--check-cfg" "cfg(test,FALSE)" "-O" "-Copt-level=1" "-Zdump-mir=GVN | GVN | GVN" "-Zvalidate-mir" "-Zlint-mir" "-Zdump-mir-exclude-pass-number" "-Zmir-include-spans=false" "--crate-type=rlib" "-Zmir-opt-level=0" "-Zmir-enable-passes=+GVN" "-Zdump-mir-dir=/checkout/obj/build/x86_64-unknown-linux-gnu/test/mir-opt/gvn_const_eval_polymorphic" "--emit" "mir" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/mir-opt/gvn_const_eval_polymorphic" "-A" "internal_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "--crate-type" "lib"stdout: none--- stderr -------------------------------error: `std::any::type_name` is not yet stable as a const fn##[error]  --> /checkout/tests/mir-opt/gvn_const_eval_polymorphic.rs:17:16   |

@traviscrosstraviscross added the P-lang-drag-1Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labelJul 18, 2025
@theemathas
Copy link
Contributor

theemathas commentedJul 18, 2025
edited
Loading

Both miri and CTFE will in fact error if you mess with the bits or the provenance of the pointers in any way and then try to use the TypeId for an equality check.

I'm not sure what exactly you mean by this, but here's some weird code.

#![feature(const_type_id)]use std::mem::transmute;use std::any::TypeId;constfnassert_same_type<T:'static,U:'static>(){unsafe{let id1:TypeId =TypeId::of::<T>();let[ptr1, _]:[*constu8;2] =transmute(id1);let id2:TypeId =TypeId::of::<U>();let[ptr2, _]:[*constu8;2] =transmute(id2);let _ = ptr1.offset_from(ptr2);// This also works:// let _ = ptr1.wrapping_add(1).offset_from(ptr2.wrapping_add(1)));}}constWORKS:() =assert_same_type::<i32,i32>();constFAILS:() =assert_same_type::<i32,i64>();
Compile error
error[E0080]: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation   --> src/lib.rs:19:19    |19  | const FAILS: () = assert_same_type::<i32, i64>();    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `FAILS` failed inside this call    |note: inside `assert_same_type::<i32, i64>`   --> src/lib.rs:12:17    |12  |         let _ = ptr1.offset_from(ptr2);    |                 ^^^^^^^^^^^^^^^^^^^^^^note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`   --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:671:18    |671 |         unsafe { intrinsics::ptr_offset_from(self, origin) }    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the failure occurred hereFor more information about this error, try `rustc --explain E0080`.error: could not compile `playground` (lib) due to 1 previous error

@RalfJung
Copy link
Member

RalfJung commentedJul 18, 2025
edited
Loading

Yes it's possible to write a function that is UB (and thus may halt evaluation) if and only if the two TypeId are different. I wouldn't call this a valid equality test...

You can't use this to do anything conditional on that result though, it just stops compilation (or does whatever else const UB may do). So not sure if it's worth trying to stop this.

@oli-obk
Copy link
ContributorAuthor

LMAO I guess we should have gone with Ralf's route of not reusingAllocIds after all.

@theemathas
Copy link
Contributor

theemathas commentedJul 18, 2025
edited
Loading

I managed to write some code that compiles or not depending on the hash value insideTypeId.

Code

(tested on the playground with1.90.0-nightly (2025-07-17 e4662966273ed58b51f9)

#![feature(const_type_id)]use std::mem::transmute;use std::any::TypeId;/*// I used this code to make the compiler tell me what the hash istrait Super {}trait Sub: Super {}const SHOW_HASH_1: () = unsafe {    let id = TypeId::of::<i32>();    let [first, second]: [*const (); 2] = transmute(id);    let ptr: *const dyn Sub = transmute([second, first]);    let _: *const dyn Super = ptr;};const SHOW_HASH_2: () = unsafe {    let id = TypeId::of::<i32>();    let ptr: *const dyn Sub = transmute(id);    let _: *const dyn Super = ptr;};*/constHASH_1:usize =0x50bb9674fa2df013;constHASH_2:usize =0x56ced5e4a15bd890;constfnassert_is_i32<T:'static,constA:usize,constB:usize>(){const{unsafe{let id =TypeId::of::<T>();let[ptr1, _]:[*constu8;2] =transmute(id);&*ptr1.wrapping_sub(A).cast::<()>()}};const{unsafe{let id =TypeId::of::<T>();let[_, ptr2]:[*constu8;2] =transmute(id);&*ptr2.wrapping_sub(B).cast::<()>()}};}constWORKS:() =assert_is_i32::<i32,HASH_1,HASH_2>();constFAILS:() =assert_is_i32::<i64,HASH_1,HASH_2>();
error[E0080]: constructing invalid value: encountered a null reference  --> src/lib.rs:30:11   |30 |       const { unsafe {   |  ___________^31 | |         let id = TypeId::of::<T>();32 | |         let [ptr1, _]: [*const u8; 2] = transmute(id);33 | |         &*ptr1.wrapping_sub(A).cast::<()>()34 | |     }};   | |______^ it is undefined behavior to use this value   |   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.   = note: the raw bytes of the constant (size: 8, align: 8) {               ╾a27+0xaf86f48ac476294e<imm> (8 ptr bytes)╼                         │ ╾──────╼           }note: erroneous constant encountered  --> src/lib.rs:30:5   |30 | /     const { unsafe {31 | |         let id = TypeId::of::<T>();32 | |         let [ptr1, _]: [*const u8; 2] = transmute(id);33 | |         &*ptr1.wrapping_sub(A).cast::<()>()34 | |     }};   | |______^For more information about this error, try `rustc --explain E0080`.error: could not compile `playground` (lib) due to 1 previous error

@oli-obk
Copy link
ContributorAuthor

I think that is the same situation as you had before. You can't make anif is::<T, u32>() {5} else { 6 } check with this, even if you know the exact hash.

RalfJung reacted with thumbs up emoji

@theemathas

This comment was marked as resolved.

@RalfJung
Copy link
Member

RalfJung commentedJul 18, 2025 via email
edited
Loading

Yeah, that's fundamentally the same thing as above.

@scottmcm
Copy link
Member

I think I agree that I'm not particularly concerned by code like that. Compared to other ways that you can break compilation between versions using const, it feels very minor. For example, you cancurrently compile

u128::from_be_bytes([0; core::char::UNICODE_VERSION.0as_])

which is obviously a bad idea, so I think any transmute tricks on TypeId aren't worth worrying about so long as they don't let you bust the use-the-integer-value firewall.

@theemathas
Copy link
Contributor

In combination withconst_raw_ptr_comparison, I can actually check for type equality and actually produce abool.

Code
#![feature(const_type_id, const_raw_ptr_comparison)]use std::any::TypeId;use std::mem::transmute;use std::ptr::null;constHASH_1:usize =0x50bb9674fa2df013;constHASH_2:usize =0x56ced5e4a15bd890;constfnis_i32<T:'static>() ->bool{unsafe{let id =TypeId::of::<T>();let[ptr1, ptr2]:[*constu8;2] =transmute(id);matches!(ptr1.wrapping_sub(HASH_1).guaranteed_eq(null()),Some(false))            &&matches!(ptr2.wrapping_sub(HASH_2).guaranteed_eq(null()),Some(false))}}constYES:bool =is_i32::<i32>();constNAH:bool =is_i32::<i64>();fnmain(){println!("{YES}");// prints "true"println!("{NAH}");// prints "false"}

@programmerjake
Copy link
Member

In combination withconst_raw_ptr_comparison, I can actually check for type equality and actually produce abool.

A possible solution is to make CTFE pointer comparison check the provenance and always return unknown if either or both pointers areTypeIds, regardless of if they're the same.

@oli-obk
Copy link
ContributorAuthor

Raw pointer comparison in consts are unstable and will stabilize after const traits if ever. With const traits we get real comparisons and do not need to concern ourselves with preventing hacky workarounds

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
No reviews
Assignees

@lcnrlcnr

Labels
A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)I-lang-nominatedNominated for discussion during a lang team meeting.P-lang-drag-1Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-langS-waiting-on-reviewStatus: Awaiting review from the assignee but also interested parties.T-langRelevant to the language team
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Tracking Issue forconst fntype_id
9 participants
@oli-obk@rustbot@rust-log-analyzer@RalfJung@theemathas@scottmcm@programmerjake@traviscross@lcnr

[8]ページ先頭

©2009-2025 Movatter.jp