Skip to content

Commit 0442451

Browse files
committed
Add a scheme for moving away from extern "rust-intrinsic" entirely
1 parent 082d50d commit 0442451

File tree

17 files changed

+132
-9
lines changed

17 files changed

+132
-9
lines changed

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,17 @@ fn codegen_regular_intrinsic_call<'tcx>(
12551255

12561256
// Unimplemented intrinsics must have a fallback body. The fallback body is obtained
12571257
// by converting the `InstanceDef::Intrinsic` to an `InstanceDef::Item`.
1258-
_ => return Err(Instance::new(instance.def_id(), instance.args)),
1258+
_ => {
1259+
let intrinsic = fx.tcx.intrinsic(instance.def_id()).unwrap();
1260+
if intrinsic.must_be_overridden {
1261+
span_bug!(
1262+
source_info.span,
1263+
"intrinsic {} must be overridden by codegen_cranelift, but isn't",
1264+
intrinsic.name,
1265+
);
1266+
}
1267+
return Err(Instance::new(instance.def_id(), instance.args));
1268+
}
12591269
}
12601270

12611271
let ret_block = fx.get_block(destination.unwrap());

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
1616
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
1717
use rustc_middle::util::Providers;
1818
use rustc_session::config::{CrateType, OomStrategy};
19+
use rustc_span::sym;
1920
use rustc_target::spec::{SanitizerSet, TlsModel};
2021

2122
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
@@ -81,6 +82,10 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
8182
return library.kind.is_statically_included().then_some(def_id);
8283
}
8384

85+
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
86+
return None;
87+
}
88+
8489
// Only consider nodes that actually have exported symbols.
8590
match tcx.def_kind(def_id) {
8691
DefKind::Fn | DefKind::Static(_) => {}

compiler/rustc_codegen_ssa/src/mir/block.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
895895
MergingSucc::False
896896
};
897897
}
898-
Err(instance) => Some(instance),
898+
Err(instance) => {
899+
if intrinsic.must_be_overridden {
900+
span_bug!(
901+
span,
902+
"intrinsic {} must be overridden by codegen backend, but isn't",
903+
intrinsic.name,
904+
);
905+
}
906+
Some(instance)
907+
}
899908
}
900909
}
901910
};

compiler/rustc_feature/src/builtin_attrs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
792792
rustc_intrinsic, Normal, template!(Word), ErrorFollowing,
793793
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
794794
),
795+
rustc_attr!(
796+
rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing,
797+
"the `#[rustc_intrinsic_must_be_overridden]` attribute is used to declare intrinsics without real bodies",
798+
),
795799

796800
// ==========================================================================
797801
// Internal attributes, Testing:

compiler/rustc_metadata/src/rmeta/encoder.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1051,13 +1051,18 @@ fn should_encode_mir(
10511051
// Coroutines require optimized MIR to compute layout.
10521052
DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => (false, true),
10531053
// Full-fledged functions + closures
1054-
DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
1054+
def_kind @ (DefKind::AssocFn | DefKind::Fn | DefKind::Closure) => {
10551055
let generics = tcx.generics_of(def_id);
1056-
let opt = tcx.sess.opts.unstable_opts.always_encode_mir
1056+
let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir
10571057
|| (tcx.sess.opts.output_types.should_codegen()
10581058
&& reachable_set.contains(&def_id)
10591059
&& (generics.requires_monomorphization(tcx)
10601060
|| tcx.cross_crate_inlinable(def_id)));
1061+
if matches!(def_kind, DefKind::AssocFn | DefKind::Fn) {
1062+
if let Some(intrinsic) = tcx.intrinsic(def_id) {
1063+
opt &= !intrinsic.must_be_overridden;
1064+
}
1065+
}
10611066
// The function has a `const` modifier or is in a `#[const_trait]`.
10621067
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
10631068
|| tcx.is_const_default_method(def_id.to_def_id());

compiler/rustc_middle/src/ty/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use super::TyCtxt;
55
#[derive(Copy, Clone, Debug, Decodable, Encodable, HashStable)]
66
pub struct IntrinsicDef {
77
pub name: Symbol,
8+
/// Whether the intrinsic has no meaningful body and all backends need to shim all calls to it.
9+
pub must_be_overridden: bool,
810
}
911

1012
impl TyCtxt<'_> {

compiler/rustc_middle/src/ty/util.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,10 @@ pub fn intrinsic(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::IntrinsicDef
16461646
if matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
16471647
|| tcx.has_attr(def_id, sym::rustc_intrinsic)
16481648
{
1649-
Some(ty::IntrinsicDef { name: tcx.item_name(def_id.into()) })
1649+
Some(ty::IntrinsicDef {
1650+
name: tcx.item_name(def_id.into()),
1651+
must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden),
1652+
})
16501653
} else {
16511654
None
16521655
}

compiler/rustc_mir_transform/src/cross_crate_inline.rs

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
2323
return false;
2424
}
2525

26+
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
27+
return false;
28+
}
29+
2630
// This just reproduces the logic from Instance::requires_inline.
2731
match tcx.def_kind(def_id) {
2832
DefKind::Ctor(..) | DefKind::Closure => return true,

compiler/rustc_mir_transform/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> {
632632
}
633633

634634
fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
635+
if let Some(attr) = tcx.get_attr(did, sym::rustc_intrinsic_must_be_overridden) {
636+
span_bug!(
637+
attr.span,
638+
"this intrinsic must be overridden by the codegen backend, it has no meaningful body",
639+
)
640+
}
635641
if tcx.is_constructor(did.to_def_id()) {
636642
// There's no reason to run all of the MIR passes on constructors when
637643
// we can just output the MIR we want directly. This also saves const

compiler/rustc_monomorphize/src/collector.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,11 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
10191019
return false;
10201020
}
10211021

1022+
if tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) {
1023+
// These are implemented by backends directly and have no meaningful body.
1024+
return false;
1025+
}
1026+
10221027
if def_id.is_local() {
10231028
// Local items cannot be referred to locally without monomorphizing them locally.
10241029
return true;

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,7 @@ symbols! {
14281428
rustc_inherit_overflow_checks,
14291429
rustc_insignificant_dtor,
14301430
rustc_intrinsic,
1431+
rustc_intrinsic_must_be_overridden,
14311432
rustc_layout,
14321433
rustc_layout_scalar_valid_range_end,
14331434
rustc_layout_scalar_valid_range_start,

library/core/src/intrinsics.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -2478,9 +2478,8 @@ extern "rust-intrinsic" {
24782478
#[rustc_nounwind]
24792479
pub fn black_box<T>(dummy: T) -> T;
24802480

2481-
/// `ptr` must point to a vtable.
2482-
/// The intrinsic will return the size stored in that vtable.
24832481
#[rustc_nounwind]
2482+
#[cfg(bootstrap)]
24842483
pub fn vtable_size(ptr: *const ()) -> usize;
24852484

24862485
/// `ptr` must point to a vtable.
@@ -2660,6 +2659,17 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 {
26602659
#[cfg_attr(bootstrap, inline)]
26612660
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
26622661

2662+
/// `ptr` must point to a vtable.
2663+
/// The intrinsic will return the size stored in that vtable.
2664+
#[rustc_nounwind]
2665+
#[unstable(feature = "core_intrinsics", issue = "none")]
2666+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
2667+
#[cfg_attr(not(bootstrap), rustc_intrinsic_must_be_overridden)]
2668+
#[cfg(not(bootstrap))]
2669+
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
2670+
unreachable!()
2671+
}
2672+
26632673
// Some functions are defined here because they accidentally got made
26642674
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
26652675
// (`transmute` also falls into this category, but it cannot be wrapped due to the

src/doc/unstable-book/src/language-features/intrinsics.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,23 @@ with any regular function.
5252
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
5353
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
5454
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
55-
at all.
55+
at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
56+
or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist
57+
anymore after MIR analyses.
5658

5759
## Intrinsics without fallback logic
5860

5961
These must be implemented by all backends.
6062

63+
### `#[rustc_intrinsic]` declarations
64+
65+
These are written like intrinsics with fallback bodies, but the body is irrelevant.
66+
Use `loop {}` for the body or call the intrinsic recursively and add
67+
`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
68+
invoke the body.
69+
70+
### Legacy extern ABI based intrinsics
71+
6172
These are imported as if they were FFI functions, with the special
6273
`rust-intrinsic` ABI. For example, if one was in a freestanding
6374
context, but wished to be able to `transmute` between types, and

src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ fn check_terminator<'tcx>(
335335
// within const fns. `transmute` is allowed in all other const contexts.
336336
// This won't really scale to more intrinsics or functions. Let's allow const
337337
// transmutes in const fn before we add more hacks to this.
338-
if matches!(tcx.intrinsic(fn_def_id), Some(sym::transmute)) {
338+
if tcx.is_intrinsic(fn_def_id, sym::transmute) {
339339
return Err((
340340
span,
341341
"can only call `transmute` from const items, not `const fn`".into(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Check that `vtable_size` gets overridden by llvm backend even if there is no
2+
//! `rustc_intrinsic_must_be_overridden` attribute on this usage.
3+
#![feature(rustc_attrs)]
4+
//@run-pass
5+
6+
#[rustc_intrinsic]
7+
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
8+
panic!();
9+
}
10+
11+
trait Trait {}
12+
impl Trait for () {}
13+
14+
fn main() {
15+
let x: &dyn Trait = &();
16+
unsafe {
17+
let (_data, vtable): (*const (), *const ()) = core::mem::transmute(x);
18+
assert_eq!(vtable_size(vtable), 0);
19+
}
20+
}

tests/ui/intrinsics/not-overridden.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Check that intrinsics that do not get overridden, but are marked as such,
2+
//! cause an error instead of silently invoking the body.
3+
#![feature(rustc_attrs, effects)]
4+
//@ build-fail
5+
//@ failure-status:101
6+
//@ normalize-stderr-test ".*note: .*\n\n" -> ""
7+
//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
8+
//@ normalize-stderr-test "internal compiler error:.*: intrinsic const_deallocate " -> ""
9+
//@ rustc-env:RUST_BACKTRACE=0
10+
11+
#[rustc_intrinsic]
12+
#[rustc_intrinsic_must_be_overridden]
13+
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
14+
15+
fn main() {
16+
unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
17+
//~^ ERROR: must be overridden
18+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: must be overridden by codegen backend, but isn't
2+
--> $DIR/not-overridden.rs:16:14
3+
|
4+
LL | unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
query stack during panic:
8+
end of query stack
9+
error: aborting due to 1 previous error
10+

0 commit comments

Comments
 (0)