| //! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its | |
| //! definition alone (irrespective of any specific caller). | |
| use rustc_hir::attrs::{AttributeKind, InlineAttr}; | |
| use rustc_hir::def_id::DefId; | |
| use rustc_hir::find_attr; | |
| use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; | |
| use rustc_middle::mir::{Body, TerminatorKind}; | |
| use rustc_middle::ty; | |
| use rustc_middle::ty::TyCtxt; | |
| use crate::pass_manager::MirLint; | |
| pub(super) struct CheckForceInline; | |
| impl<'tcx> MirLint<'tcx> for CheckForceInline { | |
| fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { | |
| let def_id = body.source.def_id(); | |
| if !tcx.hir_body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() { | |
| return; | |
| } | |
| let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else { | |
| return; | |
| }; | |
| if let Err(reason) = | |
| is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body)) | |
| { | |
| tcx.dcx().emit_err(crate::errors::InvalidForceInline { | |
| attr_span, | |
| callee_span: tcx.def_span(def_id), | |
| callee: tcx.def_path_str(def_id), | |
| reason, | |
| }); | |
| } | |
| } | |
| } | |
| pub(super) fn is_inline_valid_on_fn<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: DefId, | |
| ) -> Result<(), &'static str> { | |
| let codegen_attrs = tcx.codegen_fn_attrs(def_id); | |
| if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcNoMirInline) { | |
| return Err("#[rustc_no_mir_inline]"); | |
| } | |
| let ty = tcx.type_of(def_id); | |
| if match ty.instantiate_identity().kind() { | |
| ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(), | |
| ty::Closure(_, args) => args.as_closure().sig().c_variadic(), | |
| _ => false, | |
| } { | |
| return Err("C variadic"); | |
| } | |
| if codegen_attrs.flags.contains(CodegenFnAttrFlags::COLD) { | |
| return Err("cold"); | |
| } | |
| // Intrinsic fallback bodies are automatically made cross-crate inlineable, | |
| // but at this stage we don't know whether codegen knows the intrinsic, | |
| // so just conservatively don't inline it. This also ensures that we do not | |
| // accidentally inline the body of an intrinsic that *must* be overridden. | |
| if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcIntrinsic) { | |
| return Err("callee is an intrinsic"); | |
| } | |
| Ok(()) | |
| } | |
| pub(super) fn is_inline_valid_on_body<'tcx>( | |
| _: TyCtxt<'tcx>, | |
| body: &Body<'tcx>, | |
| ) -> Result<(), &'static str> { | |
| if body | |
| .basic_blocks | |
| .iter() | |
| .any(|bb| matches!(bb.terminator().kind, TerminatorKind::TailCall { .. })) | |
| { | |
| return Err("can't inline functions with tail calls"); | |
| } | |
| Ok(()) | |
| } |