Skip to content

Commit e28e2b8

Browse files
authored
Rollup merge of #135705 - compiler-errors:lint, r=estebank
Consolidate ad-hoc MIR lints into real pass-manager-based MIR lints It feels much cleaner to do all MIR-related things using the pass manager.
2 parents 7e1ce54 + b08f3d5 commit e28e2b8

File tree

11 files changed

+120
-110
lines changed

11 files changed

+120
-110
lines changed

compiler/rustc_mir_build/messages.ftl

-12
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
118118
.note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
119119
.label = use of extern static
120120
121-
mir_build_force_inline =
122-
`{$callee}` is incompatible with `#[rustc_force_inline]`
123-
.attr = annotation here
124-
.callee = `{$callee}` defined here
125-
.note = incompatible due to: {$reason}
126-
127121
mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
128122
129123
mir_build_initializing_type_with_requires_unsafe =
@@ -330,12 +324,6 @@ mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/s
330324
mir_build_type_not_structural_tip =
331325
the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
332326
333-
mir_build_unconditional_recursion = function cannot return without recursing
334-
.label = cannot return without recursing
335-
.help = a `loop` may express intention better if this is on purpose
336-
337-
mir_build_unconditional_recursion_call_site_label = recursive call site
338-
339327
mir_build_union_field_requires_unsafe =
340328
access to union field is unsafe and requires unsafe block
341329
.note = the field may not be properly initialized: using uninitialized data will cause undefined behavior

compiler/rustc_mir_build/src/builder/mod.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode
2626
use rustc_middle::{bug, span_bug};
2727
use rustc_span::{Span, Symbol, sym};
2828

29-
use super::lints;
3029
use crate::builder::expr::as_place::PlaceBuilder;
3130
use crate::builder::scope::DropKind;
32-
use crate::check_inline;
3331

3432
pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
3533
tcx: TyCtxt<'tcx>,
@@ -48,7 +46,7 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
4846
}
4947

5048
/// Construct the MIR for a given `DefId`.
51-
pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
49+
pub(crate) fn build_mir<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> {
5250
let tcx = tcx.tcx;
5351
tcx.ensure_with_value().thir_abstract_const(def);
5452
if let Err(e) = tcx.check_match(def) {
@@ -80,9 +78,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
8078
}
8179
};
8280

83-
lints::check(tcx, &body);
84-
check_inline::check_force_inline(tcx, &body);
85-
8681
// The borrow checker will replace all the regions here with its own
8782
// inference variables. There's no point having non-erased regions here.
8883
// The exception is `body.user_type_annotations`, which is used unmodified

compiler/rustc_mir_build/src/errors.rs

-22
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,6 @@ use rustc_span::{Span, Symbol};
1111

1212
use crate::fluent_generated as fluent;
1313

14-
#[derive(LintDiagnostic)]
15-
#[diag(mir_build_unconditional_recursion)]
16-
#[help]
17-
pub(crate) struct UnconditionalRecursion {
18-
#[label]
19-
pub(crate) span: Span,
20-
#[label(mir_build_unconditional_recursion_call_site_label)]
21-
pub(crate) call_sites: Vec<Span>,
22-
}
23-
2414
#[derive(LintDiagnostic)]
2515
#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)]
2616
pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
@@ -1107,15 +1097,3 @@ impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> {
11071097
);
11081098
}
11091099
}
1110-
1111-
#[derive(Diagnostic)]
1112-
#[diag(mir_build_force_inline)]
1113-
#[note]
1114-
pub(crate) struct InvalidForceInline {
1115-
#[primary_span]
1116-
pub attr_span: Span,
1117-
#[label(mir_build_callee)]
1118-
pub callee_span: Span,
1119-
pub callee: String,
1120-
pub reason: &'static str,
1121-
}

compiler/rustc_mir_build/src/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
// "Go to file" feature to silently ignore all files in the module, probably
1616
// because it assumes that "build" is a build-output directory. See #134365.
1717
mod builder;
18-
pub mod check_inline;
1918
mod check_tail_calls;
2019
mod check_unsafety;
2120
mod errors;
22-
pub mod lints;
2321
mod thir;
2422

2523
use rustc_middle::util::Providers;
@@ -29,7 +27,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
2927
pub fn provide(providers: &mut Providers) {
3028
providers.check_match = thir::pattern::check_match;
3129
providers.lit_to_const = thir::constant::lit_to_const;
32-
providers.hooks.build_mir = builder::mir_build;
30+
providers.hooks.build_mir = builder::build_mir;
3331
providers.closure_saved_names_of_captured_variables =
3432
builder::closure_saved_names_of_captured_variables;
3533
providers.check_unsafety = check_unsafety::check_unsafety;

compiler/rustc_mir_transform/messages.ftl

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ mir_transform_force_inline =
2727
.callee = `{$callee}` defined here
2828
.note = could not be inlined due to: {$reason}
2929
30+
mir_transform_force_inline_attr =
31+
`{$callee}` is incompatible with `#[rustc_force_inline]`
32+
.attr = annotation here
33+
.callee = `{$callee}` defined here
34+
.note = incompatible due to: {$reason}
35+
3036
mir_transform_force_inline_justification =
3137
`{$callee}` is required to be inlined to: {$sym}
3238
@@ -66,6 +72,12 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned
6672
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
6773
.help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
6874
75+
mir_transform_unconditional_recursion = function cannot return without recursing
76+
.label = cannot return without recursing
77+
.help = a `loop` may express intention better if this is on purpose
78+
79+
mir_transform_unconditional_recursion_call_site_label = recursive call site
80+
6981
mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval
7082
.note = at compile-time, pointers do not have an integer value
7183
.note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior

compiler/rustc_mir_build/src/lints.rs renamed to compiler/rustc_mir_transform/src/check_call_recursion.rs

+43-38
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,54 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
1010
use rustc_span::Span;
1111

1212
use crate::errors::UnconditionalRecursion;
13+
use crate::pass_manager::MirLint;
1314

14-
pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
15-
check_call_recursion(tcx, body);
15+
pub(super) struct CheckCallRecursion;
16+
17+
impl<'tcx> MirLint<'tcx> for CheckCallRecursion {
18+
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
19+
let def_id = body.source.def_id().expect_local();
20+
21+
if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
22+
// If this is trait/impl method, extract the trait's args.
23+
let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
24+
Some(trait_def_id) => {
25+
let trait_args_count = tcx.generics_of(trait_def_id).count();
26+
&GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
27+
}
28+
_ => &[],
29+
};
30+
31+
check_recursion(tcx, body, CallRecursion { trait_args })
32+
}
33+
}
1634
}
1735

18-
fn check_call_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
19-
let def_id = body.source.def_id().expect_local();
36+
/// Requires drop elaboration to have been performed.
37+
pub(super) struct CheckDropRecursion;
2038

21-
if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) {
22-
// If this is trait/impl method, extract the trait's args.
23-
let trait_args = match tcx.trait_of_item(def_id.to_def_id()) {
24-
Some(trait_def_id) => {
25-
let trait_args_count = tcx.generics_of(trait_def_id).count();
26-
&GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count]
27-
}
28-
_ => &[],
29-
};
39+
impl<'tcx> MirLint<'tcx> for CheckDropRecursion {
40+
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
41+
let def_id = body.source.def_id().expect_local();
3042

31-
check_recursion(tcx, body, CallRecursion { trait_args })
43+
// First check if `body` is an `fn drop()` of `Drop`
44+
if let DefKind::AssocFn = tcx.def_kind(def_id)
45+
&& let Some(trait_ref) =
46+
tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
47+
&& let Some(drop_trait) = tcx.lang_items().drop_trait()
48+
&& drop_trait == trait_ref.instantiate_identity().def_id
49+
// avoid erroneous `Drop` impls from causing ICEs below
50+
&& let sig = tcx.fn_sig(def_id).instantiate_identity()
51+
&& sig.inputs().skip_binder().len() == 1
52+
{
53+
// It was. Now figure out for what type `Drop` is implemented and then
54+
// check for recursion.
55+
if let ty::Ref(_, dropped_ty, _) =
56+
tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
57+
{
58+
check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
59+
}
60+
}
3261
}
3362
}
3463

@@ -61,30 +90,6 @@ fn check_recursion<'tcx>(
6190
}
6291
}
6392

64-
/// Requires drop elaboration to have been performed first.
65-
pub fn check_drop_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
66-
let def_id = body.source.def_id().expect_local();
67-
68-
// First check if `body` is an `fn drop()` of `Drop`
69-
if let DefKind::AssocFn = tcx.def_kind(def_id)
70-
&& let Some(trait_ref) =
71-
tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id))
72-
&& let Some(drop_trait) = tcx.lang_items().drop_trait()
73-
&& drop_trait == trait_ref.instantiate_identity().def_id
74-
// avoid erroneous `Drop` impls from causing ICEs below
75-
&& let sig = tcx.fn_sig(def_id).instantiate_identity()
76-
&& sig.inputs().skip_binder().len() == 1
77-
{
78-
// It was. Now figure out for what type `Drop` is implemented and then
79-
// check for recursion.
80-
if let ty::Ref(_, dropped_ty, _) =
81-
tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind()
82-
{
83-
check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty });
84-
}
85-
}
86-
}
87-
8893
trait TerminatorClassifier<'tcx> {
8994
fn is_recursive_terminator(
9095
&self,

compiler/rustc_mir_build/src/check_inline.rs renamed to compiler/rustc_mir_transform/src/check_inline.rs

+31-21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
2+
//! definition alone (irrespective of any specific caller).
3+
14
use rustc_attr_parsing::InlineAttr;
25
use rustc_hir::def_id::DefId;
36
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -6,30 +9,37 @@ use rustc_middle::ty;
69
use rustc_middle::ty::TyCtxt;
710
use rustc_span::sym;
811

9-
/// Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its
10-
/// definition alone (irrespective of any specific caller).
11-
pub(crate) fn check_force_inline<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
12-
let def_id = body.source.def_id();
13-
if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() {
14-
return;
15-
}
16-
let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else {
17-
return;
18-
};
12+
use crate::pass_manager::MirLint;
1913

20-
if let Err(reason) =
21-
is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body))
22-
{
23-
tcx.dcx().emit_err(crate::errors::InvalidForceInline {
24-
attr_span,
25-
callee_span: tcx.def_span(def_id),
26-
callee: tcx.def_path_str(def_id),
27-
reason,
28-
});
14+
pub(super) struct CheckForceInline;
15+
16+
impl<'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+
}
2936
}
3037
}
3138

32-
pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(), &'static str> {
39+
pub(super) fn is_inline_valid_on_fn<'tcx>(
40+
tcx: TyCtxt<'tcx>,
41+
def_id: DefId,
42+
) -> Result<(), &'static str> {
3343
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
3444
if tcx.has_attr(def_id, sym::rustc_no_mir_inline) {
3545
return Err("#[rustc_no_mir_inline]");
@@ -65,7 +75,7 @@ pub fn is_inline_valid_on_fn<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<(
6575
Ok(())
6676
}
6777

68-
pub fn is_inline_valid_on_body<'tcx>(
78+
pub(super) fn is_inline_valid_on_body<'tcx>(
6979
_: TyCtxt<'tcx>,
7080
body: &Body<'tcx>,
7181
) -> Result<(), &'static str> {

compiler/rustc_mir_transform/src/errors.rs

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ use rustc_span::{Span, Symbol};
99

1010
use crate::fluent_generated as fluent;
1111

12+
#[derive(LintDiagnostic)]
13+
#[diag(mir_transform_unconditional_recursion)]
14+
#[help]
15+
pub(crate) struct UnconditionalRecursion {
16+
#[label]
17+
pub(crate) span: Span,
18+
#[label(mir_transform_unconditional_recursion_call_site_label)]
19+
pub(crate) call_sites: Vec<Span>,
20+
}
21+
22+
#[derive(Diagnostic)]
23+
#[diag(mir_transform_force_inline_attr)]
24+
#[note]
25+
pub(crate) struct InvalidForceInline {
26+
#[primary_span]
27+
pub attr_span: Span,
28+
#[label(mir_transform_callee)]
29+
pub callee_span: Span,
30+
pub callee: String,
31+
pub reason: &'static str,
32+
}
33+
1234
#[derive(LintDiagnostic)]
1335
pub(crate) enum ConstMutate {
1436
#[diag(mir_transform_const_modify)]

compiler/rustc_mir_transform/src/inline.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use tracing::{debug, instrument, trace, trace_span};
2121
use crate::cost_checker::CostChecker;
2222
use crate::deref_separator::deref_finder;
2323
use crate::simplify::simplify_cfg;
24-
use crate::util;
2524
use crate::validate::validate_types;
25+
use crate::{check_inline, util};
2626

2727
pub(crate) mod cycle;
2828

@@ -575,7 +575,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
575575
check_mir_is_available(inliner, caller_body, callsite.callee)?;
576576

577577
let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id());
578-
rustc_mir_build::check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
578+
check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?;
579579
check_codegen_attributes(inliner, callsite, callee_attrs)?;
580580

581581
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
@@ -590,7 +590,7 @@ fn try_inlining<'tcx, I: Inliner<'tcx>>(
590590
}
591591

592592
let callee_body = try_instance_mir(tcx, callsite.callee.def)?;
593-
rustc_mir_build::check_inline::is_inline_valid_on_body(tcx, callee_body)?;
593+
check_inline::is_inline_valid_on_body(tcx, callee_body)?;
594594
inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?;
595595

596596
let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions(

0 commit comments

Comments
 (0)