Skip to content

Commit ee00432

Browse files
committed
Add opaque TypeId handles for CTFE
1 parent 0d11be5 commit ee00432

File tree

42 files changed

+424
-147
lines changed

Some content is hidden

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

42 files changed

+424
-147
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
@@ -138,7 +137,7 @@ fn codegen_static_initializer<'ll, 'tcx>(
138137
def_id: DefId,
139138
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
140139
let alloc = cx.tcx.eval_static_initializer(def_id)?;
141-
Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc))
140+
Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc))
142141
}
143142

144143
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: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
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};
9+
use rustc_ast::Mutability;
10+
use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range};
911
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
1012
use rustc_middle::ty::layout::TyAndLayout;
1113
use rustc_middle::ty::{Ty, TyCtxt};
@@ -29,6 +31,37 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
2931
tcx.mk_const_alloc(alloc)
3032
}
3133

34+
pub(crate) fn alloc_type_id<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> AllocId {
35+
let size = Size::from_bytes(16);
36+
let align = tcx.data_layout.pointer_align;
37+
let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ());
38+
let ptr_size = tcx.data_layout.pointer_size;
39+
let type_id_hash = tcx.type_id_hash(ty).as_u128();
40+
alloc
41+
.write_scalar(
42+
&tcx,
43+
alloc_range(Size::ZERO, Size::from_bytes(16)),
44+
Scalar::from_u128(type_id_hash),
45+
)
46+
.unwrap();
47+
48+
// Give the first pointer-size bytes provenance that knows about the type id
49+
50+
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
51+
let offset = alloc
52+
.read_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), false)
53+
.unwrap()
54+
.to_target_usize(&tcx)
55+
.unwrap();
56+
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
57+
let val = Scalar::from_pointer(ptr, &tcx);
58+
alloc.write_scalar(&tcx, alloc_range(Size::ZERO, ptr_size), val).unwrap();
59+
60+
alloc.mutability = Mutability::Not;
61+
62+
tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc))
63+
}
64+
3265
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
3366
/// Returns `true` if emulation happened.
3467
/// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
@@ -63,10 +96,50 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
6396
sym::type_id => {
6497
let tp_ty = instance.args.type_at(0);
6598
ensure_monomorphic_enough(tcx, tp_ty)?;
66-
let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128());
99+
let alloc_id = alloc_type_id(tcx, tp_ty);
100+
let val = ConstValue::Indirect { alloc_id, offset: Size::ZERO };
67101
let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?;
68102
self.copy_op(&val, dest)?;
69103
}
104+
sym::type_id_eq => {
105+
// Both operands are `TypeId`, which is a newtype around an array of pointers.
106+
// Project until we have the array elements.
107+
let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
108+
let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
109+
110+
let mut a_fields = self.project_array_fields(&a_fields)?;
111+
let mut b_fields = self.project_array_fields(&b_fields)?;
112+
113+
let (_idx, a) = a_fields
114+
.next(self)?
115+
.expect("we know the layout of TypeId has at least 2 array elements");
116+
let a = self.deref_pointer(&a)?;
117+
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
118+
119+
let (_idx, b) = b_fields
120+
.next(self)?
121+
.expect("we know the layout of TypeId has at least 2 array elements");
122+
let b = self.deref_pointer(&b)?;
123+
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
124+
125+
let provenance_matches = a == b;
126+
127+
let mut eq_id = offset_a == offset_b;
128+
129+
while let Some((_, a)) = a_fields.next(self)? {
130+
let (_, b) = b_fields.next(self)?.unwrap();
131+
132+
let a = self.read_target_usize(&a)?;
133+
let b = self.read_target_usize(&b)?;
134+
eq_id &= a == b;
135+
}
136+
137+
if !eq_id && provenance_matches {
138+
throw_ub_format!("modifying `TypeId` internals is not permitted")
139+
}
140+
141+
self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
142+
}
70143
sym::variant_count => {
71144
let tp_ty = instance.args.type_at(0);
72145
let ty = match tp_ty.kind() {

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)