mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-07 12:48:30 +00:00
Auto merge of #134523 - dingxiangfei2009:issue-130836-attempt-2, r=nikomatsakis
Run borrowck tests on BIDs and emit tail-expr-drop-order lints for violations Fix #132861 r? `@nikomatsakis` cc `@compiler-errors` This patch enlarges the scope where the `tail-expr-drop-order` lint applies, so that all locals involved in tail expressions are inspected. This is necessary to run borrow-checking to capture the cases where it used to compile under Edition 2021 but is not going to pass borrow-checking from Edition 2024 onwards. The way it works is to inspect each BID against the set of borrows that are still live. If the local involved in BID has a borrow index which happens to be live as well at the location of this BID statement, in the future this will be a borrow-checking violation. The lint will fire in this case.
This commit is contained in:
commit
a580b5c379
@ -213,6 +213,10 @@ borrowck_suggest_create_fresh_reborrow =
|
|||||||
borrowck_suggest_iterate_over_slice =
|
borrowck_suggest_iterate_over_slice =
|
||||||
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
|
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 =
|
borrowck_ty_no_impl_copy =
|
||||||
{$is_partial_move ->
|
{$is_partial_move ->
|
||||||
[true] partial move
|
[true] partial move
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use std::assert_matches::assert_matches;
|
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 as hir;
|
||||||
use rustc_hir::intravisit::Visitor;
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_infer::infer::NllRegionVariableOrigin;
|
use rustc_infer::infer::NllRegionVariableOrigin;
|
||||||
@ -61,10 +61,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
pub(crate) fn is_explained(&self) -> bool {
|
pub(crate) fn is_explained(&self) -> bool {
|
||||||
!matches!(self, BorrowExplanation::Unexplained)
|
!matches!(self, BorrowExplanation::Unexplained)
|
||||||
}
|
}
|
||||||
pub(crate) fn add_explanation_to_diagnostic(
|
pub(crate) fn add_explanation_to_diagnostic<G: EmissionGuarantee>(
|
||||||
&self,
|
&self,
|
||||||
cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
|
cx: &MirBorrowckCtxt<'_, '_, 'tcx>,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
borrow_desc: &str,
|
borrow_desc: &str,
|
||||||
borrow_span: Option<Span>,
|
borrow_span: Option<Span>,
|
||||||
multiple_borrow_span: Option<(Span, Span)>,
|
multiple_borrow_span: Option<(Span, Span)>,
|
||||||
@ -346,10 +346,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_object_lifetime_default_note(
|
fn add_object_lifetime_default_note<G: EmissionGuarantee>(
|
||||||
&self,
|
&self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
unsize_ty: Ty<'tcx>,
|
unsize_ty: Ty<'tcx>,
|
||||||
) {
|
) {
|
||||||
if let ty::Adt(def, args) = unsize_ty.kind() {
|
if let ty::Adt(def, args) = unsize_ty.kind() {
|
||||||
@ -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,
|
&self,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
category: &ConstraintCategory<'tcx>,
|
category: &ConstraintCategory<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
region_name: &RegionName,
|
region_name: &RegionName,
|
||||||
@ -432,14 +432,14 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_rewrite_if_let(
|
fn suggest_rewrite_if_let<G: EmissionGuarantee>(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &hir::Expr<'_>,
|
||||||
pat: &str,
|
pat: &str,
|
||||||
init: &hir::Expr<'_>,
|
init: &hir::Expr<'_>,
|
||||||
conseq: &hir::Expr<'_>,
|
conseq: &hir::Expr<'_>,
|
||||||
alt: Option<&hir::Expr<'_>>,
|
alt: Option<&hir::Expr<'_>>,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
) {
|
) {
|
||||||
let source_map = tcx.sess.source_map();
|
let source_map = tcx.sess.source_map();
|
||||||
err.span_note(
|
err.span_note(
|
||||||
|
@ -4,7 +4,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use rustc_abi::{FieldIdx, VariantIdx};
|
use rustc_abi::{FieldIdx, VariantIdx};
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
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::def::{CtorKind, Namespace};
|
||||||
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
||||||
use rustc_index::IndexSlice;
|
use rustc_index::IndexSlice;
|
||||||
@ -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
|
/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
|
||||||
/// implicitly introduce an "outlives `'static`" constraint.
|
/// implicitly introduce an "outlives `'static`" constraint.
|
||||||
fn add_placeholder_from_predicate_note(
|
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
path: &[OutlivesConstraint<'tcx>],
|
path: &[OutlivesConstraint<'tcx>],
|
||||||
) {
|
) {
|
||||||
let predicate_span = path.iter().find_map(|constraint| {
|
let predicate_span = path.iter().find_map(|constraint| {
|
||||||
@ -651,9 +651,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||||||
|
|
||||||
/// Add a label to region errors and borrow explanations when outlives constraints arise from
|
/// Add a label to region errors and borrow explanations when outlives constraints arise from
|
||||||
/// proving a type implements `Sized` or `Copy`.
|
/// proving a type implements `Sized` or `Copy`.
|
||||||
fn add_sized_or_copy_bound_info(
|
fn add_sized_or_copy_bound_info<G: EmissionGuarantee>(
|
||||||
&self,
|
&self,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_, G>,
|
||||||
blamed_category: ConstraintCategory<'tcx>,
|
blamed_category: ConstraintCategory<'tcx>,
|
||||||
path: &[OutlivesConstraint<'tcx>],
|
path: &[OutlivesConstraint<'tcx>],
|
||||||
) {
|
) {
|
||||||
@ -1042,6 +1042,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||||||
kind,
|
kind,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
normal_ret
|
normal_ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use std::fmt::{self, Display};
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use rustc_data_structures::fx::IndexEntry;
|
use rustc_data_structures::fx::IndexEntry;
|
||||||
use rustc_errors::Diag;
|
use rustc_errors::{Diag, EmissionGuarantee};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_middle::ty::print::RegionHighlightMode;
|
use rustc_middle::ty::print::RegionHighlightMode;
|
||||||
@ -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 {
|
match &self.source {
|
||||||
RegionNameSource::NamedLateParamRegion(span)
|
RegionNameSource::NamedLateParamRegion(span)
|
||||||
| RegionNameSource::NamedEarlyParamRegion(span) => {
|
| RegionNameSource::NamedEarlyParamRegion(span) => {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#![warn(unreachable_pub)]
|
#![warn(unreachable_pub)]
|
||||||
// tidy-alphabetical-end
|
// tidy-alphabetical-end
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{ControlFlow, Deref};
|
use std::ops::{ControlFlow, Deref};
|
||||||
@ -23,7 +24,9 @@ use std::ops::{ControlFlow, Deref};
|
|||||||
use rustc_abi::FieldIdx;
|
use rustc_abi::FieldIdx;
|
||||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||||
use rustc_data_structures::graph::dominators::Dominators;
|
use rustc_data_structures::graph::dominators::Dominators;
|
||||||
|
use rustc_errors::LintDiagnostic;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::CRATE_HIR_ID;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||||
use rustc_index::{IndexSlice, IndexVec};
|
use rustc_index::{IndexSlice, IndexVec};
|
||||||
@ -43,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{
|
|||||||
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
||||||
};
|
};
|
||||||
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
|
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 rustc_span::{Span, Symbol};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
@ -636,9 +639,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
|
|||||||
| StatementKind::Coverage(..)
|
| StatementKind::Coverage(..)
|
||||||
// These do not actually affect borrowck
|
// These do not actually affect borrowck
|
||||||
| StatementKind::ConstEvalCounter
|
| StatementKind::ConstEvalCounter
|
||||||
// This do not affect borrowck
|
|
||||||
| StatementKind::BackwardIncompatibleDropHint { .. }
|
|
||||||
| StatementKind::StorageLive(..) => {}
|
| 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) => {
|
StatementKind::StorageDead(local) => {
|
||||||
self.access_place(
|
self.access_place(
|
||||||
location,
|
location,
|
||||||
@ -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))]
|
#[instrument(level = "debug", skip(self, state))]
|
||||||
fn check_access_for_conflict(
|
fn check_access_for_conflict(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1018,18 +1041,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let mut error_reported = false;
|
let mut error_reported = false;
|
||||||
|
|
||||||
// Use polonius output if it has been enabled.
|
let borrows_in_scope = self.borrows_in_scope(location, state);
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
each_borrow_involving_path(
|
each_borrow_involving_path(
|
||||||
self,
|
self,
|
||||||
@ -1149,6 +1161,61 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||||||
error_reported
|
error_reported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Through #123739, backward incompatible drops (BIDs) are introduced.
|
||||||
|
/// 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(
|
fn mutate_place(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst {
|
|||||||
pub arg: usize,
|
pub arg: usize,
|
||||||
pub intrinsic: String,
|
pub intrinsic: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(borrowck_tail_expr_drop_order)]
|
||||||
|
pub(crate) struct TailExprDropOrder {
|
||||||
|
#[label]
|
||||||
|
pub borrowed: Span,
|
||||||
|
}
|
||||||
|
@ -1131,15 +1131,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
|
|
||||||
/// Schedule emission of a backwards incompatible drop lint hint.
|
/// Schedule emission of a backwards incompatible drop lint hint.
|
||||||
/// Applicable only to temporary values for now.
|
/// Applicable only to temporary values for now.
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn schedule_backwards_incompatible_drop(
|
pub(crate) fn schedule_backwards_incompatible_drop(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
region_scope: region::Scope,
|
region_scope: region::Scope,
|
||||||
local: Local,
|
local: Local,
|
||||||
) {
|
) {
|
||||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
|
// Note that we are *not* gating BIDs here on whether they have significant destructor.
|
||||||
return;
|
// 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() {
|
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||||
// Since we are inserting linting MIR statement, we have to invalidate the caches
|
// Since we are inserting linting MIR statement, we have to invalidate the caches
|
||||||
scope.invalidate_cache();
|
scope.invalidate_cache();
|
||||||
|
@ -351,6 +351,11 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
|||||||
{
|
{
|
||||||
return;
|
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 ##
|
// ## About BIDs in blocks ##
|
||||||
// Track the set of blocks that contain a backwards-incompatible drop (BID)
|
// Track the set of blocks that contain a backwards-incompatible drop (BID)
|
||||||
// and, for each block, the vector of locations.
|
// and, for each block, the vector of locations.
|
||||||
@ -358,7 +363,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
|||||||
// We group them per-block because they tend to scheduled in the same drop ladder block.
|
// 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_per_block = IndexMap::default();
|
||||||
let mut bid_places = UnordSet::new();
|
let mut bid_places = UnordSet::new();
|
||||||
let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
|
|
||||||
let mut ty_dropped_components = UnordMap::default();
|
let mut ty_dropped_components = UnordMap::default();
|
||||||
for (block, data) in body.basic_blocks.iter_enumerated() {
|
for (block, data) in body.basic_blocks.iter_enumerated() {
|
||||||
for (statement_index, stmt) in data.statements.iter().enumerate() {
|
for (statement_index, stmt) in data.statements.iter().enumerate() {
|
||||||
|
@ -74,6 +74,7 @@ fn method_1(_1: Guard) -> () {
|
|||||||
|
|
||||||
bb7: {
|
bb7: {
|
||||||
backward incompatible drop(_2);
|
backward incompatible drop(_2);
|
||||||
|
backward incompatible drop(_4);
|
||||||
backward incompatible drop(_5);
|
backward incompatible drop(_5);
|
||||||
goto -> bb21;
|
goto -> bb21;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ fn method_1(_1: Guard) -> () {
|
|||||||
|
|
||||||
bb7: {
|
bb7: {
|
||||||
backward incompatible drop(_2);
|
backward incompatible drop(_2);
|
||||||
|
backward incompatible drop(_4);
|
||||||
backward incompatible drop(_5);
|
backward incompatible drop(_5);
|
||||||
goto -> bb21;
|
goto -> bb21;
|
||||||
}
|
}
|
||||||
|
51
tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs
Normal file
51
tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Edition 2024 lint for change in drop order at tail expression
|
||||||
|
// This lint is to capture potential borrow-checking errors
|
||||||
|
// due to implementation of RFC 3606 <https://github.com/rust-lang/rfcs/pull/3606>
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here
|
||||||
|
|
||||||
|
fn should_lint_with_potential_borrowck_err() {
|
||||||
|
let _ = { String::new().as_str() }.len();
|
||||||
|
//~^ ERROR: relative drop order changing
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| NOTE: this temporary value will be dropped at the end of the block
|
||||||
|
//~| borrow later used by call
|
||||||
|
//~| NOTE: for more information, see
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_lint_with_unsafe_block() {
|
||||||
|
fn f(_: usize) {}
|
||||||
|
f(unsafe { String::new().as_str() }.len());
|
||||||
|
//~^ ERROR: relative drop order changing
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| NOTE: this temporary value will be dropped at the end of the block
|
||||||
|
//~| borrow later used by call
|
||||||
|
//~| NOTE: for more information, see
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn should_lint_with_big_block() {
|
||||||
|
fn f<T>(_: T) {}
|
||||||
|
f({
|
||||||
|
&mut || 0
|
||||||
|
//~^ ERROR: relative drop order changing
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| NOTE: this temporary value will be dropped at the end of the block
|
||||||
|
//~| borrow later used here
|
||||||
|
//~| NOTE: for more information, see
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn another_temp_that_is_copy_in_arg() {
|
||||||
|
fn f() {}
|
||||||
|
fn g(_: &()) {}
|
||||||
|
g({ &f() });
|
||||||
|
//~^ ERROR: relative drop order changing
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| NOTE: this temporary value will be dropped at the end of the block
|
||||||
|
//~| borrow later used by call
|
||||||
|
//~| NOTE: for more information, see
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
52
tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr
Normal file
52
tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
error: relative drop order changing in Rust 2024
|
||||||
|
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:9:15
|
||||||
|
|
|
||||||
|
LL | let _ = { String::new().as_str() }.len();
|
||||||
|
| ^^^^^^^^^^^^^ --- borrow later used by call
|
||||||
|
| |
|
||||||
|
| this temporary value will be dropped at the end of the block
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:6:9
|
||||||
|
|
|
||||||
|
LL | #![deny(tail_expr_drop_order)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: relative drop order changing in Rust 2024
|
||||||
|
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:19:16
|
||||||
|
|
|
||||||
|
LL | f(unsafe { String::new().as_str() }.len());
|
||||||
|
| ^^^^^^^^^^^^^ --- borrow later used by call
|
||||||
|
| |
|
||||||
|
| this temporary value will be dropped at the end of the block
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||||
|
|
||||||
|
error: relative drop order changing in Rust 2024
|
||||||
|
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:31:9
|
||||||
|
|
|
||||||
|
LL | &mut || 0
|
||||||
|
| ^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| this temporary value will be dropped at the end of the block
|
||||||
|
| borrow later used here
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||||
|
|
||||||
|
error: relative drop order changing in Rust 2024
|
||||||
|
--> $DIR/lint-tail-expr-drop-order-borrowck.rs:43:9
|
||||||
|
|
|
||||||
|
LL | g({ &f() });
|
||||||
|
| - ^^^^ this temporary value will be dropped at the end of the block
|
||||||
|
| |
|
||||||
|
| borrow later used by call
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
@ -17,7 +17,6 @@ impl Drop for LoudDropper {
|
|||||||
//~| NOTE: `#1` invokes this custom destructor
|
//~| NOTE: `#1` invokes this custom destructor
|
||||||
//~| NOTE: `x` invokes this custom destructor
|
//~| NOTE: `x` invokes this custom destructor
|
||||||
//~| NOTE: `#1` invokes this custom destructor
|
//~| NOTE: `#1` invokes this custom destructor
|
||||||
//~| NOTE: `future` invokes this custom destructor
|
|
||||||
//~| NOTE: `_x` invokes this custom destructor
|
//~| NOTE: `_x` invokes this custom destructor
|
||||||
//~| NOTE: `#1` invokes this custom destructor
|
//~| NOTE: `#1` invokes this custom destructor
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:41:15
|
--> $DIR/lint-tail-expr-drop-order.rs:40:15
|
||||||
|
|
|
|
||||||
LL | let x = LoudDropper;
|
LL | let x = LoudDropper;
|
||||||
| -
|
| -
|
||||||
@ -40,7 +40,7 @@ LL | #![deny(tail_expr_drop_order)]
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:66:19
|
--> $DIR/lint-tail-expr-drop-order.rs:65:19
|
||||||
|
|
|
|
||||||
LL | let x = LoudDropper;
|
LL | let x = LoudDropper;
|
||||||
| -
|
| -
|
||||||
@ -76,7 +76,7 @@ LL | | }
|
|||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:93:7
|
--> $DIR/lint-tail-expr-drop-order.rs:92:7
|
||||||
|
|
|
|
||||||
LL | let x = LoudDropper;
|
LL | let x = LoudDropper;
|
||||||
| -
|
| -
|
||||||
@ -112,7 +112,7 @@ LL | | }
|
|||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:146:5
|
--> $DIR/lint-tail-expr-drop-order.rs:145:5
|
||||||
|
|
|
|
||||||
LL | let future = f();
|
LL | let future = f();
|
||||||
| ------
|
| ------
|
||||||
@ -136,19 +136,12 @@ note: `#1` invokes this custom destructor
|
|||||||
|
|
|
|
||||||
LL | / impl Drop for LoudDropper {
|
LL | / impl Drop for LoudDropper {
|
||||||
... |
|
... |
|
||||||
LL | | }
|
|
||||||
| |_^
|
|
||||||
note: `future` invokes this custom destructor
|
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:10:1
|
|
||||||
|
|
|
||||||
LL | / impl Drop for LoudDropper {
|
|
||||||
... |
|
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_^
|
| |_^
|
||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:163:14
|
--> $DIR/lint-tail-expr-drop-order.rs:162:14
|
||||||
|
|
|
|
||||||
LL | let x = T::default();
|
LL | let x = T::default();
|
||||||
| -
|
| -
|
||||||
@ -170,7 +163,7 @@ LL | }
|
|||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:177:5
|
--> $DIR/lint-tail-expr-drop-order.rs:176:5
|
||||||
|
|
|
|
||||||
LL | let x: Result<LoudDropper, ()> = Ok(LoudDropper);
|
LL | let x: Result<LoudDropper, ()> = Ok(LoudDropper);
|
||||||
| -
|
| -
|
||||||
@ -206,7 +199,7 @@ LL | | }
|
|||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:221:5
|
--> $DIR/lint-tail-expr-drop-order.rs:220:5
|
||||||
|
|
|
|
||||||
LL | let x = LoudDropper2;
|
LL | let x = LoudDropper2;
|
||||||
| -
|
| -
|
||||||
@ -226,7 +219,7 @@ LL | }
|
|||||||
= warning: this changes meaning in Rust 2024
|
= warning: this changes meaning in Rust 2024
|
||||||
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
|
||||||
note: `#1` invokes this custom destructor
|
note: `#1` invokes this custom destructor
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:194:5
|
--> $DIR/lint-tail-expr-drop-order.rs:193:5
|
||||||
|
|
|
|
||||||
LL | / impl Drop for LoudDropper3 {
|
LL | / impl Drop for LoudDropper3 {
|
||||||
LL | |
|
LL | |
|
||||||
@ -236,7 +229,7 @@ LL | | }
|
|||||||
LL | | }
|
LL | | }
|
||||||
| |_____^
|
| |_____^
|
||||||
note: `x` invokes this custom destructor
|
note: `x` invokes this custom destructor
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:206:5
|
--> $DIR/lint-tail-expr-drop-order.rs:205:5
|
||||||
|
|
|
|
||||||
LL | / impl Drop for LoudDropper2 {
|
LL | / impl Drop for LoudDropper2 {
|
||||||
LL | |
|
LL | |
|
||||||
@ -248,7 +241,7 @@ LL | | }
|
|||||||
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
= note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
|
||||||
|
|
||||||
error: relative drop order changing in Rust 2024
|
error: relative drop order changing in Rust 2024
|
||||||
--> $DIR/lint-tail-expr-drop-order.rs:234:13
|
--> $DIR/lint-tail-expr-drop-order.rs:233:13
|
||||||
|
|
|
|
||||||
LL | LoudDropper.get()
|
LL | LoudDropper.get()
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
@ -4,10 +4,14 @@ error: relative drop order changing in Rust 2024
|
|||||||
LL | match func().await {
|
LL | match func().await {
|
||||||
| ^^^^^^^-----
|
| ^^^^^^^-----
|
||||||
| | |
|
| | |
|
||||||
|
| | this value will be stored in a temporary; let us call it `#3`
|
||||||
|
| | up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024
|
||||||
| | this value will be stored in a temporary; let us call it `#1`
|
| | this value will be stored in a temporary; let us call it `#1`
|
||||||
| | `#1` will be dropped later as of Edition 2024
|
| | `#1` will be dropped later as of Edition 2024
|
||||||
| this value will be stored in a temporary; let us call it `#2`
|
| this value will be stored in a temporary; let us call it `#2`
|
||||||
| up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
|
| up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
|
||||||
|
| `__awaitee` calls a custom destructor
|
||||||
|
| `__awaitee` will be dropped later as of Edition 2024
|
||||||
...
|
...
|
||||||
LL | Err(e) => {}
|
LL | Err(e) => {}
|
||||||
| -
|
| -
|
||||||
|
13
tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs
Normal file
13
tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
// Make sure we don't cycle error when normalizing types for tail expr drop order lint.
|
||||||
|
|
||||||
|
#![deny(tail_expr_drop_order)]
|
||||||
|
|
||||||
|
async fn test() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Box::pin(test()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
56
tests/ui/drop/tail_expr_drop_order-on-thread-local.rs
Normal file
56
tests/ui/drop/tail_expr_drop_order-on-thread-local.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
#![feature(thread_local)]
|
||||||
|
#![deny(tail_expr_drop_order)]
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
pub struct Global;
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
static REENTRANCY_STATE: State<Global> = State { marker: PhantomData, controller: Global };
|
||||||
|
|
||||||
|
pub struct Token(PhantomData<*mut ()>);
|
||||||
|
|
||||||
|
pub fn with_mut<T>(f: impl FnOnce(&mut Token) -> T) -> T {
|
||||||
|
f(&mut REENTRANCY_STATE.borrow_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State<T: ?Sized = Global> {
|
||||||
|
marker: PhantomData<*mut ()>,
|
||||||
|
controller: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> State<T> {
|
||||||
|
pub fn borrow_mut(&self) -> TokenMut<'_, T> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TokenMut<'a, T: ?Sized = Global> {
|
||||||
|
state: &'a State<T>,
|
||||||
|
token: Token,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for TokenMut<'_, T> {
|
||||||
|
type Target = Token;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for TokenMut<'_, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Drop for TokenMut<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Reference in New Issue
Block a user