Skip to content

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ pub(crate) fn codegen_const_value<'tcx>(
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
// TODO: generate segment of type id hash
GlobalAlloc::Type { .. } => todo!(),
GlobalAlloc::Static(def_id) => {
assert!(fx.tcx.is_static(def_id));
let data_id = data_id_for_static(
Expand Down Expand Up @@ -360,6 +362,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
GlobalAlloc::Memory(alloc) => alloc,
GlobalAlloc::Function { .. }
| GlobalAlloc::Static(_)
| GlobalAlloc::Type { .. }
| GlobalAlloc::VTable(..) => {
unreachable!()
}
Expand Down Expand Up @@ -471,6 +474,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
.principal()
.map(|principal| tcx.instantiate_bound_regions_with_erased(principal)),
),
// TODO
GlobalAlloc::Type { .. } => todo!(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs something like data_id_for_type similar to data_id_for_vtable, which can then also be used in codegen_const_value.

GlobalAlloc::Static(def_id) => {
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
{
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
dest.write_cvalue(fx, val);
}

sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => {
sym::needs_drop | sym::type_name | sym::variant_count => {
intrinsic_args!(fx, args => (); intrinsic);

let const_val = fx
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_gcc/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
let init = self.const_data_from_alloc(alloc);
self.static_addr_of(init, alloc.inner().align, None)
}
// TODO: generate segment of type id hash
GlobalAlloc::Type { .. } => todo!(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be mostly the same as the LLVM implementation, except the call to LLVMConstIntToPtr will be a call to Builder::inttoptr and the call to const_alloc_to_llvm should be a call to CodegenCx::const_data_from_alloc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I should really move this to codegen_as and expose those primitives

GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id));
self.get_static(def_id).get_address(None)
Expand Down
41 changes: 33 additions & 8 deletions compiler/rustc_codegen_llvm/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
use std::borrow::Borrow;

use libc::{c_char, c_uint};
use rustc_abi as abi;
use rustc_abi::HasDataLayout;
use rustc_abi::Primitive::Pointer;
use rustc_abi::{self as abi, Align, HasDataLayout as _, Size};
use rustc_ast::Mutability;
use rustc_codegen_ssa::common::TypeKind;
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hashes::Hash128;
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
use rustc_middle::mir::interpret::{
AllocInit, Allocation, ConstAllocation, GlobalAlloc, Scalar, alloc_range,
};
use rustc_middle::ty::TyCtxt;
use rustc_session::cstore::DllImport;
use tracing::debug;
Expand Down Expand Up @@ -284,7 +285,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
self.const_bitcast(llval, llty)
};
} else {
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
let init =
const_alloc_to_llvm(self, alloc.inner(), /*static*/ false);
let alloc = alloc.inner();
let value = match alloc.mutability {
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
Expand Down Expand Up @@ -316,15 +318,38 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
}),
)))
.unwrap_memory();
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
let value = self.static_addr_of_impl(init, alloc.inner().align, None);
value
let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false);
self.static_addr_of_impl(init, alloc.inner().align, None)
}
GlobalAlloc::Static(def_id) => {
assert!(self.tcx.is_static(def_id));
assert!(!self.tcx.is_thread_local_static(def_id));
self.get_static(def_id)
}
GlobalAlloc::Type { ty, segment } => {
let type_id = self.tcx.type_id_hash(ty).as_u128();
let mut alloc: Allocation = Allocation::new(
Size::from_bytes(16),
Align::from_bytes(8).unwrap(),
AllocInit::Uninit,
(),
);
alloc
.write_scalar(
&self.tcx,
alloc_range(Size::ZERO, Size::from_bytes(16)),
Scalar::from_u128(type_id),
)
.unwrap();
let pointer_size = self.tcx.data_layout.pointer_size;
let offset = pointer_size * u64::from(segment);
let value = alloc
.read_scalar(&self.tcx, alloc_range(offset, pointer_size), false)
.unwrap();
let data = value.to_bits(pointer_size).unwrap() as u64;
let llval = self.const_usize(data);
return unsafe { llvm::LLVMConstIntToPtr(llval, llty) };
}
};
let base_addr_space = global_alloc.address_space(self);
let llval = unsafe {
Expand All @@ -346,7 +371,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
}

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

fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ use crate::{base, debuginfo};

pub(crate) fn const_alloc_to_llvm<'ll>(
cx: &CodegenCx<'ll, '_>,
alloc: ConstAllocation<'_>,
alloc: &Allocation,
is_static: bool,
) -> &'ll Value {
let alloc = alloc.inner();
// We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
// integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
// producing empty LLVM allocations as they're just adding noise to binaries and forcing less
Expand Down Expand Up @@ -138,7 +137,7 @@ fn codegen_static_initializer<'ll, 'tcx>(
def_id: DefId,
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
let alloc = cx.tcx.eval_static_initializer(def_id)?;
Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc))
Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc))
}

fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
value
}
sym::needs_drop | sym::type_id | sym::type_name | sym::variant_count => {
sym::needs_drop | sym::type_name | sym::variant_count => {
let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap();
OperandRef::from_const(bx, value, result.layout.ty).immediate_or_packed_pair(bx)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
offset: Size,
) -> Self {
let alloc_align = alloc.inner().align;
assert!(alloc_align >= layout.align.abi);
assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi);

let read_scalar = |start, size, s: abi::Scalar, ty| {
match alloc.0.read_scalar(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ const_eval_dealloc_kind_mismatch =
const_eval_deref_function_pointer =
accessing {$allocation} which contains a function
const_eval_deref_typeid_pointer =
accessing {$allocation} which contains a `TypeId`
const_eval_deref_vtable_pointer =
accessing {$allocation} which contains a vtable
const_eval_division_by_zero =
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> {
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def {
if let ty::InstanceKind::Intrinsic(def_id) = key.value.instance.def
&& key.value.promoted.is_none()
{
let ty = key.value.instance.ty(tcx, key.typing_env);
let ty::FnDef(_, args) = ty.kind() else {
bug!("intrinsic with type {:?}", ty);
Expand Down
33 changes: 32 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::borrow::{Borrow, Cow};
use std::fmt;
use std::hash::Hash;

use rustc_abi::{Align, Size};
use rustc_abi::{Align, FieldIdx, Size};
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry};
use rustc_hir::def_id::{DefId, LocalDefId};
Expand Down Expand Up @@ -403,6 +403,37 @@ 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 => {
let a = ecx.project_field(&args[0], FieldIdx::ZERO)?;
let b = ecx.project_field(&args[1], FieldIdx::ZERO)?;
let mut eq = true;
for index in 0..(16 / ecx.tcx.data_layout.pointer_size.bytes()) {
let a = ecx.project_index(&a, index)?;
let a = ecx.deref_pointer(&a)?;
let (a, offset) = a.ptr().into_parts();
assert_eq!(offset, Size::ZERO);
let a = a.unwrap().alloc_id();
let GlobalAlloc::Type { ty: a, segment: a_segment } = ecx.tcx.global_alloc(a)
else {
bug!()
};
let b = ecx.project_index(&b, index)?;
let b = ecx.deref_pointer(&b)?;
let (b, offset) = b.ptr().into_parts();
assert_eq!(offset, Size::ZERO);
let b = b.unwrap().alloc_id();
let GlobalAlloc::Type { ty: b, segment: b_segment } = ecx.tcx.global_alloc(b)
else {
bug!()
};

eq &= a == b && a_segment == b_segment;
if !eq {
break;
}
}
ecx.write_scalar(Scalar::from_bool(eq), dest)?;
}
sym::const_allocate => {
let size = ecx.read_scalar(&args[0])?.to_target_usize(ecx)?;
let align = ecx.read_scalar(&args[1])?.to_target_usize(ecx)?;
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
WriteToReadOnly(_) => const_eval_write_to_read_only,
DerefFunctionPointer(_) => const_eval_deref_function_pointer,
DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer,
InvalidBool(_) => const_eval_invalid_bool,
InvalidChar(_) => const_eval_invalid_char,
InvalidTag(_) => const_eval_invalid_tag,
Expand Down Expand Up @@ -603,7 +604,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
diag.arg("has", has.bytes());
diag.arg("msg", format!("{msg:?}"));
}
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
WriteToReadOnly(alloc)
| DerefFunctionPointer(alloc)
| DerefVTablePointer(alloc)
| DerefTypeIdPointer(alloc) => {
diag.arg("allocation", alloc);
}
InvalidBool(b) => {
Expand Down
23 changes: 22 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use std::assert_matches::assert_matches;

use rustc_abi::Size;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::interpret::{AllocId, AllocInit, alloc_range};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::ty::layout::{LayoutOf as _, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{GenericArgsRef, Ty, TyCtxt};
Expand All @@ -30,6 +32,24 @@ 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 {
let size = Size::from_bytes(16);
let align = tcx.data_layout.pointer_align;
let mut alloc = Allocation::new(size, *align, AllocInit::Uninit, ());
let ptr_size = tcx.data_layout.pointer_size;
for step in 0..size.bytes() / ptr_size.bytes() {
let offset = ptr_size * step;
let alloc_id = tcx.reserve_and_set_type_id_alloc(ty, step.try_into().unwrap());
let ptr = Pointer::new(alloc_id.into(), Size::ZERO);
let val = Scalar::from_pointer(ptr, &tcx);
alloc.write_scalar(&tcx, alloc_range(offset, ptr_size), val).unwrap();
}

alloc.mutability = Mutability::Not;

tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc))
}

/// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated
/// inside an `InterpCx` and instead have their value computed directly from rustc internal info.
pub(crate) fn eval_nullary_intrinsic<'tcx>(
Expand All @@ -52,7 +72,8 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
}
sym::type_id => {
ensure_monomorphic_enough(tcx, tp_ty)?;
ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128())
let alloc_id = alloc_type_id(tcx, tp_ty);
ConstValue::Indirect { alloc_id, offset: Size::ZERO }
}
sym::variant_count => match match tp_ty.kind() {
// Pattern types have the same number of variants as their base type.
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
kind = "vtable",
)
}
Some(GlobalAlloc::Type { .. }) => {
err_ub_custom!(
fluent::const_eval_invalid_dealloc,
alloc_id = alloc_id,
kind = "typeid",
)
}
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
err_ub_custom!(
fluent::const_eval_invalid_dealloc,
Expand Down Expand Up @@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
}
Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
Some(GlobalAlloc::Type { .. }) => throw_ub!(DerefTypeIdPointer(id)),
None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)),
Some(GlobalAlloc::Static(def_id)) => {
assert!(self.tcx.is_static(def_id));
Expand Down Expand Up @@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
let kind = match global_alloc {
GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData,
GlobalAlloc::Type { .. }
| GlobalAlloc::Static { .. }
| GlobalAlloc::Memory { .. } => AllocKind::LiveData,
GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"),
GlobalAlloc::VTable { .. } => AllocKind::VTable,
};
Expand Down Expand Up @@ -1206,6 +1216,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
Some(GlobalAlloc::VTable(ty, dyn_ty)) => {
write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?;
}
Some(GlobalAlloc::Type { ty, segment }) => {
write!(fmt, " (typeid segment {segment } for {ty})")?;
}
Some(GlobalAlloc::Static(did)) => {
write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
}
Expand Down
Loading
Loading