From 36d68c5d0ffdb1d00e37258e2efceee6561d5bba Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 3 Nov 2024 16:29:35 +0100 Subject: [PATCH] Support new #[rustc_intrinsic] attribute and fallback bodies --- crates/hir-def/src/data.rs | 4 - crates/hir-expand/src/inert_attr_macro.rs | 13 ++ .../hir-ty/src/consteval/tests/intrinsics.rs | 97 ++++++------ crates/hir-ty/src/diagnostics/decl_check.rs | 2 +- crates/hir-ty/src/mir/eval/shim.rs | 144 +++++++----------- crates/hir-ty/src/utils.rs | 22 +-- crates/hir/src/lib.rs | 19 +-- crates/intern/src/symbol/symbols.rs | 6 +- crates/test-utils/src/minicore.rs | 10 +- 9 files changed, 143 insertions(+), 174 deletions(-) diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 6d07dc8f9beb..2a13f74aac71 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -13,7 +13,6 @@ use syntax::{ast, Parse}; use triomphe::Arc; use crate::{ - attr::Attrs, db::DefDatabase, expander::{Expander, Mark}, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, TreeId}, @@ -37,8 +36,6 @@ pub struct FunctionData { pub name: Name, pub params: Box<[TypeRefId]>, pub ret_type: TypeRefId, - // FIXME: why are these stored here? They should be accessed via the query - pub attrs: Attrs, pub visibility: RawVisibility, pub abi: Option, pub legacy_const_generics_indices: Option>>, @@ -115,7 +112,6 @@ impl FunctionData { .filter_map(|(_, param)| param.type_ref) .collect(), ret_type: func.ret_type, - attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), visibility, abi: func.abi.clone(), legacy_const_generics_indices, diff --git a/crates/hir-expand/src/inert_attr_macro.rs b/crates/hir-expand/src/inert_attr_macro.rs index 5c25a55362e6..95dfe56ff544 100644 --- a/crates/hir-expand/src/inert_attr_macro.rs +++ b/crates/hir-expand/src/inert_attr_macro.rs @@ -632,6 +632,19 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_safe_intrinsic, Normal, template!(Word), WarnFollowing, "the `#[rustc_safe_intrinsic]` attribute is used internally to mark intrinsics as safe" ), + rustc_attr!( + rustc_intrinsic, Normal, template!(Word), ErrorFollowing, + "the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies", + ), + rustc_attr!( + rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, + "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" + ), + rustc_attr!( + rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, + "the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies", + ), + rustc_attr!( rustc_deprecated_safe_2024, Normal, template!(Word), WarnFollowing, "the `#[rustc_safe_intrinsic]` marks functions as unsafe in Rust 2024", diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index c5706172b20c..c1ac7ae173b8 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -4,9 +4,8 @@ use super::*; fn size_of() { check_number( r#" - extern "rust-intrinsic" { - pub fn size_of() -> usize; - } + #[rustc_intrinsic] + pub fn size_of() -> usize; const GOAL: usize = size_of::(); "#, @@ -19,9 +18,8 @@ fn size_of_val() { check_number( r#" //- minicore: coerce_unsized - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; struct X(i32, u8); @@ -32,9 +30,8 @@ fn size_of_val() { check_number( r#" //- minicore: coerce_unsized - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; const GOAL: usize = { let it: &[i32] = &[1, 2, 3]; @@ -48,9 +45,8 @@ fn size_of_val() { //- minicore: coerce_unsized, transmute use core::mem::transmute; - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; struct X { x: i64, @@ -70,9 +66,8 @@ fn size_of_val() { //- minicore: coerce_unsized, transmute use core::mem::transmute; - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; struct X { x: i32, @@ -90,9 +85,8 @@ fn size_of_val() { check_number( r#" //- minicore: coerce_unsized, fmt, builtin_impls, dispatch_from_dyn - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; const GOAL: usize = { let x: &i16 = &5; @@ -106,9 +100,8 @@ fn size_of_val() { check_number( r#" //- minicore: coerce_unsized - extern "rust-intrinsic" { - pub fn size_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn size_of_val(_: *const T) -> usize; const GOAL: usize = { size_of_val("salam") @@ -123,9 +116,8 @@ fn min_align_of_val() { check_number( r#" //- minicore: coerce_unsized - extern "rust-intrinsic" { - pub fn min_align_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn min_align_of_val(_: *const T) -> usize; struct X(i32, u8); @@ -136,9 +128,8 @@ fn min_align_of_val() { check_number( r#" //- minicore: coerce_unsized - extern "rust-intrinsic" { - pub fn min_align_of_val(_: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn min_align_of_val(_: *const T) -> usize; const GOAL: usize = { let x: &[i32] = &[1, 2, 3]; @@ -153,9 +144,8 @@ fn min_align_of_val() { fn type_name() { check_str( r#" - extern "rust-intrinsic" { - pub fn type_name() -> &'static str; - } + #[rustc_intrinsic] + pub fn type_name() -> &'static str; const GOAL: &str = type_name::(); "#, @@ -163,9 +153,8 @@ fn type_name() { ); check_str( r#" - extern "rust-intrinsic" { - pub fn type_name() -> &'static str; - } + #[rustc_intrinsic] + pub fn type_name() -> &'static str; mod mod1 { pub mod mod2 { @@ -183,9 +172,8 @@ fn type_name() { fn transmute() { check_number( r#" - extern "rust-intrinsic" { - pub fn transmute(e: T) -> U; - } + #[rustc_intrinsic] + pub fn transmute(e: T) -> U; const GOAL: i32 = transmute((1i16, 1i16)); "#, @@ -197,10 +185,10 @@ fn transmute() { fn read_via_copy() { check_number( r#" - extern "rust-intrinsic" { - pub fn read_via_copy(e: *const T) -> T; - pub fn volatile_load(e: *const T) -> T; - } + #[rustc_intrinsic] + pub fn read_via_copy(e: *const T) -> T; + #[rustc_intrinsic] + pub fn volatile_load(e: *const T) -> T; const GOAL: i32 = { let x = 2; @@ -399,9 +387,14 @@ fn discriminant_value() { fn likely() { check_number( r#" - extern "rust-intrinsic" { - pub fn likely(b: bool) -> bool; - pub fn unlikely(b: bool) -> bool; + #[rustc_intrinsic] + pub const fn likely(b: bool) -> bool { + b + } + + #[rustc_intrinsic] + pub const fn unlikely(b: bool) -> bool { + b } const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false); @@ -704,9 +697,8 @@ fn rotate() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn rotate_right(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_right(x: T, y: T) -> T; const GOAL: i32 = rotate_right(10006016, 1020315); "#, @@ -721,9 +713,8 @@ fn simd() { pub struct i8x16( i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8, ); - extern "platform-intrinsic" { - pub fn simd_bitmask(x: T) -> U; - } + #[rustc_intrinsic] + pub fn simd_bitmask(x: T) -> U; const GOAL: u16 = simd_bitmask(i8x16( 0, 1, 0, 0, 2, 255, 100, 0, 50, 0, 1, 1, 0, 0, 0, 0 )); @@ -735,10 +726,10 @@ fn simd() { pub struct i8x16( i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8,i8, ); - extern "platform-intrinsic" { - pub fn simd_lt(x: T, y: T) -> U; - pub fn simd_bitmask(x: T) -> U; - } + #[rustc_intrinsic] + pub fn simd_lt(x: T, y: T) -> U; + #[rustc_intrinsic] + pub fn simd_bitmask(x: T) -> U; const GOAL: u16 = simd_bitmask(simd_lt::( i8x16( -105, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index c9ab0acc0849..4991d173b9c4 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -201,7 +201,7 @@ impl<'a> DeclValidator<'a> { // Don't run the lint on extern "[not Rust]" fn items with the // #[no_mangle] attribute. - let no_mangle = data.attrs.by_key(&sym::no_mangle).exists(); + let no_mangle = self.db.attrs(func.into()).by_key(&sym::no_mangle).exists(); if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) { cov_mark::hit!(extern_func_no_mangle_ignored); } else { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 0cdad74a4f6c..0a78f4a5b24b 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -9,7 +9,7 @@ use hir_def::{ resolver::HasResolver, }; use hir_expand::name::Name; -use intern::sym; +use intern::{sym, Symbol}; use crate::{ error_lifetime, @@ -54,49 +54,32 @@ impl Evaluator<'_> { } let function_data = self.db.function_data(def); - let is_intrinsic = match &function_data.abi { - Some(abi) => *abi == sym::rust_dash_intrinsic, - None => match def.lookup(self.db.upcast()).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - let id = block.lookup(self.db.upcast()).id; - id.item_tree(self.db.upcast())[id.value].abi.as_ref() - == Some(&sym::rust_dash_intrinsic) - } - _ => false, - }, - }; + let attrs = self.db.attrs(def.into()); + let is_intrinsic = attrs.by_key(&sym::rustc_intrinsic).exists() + // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used + || (match &function_data.abi { + Some(abi) => *abi == sym::rust_dash_intrinsic, + None => match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value].abi.as_ref() + == Some(&sym::rust_dash_intrinsic) + } + _ => false, + }, + }); + if is_intrinsic { - self.exec_intrinsic( + return self.exec_intrinsic( function_data.name.as_str(), args, generic_args, destination, locals, span, - )?; - return Ok(true); - } - let is_platform_intrinsic = match &function_data.abi { - Some(abi) => *abi == sym::platform_dash_intrinsic, - None => match def.lookup(self.db.upcast()).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - let id = block.lookup(self.db.upcast()).id; - id.item_tree(self.db.upcast())[id.value].abi.as_ref() - == Some(&sym::platform_dash_intrinsic) - } - _ => false, - }, - }; - if is_platform_intrinsic { - self.exec_platform_intrinsic( - function_data.name.as_str(), - args, - generic_args, - destination, - locals, - span, - )?; - return Ok(true); + !function_data.has_body() + || attrs.by_key(&sym::rustc_intrinsic_must_be_overridden).exists(), + ); } let is_extern_c = match def.lookup(self.db.upcast()).container { hir_def::ItemContainerId::ExternBlockId(block) => { @@ -106,27 +89,25 @@ impl Evaluator<'_> { _ => false, }; if is_extern_c { - self.exec_extern_c( - function_data.name.as_str(), - args, - generic_args, - destination, - locals, - span, - )?; - return Ok(true); + return self + .exec_extern_c( + function_data.name.as_str(), + args, + generic_args, + destination, + locals, + span, + ) + .map(|()| true); } - let alloc_fn = function_data - .attrs - .iter() - .filter_map(|it| it.path().as_ident()) - .map(|it| it.as_str()) - .find(|it| { + + let alloc_fn = + attrs.iter().filter_map(|it| it.path().as_ident()).map(|it| it.symbol()).find(|it| { [ - "rustc_allocator", - "rustc_deallocator", - "rustc_reallocator", - "rustc_allocator_zeroed", + &sym::rustc_allocator, + &sym::rustc_deallocator, + &sym::rustc_reallocator, + &sym::rustc_allocator_zeroed, ] .contains(it) }); @@ -270,12 +251,12 @@ impl Evaluator<'_> { fn exec_alloc_fn( &mut self, - alloc_fn: &str, + alloc_fn: &Symbol, args: &[IntervalAndTy], destination: Interval, ) -> Result<()> { match alloc_fn { - "rustc_allocator_zeroed" | "rustc_allocator" => { + _ if *alloc_fn == sym::rustc_allocator_zeroed || *alloc_fn == sym::rustc_allocator => { let [size, align] = args else { return Err(MirEvalError::InternalError( "rustc_allocator args are not provided".into(), @@ -286,8 +267,8 @@ impl Evaluator<'_> { let result = self.heap_allocate(size, align)?; destination.write_from_bytes(self, &result.to_bytes())?; } - "rustc_deallocator" => { /* no-op for now */ } - "rustc_reallocator" => { + _ if *alloc_fn == sym::rustc_deallocator => { /* no-op for now */ } + _ if *alloc_fn == sym::rustc_reallocator => { let [ptr, old_size, align, new_size] = args else { return Err(MirEvalError::InternalError( "rustc_allocator args are not provided".into(), @@ -603,21 +584,6 @@ impl Evaluator<'_> { } } - fn exec_platform_intrinsic( - &mut self, - name: &str, - args: &[IntervalAndTy], - generic_args: &Substitution, - destination: Interval, - locals: &Locals, - span: MirSpan, - ) -> Result<()> { - if let Some(name) = name.strip_prefix("simd_") { - return self.exec_simd_intrinsic(name, args, generic_args, destination, locals, span); - } - not_supported!("unknown platform intrinsic {name}"); - } - fn exec_intrinsic( &mut self, name: &str, @@ -626,9 +592,17 @@ impl Evaluator<'_> { destination: Interval, locals: &Locals, span: MirSpan, - ) -> Result<()> { + needs_override: bool, + ) -> Result { if let Some(name) = name.strip_prefix("atomic_") { - return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span); + return self + .exec_atomic_intrinsic(name, args, generic_args, destination, locals, span) + .map(|()| true); + } + if let Some(name) = name.strip_prefix("simd_") { + return self + .exec_simd_intrinsic(name, args, generic_args, destination, locals, span) + .map(|()| true); } // FIXME(#17451): Add `f16` and `f128` intrinsics. if let Some(name) = name.strip_suffix("f64") { @@ -701,7 +675,7 @@ impl Evaluator<'_> { } _ => not_supported!("unknown f64 intrinsic {name}"), }; - return destination.write_from_bytes(self, &result.to_le_bytes()); + return destination.write_from_bytes(self, &result.to_le_bytes()).map(|()| true); } if let Some(name) = name.strip_suffix("f32") { let result = match name { @@ -773,7 +747,7 @@ impl Evaluator<'_> { } _ => not_supported!("unknown f32 intrinsic {name}"), }; - return destination.write_from_bytes(self, &result.to_le_bytes()); + return destination.write_from_bytes(self, &result.to_le_bytes()).map(|()| true); } match name { "size_of" => { @@ -1146,12 +1120,6 @@ impl Evaluator<'_> { }; destination.write_from_interval(self, arg.interval) } - "likely" | "unlikely" => { - let [arg] = args else { - return Err(MirEvalError::InternalError("likely arg is not provided".into())); - }; - destination.write_from_interval(self, arg.interval) - } "ctpop" => { let [arg] = args else { return Err(MirEvalError::InternalError("ctpop arg is not provided".into())); @@ -1296,7 +1264,7 @@ impl Evaluator<'_> { None, span, )?; - return Ok(()); + return Ok(true); } } not_supported!("FnOnce was not available for executing const_eval_select"); @@ -1349,8 +1317,10 @@ impl Evaluator<'_> { self.write_memory_using_ref(dst, size)?.fill(val); Ok(()) } - _ => not_supported!("unknown intrinsic {name}"), + _ if needs_override => not_supported!("intrinsic {name} is not implemented"), + _ => return Ok(false), } + .map(|()| true) } fn size_align_of_unsized( diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 0a436ff2b41a..7429ce3c73e0 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -259,25 +259,25 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { return true; } + let is_intrinsic = db.attrs(func.into()).by_key(&sym::rustc_intrinsic).exists() + || data.abi.as_ref() == Some(&sym::rust_dash_intrinsic); + let loc = func.lookup(db.upcast()); match loc.container { hir_def::ItemContainerId::ExternBlockId(block) => { - // Function in an `extern` block are always unsafe to call, except when - // it is marked as `safe` or it has `"rust-intrinsic"` ABI there are a - // few exceptions. - let id = block.lookup(db.upcast()).id; - - let is_intrinsic = - id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic); - - if is_intrinsic { + if is_intrinsic || { + let id = block.lookup(db.upcast()).id; + id.item_tree(db.upcast())[id.value].abi.as_ref() == Some(&sym::rust_dash_intrinsic) + } { // Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - !data.attrs.by_key(&sym::rustc_safe_intrinsic).exists() + !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() } else { - // Extern items without `safe` modifier are always unsafe + // Function in an `extern` block are always unsafe to call, except when + // it is marked as `safe`. !data.is_safe() } } + _ if is_intrinsic => !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists(), _ => false, } } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ebd84fd2be2a..8c3b7a6d3cf5 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2246,35 +2246,33 @@ impl Function { /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).attrs.is_test() + db.attrs(self.id.into()).is_test() } /// is this a `fn main` or a function with an `export_name` of `main`? pub fn is_main(self, db: &dyn HirDatabase) -> bool { - let data = db.function_data(self.id); - data.attrs.export_name() == Some(&sym::main) - || self.module(db).is_crate_root() && data.name == sym::main + db.attrs(self.id.into()).export_name() == Some(&sym::main) + || self.module(db).is_crate_root() && db.function_data(self.id).name == sym::main } /// Is this a function with an `export_name` of `main`? pub fn exported_main(self, db: &dyn HirDatabase) -> bool { - let data = db.function_data(self.id); - data.attrs.export_name() == Some(&sym::main) + db.attrs(self.id.into()).export_name() == Some(&sym::main) } /// Does this function have the ignore attribute? pub fn is_ignore(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).attrs.is_ignore() + db.attrs(self.id.into()).is_ignore() } /// Does this function have `#[bench]` attribute? pub fn is_bench(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).attrs.is_bench() + db.attrs(self.id.into()).is_bench() } /// Is this function marked as unstable with `#[feature]` attribute? pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).attrs.is_unstable() + db.attrs(self.id.into()).is_unstable() } pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool { @@ -2289,8 +2287,7 @@ impl Function { } pub fn as_proc_macro(self, db: &dyn HirDatabase) -> Option { - let function_data = db.function_data(self.id); - let attrs = &function_data.attrs; + let attrs = db.attrs(self.id.into()); // FIXME: Store this in FunctionData flags? if !(attrs.is_proc_macro() || attrs.is_proc_macro_attribute() diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index aecafb444c30..865518fe941e 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -392,18 +392,22 @@ define_symbols! { rust_2024, rust_analyzer, Rust, + rustc_allocator_zeroed, + rustc_allocator, rustc_allow_incoherent_impl, rustc_builtin_macro, rustc_coherence_is_core, rustc_const_panic_str, + rustc_deallocator, rustc_deprecated_safe_2024, rustc_has_incoherent_inherent_impls, - rustc_intrinsic, rustc_intrinsic_must_be_overridden, + rustc_intrinsic, rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_macro_transparency, + rustc_reallocator, rustc_reservation_impl, rustc_safe_intrinsic, rustc_skip_array_during_method_dispatch, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 67629fcf7cc5..07767d5ae9f6 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -370,15 +370,13 @@ pub mod mem { // endregion:drop // region:transmute - extern "rust-intrinsic" { - pub fn transmute(src: Src) -> Dst; - } + #[rustc_intrinsic] + pub fn transmute(src: Src) -> Dst; // endregion:transmute // region:size_of - extern "rust-intrinsic" { - pub fn size_of() -> usize; - } + #[rustc_intrinsic] + pub fn size_of() -> usize; // endregion:size_of // region:discriminant