-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Make TypeId const comparable #142789
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Make TypeId const comparable #142789
Conversation
Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr Some changes occurred in compiler/rustc_codegen_cranelift cc @bjorn3 Some changes occurred to the CTFE / Miri interpreter cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr Some changes occurred to the CTFE / Miri interpreter cc @rust-lang/miri Some changes occurred to the CTFE machinery Some changes occurred in compiler/rustc_codegen_gcc |
This comment has been minimized.
This comment has been minimized.
3cddd21
to
1fd7b66
Compare
This comment has been minimized.
This comment has been minimized.
It will be a while until I have the capacity to review a PR of this scale. Meanwhile, could you say a bit more about the architecture of the change? It seems you want for the "new kind of allocation" approach, but it's not clear from the PR description how exactly that shows up in Also, I am definitely not comfortable landing this by myself, I can only review the const-eval parts. Changing the representation of |
Some changes occurred in compiler/rustc_codegen_ssa |
Well, I got private feedback yesterday that instead of encoding a 16 byte value as an 8 byte pointer to the 16 byte value and an 8 byte hash, I should just do the thing where we split up type id internally into pointer sized chunks and codegen will make a hash out of it again. TLDR: no changes to runtime type id anymore in the latest revision of this PR. Only compile-time type id is now a bit funny |
I'm splitting unrelated parts out, so the high level feedback is already useful and I'll look for libs and codegen ppl to review the appropriate parts |
This comment has been minimized.
This comment has been minimized.
Make `PartialEq` a `const_trait` r? `@fee1-dead` or `@compiler-errors` something generally useful but also required for rust-lang#142789
Make `PartialEq` a `const_trait` r? ``@fee1-dead`` or ``@compiler-errors`` something generally useful but also required for rust-lang#142789
☔ The latest upstream changes (presumably #142906) made this pull request unmergeable. Please resolve the merge conflicts. |
b8a7a10
to
1c47a64
Compare
This comment has been minimized.
This comment has been minimized.
1c47a64
to
bcb4aa2
Compare
This comment has been minimized.
This comment has been minimized.
afa74de
to
6fea6c3
Compare
The Miri subtree was changed cc @rust-lang/miri |
This comment has been minimized.
This comment has been minimized.
6fea6c3
to
2e8cece
Compare
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #143091) made this pull request unmergeable. Please resolve the merge conflicts. |
@rustbot author |
Reminder, once the PR becomes ready for a review, use |
2e8cece
to
aa4c156
Compare
This comment has been minimized.
This comment has been minimized.
aa4c156
to
8a42250
Compare
I implemented this in 8a42250 It made things a lot cleaner and simpler @rustbot ready |
This comment has been minimized.
This comment has been minimized.
8a42250
to
82aa2a9
Compare
☔ The latest upstream changes (presumably #143350) made this pull request unmergeable. Please resolve the merge conflicts. |
82aa2a9
to
871e4eb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay yes I think I can live with this approach. :)
let size = Size::from_bytes(16); | ||
let align = tcx.data_layout.pointer_align; | ||
let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance we can assert that this matches the size and align of the real TypeId
?
@@ -29,6 +31,37 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll | |||
tcx.mk_const_alloc(alloc) | |||
} | |||
|
|||
pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { | |
/// This returns the `AllocId` of a place where a [`TypeId`](https://doc.rust-lang.org/nightly/std/any/struct.TypeId.html) for the given `ty` is stored. | |
pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId { |
I think ideally this would return an OpTy
rather than an AllocId
, makes it conceptually much more clear -- we don't even want a place here.
Or, if we want to optimize for performance, this function should take as argument an MPlaceTy
for where to store the TypeId
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a local commit for making it write to a dest directly. Which is a change I want to do for all the intrinsics that are currently generating a ConstValue and writing that from within the interpreter
I can land that first on master if you prefer. Or just move the commit for typeid into this PR
|
||
// Give the first pointer-size bytes provenance that knows about the type id | ||
|
||
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's two AllocId
in this function, this one and the one being returned. Please give it a more clearly distinguished name.
} | ||
} | ||
} | ||
// These are controlled by rustc and not available for CTFE | ||
GlobalAlloc::Type { .. } => skip_recursive_check = true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this need for Type
but not for functions and vtables?
GlobalAlloc::Type { .. } => { | ||
// Drop the provenance, the offset contains the bytes of the hash | ||
let llval = self.const_usize(offset.bytes()); | ||
return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have to emit an int-to-ptr cast here? I think we should avoid that if at all possible...
@@ -461,6 +461,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | |||
// Make these a NOP, so we get the better Miri-native error messages. | |||
} | |||
|
|||
"type_id_eq" => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what this should do is actually look up the two first pointers, get the corresponding Ty
, and compare those. That also ensures that these are indeed (looking like) TypeId
.
We can then also compare the raw bytes to catch programs doing nasty things, but comparing the actual true types is the main operation IMO. I'd rather not compare provenance as that's abstract data that cannot exist at runtime (as opposed to the Type
allocations which we actually could manifest).
Please add a Miri test that transmutes two u128
to TypeId
and compares them; that should be UB.
const fn type_id_of_val<T: 'static>(_: &T) -> u128 { | ||
std::intrinsics::type_id::<T>() | ||
let name = std::intrinsics::type_name::<T>(); | ||
let len = name.len() as u64; | ||
let len = u64::to_be_bytes(len); | ||
let mut ret = [0; 16]; | ||
let mut i = 0; | ||
while i < 8 { | ||
ret[i] = len[i]; | ||
i += 1; | ||
} | ||
while i < 16 { | ||
ret[i] = name.as_bytes()[i - 8]; | ||
i += 1; | ||
} | ||
u128::from_be_bytes(ret) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this doing...? This definitely cannot stand without a comment.^^
|
||
const _: () = { | ||
let id = TypeId::of::<u8>(); | ||
let id: u8 = unsafe { (&id as *const TypeId).cast::<u8>().read() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let id: u8 = unsafe { (&id as *const TypeId).cast::<u8>().read() }; | |
let id: u8 = unsafe { (&raw const id).cast::<u8>().read() }; |
@@ -403,6 +403,22 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { | |||
let cmp = ecx.guaranteed_cmp(a, b)?; | |||
ecx.write_scalar(Scalar::from_u8(cmp), dest)?; | |||
} | |||
sym::type_id_eq => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think const-eval and Miri can use the same implementation.
|
||
#[inline] | ||
fn rt(a: &TypeId, b: &TypeId) -> bool { | ||
a.data == b.data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means Miri never calls the intrinsic, right?
This should unblock stabilizing const
TypeId::of
and allow us to progress into any possible future we want to takeTypeId
to.To achieve that
TypeId
now contains16 / size_of<usize>()
pointers which each are actually justsize_of<usize>()
bytes of the stable hash. At compile-time the first of these pointers cannot be dereferenced or otherwise inspected (at present doing so might ICE the compiler). Preventing inspection of this data allows us to refactorTypeId
to any other scheme in the future without breaking anyone who was tempted to transmuteTypeId
to obtain the hash at compile-time.cc @eddyb for their previous work on #95845 (which we still can do in the future if we want to get rid of the hash as the final thing that declares two TypeIds as equal).
const fn
type_id
#77125r? @RalfJung