Skip to content

Commit da4d104

Browse files
committed
Diagnose liveness on MIR.
1 parent d1311f5 commit da4d104

File tree

79 files changed

+2169
-2403
lines changed

Some content is hidden

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

79 files changed

+2169
-2403
lines changed

compiler/rustc_interface/src/passes.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
798798
rustc_mir_transform::check_unsafety::check_unsafety(tcx, def_id);
799799
}
800800
tcx.ensure().has_ffi_unwind_calls(def_id);
801+
tcx.ensure().check_liveness(def_id);
801802

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

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ pub enum BindingForm<'tcx> {
705705
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
706706
ImplicitSelf(ImplicitSelfKind),
707707
/// Reference used in a guard expression to ensure immutability.
708-
RefForGuard,
708+
RefForGuard(Local),
709709
}
710710

711711
TrivialTypeTraversalAndLiftImpls! {
@@ -724,7 +724,7 @@ mod binding_form_impl {
724724
match self {
725725
Var(binding) => binding.hash_stable(hcx, hasher),
726726
ImplicitSelf(kind) => kind.hash_stable(hcx, hasher),
727-
RefForGuard => (),
727+
RefForGuard(local) => local.hash_stable(hcx, hasher),
728728
}
729729
}
730730
}
@@ -955,7 +955,7 @@ impl<'tcx> LocalDecl<'tcx> {
955955
/// expression that is used to access said variable for the guard of the
956956
/// match arm.
957957
pub fn is_ref_for_guard(&self) -> bool {
958-
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard))
958+
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard(_)))
959959
}
960960

961961
/// Returns `Some` if this is a reference to a static item that is used to
@@ -1519,7 +1519,7 @@ impl<'tcx> StatementKind<'tcx> {
15191519
impl<V, T> ProjectionElem<V, T> {
15201520
/// Returns `true` if the target of this projection may refer to a different region of memory
15211521
/// than the base.
1522-
fn is_indirect(&self) -> bool {
1522+
pub fn is_indirect(&self) -> bool {
15231523
match self {
15241524
Self::Deref => true,
15251525

@@ -1574,7 +1574,7 @@ impl<V, T> ProjectionElem<V, T> {
15741574
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
15751575
pub type ProjectionKind = ProjectionElem<(), ()>;
15761576

1577-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1577+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
15781578
pub struct PlaceRef<'tcx> {
15791579
pub local: Local,
15801580
pub projection: &'tcx [PlaceElem<'tcx>],
@@ -1661,6 +1661,13 @@ impl From<Local> for Place<'_> {
16611661
}
16621662
}
16631663

1664+
impl From<Local> for PlaceRef<'_> {
1665+
#[inline]
1666+
fn from(local: Local) -> Self {
1667+
PlaceRef { local, projection: List::empty() }
1668+
}
1669+
}
1670+
16641671
impl<'tcx> PlaceRef<'tcx> {
16651672
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
16661673
/// a single deref of a local.
@@ -1746,7 +1753,13 @@ impl<'tcx> PlaceRef<'tcx> {
17461753

17471754
impl Debug for Place<'_> {
17481755
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1749-
for elem in self.projection.iter().rev() {
1756+
Debug::fmt(&self.as_ref(), fmt)
1757+
}
1758+
}
1759+
1760+
impl Debug for PlaceRef<'_> {
1761+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
1762+
for &elem in self.projection.iter().rev() {
17501763
match elem {
17511764
ProjectionElem::OpaqueCast(_)
17521765
| ProjectionElem::Downcast(_, _)
@@ -1764,7 +1777,7 @@ impl Debug for Place<'_> {
17641777

17651778
write!(fmt, "{:?}", self.local)?;
17661779

1767-
for elem in self.projection.iter() {
1780+
for &elem in self.projection.iter() {
17681781
match elem {
17691782
ProjectionElem::OpaqueCast(ty) => {
17701783
write!(fmt, " as {})", ty)?;

compiler/rustc_middle/src/query/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,10 @@ rustc_queries! {
930930
desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) }
931931
}
932932

933-
query check_liveness(key: LocalDefId) {
934-
desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key) }
933+
query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::BitSet<abi::FieldIdx> {
934+
arena_cache
935+
desc { |tcx| "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) }
936+
cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }
935937
}
936938

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2277,7 +2277,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22772277
source_info,
22782278
internal: false,
22792279
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(
2280-
BindingForm::RefForGuard,
2280+
BindingForm::RefForGuard(for_arm_body),
22812281
))),
22822282
});
22832283
self.var_debug_info.push(VarDebugInfo {

compiler/rustc_mir_build/src/build/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,6 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
6969
let thir = thir.steal();
7070

7171
tcx.ensure().check_match(def);
72-
// this must run before MIR dump, because
73-
// "not all control paths return a value" is reported here.
74-
//
75-
// maybe move the check to a MIR pass?
76-
tcx.ensure().check_liveness(def);
7772

7873
match thir.body_type {
7974
thir::BodyTy::Fn(fn_sig) => construct_fn(tcx, def, &thir, expr, fn_sig),

compiler/rustc_mir_transform/messages.ftl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ mir_transform_fn_item_ref = taking a reference to a function item does not give
2525
2626
mir_transform_initializing_valid_range_label = initializing type with `rustc_layout_scalar_valid_range` attr
2727
mir_transform_initializing_valid_range_note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
28+
29+
mir_transform_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
30+
2831
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
2932
.label = the value is held across this suspend point
3033
.note = {$reason}
@@ -44,6 +47,8 @@ mir_transform_requires_unsafe = {$details} is unsafe and requires unsafe {$op_in
4447
4548
mir_transform_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
4649
50+
mir_transform_string_interpolation_only_works = string interpolation only works in `format!` invocations
51+
4752
mir_transform_target_feature_call_label = call to function with `#[target_feature]`
4853
mir_transform_target_feature_call_note = can only be called if the required target features are available
4954
@@ -58,9 +63,27 @@ mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe
5863
.suggestion = consider wrapping the function body in an unsafe block
5964
.note = an unsafe function restricts its caller, but its body is safe by default
6065
66+
mir_transform_unused_assign = value assigned to `{$name}` is never read
67+
.help = maybe it is overwritten before being read?
68+
69+
mir_transform_unused_assign_passed = value passed to `{$name}` is never read
70+
.help = maybe it is overwritten before being read?
71+
72+
mir_transform_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read
73+
.help = did you mean to capture by reference instead?
74+
6175
mir_transform_unused_unsafe = unnecessary `unsafe` block
6276
.label = because it's nested under this `unsafe` block
6377
78+
mir_transform_unused_var_assigned_only = variable `{$name}` is assigned to, but never used
79+
.note = consider using `_{$name}` instead
80+
81+
mir_transform_unused_var_underscore = if this is intentional, prefix it with an underscore
82+
83+
mir_transform_unused_variable = unused variable: `{$name}`
84+
85+
mir_transform_unused_variable_try_ignore = try ignoring the field
86+
6487
mir_transform_use_of_asm_label = use of inline assembly
6588
mir_transform_use_of_asm_note = inline assembly is entirely unchecked and can cause undefined behavior
6689
mir_transform_use_of_extern_static_label = use of extern static

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_errors::{
2-
Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
3-
IntoDiagnostic,
2+
AddToDiagnostic, Applicability, DecorateLint, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
3+
EmissionGuarantee, Handler, IntoDiagnostic,
44
};
55
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
66
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -237,6 +237,90 @@ pub(crate) struct FnItemRef {
237237
pub ident: String,
238238
}
239239

240+
#[derive(LintDiagnostic)]
241+
#[diag(mir_transform_unused_capture_maybe_capture_ref)]
242+
#[help]
243+
pub(crate) struct UnusedCaptureMaybeCaptureRef {
244+
pub name: String,
245+
}
246+
247+
#[derive(LintDiagnostic)]
248+
#[diag(mir_transform_unused_var_assigned_only)]
249+
#[note]
250+
pub(crate) struct UnusedVarAssignedOnly {
251+
pub name: String,
252+
}
253+
254+
#[derive(LintDiagnostic)]
255+
#[diag(mir_transform_unused_assign)]
256+
#[help]
257+
pub(crate) struct UnusedAssign {
258+
pub name: String,
259+
}
260+
261+
#[derive(LintDiagnostic)]
262+
#[diag(mir_transform_unused_assign_passed)]
263+
#[help]
264+
pub(crate) struct UnusedAssignPassed {
265+
pub name: String,
266+
}
267+
268+
#[derive(LintDiagnostic)]
269+
#[diag(mir_transform_unused_variable)]
270+
pub(crate) struct UnusedVariable {
271+
pub name: String,
272+
#[subdiagnostic]
273+
pub string_interp: Vec<UnusedVariableStringInterp>,
274+
#[subdiagnostic]
275+
pub sugg: UnusedVariableSugg,
276+
}
277+
278+
#[derive(Subdiagnostic)]
279+
pub(crate) enum UnusedVariableSugg {
280+
#[multipart_suggestion(
281+
mir_transform_unused_variable_try_ignore,
282+
applicability = "machine-applicable"
283+
)]
284+
TryIgnore {
285+
#[suggestion_part(code = "{name}: _")]
286+
shorthands: Vec<Span>,
287+
#[suggestion_part(code = "_")]
288+
non_shorthands: Vec<Span>,
289+
name: String,
290+
},
291+
292+
#[multipart_suggestion(
293+
mir_transform_unused_var_underscore,
294+
applicability = "machine-applicable"
295+
)]
296+
TryPrefix {
297+
#[suggestion_part(code = "_{name}")]
298+
spans: Vec<Span>,
299+
name: String,
300+
},
301+
}
302+
303+
pub(crate) struct UnusedVariableStringInterp {
304+
pub lit: Span,
305+
}
306+
307+
impl AddToDiagnostic for UnusedVariableStringInterp {
308+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) {
309+
diag.span_label(
310+
self.lit,
311+
crate::fluent_generated::mir_transform_maybe_string_interpolation,
312+
);
313+
diag.multipart_suggestion(
314+
crate::fluent_generated::mir_transform_string_interpolation_only_works,
315+
vec![
316+
(self.lit.shrink_to_lo(), String::from("format!(")),
317+
(self.lit.shrink_to_hi(), String::from(")")),
318+
],
319+
Applicability::MachineApplicable,
320+
);
321+
}
322+
}
323+
240324
#[derive(LintDiagnostic)]
241325
#[diag(mir_transform_must_not_suspend)]
242326
pub(crate) struct MustNotSupend<'a> {

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ mod generator;
7878
mod inline;
7979
mod instsimplify;
8080
mod large_enums;
81+
mod liveness;
8182
mod lower_intrinsics;
8283
mod lower_slice_len;
8384
mod match_branches;
@@ -105,6 +106,7 @@ mod sroa;
105106
mod uninhabited_enum_branching;
106107
mod unreachable_prop;
107108

109+
use liveness::check_liveness;
108110
use rustc_const_eval::transform::check_consts::{self, ConstCx};
109111
use rustc_const_eval::transform::promote_consts;
110112
use rustc_const_eval::transform::validate;
@@ -129,6 +131,7 @@ pub fn provide(providers: &mut Providers) {
129131
mir_for_ctfe,
130132
mir_generator_witnesses: generator::mir_generator_witnesses,
131133
optimized_mir,
134+
check_liveness,
132135
is_mir_available,
133136
is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did),
134137
mir_callgraph_reachable: inline::cycle::mir_callgraph_reachable,
@@ -394,6 +397,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
394397
}
395398
}
396399

400+
tcx.ensure_with_value().check_liveness(def);
401+
397402
let (body, _) = tcx.mir_promoted(def);
398403
let mut body = body.steal();
399404
if let Some(error_reported) = mir_borrowck.tainted_by_errors {

0 commit comments

Comments
 (0)