rustc_mir_transform/
check_inline.rs1use rustc_hir::attrs::{AttributeKind, InlineAttr};
5use rustc_hir::def_id::DefId;
6use rustc_hir::find_attr;
7use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
8use rustc_middle::mir::{Body, TerminatorKind};
9use rustc_middle::ty;
10use rustc_middle::ty::TyCtxt;
11
12use crate::pass_manager::MirLint;
13
14pub(super) struct CheckForceInline;
15
16impl<'tcx> MirLint<'tcx> for CheckForceInline {
17 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
18 let def_id = body.source.def_id();
19 if !tcx.hir_body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
20 return;
21 }
22 let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
23 return;
24 };
25
26 if let Err(reason) =
27 is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
28 {
29 tcx.dcx().emit_err(crate::errors::InvalidForceInline {
30 attr_span,
31 callee_span: tcx.def_span(def_id),
32 callee: tcx.def_path_str(def_id),
33 reason,
34 });
35 }
36 }
37}
38
39pub(super) fn is_inline_valid_on_fn<'tcx>(
40 tcx: TyCtxt<'tcx>,
41 def_id: DefId,
42) -> Result<(), &'static str> {
43 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
44
45 if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcNoMirInline) {
46 return Err("#[rustc_no_mir_inline]");
47 }
48
49 let ty = tcx.type_of(def_id);
50 if match ty.instantiate_identity().kind() {
51 ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(),
52 ty::Closure(_, args) => args.as_closure().sig().c_variadic(),
53 _ => false,
54 } {
55 return Err("C variadic");
56 }
57
58 if codegen_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
59 return Err("cold");
60 }
61
62 if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) {
67 return Err("callee is an intrinsic");
68 }
69
70 Ok(())
71}
72
73pub(super) fn is_inline_valid_on_body<'tcx>(
74 _: TyCtxt<'tcx>,
75 body: &Body<'tcx>,
76) -> Result<(), &'static str> {
77 if body
78 .basic_blocks
79 .iter()
80 .any(|bb| matches!(bb.terminator().kind, TerminatorKind::TailCall { .. }))
81 {
82 return Err("can't inline functions with tail calls");
83 }
84
85 Ok(())
86}