Skip to content

Commit 3cd3649

Browse files
committed
rustc_intrinsic: support functions without body; they are implicitly marked as must-be-overridden
1 parent be65012 commit 3cd3649

File tree

19 files changed

+118
-68
lines changed

19 files changed

+118
-68
lines changed

compiler/rustc_ast_lowering/src/item.rs

+39-19
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
222222
decl,
223223
coroutine_kind,
224224
body.as_deref(),
225+
attrs,
225226
);
226227

227228
let itctx = ImplTraitContext::Universal;
@@ -233,7 +234,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
233234
header: this.lower_fn_header(*header, hir::Safety::Safe),
234235
span: this.lower_span(*fn_sig_span),
235236
};
236-
hir::ItemKind::Fn { sig, generics, body: body_id }
237+
hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
237238
})
238239
}
239240
ItemKind::Mod(_, mod_kind) => match mod_kind {
@@ -439,6 +440,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
439440
sig: delegation_results.sig,
440441
generics: delegation_results.generics,
441442
body: delegation_results.body_id,
443+
has_body: true,
442444
}
443445
}
444446
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
@@ -747,7 +749,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
747749

748750
fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
749751
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
750-
self.lower_attrs(hir_id, &i.attrs);
752+
let attrs = self.lower_attrs(hir_id, &i.attrs);
751753
let trait_item_def_id = hir_id.expect_owner();
752754

753755
let (generics, kind, has_default) = match &i.kind {
@@ -785,6 +787,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
785787
&sig.decl,
786788
sig.header.coroutine_kind,
787789
Some(body),
790+
attrs,
788791
);
789792
let (generics, sig) = self.lower_method_sig(
790793
generics,
@@ -877,7 +880,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
877880
let has_value = true;
878881
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
879882
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
880-
self.lower_attrs(hir_id, &i.attrs);
883+
let attrs = self.lower_attrs(hir_id, &i.attrs);
881884

882885
let (generics, kind) = match &i.kind {
883886
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
@@ -900,6 +903,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
900903
&sig.decl,
901904
sig.header.coroutine_kind,
902905
body.as_deref(),
906+
attrs,
903907
);
904908
let (generics, sig) = self.lower_method_sig(
905909
generics,
@@ -1054,20 +1058,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
10541058
})
10551059
}
10561060

1057-
fn lower_fn_body_block(
1058-
&mut self,
1059-
span: Span,
1060-
decl: &FnDecl,
1061-
body: Option<&Block>,
1062-
) -> hir::BodyId {
1063-
self.lower_fn_body(decl, |this| this.lower_block_expr_opt(span, body))
1064-
}
1065-
1066-
fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
1067-
match block {
1068-
Some(block) => self.lower_block_expr(block),
1069-
None => self.expr_err(span, self.dcx().has_errors().unwrap()),
1070-
}
1061+
fn lower_fn_body_block(&mut self, decl: &FnDecl, body: &Block) -> hir::BodyId {
1062+
self.lower_fn_body(decl, |this| this.lower_block_expr(body))
10711063
}
10721064

10731065
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
@@ -1089,9 +1081,37 @@ impl<'hir> LoweringContext<'_, 'hir> {
10891081
decl: &FnDecl,
10901082
coroutine_kind: Option<CoroutineKind>,
10911083
body: Option<&Block>,
1084+
attrs: &'hir [hir::Attribute],
10921085
) -> hir::BodyId {
1093-
let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else {
1094-
return self.lower_fn_body_block(span, decl, body);
1086+
let Some(body) = body else {
1087+
// Functions without a body are an error, except if this is an intrinsic. For those we
1088+
// create a fake body so that the entire rest of the compiler doesn't have to deal with
1089+
// this as a special case.
1090+
return self.lower_fn_body(decl, |this| {
1091+
if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) {
1092+
let empty_block = hir::Block {
1093+
hir_id: this.next_id(),
1094+
stmts: &[],
1095+
expr: None,
1096+
rules: hir::BlockCheckMode::DefaultBlock,
1097+
span,
1098+
targeted_by_break: false,
1099+
};
1100+
let loop_ = hir::ExprKind::Loop(
1101+
this.arena.alloc(empty_block),
1102+
None,
1103+
hir::LoopSource::Loop,
1104+
span,
1105+
);
1106+
hir::Expr { hir_id: this.next_id(), kind: loop_, span }
1107+
} else {
1108+
this.expr_err(span, this.dcx().has_errors().unwrap())
1109+
}
1110+
});
1111+
};
1112+
let Some(coroutine_kind) = coroutine_kind else {
1113+
// Typical case: not a coroutine.
1114+
return self.lower_fn_body_block(decl, body);
10951115
};
10961116
self.lower_body(|this| {
10971117
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(

compiler/rustc_ast_passes/src/ast_validation.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
920920
ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
921921
self.check_defaultness(item.span, *defaultness);
922922

923-
if body.is_none() {
923+
let is_intrinsic =
924+
item.attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic);
925+
if body.is_none() && !is_intrinsic {
924926
self.dcx().emit_err(errors::FnWithoutBody {
925927
span: item.span,
926928
replace_span: self.ending_semi_or_hi(item.span),

compiler/rustc_const_eval/src/interpret/call.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
566566
| ty::InstanceKind::ThreadLocalShim(..)
567567
| ty::InstanceKind::AsyncDropGlueCtorShim(..)
568568
| ty::InstanceKind::Item(_) => {
569-
// We need MIR for this fn
569+
// We need MIR for this fn.
570+
// Note that this can be an intrinsic, if we are executing its fallback body.
570571
let Some((body, instance)) = M::find_mir_or_eval_fn(
571572
self,
572573
instance,

compiler/rustc_feature/src/builtin_attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
10131013
),
10141014
gated!(
10151015
rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics,
1016-
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
1016+
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items",
10171017
),
10181018
gated!(
10191019
rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics,

compiler/rustc_hir/src/hir.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -3640,7 +3640,7 @@ impl<'hir> Item<'hir> {
36403640
ItemKind::Const(ty, generics, body), (ty, generics, *body);
36413641

36423642
expect_fn, (&FnSig<'hir>, &'hir Generics<'hir>, BodyId),
3643-
ItemKind::Fn { sig, generics, body }, (sig, generics, *body);
3643+
ItemKind::Fn { sig, generics, body, .. }, (sig, generics, *body);
36443644

36453645
expect_macro, (&ast::MacroDef, MacroKind), ItemKind::Macro(def, mk), (def, *mk);
36463646

@@ -3768,7 +3768,15 @@ pub enum ItemKind<'hir> {
37683768
/// A `const` item.
37693769
Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId),
37703770
/// A function declaration.
3771-
Fn { sig: FnSig<'hir>, generics: &'hir Generics<'hir>, body: BodyId },
3771+
Fn {
3772+
sig: FnSig<'hir>,
3773+
generics: &'hir Generics<'hir>,
3774+
body: BodyId,
3775+
/// Whether this function actually has a body.
3776+
/// For functions without a body, `body` is synthesized (to avoid ICEs all over the
3777+
/// compiler), but that code should never be translated.
3778+
has_body: bool,
3779+
},
37723780
/// A MBE macro definition (`macro_rules!` or `macro`).
37733781
Macro(&'hir ast::MacroDef, MacroKind),
37743782
/// A module.

compiler/rustc_middle/src/ty/util.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1778,9 +1778,16 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Intrinsi
17781778
&& (matches!(tcx.fn_sig(def_id).skip_binder().abi(), ExternAbi::RustIntrinsic)
17791779
|| tcx.has_attr(def_id, sym::rustc_intrinsic))
17801780
{
1781+
let must_be_overridden = tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden)
1782+
|| match tcx.hir_node_by_def_id(def_id) {
1783+
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { has_body, .. }, .. }) => {
1784+
!has_body
1785+
}
1786+
_ => true,
1787+
};
17811788
Some(ty::IntrinsicDef {
17821789
name: tcx.item_name(def_id.into()),
1783-
must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden),
1790+
must_be_overridden,
17841791
const_stable: tcx.has_attr(def_id, sym::rustc_intrinsic_const_stable_indirect),
17851792
})
17861793
} else {

compiler/rustc_mir_build/src/builder/mod.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1005,11 +1005,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
10051005
if let Some(source_scope) = scope {
10061006
self.source_scope = source_scope;
10071007
}
1008+
10081009
if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) {
10091010
let source_info = self.source_info(rustc_span::DUMMY_SP);
10101011
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
10111012
self.cfg.start_new_block().unit()
10121013
} else {
1014+
// Ensure we don't silently codegen functions with fake bodies.
1015+
match self.tcx.hir_node(self.hir_id) {
1016+
hir::Node::Item(hir::Item {
1017+
kind: hir::ItemKind::Fn { has_body: false, .. },
1018+
..
1019+
}) => {
1020+
self.tcx.dcx().span_delayed_bug(
1021+
expr_span,
1022+
format!("fn item without body has reached MIR building: {:?}", self.def_id),
1023+
);
1024+
}
1025+
_ => {}
1026+
}
10131027
self.expr_into_dest(Place::return_place(), block, expr_id)
10141028
}
10151029
}

compiler/rustc_mir_transform/src/inline.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ impl<'tcx> Inliner<'tcx> {
190190

191191
// Intrinsic fallback bodies are automatically made cross-crate inlineable,
192192
// but at this stage we don't know whether codegen knows the intrinsic,
193-
// so just conservatively don't inline it.
193+
// so just conservatively don't inline it. This also ensures that we do not
194+
// accidentally inline the body of an intrinsic that *must* be overridden.
194195
if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_intrinsic) {
195196
return Err("Callee is an intrinsic, do not inline fallback bodies");
196197
}

compiler/rustc_monomorphize/src/collector.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ use rustc_middle::{bug, span_bug};
232232
use rustc_session::Limit;
233233
use rustc_session::config::EntryFnType;
234234
use rustc_span::source_map::{Spanned, dummy_spanned, respan};
235-
use rustc_span::{DUMMY_SP, Span, sym};
235+
use rustc_span::{DUMMY_SP, Span};
236236
use tracing::{debug, instrument, trace};
237237

238238
use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit};
@@ -894,9 +894,8 @@ fn visit_instance_use<'tcx>(
894894
if !tcx.should_codegen_locally(instance) {
895895
return;
896896
}
897-
if let ty::InstanceKind::Intrinsic(def_id) = instance.def {
898-
let name = tcx.item_name(def_id);
899-
if let Some(_requirement) = ValidityRequirement::from_intrinsic(name) {
897+
if let Some(intrinsic) = tcx.intrinsic(instance.def_id()) {
898+
if let Some(_requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) {
900899
// The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will
901900
// be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any
902901
// of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to
@@ -906,11 +905,12 @@ fn visit_instance_use<'tcx>(
906905
if tcx.should_codegen_locally(panic_instance) {
907906
output.push(create_fn_mono_item(tcx, panic_instance, source));
908907
}
909-
} else if tcx.has_attr(def_id, sym::rustc_intrinsic)
910-
&& !tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden)
911-
{
912-
// Codegen the fallback body of intrinsics with fallback bodies
913-
let instance = ty::Instance::new(def_id, instance.args);
908+
} else if !intrinsic.must_be_overridden {
909+
// Codegen the fallback body of intrinsics with fallback bodies.
910+
// We explicitly skip this otherwise to ensure we get a linker error
911+
// if anyone tries to call this intrinsic and the codegen backend did not
912+
// override the implementation.
913+
let instance = ty::Instance::new(instance.def_id(), instance.args);
914914
if tcx.should_codegen_locally(instance) {
915915
output.push(create_fn_mono_item(tcx, instance, source));
916916
}

src/tools/miri/src/bin/miri.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
242242
let is_reachable_non_generic = matches!(
243243
tcx.hir_node_by_def_id(local_def_id),
244244
Node::Item(&hir::Item {
245-
kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn { .. },
245+
kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. },
246246
..
247247
}) | Node::ImplItem(&hir::ImplItem {
248248
kind: hir::ImplItemKind::Fn(..),

tests/ui/consts/auxiliary/unstable_intrinsic.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33

44
#[unstable(feature = "unstable", issue = "42")]
55
#[rustc_intrinsic]
6-
#[rustc_intrinsic_must_be_overridden]
7-
pub const unsafe fn size_of_val<T>(x: *const T) -> usize { 42 }
6+
pub const unsafe fn size_of_val<T>(x: *const T) -> usize;
87

98
#[unstable(feature = "unstable", issue = "42")]
109
#[rustc_const_unstable(feature = "unstable", issue = "42")]
1110
#[rustc_intrinsic]
12-
#[rustc_intrinsic_must_be_overridden]
13-
pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize { 42 }
11+
pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize;

tests/ui/consts/const-unstable-intrinsic.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! neither within a crate nor cross-crate.
33
//@ aux-build:unstable_intrinsic.rs
44
#![feature(staged_api, rustc_attrs, intrinsics)]
5-
#![stable(since="1.0.0", feature = "stable")]
5+
#![stable(since = "1.0.0", feature = "stable")]
66
#![feature(local)]
77

88
extern crate unstable_intrinsic;
@@ -30,25 +30,20 @@ const fn const_main() {
3030

3131
#[unstable(feature = "local", issue = "42")]
3232
#[rustc_intrinsic]
33-
#[rustc_intrinsic_must_be_overridden]
34-
pub const unsafe fn size_of_val<T>(x: *const T) -> usize { 42 }
33+
pub const unsafe fn size_of_val<T>(x: *const T) -> usize;
3534

3635
#[unstable(feature = "local", issue = "42")]
3736
#[rustc_const_unstable(feature = "local", issue = "42")]
3837
#[rustc_intrinsic]
39-
#[rustc_intrinsic_must_be_overridden]
40-
pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize { 42 }
38+
pub const unsafe fn min_align_of_val<T>(x: *const T) -> usize;
4139

4240
#[stable(feature = "rust1", since = "1.0.0")]
4341
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")]
4442
#[inline]
4543
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
4644
// Const stability attributes are not inherited from parent items.
4745
#[rustc_intrinsic]
48-
#[rustc_intrinsic_must_be_overridden]
49-
const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
50-
unimplemented!()
51-
}
46+
const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
5247

5348
unsafe { copy(src, dst, count) }
5449
//~^ ERROR cannot be (indirectly) exposed to stable

tests/ui/consts/const-unstable-intrinsic.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@ LL | const fn const_main() {
6969
|
7070

7171
error: intrinsic `copy::copy` cannot be (indirectly) exposed to stable
72-
--> $DIR/const-unstable-intrinsic.rs:53:14
72+
--> $DIR/const-unstable-intrinsic.rs:48:14
7373
|
7474
LL | unsafe { copy(src, dst, count) }
7575
| ^^^^^^^^^^^^^^^^^^^^^
7676
|
7777
= help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval)
7878

7979
error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]`
80-
--> $DIR/const-unstable-intrinsic.rs:61:9
80+
--> $DIR/const-unstable-intrinsic.rs:56:9
8181
|
8282
LL | super::size_of_val(src);
8383
| ^^^^^^^^^^^^^^^^^^^^^^^

tests/ui/intrinsics/not-overridden.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
//@ rustc-env:RUST_BACKTRACE=0
1010

1111
#[rustc_intrinsic]
12-
#[rustc_intrinsic_must_be_overridden]
13-
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {}
12+
pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize);
1413

1514
fn main() {
1615
unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }

tests/ui/intrinsics/not-overridden.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: must be overridden by codegen backend, but isn't
2-
--> $DIR/not-overridden.rs:16:14
2+
--> $DIR/not-overridden.rs:15:14
33
|
44
LL | unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) }
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/ui/parser/fn-body-optional-semantic-fail.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// Tests the different rules for `fn` forms requiring the presence or lack of a body.
2+
// Also ensures that functions without a body don't show other odd errors.
3+
4+
trait Trait {}
25

36
fn main() {
47
fn f1(); //~ ERROR free function without a body
8+
fn f1_rpit() -> impl Trait; //~ ERROR free function without a body
59
fn f2() {} // OK.
610

711
trait X {

0 commit comments

Comments
 (0)