Skip to content
21 changes: 21 additions & 0 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,27 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
ecx.write_discriminant(variant_index, dest)?;
}

sym::type_id_element_ty => {
let ty = ecx.read_type_id(&args[0])?;
let variant_index = if let ty::Array(ty, _) | ty::Slice(ty) = ty.kind() {
let (variant_idx, variant_place) =
ecx.project_downcast_named(dest, sym::Some)?;
let type_id_field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?;
ecx.write_type_id(*ty, &type_id_field_place)?;
variant_idx
} else {
ecx.project_downcast_named(dest, sym::None)?.0
};
ecx.write_discriminant(variant_index, dest)?;
}

sym::type_id_array_len => {
let ty = ecx.read_type_id(&args[0])?;
let len =
if let ty::Array(_, len) = ty.kind() { len.to_leaf().to_u64() } else { 0 };
ecx.write_scalar(Scalar::from_target_usize(len, ecx), dest)?;
}

sym::type_id_fields => {
let ty = ecx.read_type_id(&args[0])?;
let variant_idx = ecx.read_target_usize(&args[1])? as usize;
Expand Down
63 changes: 5 additions & 58 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_ast::Mutability;
use rustc_hir::attrs::lang_items::LangItem;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Const, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt};
use rustc_middle::ty::{self, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt};
use rustc_span::{Symbol, sym};

use crate::const_eval::CompileTimeMachine;
Expand Down Expand Up @@ -83,22 +83,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
self.write_tuple_type_info(tuple_place, fields, ty)?;
variant
}
ty::Array(ty, len) => {
let (variant, variant_place) =
ty::Array(_, _) => {
let (variant, _variant_place) =
self.project_downcast_named(&field_dest, sym::Array)?;
let array_place = self.project_field(&variant_place, FieldIdx::ZERO)?;

self.write_array_type_info(array_place, *ty, *len)?;

variant
}
ty::Slice(ty) => {
let (variant, variant_place) =
ty::Slice(_) => {
let (variant, _variant_place) =
self.project_downcast_named(&field_dest, sym::Slice)?;
let slice_place = self.project_field(&variant_place, FieldIdx::ZERO)?;

self.write_slice_type_info(slice_place, *ty)?;

variant
}
ty::Adt(adt_def, generics) => {
Expand Down Expand Up @@ -256,51 +248,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
)
}

pub(crate) fn write_array_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
len: Const<'tcx>,
) -> InterpResult<'tcx> {
// Iterate over all fields of `type_info::Array`.
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;

match field.name {
// Write the `TypeId` of the array's elements to the `element_ty` field.
sym::element_ty => self.write_type_id(ty, &field_place)?,
// Write the length of the array to the `len` field.
sym::len => self.write_scalar(len.to_leaf(), &field_place)?,
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}

interp_ok(())
}

pub(crate) fn write_slice_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
) -> InterpResult<'tcx> {
// Iterate over all fields of `type_info::Slice`.
for (field_idx, field) in
place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&place, field_idx)?;

match field.name {
// Write the `TypeId` of the slice's elements to the `element_ty` field.
sym::element_ty => self.write_type_id(ty, &field_place)?,
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}

interp_ok(())
}

pub(crate) fn write_reference_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::truncf64
| sym::truncf128
| sym::type_id
| sym::type_id_array_len
| sym::type_id_element_ty
| sym::type_id_eq
| sym::type_id_field_representing_type
| sym::type_id_fields
Expand Down Expand Up @@ -312,6 +314,8 @@ pub(crate) fn check_intrinsic_type(

sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)),
sym::type_id => (1, 0, vec![], type_id_ty()),
sym::type_id_array_len => (0, 0, vec![type_id_ty()], tcx.types.usize),
sym::type_id_element_ty => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())),
sym::type_id_eq => (0, 0, vec![type_id_ty(), type_id_ty()], tcx.types.bool),
sym::type_id_field_representing_type => {
(0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty())
Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_mir_transform/src/coverage/query.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_hir::attrs::CoverageAttrKind;
use rustc_hir::find_attr;
use rustc_hir::def::DefKind;
use rustc_hir::{self as hir, find_attr};
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::{
Expand Down Expand Up @@ -30,11 +31,23 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
// Closures are carved out by their initial `Assign` statement.)
if !tcx.def_kind(def_id).is_fn_like() {
let def_kind = tcx.def_kind(def_id);
if !def_kind.is_fn_like() {
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
return false;
}

// Comptime functions can't exist at runtime, so instrumenting them is useless.
// This also avoids an ICE when getting the symbol name for an unused-function record
// (due to <https://github.com/rust-lang/rust/pull/159777>).
// We check `def_kind` first to avoid any unexpected panics from merely asking for constness.
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn)
&& matches!(tcx.constness(def_id), hir::Constness::Const { always: true })
{
trace!("InstrumentCoverage skipped for {def_id:?} (comptime)");
return false;
}

if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) {
trace!("InstrumentCoverage skipped for {def_id:?} (`#[naked]`)");
return false;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,8 @@ symbols! {
type_ascription,
type_changing_struct_update,
type_id,
type_id_array_len,
type_id_element_ty,
type_id_eq,
type_id_field_representing_type,
type_id_fields,
Expand Down
16 changes: 16 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3009,6 +3009,22 @@ pub const fn type_id_eq(a: crate::any::TypeId, b: crate::any::TypeId) -> bool {
#[rustc_comptime]
pub fn type_id_is_signed(_id: crate::any::TypeId) -> bool;

/// Gets the length of the array represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::array_len`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_array_len(_id: crate::any::TypeId) -> usize;

/// Gets the type of each element of the array or slice represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::element_ty`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_element_ty(_id: crate::any::TypeId) -> Option<crate::any::TypeId>;

/// Gets the size of the type represented by this `TypeId`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::size`].
Expand Down
62 changes: 40 additions & 22 deletions library/core/src/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ pub enum TypeKind {
/// Tuples.
Tuple,
/// Arrays.
Array(Array),
Array,
/// Slices.
Slice(Slice),
Slice,
/// Dynamic Traits.
DynTrait(DynTrait),
/// Structs.
Expand Down Expand Up @@ -109,26 +109,6 @@ pub enum TypeKind {
Other,
}

/// Compile-time type information about arrays.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Array {
/// The type of each element in the array.
pub element_ty: TypeId,
/// The length of the array.
pub len: usize,
}

/// Compile-time type information about slices.
#[derive(Debug)]
#[non_exhaustive]
#[unstable(feature = "type_info", issue = "146922")]
pub struct Slice {
/// The type of each element in the slice.
pub element_ty: TypeId,
}

/// Compile-time type information about dynamic traits.
/// FIXME(#146922): Add super traits and generics
#[derive(Debug)]
Expand Down Expand Up @@ -303,6 +283,44 @@ impl TypeId {
intrinsics::type_id_is_signed(self)
}

/// When called on a `TypeId` representing an array or slice this returns the type of each
/// element otherwise this returns `None`.
///
/// # Examples
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// assert_eq!(const { TypeId::of::<[u32; 16]>().element_ty() }, Some(TypeId::of::<u32>()));
/// assert_eq!(const { TypeId::of::<u8>().element_ty() }, None); // not an array or slice
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
#[rustc_comptime]
pub fn element_ty(self) -> Option<TypeId> {
intrinsics::type_id_element_ty(self)
}

/// When called on a `TypeId` representing an array this returns the length of the array in
/// all other cases this returns zero.
///
/// # Examples
///
/// ```
/// #![feature(type_info)]
/// use std::any::TypeId;
///
/// assert_eq!(const { TypeId::of::<[u32; 16]>().array_len() }, 16);
/// assert_eq!(const { TypeId::of::<u8>().array_len() }, 0); // not an array
/// ```
#[unstable(feature = "type_info", issue = "146922")]
#[rustc_const_unstable(feature = "type_info", issue = "146922")]
#[rustc_comptime]
pub fn array_len(self) -> usize {
intrinsics::type_id_array_len(self)
}

/// Returns the size of the type represented by this `TypeId`. `None` if it is unsized.
///
/// # Examples
Expand Down
28 changes: 13 additions & 15 deletions library/coretests/tests/mem/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,27 @@ use std::mem::type_info::{Const, Generic, GenericType, Type, TypeKind};
#[test]
fn test_arrays() {
// Normal array.
match const { Type::of::<[u16; 4]>() }.kind {
TypeKind::Array(array) => {
assert_eq!(array.element_ty, TypeId::of::<u16>());
assert_eq!(array.len, 4);
}
_ => unreachable!(),
assert!(matches!(Type::of::<[u16; 4]>().kind, TypeKind::Array));
const {
let ty_id = TypeId::of::<[u16; 4]>();
assert!(ty_id.element_ty() == Some(TypeId::of::<u16>()));
assert!(ty_id.array_len() == 4);
}

// Zero-length array.
match const { Type::of::<[bool; 0]>() }.kind {
TypeKind::Array(array) => {
assert_eq!(array.element_ty, TypeId::of::<bool>());
assert_eq!(array.len, 0);
}
_ => unreachable!(),
assert!(matches!(Type::of::<[bool; 0]>().kind, TypeKind::Array));
const {
let ty_id = TypeId::of::<[bool; 0]>();
assert!(ty_id.element_ty() == Some(TypeId::of::<bool>()));
assert!(ty_id.array_len() == 0);
}
}

#[test]
fn test_slices() {
match const { Type::of::<[usize]>() }.kind {
TypeKind::Slice(slice) => assert_eq!(slice.element_ty, TypeId::of::<usize>()),
_ => unreachable!(),
assert!(matches!(Type::of::<[usize]>().kind, TypeKind::Slice));
const {
assert!(TypeId::of::<[usize]>().element_ty() == Some(TypeId::of::<usize>()));
}
}

Expand Down
10 changes: 10 additions & 0 deletions tests/coverage/comptime.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Function name: comptime::main
Raw bytes (14): 0x[01, 01, 00, 02, 01, 0b, 01, 00, 0a, 01, 00, 0c, 00, 0d]
Number of files: 1
- file 0 => $DIR/comptime.rs
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 11, 1) to (start + 0, 10)
- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13)
Highest counter ID seen: c0

12 changes: 12 additions & 0 deletions tests/coverage/comptime.coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
LL| |#![feature(rustc_attrs)]
LL| |//@ edition: 2024
LL| |
LL| |// Check that instrumenting a crate with a comptime function doesn't ICE.
LL| |// (The function itself doesn't need to be instrumented, and probably shouldn't be.)
LL| |// Regression test for <https://github.com/rust-lang/rust/pull/161808>.
LL| |
LL| |#[rustc_comptime]
LL| |fn comptime_fn() {}
LL| |
LL| 1|fn main() {}

11 changes: 11 additions & 0 deletions tests/coverage/comptime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(rustc_attrs)]
//@ edition: 2024

// Check that instrumenting a crate with a comptime function doesn't ICE.
// (The function itself doesn't need to be instrumented, and probably shouldn't be.)
// Regression test for <https://github.com/rust-lang/rust/pull/161808>.

#[rustc_comptime]
fn comptime_fn() {}

fn main() {}
25 changes: 25 additions & 0 deletions tests/ui/consts/const-eval/do_not_const_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Ensure that we refuse to run a do_not_const_check function, even if the body *would* const-check
//! at the moment.
#![feature(rustc_attrs, intrinsics)]

#[rustc_do_not_const_check]
const fn mostly_harmless() {}

const _: () = {
mostly_harmless(); //~ERROR: calling non-const function
};

// Also ensure the same happens with intrinsics.
// Here we need some intrinsic that the interpreter does *not* have a native implementation for.
// Let's hope nobody adds one...
#[rustc_intrinsic]
#[rustc_do_not_const_check]
pub const fn integer_min<T: Copy>(a: T, b: T) -> T {
a
}

const _: () = {
integer_min(0, 1); //~ERROR: calling non-const function
};

fn main() {}
Loading
Loading