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`
This commit is contained in:
bors 2025-01-04 12:50:38 +00:00
commit fd127a3a84
81 changed files with 272 additions and 175 deletions

View file

@ -2810,7 +2810,7 @@ fn clean_maybe_renamed_item<'tcx>(
}),
ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx),
// proc macros can have a name set by attributes
ItemKind::Fn(ref sig, generics, body_id) => {
ItemKind::Fn { ref sig, generics, body: body_id, .. } => {
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
}
ItemKind::Trait(_, _, generics, bounds, item_ids) => {

View file

@ -245,7 +245,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
match item.kind {
ItemKind::Static(_, _, _)
| ItemKind::Const(_, _, _)
| ItemKind::Fn(_, _, _)
| ItemKind::Fn { .. }
| ItemKind::Macro(_, _)
| ItemKind::TyAlias(_, _)
| ItemKind::Enum(_, _)

View file

@ -499,7 +499,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::ItemKind::Mod(m) => {
self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id);
}
hir::ItemKind::Fn(..)
hir::ItemKind::Fn { .. }
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)

View file

@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
ItemKind::Use(..) => Use,
ItemKind::Static(..) => Static,
ItemKind::Const(..) => Const,
ItemKind::Fn(..) => Fn,
ItemKind::Fn{ .. } => Fn,
ItemKind::Macro(..) => Macro,
ItemKind::Mod(..) => Mod,
ItemKind::ForeignMod { .. } => ForeignMod,

View file

@ -21,7 +21,7 @@ pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
}
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Fn(_, _, eid) = item.kind {
if let ItemKind::Fn { body: eid, .. } = item.kind {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
} else {
true

View file

@ -639,7 +639,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
self.check_private_items,
);
match item.kind {
ItemKind::Fn(sig, _, body_id) => {
ItemKind::Fn { sig, body: body_id, .. } => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
|| in_external_macro(cx.tcx.sess, item.span))
{

View file

@ -25,7 +25,7 @@ pub(super) fn check(
// page. So associated items or impl blocks are not part of this list.
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Fn(..)
| ItemKind::Fn{ .. }
| ItemKind::Macro(..)
| ItemKind::Mod(..)
| ItemKind::TyAlias(..)

View file

@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
&& let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
&& let parent = cx.tcx.hir().get_parent_item(e.hir_id)
&& let OwnerNode::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_owner_node(parent)
&& let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent)
// If the next item up is a function we check if it is an entry point
// and only then emit a linter warning
&& !is_entrypoint_fn(cx, parent.to_def_id())

View file

@ -253,7 +253,11 @@ fn is_empty_body(cx: &LateContext<'_>, body: BodyId) -> bool {
impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(_, generics, body_id) = item.kind
if let ItemKind::Fn {
generics,
body: body_id,
..
} = item.kind
&& !generics.params.is_empty()
&& !is_empty_body(cx, body_id)
&& (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id))

View file

@ -24,7 +24,12 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
if let hir::ItemKind::Fn {
ref sig,
body: ref body_id,
..
} = item.kind
{
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some(attr) = attr {

View file

@ -36,7 +36,7 @@ fn result_err_ty<'tcx>(
}
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64, msrv: &Msrv) {
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
if let hir::ItemKind::Fn { ref sig, .. } = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span)
{
if cx.effective_visibilities.is_exported(item.owner_id.def_id) {

View file

@ -149,7 +149,12 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
);
}
},
ItemKind::Fn(ref sig, generics, body_id) => {
ItemKind::Fn {
ref sig,
generics,
body: body_id,
..
} => {
let body = cx.tcx.hir().body(body_id);
for ty in sig.decl.inputs {

View file

@ -95,7 +95,13 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Fn(ref sig, generics, id) = item.kind {
if let ItemKind::Fn {
ref sig,
generics,
body: id,
..
} = item.kind
{
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
} else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() {

View file

@ -75,7 +75,11 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
..
}) => (),
Node::Item(hir::Item {
kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _),
kind:
hir::ItemKind::Fn {
sig: FnSig { decl, .. },
..
},
..
})
| Node::TraitItem(hir::TraitItem {

View file

@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
},
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
Node::Item(item) => {
if let ItemKind::Fn(..) = item.kind {
if let ItemKind::Fn{ .. } = item.kind {
let output = cx
.tcx
.fn_sig(item.owner_id)

View file

@ -496,7 +496,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
Node::Stmt(_) => return true,
Node::Block(..) => continue,
Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind
if let ItemKind::Fn { body: body_id, .. } = &item.kind
&& let output_ty = return_ty(cx, item.owner_id)
&& rustc_hir_typeck::can_coerce(cx.tcx, cx.param_env, item.owner_id.def_id, ty, output_ty)
{

View file

@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
match it.kind {
hir::ItemKind::Fn(..) => {
hir::ItemKind::Fn{ .. } => {
// ignore main()
if it.ident.name == sym::main {
let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID;

View file

@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
return;
}
match it.kind {
hir::ItemKind::Fn(..) => {
hir::ItemKind::Fn{ .. } => {
let desc = "a function";
let attrs = cx.tcx.hir().attrs(it.hir_id());
check_missing_inline_attrs(cx, attrs, it.span, desc);

View file

@ -76,7 +76,7 @@ impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]);
impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
if let hir::ItemKind::Fn { ref sig, .. } = item.kind {
self.check_sig(cx, item.owner_id.def_id, sig.decl);
}
}

View file

@ -144,7 +144,7 @@ impl NoEffect {
|diag| {
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
if let Node::Item(item) = parent.1
&& let ItemKind::Fn(..) = item.kind
&& let ItemKind::Fn{ .. } = item.kind
&& let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id)
&& let [.., final_stmt] = block.stmts
&& final_stmt.hir_id == stmt.hir_id

View file

@ -37,7 +37,7 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut app = Applicability::MaybeIncorrect;
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app);

View file

@ -189,7 +189,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
let mut parents = hir.parent_iter(body.value.hir_id);
let (item_id, sig, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn(sig, ..) = &i.kind {
if let ItemKind::Fn { sig, .. } = &i.kind {
(i.owner_id, sig, false)
} else {
return;

View file

@ -204,7 +204,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
// Ensure this is not the final stmt, otherwise removing it would cause a compile error
&& let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id))
&& let ItemKind::Fn(_, _, body) = item.kind
&& let ItemKind::Fn { body, .. } = item.kind
&& let block = cx.tcx.hir().body(body).value
&& let ExprKind::Block(block, _) = block.kind
&& !is_inside_let_else(cx.tcx, expr)

View file

@ -130,9 +130,9 @@ impl LateLintPass<'_> for UnnecessaryBoxReturns {
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
let ItemKind::Fn(signature, ..) = &item.kind else {
let ItemKind::Fn { sig, .. } = &item.kind else {
return;
};
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
self.check_fn_item(cx, sig.decl, item.owner_id.def_id, item.ident.name);
}
}

View file

@ -245,7 +245,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
ItemKind::ExternCrate(_) => (Pat::Str("extern"), Pat::Str(";")),
ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
ItemKind::Fn { sig, .. } => (fn_header_search_pat(sig.header), Pat::Str("")),
ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),

View file

@ -1397,7 +1397,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
enclosing_node.and_then(|node| match node {
Node::Block(block) => Some(block),
Node::Item(&Item {
kind: ItemKind::Fn(_, _, eid),
kind: ItemKind::Fn { body: eid, .. },
..
})
| Node::ImplItem(&ImplItem {
@ -2565,7 +2565,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
// function scope
.any(|(_id, node)| {
if let Node::Item(item) = node {
if let ItemKind::Fn(_, _, _) = item.kind {
if let ItemKind::Fn { .. } = item.kind {
// Note that we have sorted the item names in the visitor,
// so the binary_search gets the same as `contains`, but faster.
return names.binary_search(&item.ident.name).is_ok();
@ -2722,7 +2722,7 @@ impl<'tcx> ExprUseCtxt<'tcx> {
}) => ExprUseNode::ConstStatic(owner_id),
Node::Item(&Item {
kind: ItemKind::Fn(..),
kind: ItemKind::Fn { .. },
owner_id,
..
})

View file

@ -242,7 +242,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
let is_reachable_non_generic = matches!(
tcx.hir_node_by_def_id(local_def_id),
Node::Item(&hir::Item {
kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn(..),
kind: hir::ItemKind::Static(..) | hir::ItemKind::Fn{ .. },
..
}) | Node::ImplItem(&hir::ImplItem {
kind: hir::ImplItemKind::Fn(..),