Skip to content

Commit 048b879

Browse files
committed
Auto merge of #142789 - oli-obk:constable-type-id, r=<try>
Make TypeId const comparable This should unblock stabilizing const `TypeId::of` and allow us to progress into any possible future we want to take `TypeId` to. To achieve that `TypeId` now contains `16 / size_of<usize>()` pointers which each are actually just `size_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 refactor `TypeId` to any other scheme in the future without breaking anyone who was tempted to transmute `TypeId` 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). * #77125 r? `@RalfJung`
2 parents 6b3ae3f + b1aec1a commit 048b879

File tree

43 files changed

+432
-151
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+432
-151
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ pub(crate) fn codegen_const_value<'tcx>(
175175
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
176176
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
177177
}
178+
GlobalAlloc::TypeId { .. } => {
179+
return CValue::const_val(
180+
fx,
181+
layout,
182+
ScalarInt::try_from_target_usize(offset.bytes(), fx.tcx).unwrap(),
183+
);
184+
}
178185
GlobalAlloc::Static(def_id) => {
179186
assert!(fx.tcx.is_static(def_id));
180187
let data_id = data_id_for_static(
@@ -360,6 +367,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
360367
GlobalAlloc::Memory(alloc) => alloc,
361368
GlobalAlloc::Function { .. }
362369
| GlobalAlloc::Static(_)
370+
| GlobalAlloc::TypeId { .. }
363371
| GlobalAlloc::VTable(..) => {
364372
unreachable!()
365373
}
@@ -471,6 +479,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
471479
.principal()
472480
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
473481
),
482+
GlobalAlloc::TypeId { .. } => {
483+
// Nothing to do, the bytes/offset of this pointer have already been written together with all other bytes,
484+
// so we just need to drop this provenance.
485+
continue;
486+
}
474487
GlobalAlloc::Static(def_id) => {
475488
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
476489
{

compiler/rustc_codegen_gcc/src/common.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use gccjit::{LValue, RValue, ToRValue, Type};
2-
use rustc_abi as abi;
3-
use rustc_abi::HasDataLayout;
42
use rustc_abi::Primitive::Pointer;
3+
use rustc_abi::{self as abi, HasDataLayout};
54
use rustc_codegen_ssa::traits::{
65
BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods,
76
};
@@ -282,6 +281,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
282281
let init = self.const_data_from_alloc(alloc);
283282
self.static_addr_of(init, alloc.inner().align, None)
284283
}
284+
GlobalAlloc::TypeId { .. } => {
285+
let val = self.const_usize(offset.bytes());
286+
// This is still a variable of pointer type, even though we only use the provenance
287+
// of that pointer in CTFE and Miri. But to make LLVM's type system happy,
288+
// we need an int-to-ptr cast here (it doesn't matter at all which provenance that picks).
289+
return self.context.new_cast(None, val, ty);
290+
}
285291
GlobalAlloc::Static(def_id) => {
286292
assert!(self.tcx.is_static(def_id));
287293
self.get_static(def_id).get_address(None)

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
use std::borrow::Borrow;
44

55
use libc::{c_char, c_uint};
6-
use rustc_abi as abi;
7-
use rustc_abi::HasDataLayout;
86
use rustc_abi::Primitive::Pointer;
7+
use rustc_abi::{self as abi, HasDataLayout as _};
98
use rustc_ast::Mutability;
109
use rustc_codegen_ssa::common::TypeKind;
1110
use rustc_codegen_ssa::traits::*;
@@ -284,7 +283,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
284283
self.const_bitcast(llval, llty)
285284
};
286285
} else {
287-
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
286+
let init =
287+
const_alloc_to_llvm(self, alloc.inner(), /*static*/ false);
288288
let alloc = alloc.inner();
289289
let value = match alloc.mutability {
290290
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
@@ -316,15 +316,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
316316
}),
317317
)))
318318
.unwrap_memory();
319-
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
320-
let value = self.static_addr_of_impl(init, alloc.inner().align, None);
321-
value
319+
let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false);
320+
self.static_addr_of_impl(init, alloc.inner().align, None)
322321
}
323322
GlobalAlloc::Static(def_id) => {
324323
assert!(self.tcx.is_static(def_id));
325324
assert!(!self.tcx.is_thread_local_static(def_id));
326325
self.get_static(def_id)
327326
}
327+
GlobalAlloc::TypeId { .. } => {
328+
// Drop the provenance, the offset contains the bytes of the hash
329+
let llval = self.const_usize(offset.bytes());
330+
return unsafe { llvm::LLVMConstIntToPtr(llval, llty) };
331+
}
328332
};
329333
let base_addr_space = global_alloc.address_space(self);
330334
let llval = unsafe {
@@ -346,7 +350,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
346350
}
347351

348352
fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value {
349-
const_alloc_to_llvm(self, alloc, /*static*/ false)
353+
const_alloc_to_llvm(self, alloc.inner(), /*static*/ false)
350354
}
351355

352356
fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,9 @@ use crate::{base, debuginfo};
2727

2828
pub(crate) fn const_alloc_to_llvm<'ll>(
2929
cx: &CodegenCx<'ll, '_>,
30-
alloc: ConstAllocation<'_>,
30+
alloc: &Allocation,
3131
is_static: bool,
3232
) -> &'ll Value {
33-
let alloc = alloc.inner();
3433
// We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
3534
// integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
3635
// producing empty LLVM allocations as they're just adding noise to binaries and forcing less
@@ -141,7 +140,7 @@ fn codegen_static_initializer<'ll, 'tcx>(
141140
def_id: DefId,
142141
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
143142
let alloc = cx.tcx.eval_static_initializer(def_id)?;
144-
Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc))
143+
Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc))
145144
}
146145

147146
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {

compiler/rustc_codegen_ssa/src/mir/operand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
186186
offset: Size,
187187
) -> Self {
188188
let alloc_align = alloc.inner().align;
189-
assert!(alloc_align >= layout.align.abi);
189+
assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi);
190190

191191
let read_scalar = |start, size, s: abi::Scalar, ty| {
192192
match alloc.0.read_scalar(

compiler/rustc_const_eval/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const_eval_dealloc_kind_mismatch =
7878
7979
const_eval_deref_function_pointer =
8080
accessing {$allocation} which contains a function
81+
const_eval_deref_typeid_pointer =
82+
accessing {$allocation} which contains a `TypeId`
8183
const_eval_deref_vtable_pointer =
8284
accessing {$allocation} which contains a vtable
8385
const_eval_division_by_zero =

compiler/rustc_const_eval/src/errors.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
475475
WriteToReadOnly(_) => const_eval_write_to_read_only,
476476
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
477477
DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
478+
DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer,
478479
InvalidBool(_) => const_eval_invalid_bool,
479480
InvalidChar(_) => const_eval_invalid_char,
480481
InvalidTag(_) => const_eval_invalid_tag,
@@ -588,7 +589,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
588589
diag.arg("has", has.bytes());
589590
diag.arg("msg", format!("{msg:?}"));
590591
}
591-
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
592+
WriteToReadOnly(alloc)
593+
| DerefFunctionPointer(alloc)
594+
| DerefVTablePointer(alloc)
595+
| DerefTypeIdPointer(alloc) => {
592596
diag.arg("allocation", alloc);
593597
}
594598
InvalidBool(b) => {

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use std::assert_matches::assert_matches;
66

7-
use rustc_abi::Size;
7+
use rustc_abi::{FieldIdx, Size};
88
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
99
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
1010
use rustc_middle::ty::layout::TyAndLayout;
@@ -28,8 +28,35 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
2828
let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ());
2929
tcx.mk_const_alloc(alloc)
3030
}
31-
3231
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
32+
/// Generates a value of `TypeId` for `ty` in-place.
33+
pub(crate) fn write_type_id(
34+
&mut self,
35+
ty: Ty<'tcx>,
36+
dest: &PlaceTy<'tcx, M::Provenance>,
37+
) -> InterpResult<'tcx, ()> {
38+
let tcx = self.tcx;
39+
let type_id_hash = tcx.type_id_hash(ty).as_u128();
40+
let op = self.const_val_to_op(
41+
ConstValue::Scalar(Scalar::from_u128(type_id_hash)),
42+
tcx.types.u128,
43+
None,
44+
)?;
45+
self.copy_op_allow_transmute(&op, dest)?;
46+
47+
// Give the first pointer-size bytes provenance that knows about the type id.
48+
// Here we rely on `TypeId` being a newtype around an array of pointers, so we
49+
// first project to its only field and then the first array element.
50+
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
51+
let first = self.project_field(dest, FieldIdx::ZERO)?;
52+
let first = self.project_index(&first, 0)?;
53+
let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?;
54+
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
55+
let ptr = self.global_root_pointer(ptr)?;
56+
let val = Scalar::from_pointer(ptr, &tcx);
57+
self.write_scalar(val, &first)
58+
}
59+
3360
/// Returns `true` if emulation happened.
3461
/// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
3562
/// intrinsic handling.
@@ -63,9 +90,48 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6390
sym::type_id => {
6491
let tp_ty = instance.args.type_at(0);
6592
ensure_monomorphic_enough(tcx, tp_ty)?;
66-
let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128());
67-
let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
68-
self.copy_op(&val, dest)?;
93+
self.write_type_id(tp_ty, dest)?;
94+
}
95+
sym::type_id_eq => {
96+
// Both operands are `TypeId`, which is a newtype around an array of pointers.
97+
// Project until we have the array elements.
98+
let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
99+
let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
100+
101+
let mut a_fields = self.project_array_fields(&a_fields)?;
102+
let mut b_fields = self.project_array_fields(&b_fields)?;
103+
104+
let (_idx, a) = a_fields
105+
.next(self)?
106+
.expect("we know the layout of TypeId has at least 2 array elements");
107+
let a = self.deref_pointer(&a)?;
108+
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
109+
110+
let (_idx, b) = b_fields
111+
.next(self)?
112+
.expect("we know the layout of TypeId has at least 2 array elements");
113+
let b = self.deref_pointer(&b)?;
114+
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
115+
116+
let provenance_matches = a == b;
117+
118+
let mut eq_id = offset_a == offset_b;
119+
120+
while let Some((_, a)) = a_fields.next(self)? {
121+
let (_, b) = b_fields.next(self)?.unwrap();
122+
123+
let a = self.read_target_usize(&a)?;
124+
let b = self.read_target_usize(&b)?;
125+
eq_id &= a == b;
126+
}
127+
128+
if !eq_id && provenance_matches {
129+
throw_ub_format!(
130+
"type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
131+
)
132+
}
133+
134+
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
69135
}
70136
sym::variant_count => {
71137
let tp_ty = instance.args.type_at(0);

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use std::{fmt, ptr};
1515
use rustc_abi::{Align, HasDataLayout, Size};
1616
use rustc_ast::Mutability;
1717
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
18-
use rustc_middle::bug;
1918
use rustc_middle::mir::display_allocation;
2019
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
20+
use rustc_middle::{bug, throw_ub_format};
2121
use tracing::{debug, instrument, trace};
2222

2323
use super::{
@@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
346346
kind = "vtable",
347347
)
348348
}
349+
Some(GlobalAlloc::TypeId { .. }) => {
350+
err_ub_custom!(
351+
fluent::const_eval_invalid_dealloc,
352+
alloc_id = alloc_id,
353+
kind = "typeid",
354+
)
355+
}
349356
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
350357
err_ub_custom!(
351358
fluent::const_eval_invalid_dealloc,
@@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
615622
}
616623
Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
617624
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
625+
Some(GlobalAlloc::TypeId { .. }) => throw_ub!(DerefTypeIdPointer(id)),
618626
None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
619627
Some(GlobalAlloc::Static(def_id)) => {
620628
assert!(self.tcx.is_static(def_id));
@@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
896904
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
897905
let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
898906
let kind = match global_alloc {
899-
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
907+
GlobalAlloc::TypeId { .. }
908+
| GlobalAlloc::Static { .. }
909+
| GlobalAlloc::Memory { .. } => AllocKind::LiveData,
900910
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
901911
GlobalAlloc::VTable { .. } => AllocKind::VTable,
902912
};
@@ -936,6 +946,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
936946
}
937947
}
938948

949+
/// Takes a pointer that is the first chunk of a `TypeId` and return the type that its
950+
/// provenance refers to, as well as the segment of the hash that this pointer covers.
951+
pub fn get_ptr_type_id(
952+
&self,
953+
ptr: Pointer<Option<M::Provenance>>,
954+
) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
955+
let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
956+
let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
957+
throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
958+
};
959+
interp_ok((ty, offset))
960+
}
961+
939962
pub fn get_ptr_fn(
940963
&self,
941964
ptr: Pointer<Option<M::Provenance>>,
@@ -1197,6 +1220,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
11971220
Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
11981221
write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?;
11991222
}
1223+
Some(GlobalAlloc::TypeId { ty }) => {
1224+
write!(fmt, " (typeid for {ty})")?;
1225+
}
12001226
Some(GlobalAlloc::Static(did)) => {
12011227
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
12021228
}

compiler/rustc_const_eval/src/interpret/projection.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,11 @@ where
296296
base: &'a P,
297297
) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> {
298298
let abi::FieldsShape::Array { stride, .. } = base.layout().fields else {
299-
span_bug!(self.cur_span(), "project_array_fields: expected an array layout");
299+
span_bug!(
300+
self.cur_span(),
301+
"project_array_fields: expected an array layout, got {:#?}",
302+
base.layout()
303+
);
300304
};
301305
let len = base.len(self)?;
302306
let field_layout = base.layout().field(self, 0);

0 commit comments

Comments
 (0)