diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index e35b4029b45..a4d97200cdb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -787,7 +787,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Handle intrinsics old codegen wants Expr's for, ourselves. let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().item_name(def_id)), + Some(ty::InstanceDef::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()), _ => None, }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 019cc1c847e..155eb834ecd 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -788,6 +788,10 @@ pub const BUILTIN_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", + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index a738fc86a06..a24961df67b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1746,8 +1746,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.attr_flags.get(self, index) } - fn get_intrinsic(self, index: DefIndex) -> bool { - self.root.tables.intrinsic.get(self, index) + fn get_intrinsic(self, index: DefIndex) -> Option { + self.root.tables.intrinsic.get(self, index).map(|d| d.decode(self)) } fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 1faddee2e98..1cbf854f733 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -348,7 +348,7 @@ provide! { tcx, def_id, other, cdata, cdata.get_stability_implications(tcx).iter().copied().collect() } stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) } - intrinsic => { cdata.get_intrinsic(def_id.index).then(|| tcx.item_name(def_id)) } + intrinsic => { cdata.get_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } missing_lang_items => { cdata.get_missing_lang_items(tcx) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f3213185449..54061a2c6fc 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1411,7 +1411,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Fn | DefKind::AssocFn = def_kind { self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id)); record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id)); - self.tables.intrinsic.set(def_id.index, tcx.intrinsic(def_id).is_some()); + if let Some(name) = tcx.intrinsic(def_id) { + record!(self.tables.intrinsic[def_id] <- name); + } } if let DefKind::TyParam = def_kind { let default = self.tcx.object_lifetime_default(def_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 49c97b8c2e6..6b4b12e9d6a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -375,7 +375,7 @@ macro_rules! define_tables { define_tables! { - defaulted: - intrinsic: Table, + intrinsic: Table>>, is_macro_rules: Table, is_type_alias_impl_trait: Table, type_alias_is_lazy: Table, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 626ea0342e5..1f64c76a57c 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1549,9 +1549,10 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { .any(|items| items.iter().any(|item| item.has_name(sym::notable_trait))) } -/// Determines whether an item is an intrinsic by Abi. +/// Determines whether an item is an intrinsic by Abi. or by whether it has a `rustc_intrinsic` attribute pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic) + || tcx.has_attr(def_id, sym::rustc_intrinsic) { Some(tcx.item_name(def_id.into())) } else { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index aa912c93c08..0b5ee2bc51b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1418,6 +1418,7 @@ symbols! { rustc_if_this_changed, rustc_inherit_overflow_checks, rustc_insignificant_dtor, + rustc_intrinsic, rustc_layout, rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index c8259c0024c..375fa1350b5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2517,79 +2517,79 @@ extern "rust-intrinsic" { where G: FnOnce, F: FnOnce; - - /// Returns whether the argument's value is statically known at - /// compile-time. - /// - /// This is useful when there is a way of writing the code that will - /// be *faster* when some variables have known values, but *slower* - /// in the general case: an `if is_val_statically_known(var)` can be used - /// to select between these two variants. The `if` will be optimized away - /// and only the desired branch remains. - /// - /// Formally speaking, this function non-deterministically returns `true` - /// or `false`, and the caller has to ensure sound behavior for both cases. - /// In other words, the following code has *Undefined Behavior*: - /// - /// ```no_run - /// #![feature(is_val_statically_known)] - /// #![feature(core_intrinsics)] - /// # #![allow(internal_features)] - /// use std::hint::unreachable_unchecked; - /// use std::intrinsics::is_val_statically_known; - /// - /// unsafe { - /// if !is_val_statically_known(0) { unreachable_unchecked(); } - /// } - /// ``` - /// - /// This also means that the following code's behavior is unspecified; it - /// may panic, or it may not: - /// - /// ```no_run - /// #![feature(is_val_statically_known)] - /// #![feature(core_intrinsics)] - /// # #![allow(internal_features)] - /// use std::intrinsics::is_val_statically_known; - /// - /// unsafe { - /// assert_eq!(is_val_statically_known(0), is_val_statically_known(0)); - /// } - /// ``` - /// - /// Unsafe code may not rely on `is_val_statically_known` returning any - /// particular value, ever. However, the compiler will generally make it - /// return `true` only if the value of the argument is actually known. - /// - /// When calling this in a `const fn`, both paths must be semantically - /// equivalent, that is, the result of the `true` branch and the `false` - /// branch must return the same value and have the same side-effects *no - /// matter what*. - #[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")] - #[rustc_nounwind] - pub fn is_val_statically_known(arg: T) -> bool; - - /// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in - /// macro expansion. - /// - /// This always returns `false` in const eval and Miri. The interpreter provides better - /// diagnostics than the checks that this is used to implement. However, this means - /// you should only be using this intrinsic to guard requirements that, if violated, - /// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those - /// checks entirely. - /// - /// Since this is evaluated after monomorphization, branching on this value can be used to - /// implement debug assertions that are included in the precompiled standard library, but can - /// be optimized out by builds that monomorphize the standard library code with debug - /// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. - #[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")] - #[rustc_safe_intrinsic] - #[cfg(not(bootstrap))] - pub(crate) fn debug_assertions() -> bool; } -#[cfg(bootstrap)] +/// Returns whether the argument's value is statically known at +/// compile-time. +/// +/// This is useful when there is a way of writing the code that will +/// be *faster* when some variables have known values, but *slower* +/// in the general case: an `if is_val_statically_known(var)` can be used +/// to select between these two variants. The `if` will be optimized away +/// and only the desired branch remains. +/// +/// Formally speaking, this function non-deterministically returns `true` +/// or `false`, and the caller has to ensure sound behavior for both cases. +/// In other words, the following code has *Undefined Behavior*: +/// +/// ```no_run +/// #![feature(is_val_statically_known)] +/// #![feature(core_intrinsics)] +/// # #![allow(internal_features)] +/// use std::hint::unreachable_unchecked; +/// use std::intrinsics::is_val_statically_known; +/// +/// unsafe { +/// if !is_val_statically_known(0) { unreachable_unchecked(); } +/// } +/// ``` +/// +/// This also means that the following code's behavior is unspecified; it +/// may panic, or it may not: +/// +/// ```no_run +/// #![feature(is_val_statically_known)] +/// #![feature(core_intrinsics)] +/// # #![allow(internal_features)] +/// use std::intrinsics::is_val_statically_known; +/// +/// unsafe { +/// assert_eq!(is_val_statically_known(0), is_val_statically_known(0)); +/// } +/// ``` +/// +/// Unsafe code may not rely on `is_val_statically_known` returning any +/// particular value, ever. However, the compiler will generally make it +/// return `true` only if the value of the argument is actually known. +/// +/// When calling this in a `const fn`, both paths must be semantically +/// equivalent, that is, the result of the `true` branch and the `false` +/// branch must return the same value and have the same side-effects *no +/// matter what*. +#[rustc_const_unstable(feature = "is_val_statically_known", issue = "none")] +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +pub const unsafe fn is_val_statically_known(_arg: T) -> bool { + false +} + +/// Returns the value of `cfg!(debug_assertions)`, but after monomorphization instead of in +/// macro expansion. +/// +/// This always returns `false` in const eval and Miri. The interpreter provides better +/// diagnostics than the checks that this is used to implement. However, this means +/// you should only be using this intrinsic to guard requirements that, if violated, +/// immediately lead to UB. Otherwise, const-eval and Miri will miss out on those +/// checks entirely. +/// +/// Since this is evaluated after monomorphization, branching on this value can be used to +/// implement debug assertions that are included in the precompiled standard library, but can +/// be optimized out by builds that monomorphize the standard library code with debug +/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. #[rustc_const_unstable(feature = "delayed_debug_assertions", issue = "none")] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] pub(crate) const fn debug_assertions() -> bool { cfg!(debug_assertions) } diff --git a/src/doc/unstable-book/src/language-features/intrinsics.md b/src/doc/unstable-book/src/language-features/intrinsics.md index 8fa8f567d7e..9d07ae6fc67 100644 --- a/src/doc/unstable-book/src/language-features/intrinsics.md +++ b/src/doc/unstable-book/src/language-features/intrinsics.md @@ -2,13 +2,60 @@ The tracking issue for this feature is: None. -Intrinsics are never intended to be stable directly, but intrinsics are often +Intrinsics are rarely intended to be stable directly, but are usually exported in some sort of stable manner. Prefer using the stable interfaces to the intrinsic directly when you can. ------------------------ +## Intrinsics with fallback logic + +Many intrinsics can be written in pure rust, albeit inefficiently or without supporting +some features that only exist on some backends. Backends can simply not implement those +intrinsics without causing any code miscompilations or failures to compile. + +```rust +#![feature(rustc_attrs, effects)] +#![allow(internal_features)] + +#[rustc_intrinsic] +const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} +``` + +Since these are just regular functions, it is perfectly ok to create the intrinsic twice: + +```rust +#![feature(rustc_attrs, effects)] +#![allow(internal_features)] + +#[rustc_intrinsic] +const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} + +mod foo { + #[rustc_intrinsic] + const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { + panic!("noisy const dealloc") + } +} + +``` + +The behaviour on backends that override the intrinsic is exactly the same. On other +backends, the intrinsic behaviour depends on which implementation is called, just like +with any regular function. + +## Intrinsics lowered to MIR instructions + +Various intrinsics have native MIR operations that they correspond to. Instead of requiring +backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass +will convert the calls to the MIR operation. Backends do not need to know about these intrinsics +at all. + +## Intrinsics without fallback logic + +These must be implemented by all backends. + These are imported as if they were FFI functions, with the special `rust-intrinsic` ABI. For example, if one was in a freestanding context, but wished to be able to `transmute` between types, and @@ -27,4 +74,5 @@ extern "rust-intrinsic" { } ``` -As with any other FFI functions, these are always `unsafe` to call. +As with any other FFI functions, these are by default always `unsafe` to call. +You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call. diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6710193f961..f39276f1778 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -643,7 +643,7 @@ impl Item { let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { unsafety: if abi == Abi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, self.def_id().unwrap()) + intrinsic_operation_unsafety(tcx, def_id.expect_local()) } else { hir::Unsafety::Unsafe }, diff --git a/tests/ui/consts/is_val_statically_known.rs b/tests/ui/consts/is_val_statically_known.rs index b0565842eb4..b4056375543 100644 --- a/tests/ui/consts/is_val_statically_known.rs +++ b/tests/ui/consts/is_val_statically_known.rs @@ -1,7 +1,6 @@ // run-pass -#![feature(core_intrinsics)] -#![feature(is_val_statically_known)] +#![feature(core_intrinsics, is_val_statically_known)] use std::intrinsics::is_val_statically_known;