diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index f2ad7abba755d..c1ad05dc8e4a8 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -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 diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 534cd1327bbe5..2f5793d5b3672 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -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); @@ -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(( diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7c10dd04f39f3..ce4c8497463c8 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -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)?; @@ -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])?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index f6e0208d98835..8d93aee57a518 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -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; @@ -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) => { @@ -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(_) @@ -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>, @@ -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() { @@ -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(()) - } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 30d7127ccd8fa..61f72ee4b5de1 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -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 @@ -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), diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 6ffb85d7b90a8..a4d39f09b724d 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -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::{ @@ -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 ). + // 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; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7665df4a4e5ae..68d28f9227dbe 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1153,6 +1153,7 @@ symbols! { is, is_auto, is_splatted, + is_unsafe, is_val_statically_known, isa_attribute, isize, @@ -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, @@ -2263,7 +2267,6 @@ symbols! { unsafe_no_drop_flag, unsafe_pinned, unsafe_unpin, - unsafety, unsize, unsized_const_param_ty, unsized_const_params, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a99633456de0b..f8cc81228b27b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -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; + /// Checks whether this type is non-exhaustive. #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] @@ -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_, _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; + +/// 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 diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 111664775ca8d..1f38339a7421b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -100,12 +100,14 @@ pub enum TypeKind { /// String slice type. Str(Str), /// References. - Reference(Reference), + Reference, /// Pointers. - Pointer(Pointer), + Pointer, /// Function pointers. - FnPtr(FnPtr), + FnPtr, /// FIXME(#146922): add all the common types + /// non exhaustive list: + /// - Never Other, } @@ -207,65 +209,55 @@ pub struct Str { // No additional information to provide for now. } -/// Compile-time type information about references. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Reference { - /// The type of the value being referred to. - pub pointee: TypeId, - /// Whether this reference is mutable or not. - pub mutable: bool, -} - -/// Compile-time type information about pointers. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Pointer { - /// The type of the value being pointed to. - pub pointee: TypeId, - /// Whether this pointer is mutable or not. - pub mutable: bool, -} - #[derive(Debug)] +#[lang = "FnPtr"] #[unstable(feature = "type_info", issue = "146922")] /// Function pointer, e.g. fn(u8), pub struct FnPtr { - /// Unsafety, true is unsafe - pub unsafety: bool, - - /// Abi, e.g. extern "C" - pub abi: Abi, - - /// Function inputs - pub inputs: &'static [TypeId], - - /// Function return type, default is TypeId::of::<()> - pub output: TypeId, - - /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); - pub variadic: bool, - + is_unsafe: bool, + abi: Abi, + inputs: &'static [TypeId], + output: TypeId, + variadic: bool, // FIXME(splat): should these fields be private, or merged into an Option? /// Is any function argument splatted? - pub is_splatted: bool, + is_splatted: bool, - /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. - /// e.g. in `fn overload(a: u8, #[rustc_splat] b: (f32, usize))` the index is 1, and it can be called - /// as `overload(a, 1.0, 2)`. - pub splatted_index: u8, + splatted_index: u8, } impl FnPtr { /// Returns the splatted function argument index, or `None` if no argument is splatted. + /// + /// e.g. in `fn overload(a: u8, #[rustc_splat] b: (f32, usize))` the index is 1, + /// and it can be called as `overload(a, 1.0, 2)`. pub const fn splatted(&self) -> Option { if self.is_splatted { Some(self.splatted_index) } else { None } } + /// Whether this function is variadic, e.g. extern "C" fn add(n: usize, mut args: ...); + pub const fn is_variadic(&self) -> bool { + self.variadic + } + /// whether this refers to an unsafe function. + pub const fn is_unsafe(&self) -> bool { + self.is_unsafe + } + /// Returns the application binary interface. For example extern "C". + pub const fn abi(&self) -> Abi { + self.abi + } + /// The types of the functions parameters + pub const fn inputs(&self) -> &'static [TypeId] { + self.inputs + } + /// List of the types returned by the function. For a function with no output + /// specified this returns `TypeId::of<()>`. + pub const fn output(&self) -> TypeId { + self.output + } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] /// Abi of [FnPtr] @@ -567,6 +559,86 @@ impl TypeId { pub fn generics(self) -> &'static [Generic] { intrinsics::type_id_generics(self) } + + /// Given a `TypeId` that represents a pointer this returns the `TypeId` + /// which that pointer points to. When called on anything else this returns + /// None. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert_eq!( + /// const { TypeId::of::<&i32>().points_to() }, + /// const { Some(TypeId::of::()) }, + /// ); + /// + /// assert_eq!( + /// const { TypeId::of::<*const i32>().points_to() }, + /// const { Some(TypeId::of::()) }, + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn points_to(self) -> Option { + intrinsics::type_id_points_to(self) + } + + /// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. + /// When called on anything else this returns `false`. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert!(const { TypeId::of::<&mut i32>().points_mutably() }); + /// assert!(const { !TypeId::of::<&i32>().points_mutably() }); + /// + /// assert!(!const { TypeId::of::<*const i32>().points_mutably() }); + /// assert!(const { TypeId::of::<*mut i32>().points_mutably() }); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn points_mutably(self) -> bool { + intrinsics::type_id_points_mutably(self) + } + + /// Given a `TypeId` that represents a function pointer returns an + /// [`FnPtr`]. When called on something else this returns `None`. + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// use std::mem::type_info::{Abi, FnPtr}; + /// + /// const F: FnPtr = TypeId::of:: usize>() + /// .function_ptr() + /// .expect("TypeId of a function ptr"); + /// + /// assert!(F.inputs() == [TypeId::of::(), TypeId::of::()]); + /// assert!(F.output() == TypeId::of::()); + /// assert!(F.abi() == Abi::default()); + /// ``` + /// ``` + /// #![feature(type_info)] + /// # use std::any::TypeId; + /// # use std::mem::type_info::{Abi, FnPtr}; + /// # + /// const F: FnPtr = TypeId::of::() + /// .function_ptr() + /// .expect("TypeId of a function ptr"); + /// + /// assert!(F.inputs() == []); + /// assert!(F.output() == TypeId::of::<()>()); + /// assert!(F.abi() == Abi::default()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn function_ptr(self) -> Option { + intrinsics::type_id_function_ptr(self) + } } /// Variant representing type ID. Representing a variant of an enum. diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 192054bcaf66b..862048e4090d4 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -3,233 +3,101 @@ use std::mem::type_info::{Abi, FnPtr, Type, TypeKind}; const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; -const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + let f = const { TypeId::of::().function_ptr().unwrap() }; + assert_eq!(f.is_unsafe(), false); + assert_eq!(f.abi(), Abi::ExternRust); + assert_eq!(f.inputs(), &[]); + assert_eq!(f.output(), UNIT_TY); + assert_eq!(f.is_variadic(), false); + assert_eq!(f.splatted(), None); } + +#[test] +fn test_typekind() { + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!( + const { Type::of::().kind }, + TypeKind::FnPtr + )); +} + #[test] fn test_ref() { - const { - // references are tricky because the lifetimes give the references different type ids - // so we check the pointees instead - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - if output != UNIT_TY { - panic!(); - } - let TypeKind::Reference(reference) = ty1.info().kind else { - panic!(); - }; - if reference.pointee != U8_TY { - panic!(); - } - let TypeKind::Reference(reference) = ty2.info().kind else { - panic!(); - }; - if reference.pointee != U8_TY { - panic!(); - } - } + // references are tricky because the lifetimes give the references different type ids + // so we check the pointees instead + const F: FnPtr = TypeId::of::().function_ptr().unwrap(); + assert_eq!(const { F.inputs()[0].points_to() }, Some(U8_TY)); + assert_eq!(const { F.inputs()[1].points_to() }, Some(U8_TY)); } #[test] fn test_unsafe() { - let TypeKind::FnPtr(FnPtr { - unsafety: true, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!(const { TypeId::of::().function_ptr() }.unwrap().is_unsafe(), true); } + #[test] fn test_abi() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::ExternRust + ); - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternC, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::ExternC + ); - let TypeKind::FnPtr(FnPtr { - unsafety: true, - abi: Abi::Named("system"), - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::Named("system") + ); } #[test] fn test_inputs() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); - assert_eq!(ty1, STRING_TY); - assert_eq!(ty2, U8_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().inputs(), + [STRING_TY, U8_TY] + ); - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); - assert_eq!(ty1, STRING_TY); - assert_eq!(ty2, U8_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().inputs(), + [STRING_TY, U8_TY] + ); } #[test] fn test_output() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of:: u8>().kind }) - else { - panic!(); - }; - assert_eq!(output, U8_TY); + let f = const { TypeId::of:: u8>().function_ptr() }.unwrap(); + assert_eq!(f.output(), U8_TY); } #[test] fn test_variadic() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternC, - inputs: [ty1], - output, - variadic: true, - is_splatted: false, - splatted_index: _, - }) = &(const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, U8_TY); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.abi(), Abi::ExternC); + assert_eq!(f.inputs(), [U8_TY]); + assert_eq!(f.is_variadic(), true); } #[test] fn test_splat() { - #[rustfmt::skip] - let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { - panic!(); - }; - let FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: [ty1], - output, - variadic: false, - is_splatted: true, - splatted_index: 0, - } = fn_ptr_ty - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, TUPLE_STRING_U8_TY); - assert_eq!(fn_ptr_ty.splatted(), Some(0)); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.inputs(), [TUPLE_STRING_U8_TY]); + assert_eq!(f.splatted(), Some(0)); } #[test] fn test_not_splat() { - let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { - panic!(); - }; - let FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: [ty1], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - } = fn_ptr_ty - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, TUPLE_STRING_U8_TY); - assert_eq!(fn_ptr_ty.splatted(), None); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.inputs(), [TUPLE_STRING_U8_TY]); + assert_eq!(f.splatted(), None); } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index f3a69dd857aba..7fe592496f1a7 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -271,61 +271,59 @@ fn test_primitives() { #[test] fn test_references() { + use TypeKind::Reference; + // Immutable reference. - match const { Type::of::<&u8>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(!reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&u8>() else { panic!() }; + const { + let ty = TypeId::of::<&u8>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } // Mutable references. - match const { Type::of::<&mut u64>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&mut u64>() else { panic!() }; + const { + let ty = TypeId::of::<&mut u64>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.points_mutably()); } // Wide references. - match const { Type::of::<&dyn Any>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(!reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&dyn Any>() else { panic!() }; + const { + let ty = TypeId::of::<&dyn Any>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } } #[test] fn test_pointers() { + use TypeKind::Pointer; + // Immutable pointer. - match const { Type::of::<*const u8>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(!pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*const u8>() else { panic!() }; + const { + let ty = TypeId::of::<*const u8>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } // Mutable pointer. - match const { Type::of::<*mut u64>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*mut u64>() else { panic!() }; + const { + let ty = TypeId::of::<*mut u64>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.points_mutably()); } // Wide pointer. - match const { Type::of::<*const dyn Any>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(!pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*const dyn Any>() else { panic!() }; + const { + let ty = TypeId::of::<*const dyn Any>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } } diff --git a/library/std/src/thread/join_handle.rs b/library/std/src/thread/join_handle.rs index 93dcc634d2dfa..955fd524e736b 100644 --- a/library/std/src/thread/join_handle.rs +++ b/library/std/src/thread/join_handle.rs @@ -104,7 +104,7 @@ impl JoinHandle { /// Otherwise, it fully waits for the thread to finish, including all destructors /// for thread-local variables that might be running after the main function of the thread. /// - /// In terms of [atomic memory orderings], the completion of the associated + /// In terms of [atomic memory orderings], the completion of the associated /// thread synchronizes with this function returning. In other words, all /// operations performed by that thread [happen /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all diff --git a/tests/coverage/comptime.cov-map b/tests/coverage/comptime.cov-map new file mode 100644 index 0000000000000..30f91da050f6f --- /dev/null +++ b/tests/coverage/comptime.cov-map @@ -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 + diff --git a/tests/coverage/comptime.coverage b/tests/coverage/comptime.coverage new file mode 100644 index 0000000000000..1ff44169babb2 --- /dev/null +++ b/tests/coverage/comptime.coverage @@ -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 . + LL| | + LL| |#[rustc_comptime] + LL| |fn comptime_fn() {} + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/comptime.rs b/tests/coverage/comptime.rs new file mode 100644 index 0000000000000..4891051b0076d --- /dev/null +++ b/tests/coverage/comptime.rs @@ -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 . + +#[rustc_comptime] +fn comptime_fn() {} + +fn main() {} diff --git a/tests/ui/async-await/spurious-static-bound-issue-115376.rs b/tests/ui/async-await/spurious-static-bound-issue-115376.rs new file mode 100644 index 0000000000000..42cd388ab7eec --- /dev/null +++ b/tests/ui/async-await/spurious-static-bound-issue-115376.rs @@ -0,0 +1,8 @@ +//@ edition: 2021 + +async fn test(_: &u8) { + let _: &'static T; + //~^ ERROR the parameter type `T` may not live long enough +} + +fn main() {} diff --git a/tests/ui/async-await/spurious-static-bound-issue-115376.stderr b/tests/ui/async-await/spurious-static-bound-issue-115376.stderr new file mode 100644 index 0000000000000..6292823029de1 --- /dev/null +++ b/tests/ui/async-await/spurious-static-bound-issue-115376.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/spurious-static-bound-issue-115376.rs:4:12 + | +LL | let _: &'static T; + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | async fn test(_: &u8) { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs index 4fdf5470feac6..0edabe009acdb 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs @@ -14,7 +14,6 @@ impl Foo { //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough //~| ERROR the parameter type `I` may not live long enough //~| ERROR the parameter type `I` may not live long enough - //~| ERROR the parameter type `I` may not live long enough //~| ERROR `f` does not live long enough } } diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr index df86ce79f09c7..cbeb8fba8d226 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr @@ -84,19 +84,6 @@ help: consider adding an explicit lifetime bound LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { | +++++++++ -error[E0311]: the parameter type `I` may not live long enough - --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 - | -LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | --------- the parameter type `I` must be valid for the anonymous lifetime defined here... -LL | self.bar = Box::new(|baz| Box::new(f(baz))); - | ^^^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | pub fn ack<'a, I: 'a>(&'a mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | +++ ++++ ++ - error[E0597]: `f` does not live long enough --> $DIR/unconstrained-closure-lifetime-generic.rs:10:44 | @@ -113,7 +100,7 @@ LL | } | = note: due to object lifetime defaults, `Box Fn(&'a usize) -> Box<(dyn Any + 'a)>>` actually means `Box<(dyn for<'a> Fn(&'a usize) -> Box<(dyn Any + 'a)> + 'static)>` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0310, E0311, E0597. +Some errors have detailed explanations: E0310, E0597. For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/consts/const-eval/do_not_const_check.rs b/tests/ui/consts/const-eval/do_not_const_check.rs new file mode 100644 index 0000000000000..ced2557bffd19 --- /dev/null +++ b/tests/ui/consts/const-eval/do_not_const_check.rs @@ -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(a: T, b: T) -> T { + a +} + +const _: () = { + integer_min(0, 1); //~ERROR: calling non-const function +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/do_not_const_check.stderr b/tests/ui/consts/const-eval/do_not_const_check.stderr new file mode 100644 index 0000000000000..507999df218d1 --- /dev/null +++ b/tests/ui/consts/const-eval/do_not_const_check.stderr @@ -0,0 +1,15 @@ +error[E0080]: calling non-const function `mostly_harmless` + --> $DIR/do_not_const_check.rs:9:5 + | +LL | mostly_harmless(); + | ^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error[E0080]: calling non-const function `integer_min::` + --> $DIR/do_not_const_check.rs:22:5 + | +LL | integer_min(0, 1); + | ^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`.