Skip to content

Commit 26902d6

Browse files
committed
Diagnose liveness on MIR.
1 parent 96be3ff commit 26902d6

File tree

86 files changed

+2243
-2452
lines changed

Some content is hidden

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

86 files changed

+2243
-2452
lines changed

compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
749749
sess.time("MIR_effect_checking", || {
750750
for def_id in tcx.hir().body_owners() {
751751
tcx.ensure().has_ffi_unwind_calls(def_id);
752+
tcx.ensure().check_liveness(def_id);
752753

753754
// If we need to codegen, ensure that we emit all errors from
754755
// `mir_drops_elaborated_and_const_checked` now, to avoid discovering

compiler/rustc_middle/src/mir/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ pub enum BindingForm<'tcx> {
10101010
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
10111011
ImplicitSelf(ImplicitSelfKind),
10121012
/// Reference used in a guard expression to ensure immutability.
1013-
RefForGuard,
1013+
RefForGuard(Local),
10141014
}
10151015

10161016
TrivialTypeTraversalImpls! { BindingForm<'tcx> }
@@ -1027,7 +1027,7 @@ mod binding_form_impl {
10271027
match self {
10281028
Var(binding) => binding.hash_stable(hcx, hasher),
10291029
ImplicitSelf(kind) => kind.hash_stable(hcx, hasher),
1030-
RefForGuard => (),
1030+
RefForGuard(local) => local.hash_stable(hcx, hasher),
10311031
}
10321032
}
10331033
}
@@ -1242,7 +1242,7 @@ impl<'tcx> LocalDecl<'tcx> {
12421242
/// expression that is used to access said variable for the guard of the
12431243
/// match arm.
12441244
pub fn is_ref_for_guard(&self) -> bool {
1245-
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard))
1245+
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard(_)))
12461246
}
12471247

12481248
/// Returns `Some` if this is a reference to a static item that is used to

compiler/rustc_middle/src/mir/statement.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl<'tcx> StatementKind<'tcx> {
5050
impl<V, T> ProjectionElem<V, T> {
5151
/// Returns `true` if the target of this projection may refer to a different region of memory
5252
/// than the base.
53-
fn is_indirect(&self) -> bool {
53+
pub fn is_indirect(&self) -> bool {
5454
match self {
5555
Self::Deref => true,
5656

compiler/rustc_middle/src/query/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -943,8 +943,10 @@ rustc_queries! {
943943
desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) }
944944
}
945945

946-
query check_liveness(key: LocalDefId) {
947-
desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key) }
946+
query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::BitSet<abi::FieldIdx> {
947+
arena_cache
948+
desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) }
949+
cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }
948950
}
949951

950952
/// Return the live symbols in the crate for dead code check.

compiler/rustc_middle/src/ty/closure.rs

+2
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc
325325
)
326326
}
327327
},
328+
// Just change the type to the hidden type, so we can actually project.
329+
HirProjectionKind::OpaqueCast => {}
328330
proj => bug!("{:?} unexpected because it isn't captured", proj),
329331
}
330332
}

compiler/rustc_mir_build/src/build/matches/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2479,7 +2479,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
24792479
user_ty: None,
24802480
source_info,
24812481
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(
2482-
BindingForm::RefForGuard,
2482+
BindingForm::RefForGuard(for_arm_body),
24832483
))),
24842484
});
24852485
self.var_debug_info.push(VarDebugInfo {

compiler/rustc_mir_build/src/build/mod.rs

-6
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
6060
thir::BodyTy::Const(ty) => construct_const(tcx, def, thir, expr, ty),
6161
};
6262

63-
// this must run before MIR dump, because
64-
// "not all control paths return a value" is reported here.
65-
//
66-
// maybe move the check to a MIR pass?
67-
tcx.ensure().check_liveness(def);
68-
6963
// Don't steal here, instead steal in unsafeck. This is so that
7064
// pattern inline constants can be evaluated as part of building the
7165
// THIR of the parent function without a cycle.

compiler/rustc_mir_dataflow/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ pub use self::drop_flag_effects::{
1717
drop_flag_effects_for_function_entry, drop_flag_effects_for_location,
1818
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
1919
};
20+
use self::framework::SwitchIntEdgeEffects;
2021
pub use self::framework::{
2122
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, Direction, Engine,
2223
Forward, GenKill, GenKillAnalysis, JoinSemiLattice, MaybeReachable, Results, ResultsCursor,
23-
ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
24+
ResultsVisitable, ResultsVisitor,
2425
};
2526
use self::move_paths::MoveData;
2627

compiler/rustc_mir_transform/messages.ftl

+24
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,37 @@ mir_transform_ffi_unwind_call = call to {$foreign ->
1717
mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer
1818
.suggestion = cast `{$ident}` to obtain a function pointer
1919
20+
mir_transform_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
21+
2022
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
2123
.label = the value is held across this suspend point
2224
.note = {$reason}
2325
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
2426
mir_transform_operation_will_panic = this operation will panic at runtime
2527
28+
mir_transform_string_interpolation_only_works = string interpolation only works in `format!` invocations
29+
2630
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
2731
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
2832
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
2933
.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)
34+
35+
mir_transform_unused_assign = value assigned to `{$name}` is never read
36+
.help = maybe it is overwritten before being read?
37+
38+
mir_transform_unused_assign_passed = value passed to `{$name}` is never read
39+
.help = maybe it is overwritten before being read?
40+
41+
mir_transform_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read
42+
.help = did you mean to capture by reference instead?
43+
44+
mir_transform_unused_var_assigned_only = variable `{$name}` is assigned to, but never used
45+
.note = consider using `_{$name}` instead
46+
47+
mir_transform_unused_var_underscore = if this is intentional, prefix it with an underscore
48+
49+
mir_transform_unused_variable = unused variable: `{$name}`
50+
51+
mir_transform_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable
52+
53+
mir_transform_unused_variable_try_ignore = try ignoring the field

compiler/rustc_mir_transform/src/errors.rs

+99-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic};
1+
use rustc_errors::{
2+
codes::*, Applicability, Diag, DiagMessage, EmissionGuarantee, LintDiagnostic,
3+
SubdiagMessageOp, Subdiagnostic,
4+
};
25
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
36
use rustc_middle::mir::AssertKind;
47
use rustc_middle::ty::TyCtxt;
@@ -91,6 +94,101 @@ pub(crate) struct FnItemRef {
9194
pub ident: String,
9295
}
9396

97+
#[derive(LintDiagnostic)]
98+
#[diag(mir_transform_unused_capture_maybe_capture_ref)]
99+
#[help]
100+
pub(crate) struct UnusedCaptureMaybeCaptureRef {
101+
pub name: String,
102+
}
103+
104+
#[derive(LintDiagnostic)]
105+
#[diag(mir_transform_unused_var_assigned_only)]
106+
#[note]
107+
pub(crate) struct UnusedVarAssignedOnly {
108+
pub name: String,
109+
}
110+
111+
#[derive(LintDiagnostic)]
112+
#[diag(mir_transform_unused_assign)]
113+
#[help]
114+
pub(crate) struct UnusedAssign {
115+
pub name: String,
116+
}
117+
118+
#[derive(LintDiagnostic)]
119+
#[diag(mir_transform_unused_assign_passed)]
120+
#[help]
121+
pub(crate) struct UnusedAssignPassed {
122+
pub name: String,
123+
}
124+
125+
#[derive(LintDiagnostic)]
126+
#[diag(mir_transform_unused_variable)]
127+
pub(crate) struct UnusedVariable {
128+
pub name: String,
129+
#[subdiagnostic]
130+
pub string_interp: Vec<UnusedVariableStringInterp>,
131+
#[subdiagnostic]
132+
pub sugg: UnusedVariableSugg,
133+
}
134+
135+
#[derive(Subdiagnostic)]
136+
pub(crate) enum UnusedVariableSugg {
137+
#[multipart_suggestion(
138+
mir_transform_unused_variable_try_ignore,
139+
applicability = "machine-applicable"
140+
)]
141+
TryIgnore {
142+
#[suggestion_part(code = "{name}: _")]
143+
shorthands: Vec<Span>,
144+
#[suggestion_part(code = "_")]
145+
non_shorthands: Vec<Span>,
146+
name: String,
147+
},
148+
149+
#[multipart_suggestion(
150+
mir_transform_unused_var_underscore,
151+
applicability = "machine-applicable"
152+
)]
153+
TryPrefix {
154+
#[suggestion_part(code = "_{name}")]
155+
spans: Vec<Span>,
156+
name: String,
157+
},
158+
159+
#[help(mir_transform_unused_variable_args_in_macro)]
160+
NoSugg {
161+
#[primary_span]
162+
span: Span,
163+
name: String,
164+
},
165+
}
166+
167+
pub(crate) struct UnusedVariableStringInterp {
168+
pub lit: Span,
169+
}
170+
171+
impl Subdiagnostic for UnusedVariableStringInterp {
172+
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
173+
self,
174+
diag: &mut Diag<'_, G>,
175+
_: F,
176+
) {
177+
diag.span_label(
178+
self.lit,
179+
crate::fluent_generated::mir_transform_maybe_string_interpolation,
180+
);
181+
diag.multipart_suggestion(
182+
crate::fluent_generated::mir_transform_string_interpolation_only_works,
183+
vec![
184+
(self.lit.shrink_to_lo(), String::from("format!(")),
185+
(self.lit.shrink_to_hi(), String::from(")")),
186+
],
187+
Applicability::MachineApplicable,
188+
);
189+
}
190+
}
191+
94192
pub(crate) struct MustNotSupend<'tcx, 'a> {
95193
pub tcx: TyCtxt<'tcx>,
96194
pub yield_sp: Span,

compiler/rustc_mir_transform/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ mod jump_threading;
8484
mod known_panics_lint;
8585
mod large_enums;
8686
mod lint;
87+
mod liveness;
8788
mod lower_intrinsics;
8889
mod lower_slice_len;
8990
mod match_branches;
@@ -112,6 +113,7 @@ mod sroa;
112113
mod unreachable_enum_branching;
113114
mod unreachable_prop;
114115

116+
use liveness::check_liveness;
115117
use rustc_const_eval::transform::check_consts::{self, ConstCx};
116118
use rustc_const_eval::transform::validate;
117119
use rustc_mir_dataflow::rustc_peek;
@@ -132,6 +134,7 @@ pub fn provide(providers: &mut Providers) {
132134
mir_for_ctfe,
133135
mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses,
134136
optimized_mir,
137+
check_liveness,
135138
is_mir_available,
136139
is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did),
137140
mir_callgraph_reachable: inline::cycle::mir_callgraph_reachable,
@@ -400,6 +403,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
400403
}
401404
}
402405

406+
tcx.ensure_with_value().check_liveness(def);
407+
403408
let (body, _) = tcx.mir_promoted(def);
404409
let mut body = body.steal();
405410
if let Some(error_reported) = mir_borrowck.tainted_by_errors {

0 commit comments

Comments
 (0)