about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-01-30 16:03:58 +0000
committerOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2024-02-12 09:44:22 +0000
commit92281c7e815f3898f4403d45a079c7d266b78076 (patch)
treed4831cc4acae8b26a7bb97b17cf9c75cbeedfa8c
parent0eee945680754e30f0f40fb051f98ffc7b1d8c62 (diff)
downloadrust-92281c7e815f3898f4403d45a079c7d266b78076.tar.gz
rust-92281c7e815f3898f4403d45a079c7d266b78076.zip
Implement intrinsics with fallback bodies
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs3
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/intrinsics.rs138
-rw-r--r--src/doc/unstable-book/src/language-features/intrinsics.md52
-rw-r--r--src/librustdoc/clean/types.rs2
-rw-r--r--tests/ui/consts/is_val_statically_known.rs3
12 files changed, 136 insertions, 81 deletions
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<Symbol> {
+        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<DefIndex, bool>,
+    intrinsic: Table<DefIndex, Option<LazyValue<Symbol>>>,
     is_macro_rules: Table<DefIndex, bool>,
     is_type_alias_impl_trait: Table<DefIndex, bool>,
     type_alias_is_lazy: Table<DefIndex, bool>,
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<Symbol> {
     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<ARG, Output = RET>,
         F: FnOnce<ARG, Output = RET>;
+}
 
-    /// 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<T: Copy>(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;
+/// 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<T: Copy>(_arg: T) -> bool {
+    false
 }
 
-#[cfg(bootstrap)]
+/// 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;