Skip to content

Commit

Permalink
Diagnose liveness on MIR.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjgillot committed Apr 7, 2024
1 parent 937f1ff commit 4e098a0
Show file tree
Hide file tree
Showing 86 changed files with 2,243 additions and 2,452 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
sess.time("MIR_effect_checking", || {
for def_id in tcx.hir().body_owners() {
tcx.ensure().has_ffi_unwind_calls(def_id);
tcx.ensure().check_liveness(def_id);

// If we need to codegen, ensure that we emit all errors from
// `mir_drops_elaborated_and_const_checked` now, to avoid discovering
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ pub enum BindingForm<'tcx> {
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
ImplicitSelf(ImplicitSelfKind),
/// Reference used in a guard expression to ensure immutability.
RefForGuard,
RefForGuard(Local),
}

TrivialTypeTraversalImpls! { BindingForm<'tcx> }
Expand All @@ -1029,7 +1029,7 @@ mod binding_form_impl {
match self {
Var(binding) => binding.hash_stable(hcx, hasher),
ImplicitSelf(kind) => kind.hash_stable(hcx, hasher),
RefForGuard => (),
RefForGuard(local) => local.hash_stable(hcx, hasher),
}
}
}
Expand Down Expand Up @@ -1244,7 +1244,7 @@ impl<'tcx> LocalDecl<'tcx> {
/// expression that is used to access said variable for the guard of the
/// match arm.
pub fn is_ref_for_guard(&self) -> bool {
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard))
matches!(self.local_info(), LocalInfo::User(BindingForm::RefForGuard(_)))
}

/// Returns `Some` if this is a reference to a static item that is used to
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<'tcx> StatementKind<'tcx> {
impl<V, T> ProjectionElem<V, T> {
/// Returns `true` if the target of this projection may refer to a different region of memory
/// than the base.
fn is_indirect(&self) -> bool {
pub fn is_indirect(&self) -> bool {
match self {
Self::Deref => true,

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,8 +943,10 @@ rustc_queries! {
desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) }
}

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

/// Return the live symbols in the crate for dead code check.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc
)
}
},
// Just change the type to the hidden type, so we can actually project.
HirProjectionKind::OpaqueCast => {}
proj => bug!("{:?} unexpected because it isn't captured", proj),
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2479,7 +2479,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
user_ty: None,
source_info,
local_info: ClearCrossCrate::Set(Box::new(LocalInfo::User(
BindingForm::RefForGuard,
BindingForm::RefForGuard(for_arm_body),
))),
});
self.var_debug_info.push(VarDebugInfo {
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx
thir::BodyTy::Const(ty) => construct_const(tcx, def, thir, expr, ty),
};

// this must run before MIR dump, because
// "not all control paths return a value" is reported here.
//
// maybe move the check to a MIR pass?
tcx.ensure().check_liveness(def);

// Don't steal here, instead steal in unsafeck. This is so that
// pattern inline constants can be evaluated as part of building the
// THIR of the parent function without a cycle.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_dataflow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ pub use self::drop_flag_effects::{
drop_flag_effects_for_function_entry, drop_flag_effects_for_location,
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
};
use self::framework::SwitchIntEdgeEffects;
pub use self::framework::{
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, Direction, Engine,
Forward, GenKill, GenKillAnalysis, JoinSemiLattice, MaybeReachable, Results, ResultsCursor,
ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
ResultsVisitable, ResultsVisitor,
};
use self::move_paths::MoveData;

Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_mir_transform/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,37 @@ mir_transform_ffi_unwind_call = call to {$foreign ->
mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer
.suggestion = cast `{$ident}` to obtain a function pointer
mir_transform_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be
.label = the value is held across this suspend point
.note = {$reason}
.help = consider using a block (`{"{ ... }"}`) to shrink the value's scope, ending before the suspend point
mir_transform_operation_will_panic = this operation will panic at runtime
mir_transform_string_interpolation_only_works = string interpolation only works in `format!` invocations
mir_transform_unaligned_packed_ref = reference to packed field is unaligned
.note = packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
.note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
.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)
mir_transform_unused_assign = value assigned to `{$name}` is never read
.help = maybe it is overwritten before being read?
mir_transform_unused_assign_passed = value passed to `{$name}` is never read
.help = maybe it is overwritten before being read?
mir_transform_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read
.help = did you mean to capture by reference instead?
mir_transform_unused_var_assigned_only = variable `{$name}` is assigned to, but never used
.note = consider using `_{$name}` instead
mir_transform_unused_var_underscore = if this is intentional, prefix it with an underscore
mir_transform_unused_variable = unused variable: `{$name}`
mir_transform_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable
mir_transform_unused_variable_try_ignore = try ignoring the field
100 changes: 99 additions & 1 deletion compiler/rustc_mir_transform/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use rustc_errors::{codes::*, Diag, DiagMessage, LintDiagnostic};
use rustc_errors::{
codes::*, Applicability, Diag, DiagMessage, EmissionGuarantee, LintDiagnostic,
SubdiagMessageOp, Subdiagnostic,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::mir::AssertKind;
use rustc_middle::ty::TyCtxt;
Expand Down Expand Up @@ -91,6 +94,101 @@ pub(crate) struct FnItemRef {
pub ident: String,
}

#[derive(LintDiagnostic)]
#[diag(mir_transform_unused_capture_maybe_capture_ref)]
#[help]
pub(crate) struct UnusedCaptureMaybeCaptureRef {
pub name: String,
}

#[derive(LintDiagnostic)]
#[diag(mir_transform_unused_var_assigned_only)]
#[note]
pub(crate) struct UnusedVarAssignedOnly {
pub name: String,
}

#[derive(LintDiagnostic)]
#[diag(mir_transform_unused_assign)]
#[help]
pub(crate) struct UnusedAssign {
pub name: String,
}

#[derive(LintDiagnostic)]
#[diag(mir_transform_unused_assign_passed)]
#[help]
pub(crate) struct UnusedAssignPassed {
pub name: String,
}

#[derive(LintDiagnostic)]
#[diag(mir_transform_unused_variable)]
pub(crate) struct UnusedVariable {
pub name: String,
#[subdiagnostic]
pub string_interp: Vec<UnusedVariableStringInterp>,
#[subdiagnostic]
pub sugg: UnusedVariableSugg,
}

#[derive(Subdiagnostic)]
pub(crate) enum UnusedVariableSugg {
#[multipart_suggestion(
mir_transform_unused_variable_try_ignore,
applicability = "machine-applicable"
)]
TryIgnore {
#[suggestion_part(code = "{name}: _")]
shorthands: Vec<Span>,
#[suggestion_part(code = "_")]
non_shorthands: Vec<Span>,
name: String,
},

#[multipart_suggestion(
mir_transform_unused_var_underscore,
applicability = "machine-applicable"
)]
TryPrefix {
#[suggestion_part(code = "_{name}")]
spans: Vec<Span>,
name: String,
},

#[help(mir_transform_unused_variable_args_in_macro)]
NoSugg {
#[primary_span]
span: Span,
name: String,
},
}

pub(crate) struct UnusedVariableStringInterp {
pub lit: Span,
}

impl Subdiagnostic for UnusedVariableStringInterp {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self,
diag: &mut Diag<'_, G>,
_: F,
) {
diag.span_label(
self.lit,
crate::fluent_generated::mir_transform_maybe_string_interpolation,
);
diag.multipart_suggestion(
crate::fluent_generated::mir_transform_string_interpolation_only_works,
vec![
(self.lit.shrink_to_lo(), String::from("format!(")),
(self.lit.shrink_to_hi(), String::from(")")),
],
Applicability::MachineApplicable,
);
}
}

pub(crate) struct MustNotSupend<'tcx, 'a> {
pub tcx: TyCtxt<'tcx>,
pub yield_sp: Span,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ mod jump_threading;
mod known_panics_lint;
mod large_enums;
mod lint;
mod liveness;
mod lower_intrinsics;
mod lower_slice_len;
mod match_branches;
Expand Down Expand Up @@ -112,6 +113,7 @@ mod sroa;
mod unreachable_enum_branching;
mod unreachable_prop;

use liveness::check_liveness;
use rustc_const_eval::transform::check_consts::{self, ConstCx};
use rustc_const_eval::transform::validate;
use rustc_mir_dataflow::rustc_peek;
Expand All @@ -132,6 +134,7 @@ pub fn provide(providers: &mut Providers) {
mir_for_ctfe,
mir_coroutine_witnesses: coroutine::mir_coroutine_witnesses,
optimized_mir,
check_liveness,
is_mir_available,
is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did),
mir_callgraph_reachable: inline::cycle::mir_callgraph_reachable,
Expand Down Expand Up @@ -400,6 +403,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
}
}

tcx.ensure_with_value().check_liveness(def);

let (body, _) = tcx.mir_promoted(def);
let mut body = body.steal();
if let Some(error_reported) = mir_borrowck.tainted_by_errors {
Expand Down
Loading

0 comments on commit 4e098a0

Please sign in to comment.