Skip to content

Commit fd127a3

Browse files
committedJan 4, 2025
Auto merge of #135031 - RalfJung:intrinsics-without-body, r=oli-obk
rustc_intrinsic: support functions without body We synthesize a HIR body `loop {}` but such bodyless intrinsics. Most of the diff is due to turning `ItemKind::Fn` into a brace (named-field) enum variant, because it carries a `bool`-typed field now. This is to remember whether the function has a body. MIR building panics to avoid ever translating the fake `loop {}` body, and the intrinsic logic uses the lack of a body to implicitly mark that intrinsic as must-be-overridden. I first tried actually having no body rather than generating the fake body, but there's a *lot* of code that assumes that all function items have HIR and MIR, so this didn't work very well. Then I noticed that even `rustc_intrinsic_must_be_overridden` intrinsics have MIR generated (they are filled with an `Unreachable` terminator) so I guess I am not the first to discover this. ;) r? `@oli-obk`
2 parents f17cf74 + 3cd3649 commit fd127a3

File tree

81 files changed

+272
-175
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+272
-175
lines changed
 

‎compiler/rustc_ast_lowering/src/item.rs

+44-24
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_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 {
@@ -435,11 +436,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
435436
}
436437
ItemKind::Delegation(box delegation) => {
437438
let delegation_results = self.lower_delegation(delegation, id);
438-
hir::ItemKind::Fn(
439-
delegation_results.sig,
440-
delegation_results.generics,
441-
delegation_results.body_id,
442-
)
439+
hir::ItemKind::Fn {
440+
sig: delegation_results.sig,
441+
generics: delegation_results.generics,
442+
body: delegation_results.body_id,
443+
has_body: true,
444+
}
443445
}
444446
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
445447
panic!("macros should have been expanded by now")
@@ -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),

0 commit comments

Comments
 (0)
Please sign in to comment.