Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions compiler/rustc_attr_ir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,9 @@ language_item_table! {
// Used to fallback `{float}` to `f32` when `f32: From<{float}>`
From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1);
FromFn, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;

// Experimental lang item for `Reflection and comptime`(https://goals.rust-lang.org/2025h2/reflection-and-comptime.html)
FnPtr, sym::FnPtr, fn_ptr, Target::Struct, GenericRequirement::None;
}

/// The requirement imposed on the generics of a lang item
Expand Down
35 changes: 34 additions & 1 deletion compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// result in basically the exact same error being reported to
// the user. Avoid that.
let mut deduplicate_errors = FxIndexSet::default();
let mut failed_type_tests = Vec::new();

for type_test in &self.type_tests {
debug!("check_type_test: {:?}", type_test);
Expand All @@ -609,8 +610,40 @@ impl<'tcx> RegionInferenceContext<'tcx> {
continue;
}

// Type-test failed. Report the error.
// Type-test failed. Collect it so we can suppress redundant errors below.
let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind);
failed_type_tests.push((erased_generic_kind, type_test));
}

// An async body can produce both `G: 'static` and `G: 'a` type-test failures at
// the same span, as in `tests/ui/async-await/spurious-static-bound-issue-115376.rs`.
// Reporting the weaker bound adds a redundant diagnostic and suggests a lifetime
// bound that cannot fix the missing `G: 'static` requirement. Keep the `'static`
// error and suppress weaker failures for the same erased generic kind and span.
// This is a diagnostic heuristic, using the same erasure as deduplication below.
//
// Collect all failed `'static` bounds before reporting errors so suppression does
// not depend on the order of the type tests. Compare SCCs because a lower-bound
// region can be equivalent to `'static` without being `fr_static` itself.
let static_scc = self.constraint_sccs.scc(self.universal_regions().fr_static);
let static_bound_errors: FxIndexSet<_> = failed_type_tests
.iter()
.filter_map(|&(erased_generic_kind, type_test)| {
if self.constraint_sccs.scc(type_test.lower_bound) == static_scc {
Some((erased_generic_kind, type_test.span))
} else {
None
}
})
.collect();

// If `G: 'static` failed at this span, then same-span `G: 'a` failures are weaker.
for (erased_generic_kind, type_test) in failed_type_tests {
if self.constraint_sccs.scc(type_test.lower_bound) != static_scc
&& static_bound_errors.contains(&(erased_generic_kind, type_test.span))
{
continue;
}

// Skip duplicate-ish errors.
if deduplicate_errors.insert((
Expand Down
36 changes: 36 additions & 0 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,15 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
ecx.write_scalar(Scalar::from_bool(ty.is_signed()), dest)?;
}

sym::type_id_points_mutably => {
let ty = ecx.read_type_id(&args[0])?;
let is_mutable = matches!(
ty.kind(),
ty::RawPtr(_, Mutability::Mut) | &ty::Ref(_, _, Mutability::Mut)
);
ecx.write_scalar(Scalar::from_bool(is_mutable), dest)?;
}

sym::size_of_type_id => {
let ty = ecx.read_type_id(&args[0])?;
let layout = ecx.layout_of(ty)?;
Expand Down Expand Up @@ -691,6 +700,33 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
);
ecx.write_type_id(frt, dest)?;
}
sym::type_id_function_ptr => {
let ty = ecx.read_type_id(&args[0])?;
let variant_index = if let ty::FnPtr(sig, fn_header) = ty.kind() {
let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?;
let field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?;
let sig = sig.skip_binder(); // FIXME: handle lifetime bounds
ecx.write_fn_ptr_type_info(field_place, &sig, fn_header)?;
variant
} else {
ecx.project_downcast_named(dest, sym::None)?.0
};
ecx.write_discriminant(variant_index, dest)?;
}
sym::type_id_points_to => {
let ty = ecx.read_type_id(&args[0])?;
let variant_index = if let ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) =
ty.kind()
{
let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?;
let field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?;
ecx.write_type_id(*pointee_ty, &field_place)?;
variant
} else {
ecx.project_downcast_named(dest, sym::None)?.0
};
ecx.write_discriminant(variant_index, dest)?;
}

sym::type_id_variants => {
let ty = ecx.read_type_id(&args[0])?;
Expand Down
82 changes: 7 additions & 75 deletions compiler/rustc_const_eval/src/const_eval/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ mod adt;
use std::borrow::Cow;

use rustc_abi::{ExternAbi, FieldIdx};
use rustc_ast::Mutability;
use rustc_hir::attrs::lang_items::LangItem;
use rustc_middle::span_bug;
use rustc_middle::ty::layout::TyAndLayout;
Expand Down Expand Up @@ -134,23 +133,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
self.project_downcast_named(&field_dest, sym::Str)?;
variant
}
ty::Ref(_, ty, mutability) => {
let (variant, variant_place) =
ty::Ref(_, _, _) => {
let (variant, _) =
self.project_downcast_named(&field_dest, sym::Reference)?;
let reference_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_reference_type_info(reference_place, *ty, *mutability)?;

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

self.write_pointer_type_info(pointer_place, *ty, *mutability)?;

variant
}
ty::Dynamic(predicates, region) => {
Expand All @@ -160,16 +150,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::FnPtr(sig, fn_header) => {
let (variant, variant_place) =
ty::FnPtr(_, _) => {
let (variant, _) =
self.project_downcast_named(&field_dest, sym::FnPtr)?;
let fn_ptr_place =
self.project_field(&variant_place, FieldIdx::ZERO)?;

// FIXME: handle lifetime bounds
let sig = sig.skip_binder();

self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?;
variant
}
ty::Foreign(_)
Expand Down Expand Up @@ -301,31 +284,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
interp_ok(())
}

pub(crate) fn write_reference_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
mutability: Mutability,
) -> InterpResult<'tcx> {
// Iterate over all fields of `type_info::Reference`.
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 reference's inner type to the `ty` field.
sym::pointee => self.write_type_id(ty, &field_place)?,
// Write the boolean representing the reference's mutability to the `mutable` field.
sym::mutable => {
self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}
interp_ok(())
}

pub(crate) fn write_type_id_generics(
&mut self,
place: &impl Writeable<'tcx, CtfeProvenance>,
Expand Down Expand Up @@ -383,7 +341,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
let field_place = self.project_field(&place, field_idx)?;

match field.name {
sym::unsafety => {
sym::is_unsafe => {
self.write_scalar(Scalar::from_bool(!fn_sig_kind.is_safe()), &field_place)?;
}
sym::abi => match fn_sig_kind.abi() {
Expand Down Expand Up @@ -444,30 +402,4 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {

interp_ok(())
}

pub(crate) fn write_pointer_type_info(
&mut self,
place: impl Writeable<'tcx, CtfeProvenance>,
ty: Ty<'tcx>,
mutability: Mutability,
) -> InterpResult<'tcx> {
// Iterate over all fields of `type_info::Pointer`.
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 pointer's inner type to the `ty` field.
sym::pointee => self.write_type_id(ty, &field_place)?,
// Write the boolean representing the pointer's mutability to the `mutable` field.
sym::mutable => {
self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)?
}
other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"),
}
}

interp_ok(())
}
}
16 changes: 16 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,11 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
| sym::type_id_eq
| sym::type_id_field_representing_type
| sym::type_id_fields
| sym::type_id_function_ptr
| sym::type_id_generics
| sym::type_id_is_signed
| sym::type_id_points_mutably
| sym::type_id_points_to
| sym::type_id_variants
| sym::type_id_vtable
| sym::type_name
Expand Down Expand Up @@ -317,7 +320,20 @@ pub(crate) fn check_intrinsic_type(
(0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty())
}
sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize),
sym::type_id_function_ptr => {
let fn_ptr = tcx.require_lang_item(LangItem::FnPtr, span);
let fn_ptr_adt_ref = tcx.adt_def(fn_ptr);
let fn_ptr_ty = Ty::new_adt(tcx, fn_ptr_adt_ref, ty::List::empty());

let option = tcx.require_lang_item(LangItem::Option, span);
let option_adt_ref = tcx.adt_def(option);
let option_args = tcx.mk_args(&[fn_ptr_ty.into()]);
let option_fn_ptr_ty = Ty::new_adt(tcx, option_adt_ref, option_args);
(0, 0, vec![type_id_ty()], option_fn_ptr_ty)
}
sym::type_id_is_signed => (0, 0, vec![type_id_ty()], tcx.types.bool),
sym::type_id_points_mutably => (0, 0, vec![type_id_ty()], tcx.types.bool),
sym::type_id_points_to => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())),
sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize),
sym::variant_name => (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)),
sym::variant_non_exhaustive => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool),
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
5 changes: 4 additions & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,7 @@ symbols! {
is,
is_auto,
is_splatted,
is_unsafe,
is_val_statically_known,
isa_attribute,
isize,
Expand Down Expand Up @@ -2170,8 +2171,11 @@ symbols! {
type_id_eq,
type_id_field_representing_type,
type_id_fields,
type_id_function_ptr,
type_id_generics,
type_id_is_signed,
type_id_points_mutably,
type_id_points_to,
type_id_variants,
type_id_vtable,
type_info,
Expand Down Expand Up @@ -2263,7 +2267,6 @@ symbols! {
unsafe_no_drop_flag,
unsafe_pinned,
unsafe_unpin,
unsafety,
unsize,
unsized_const_param_ty,
unsized_const_params,
Expand Down
29 changes: 29 additions & 0 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3101,6 +3101,15 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati
#[rustc_comptime]
pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize;

/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`].
/// When called on something else this returns `None`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option<crate::mem::type_info::FnPtr>;

/// Checks whether this type is non-exhaustive.
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
Expand All @@ -3114,6 +3123,26 @@ pub fn non_exhaustive(_id: crate::any::TypeId) -> bool;
#[rustc_comptime]
pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic];

// FIXME(reflection): Pick a consistent naming scheme for the intrinsics. Right now we got
// type_id_<something>, <something>_type_id and intrinsics not mentioning type_id at all.
/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer
/// points to. When called on anything else this returns None.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_points_to(_id: crate::any::TypeId) -> Option<crate::any::TypeId>;

/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable.
/// When called on anything else this returns `false`.
///
/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`].
#[rustc_intrinsic]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_comptime]
pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool;

/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`.
///
/// This is used to implement functions like `slice::from_raw_parts_mut` and
Expand Down
Loading
Loading