Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run borrowck tests on BIDs and emit tail-expr-drop-order lints for violations #134523

Merged
merged 6 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_borrowck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ borrowck_suggest_create_fresh_reborrow =
borrowck_suggest_iterate_over_slice =
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop

borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024
.label = this temporary value will be dropped at the end of the block
.note = consider using a `let` binding to ensure the value will live long enough

borrowck_ty_no_impl_copy =
{$is_partial_move ->
[true] partial move
Expand Down
18 changes: 9 additions & 9 deletions compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use std::assert_matches::assert_matches;

use rustc_errors::{Applicability, Diag};
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
use rustc_infer::infer::NllRegionVariableOrigin;
Expand Down Expand Up @@ -61,10 +61,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
pub(crate) fn is_explained(&self) -> bool {
!matches!(self, BorrowExplanation::Unexplained)
}
pub(crate) fn add_explanation_to_diagnostic(
pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
&self,
cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
borrow_desc: &str,
borrow_span: Option<Span>,
multiple_borrow_span: Option<(Span, Span)>,
Expand Down Expand Up @@ -346,10 +346,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
}
}

fn add_object_lifetime_default_note(
fn add_object_lifetime_default_note<G: EmissionGuarantee>(
&self,
tcx: TyCtxt<'tcx>,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
unsize_ty: Ty<'tcx>,
) {
if let ty::Adt(def, args) = unsize_ty.kind() {
Expand Down Expand Up @@ -403,9 +403,9 @@ impl<'tcx> BorrowExplanation<'tcx> {
}
}

fn add_lifetime_bound_suggestion_to_diagnostic(
fn add_lifetime_bound_suggestion_to_diagnostic<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
category: &ConstraintCategory<'tcx>,
span: Span,
region_name: &RegionName,
Expand All @@ -432,14 +432,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
}
}

fn suggest_rewrite_if_let(
fn suggest_rewrite_if_let<G: EmissionGuarantee>(
tcx: TyCtxt<'_>,
expr: &hir::Expr<'_>,
pat: &str,
init: &hir::Expr<'_>,
conseq: &hir::Expr<'_>,
alt: Option<&hir::Expr<'_>>,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
) {
let source_map = tcx.sess.source_map();
err.span_note(
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::BTreeMap;

use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, MultiSpan};
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
use rustc_index::IndexSlice;
Expand Down Expand Up @@ -626,9 +626,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {

/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
/// implicitly introduce an "outlives `'static`" constraint.
fn add_placeholder_from_predicate_note(
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
path: &[OutlivesConstraint<'tcx>],
) {
let predicate_span = path.iter().find_map(|constraint| {
Expand All @@ -651,9 +651,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {

/// Add a label to region errors and borrow explanations when outlives constraints arise from
/// proving a type implements `Sized` or `Copy`.
fn add_sized_or_copy_bound_info(
fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_>,
err: &mut Diag<'_, G>,
blamed_category: ConstraintCategory<'tcx>,
path: &[OutlivesConstraint<'tcx>],
) {
Expand Down Expand Up @@ -1042,6 +1042,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
kind,
};
}

normal_ret
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::fmt::{self, Display};
use std::iter;

use rustc_data_structures::fx::IndexEntry;
use rustc_errors::Diag;
use rustc_errors::{Diag, EmissionGuarantee};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_middle::ty::print::RegionHighlightMode;
Expand Down Expand Up @@ -108,7 +108,7 @@ impl RegionName {
}
}

pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_>) {
pub(crate) fn highlight_region_name<G: EmissionGuarantee>(&self, diag: &mut Diag<'_, G>) {
match &self.source {
RegionNameSource::NamedLateParamRegion(span)
| RegionNameSource::NamedEarlyParamRegion(span) => {
Expand Down
97 changes: 82 additions & 15 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
#![warn(unreachable_pub)]
// tidy-alphabetical-end

use std::borrow::Cow;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::{ControlFlow, Deref};

use rustc_abi::FieldIdx;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::dominators::Dominators;
use rustc_errors::LintDiagnostic;
use rustc_hir as hir;
use rustc_hir::CRATE_HIR_ID;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::{BitSet, MixedBitSet};
use rustc_index::{IndexSlice, IndexVec};
Expand All @@ -43,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{Span, Symbol};
use smallvec::SmallVec;
use tracing::{debug, instrument};
Expand Down Expand Up @@ -636,9 +639,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
| StatementKind::Coverage(..)
// These do not actually affect borrowck
| StatementKind::ConstEvalCounter
// This do not affect borrowck
| StatementKind::BackwardIncompatibleDropHint { .. }
| StatementKind::StorageLive(..) => {}
// This does not affect borrowck
StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
self.check_backward_incompatible_drop(location, (**place, span), state);
}
StatementKind::StorageDead(local) => {
self.access_place(
location,
Expand Down Expand Up @@ -1007,6 +1012,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
}

fn borrows_in_scope<'s>(
&self,
location: Location,
state: &'s BorrowckDomain,
) -> Cow<'s, BitSet<BorrowIndex>> {
if let Some(polonius) = &self.polonius_output {
// Use polonius output if it has been enabled.
let location = self.location_table.start_index(location);
let mut polonius_output = BitSet::new_empty(self.borrow_set.len());
for &idx in polonius.errors_at(location) {
polonius_output.insert(idx);
}
Cow::Owned(polonius_output)
} else {
Cow::Borrowed(&state.borrows)
}
}

#[instrument(level = "debug", skip(self, state))]
fn check_access_for_conflict(
&mut self,
Expand All @@ -1018,18 +1041,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
) -> bool {
let mut error_reported = false;

// Use polonius output if it has been enabled.
let mut polonius_output;
let borrows_in_scope = if let Some(polonius) = &self.polonius_output {
let location = self.location_table.start_index(location);
polonius_output = BitSet::new_empty(self.borrow_set.len());
for &idx in polonius.errors_at(location) {
polonius_output.insert(idx);
}
&polonius_output
} else {
&state.borrows
};
let borrows_in_scope = self.borrows_in_scope(location, state);

each_borrow_involving_path(
self,
Expand Down Expand Up @@ -1149,6 +1161,61 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
error_reported
}

/// Through #123739, backward incompatible drops (BIDs) are introduced.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dingxiangfei2009 A tiny late nit. I have just learned from this comment what BID stand for. Is it a commonly used acronym? I wasn't able to find it using a search engine. Could only find it disentangled here.

(thanks @lqd for the pointing me here)

/// We would like to emit lints whether borrow checking fails at these future drop locations.
#[instrument(level = "debug", skip(self, state))]
fn check_backward_incompatible_drop(
&mut self,
location: Location,
(place, place_span): (Place<'tcx>, Span),
state: &BorrowckDomain,
) {
let tcx = self.infcx.tcx;
// If this type does not need `Drop`, then treat it like a `StorageDead`.
// This is needed because we track the borrows of refs to thread locals,
// and we'll ICE because we don't track borrows behind shared references.
let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) {
AccessDepth::Drop
} else {
AccessDepth::Shallow(None)
};

let borrows_in_scope = self.borrows_in_scope(location, state);

// This is a very simplified version of `Self::check_access_for_conflict`.
// We are here checking on BIDs and specifically still-live borrows of data involving the BIDs.
each_borrow_involving_path(
self,
self.infcx.tcx,
self.body,
(sd, place),
self.borrow_set,
|borrow_index| borrows_in_scope.contains(borrow_index),
|this, _borrow_index, borrow| {
if matches!(borrow.kind, BorrowKind::Fake(_)) {
return ControlFlow::Continue(());
}
let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span();
let explain = this.explain_why_borrow_contains_point(
location,
borrow,
Some((WriteKind::StorageDeadOrDrop, place)),
);
this.infcx.tcx.node_span_lint(
TAIL_EXPR_DROP_ORDER,
CRATE_HIR_ID,
borrowed,
|diag| {
session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag);
explain.add_explanation_to_diagnostic(&this, diag, "", None, None);
},
);
// We may stop at the first case
ControlFlow::Break(())
},
);
}

fn mutate_place(
&mut self,
location: Location,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_borrowck/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst {
pub arg: usize,
pub intrinsic: String,
}

#[derive(LintDiagnostic)]
#[diag(borrowck_tail_expr_drop_order)]
pub(crate) struct TailExprDropOrder {
#[label]
pub borrowed: Span,
}
6 changes: 3 additions & 3 deletions compiler/rustc_mir_build/src/builder/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,15 +1131,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

/// Schedule emission of a backwards incompatible drop lint hint.
/// Applicable only to temporary values for now.
#[instrument(level = "debug", skip(self))]
pub(crate) fn schedule_backwards_incompatible_drop(
&mut self,
span: Span,
region_scope: region::Scope,
local: Local,
) {
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
return;
}
// Note that we are *not* gating BIDs here on whether they have significant destructor.
// We need to know all of them so that we can capture potential borrow-checking errors.
for scope in self.scopes.scopes.iter_mut().rev() {
// Since we are inserting linting MIR statement, we have to invalidate the caches
scope.invalidate_cache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,14 +351,19 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
{
return;
}

// FIXME(typing_env): This should be able to reveal the opaques local to the
// body using the typeck results.
let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id);

// ## About BIDs in blocks ##
// Track the set of blocks that contain a backwards-incompatible drop (BID)
// and, for each block, the vector of locations.
//
// We group them per-block because they tend to scheduled in the same drop ladder block.
let mut bid_per_block = IndexMap::default();
let mut bid_places = UnordSet::new();
let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);

let mut ty_dropped_components = UnordMap::default();
for (block, data) in body.basic_blocks.iter_enumerated() {
for (statement_index, stmt) in data.statements.iter().enumerate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ fn method_1(_1: Guard) -> () {

bb7: {
backward incompatible drop(_2);
backward incompatible drop(_4);
backward incompatible drop(_5);
goto -> bb21;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ fn method_1(_1: Guard) -> () {

bb7: {
backward incompatible drop(_2);
backward incompatible drop(_4);
backward incompatible drop(_5);
goto -> bb21;
}
Expand Down
Loading
Loading