Skip to content

Commit 3fee0f1

Browse files
committed
Auto merge of #131326 - dingxiangfei2009:issue-130836-attempt-2, r=nikomatsakis
Reduce false positives of tail-expr-drop-order from consumed values (attempt #2) r? `@nikomatsakis` Tracked by #123739. Related to #129864 but not replacing, yet. Related to #130836. This is an implementation of the approach suggested in the [Zulip stream](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/temporary.20drop.20order.20changes). A new MIR statement `BackwardsIncompatibleDrop` is added to the MIR syntax. The lint now works by inspecting possibly live move paths before at the `BackwardsIncompatibleDrop` location and the actual drop under the current edition, which should be one before Edition 2024 in practice.
2 parents a1f2999 + 297b618 commit 3fee0f1

Some content is hidden

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

58 files changed

+2011
-534
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+1
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
641641
| mir::StatementKind::Coverage(..)
642642
| mir::StatementKind::Intrinsic(..)
643643
| mir::StatementKind::ConstEvalCounter
644+
| mir::StatementKind::BackwardIncompatibleDropHint { .. }
644645
| mir::StatementKind::Nop => {}
645646
}
646647
}

compiler/rustc_borrowck/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
652652
| StatementKind::Coverage(..)
653653
// These do not actually affect borrowck
654654
| StatementKind::ConstEvalCounter
655+
// This do not affect borrowck
656+
| StatementKind::BackwardIncompatibleDropHint { .. }
655657
| StatementKind::StorageLive(..) => {}
656658
StatementKind::StorageDead(local) => {
657659
self.access_place(

compiler/rustc_borrowck/src/polonius/loan_invalidations.rs

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
8888
| StatementKind::Nop
8989
| StatementKind::Retag { .. }
9090
| StatementKind::Deinit(..)
91+
| StatementKind::BackwardIncompatibleDropHint { .. }
9192
| StatementKind::SetDiscriminant { .. } => {
9293
bug!("Statement not allowed in this MIR phase")
9394
}

compiler/rustc_borrowck/src/type_check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
12521252
| StatementKind::Coverage(..)
12531253
| StatementKind::ConstEvalCounter
12541254
| StatementKind::PlaceMention(..)
1255+
| StatementKind::BackwardIncompatibleDropHint { .. }
12551256
| StatementKind::Nop => {}
12561257
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
12571258
bug!("Statement not allowed in this MIR phase")

compiler/rustc_codegen_cranelift/src/base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,7 @@ fn codegen_stmt<'tcx>(
924924
| StatementKind::FakeRead(..)
925925
| StatementKind::Retag { .. }
926926
| StatementKind::PlaceMention(..)
927+
| StatementKind::BackwardIncompatibleDropHint { .. }
927928
| StatementKind::AscribeUserType(..) => {}
928929

929930
StatementKind::Coverage { .. } => unreachable!(),

compiler/rustc_codegen_cranelift/src/constant.rs

+1
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
583583
| StatementKind::PlaceMention(..)
584584
| StatementKind::Coverage(_)
585585
| StatementKind::ConstEvalCounter
586+
| StatementKind::BackwardIncompatibleDropHint { .. }
586587
| StatementKind::Nop => {}
587588
}
588589
}

compiler/rustc_codegen_ssa/src/mir/statement.rs

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
9292
| mir::StatementKind::AscribeUserType(..)
9393
| mir::StatementKind::ConstEvalCounter
9494
| mir::StatementKind::PlaceMention(..)
95+
| mir::StatementKind::BackwardIncompatibleDropHint { .. }
9596
| mir::StatementKind::Nop => {}
9697
}
9798
}

compiler/rustc_const_eval/src/check_consts/check.rs

+1
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
609609
| StatementKind::Coverage(..)
610610
| StatementKind::Intrinsic(..)
611611
| StatementKind::ConstEvalCounter
612+
| StatementKind::BackwardIncompatibleDropHint { .. }
612613
| StatementKind::Nop => {}
613614
}
614615
}

compiler/rustc_const_eval/src/interpret/step.rs

+3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
143143
// Defined to do nothing. These are added by optimization passes, to avoid changing the
144144
// size of MIR constantly.
145145
Nop => {}
146+
147+
// Only used for temporary lifetime lints
148+
BackwardIncompatibleDropHint { .. } => {}
146149
}
147150

148151
interp_ok(())

compiler/rustc_hir_analysis/src/check/region.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_index::Idx;
1717
use rustc_middle::bug;
1818
use rustc_middle::middle::region::*;
1919
use rustc_middle::ty::TyCtxt;
20+
use rustc_session::lint;
2021
use rustc_span::source_map;
2122
use tracing::debug;
2223

@@ -167,8 +168,23 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
167168
}
168169
}
169170
if let Some(tail_expr) = blk.expr {
170-
if blk.span.edition().at_least_rust_2024() {
171-
visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
171+
let local_id = tail_expr.hir_id.local_id;
172+
let edition = blk.span.edition();
173+
if edition.at_least_rust_2024() {
174+
visitor.terminating_scopes.insert(local_id);
175+
} else if !visitor
176+
.tcx
177+
.lints_that_dont_need_to_run(())
178+
.contains(&lint::LintId::of(lint::builtin::TAIL_EXPR_DROP_ORDER))
179+
{
180+
// If this temporary scope will be changing once the codebase adopts Rust 2024,
181+
// and we are linting about possible semantic changes that would result,
182+
// then record this node-id in the field `backwards_incompatible_scope`
183+
// for future reference.
184+
visitor
185+
.scope_tree
186+
.backwards_incompatible_scope
187+
.insert(local_id, Scope { id: local_id, data: ScopeData::Node });
172188
}
173189
visitor.visit_expr(tail_expr);
174190
}

compiler/rustc_index/src/bit_set.rs

+136-4
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ impl<T: Idx> ChunkedBitSet<T> {
460460
self.chunks.iter().map(|chunk| chunk.count()).sum()
461461
}
462462

463+
pub fn is_empty(&self) -> bool {
464+
self.chunks.iter().all(|chunk| matches!(chunk, Chunk::Zeros(..) | Chunk::Ones(0)))
465+
}
466+
463467
/// Returns `true` if `self` contains `elem`.
464468
#[inline]
465469
pub fn contains(&self, elem: T) -> bool {
@@ -668,12 +672,140 @@ impl<T: Idx> BitRelations<ChunkedBitSet<T>> for ChunkedBitSet<T> {
668672
changed
669673
}
670674

671-
fn subtract(&mut self, _other: &ChunkedBitSet<T>) -> bool {
672-
unimplemented!("implement if/when necessary");
675+
fn subtract(&mut self, other: &ChunkedBitSet<T>) -> bool {
676+
assert_eq!(self.domain_size, other.domain_size);
677+
debug_assert_eq!(self.chunks.len(), other.chunks.len());
678+
679+
let mut changed = false;
680+
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
681+
match (&mut self_chunk, &other_chunk) {
682+
(Zeros(..), _) | (_, Zeros(..)) => {}
683+
(
684+
Ones(self_chunk_domain_size) | Mixed(self_chunk_domain_size, _, _),
685+
Ones(other_chunk_domain_size),
686+
) => {
687+
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
688+
changed = true;
689+
*self_chunk = Zeros(*self_chunk_domain_size);
690+
}
691+
(
692+
Ones(self_chunk_domain_size),
693+
Mixed(other_chunk_domain_size, other_chunk_count, other_chunk_words),
694+
) => {
695+
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
696+
changed = true;
697+
let num_words = num_words(*self_chunk_domain_size as usize);
698+
debug_assert!(num_words > 0 && num_words <= CHUNK_WORDS);
699+
let mut tail_mask =
700+
1 << (*other_chunk_domain_size - ((num_words - 1) * WORD_BITS) as u16) - 1;
701+
let mut self_chunk_words = **other_chunk_words;
702+
for word in self_chunk_words[0..num_words].iter_mut().rev() {
703+
*word = !*word & tail_mask;
704+
tail_mask = u64::MAX;
705+
}
706+
let self_chunk_count = *self_chunk_domain_size - *other_chunk_count;
707+
debug_assert_eq!(
708+
self_chunk_count,
709+
self_chunk_words[0..num_words]
710+
.iter()
711+
.map(|w| w.count_ones() as ChunkSize)
712+
.sum()
713+
);
714+
*self_chunk =
715+
Mixed(*self_chunk_domain_size, self_chunk_count, Rc::new(self_chunk_words));
716+
}
717+
(
718+
Mixed(
719+
self_chunk_domain_size,
720+
ref mut self_chunk_count,
721+
ref mut self_chunk_words,
722+
),
723+
Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words),
724+
) => {
725+
// See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation
726+
let op = |a: u64, b: u64| a & !b;
727+
let num_words = num_words(*self_chunk_domain_size as usize);
728+
if bitwise_changes(
729+
&self_chunk_words[0..num_words],
730+
&other_chunk_words[0..num_words],
731+
op,
732+
) {
733+
let self_chunk_words = Rc::make_mut(self_chunk_words);
734+
let has_changed = bitwise(
735+
&mut self_chunk_words[0..num_words],
736+
&other_chunk_words[0..num_words],
737+
op,
738+
);
739+
debug_assert!(has_changed);
740+
*self_chunk_count = self_chunk_words[0..num_words]
741+
.iter()
742+
.map(|w| w.count_ones() as ChunkSize)
743+
.sum();
744+
if *self_chunk_count == 0 {
745+
*self_chunk = Zeros(*self_chunk_domain_size);
746+
}
747+
changed = true;
748+
}
749+
}
750+
}
751+
}
752+
changed
673753
}
674754

675-
fn intersect(&mut self, _other: &ChunkedBitSet<T>) -> bool {
676-
unimplemented!("implement if/when necessary");
755+
fn intersect(&mut self, other: &ChunkedBitSet<T>) -> bool {
756+
assert_eq!(self.domain_size, other.domain_size);
757+
debug_assert_eq!(self.chunks.len(), other.chunks.len());
758+
759+
let mut changed = false;
760+
for (mut self_chunk, other_chunk) in self.chunks.iter_mut().zip(other.chunks.iter()) {
761+
match (&mut self_chunk, &other_chunk) {
762+
(Zeros(..), _) | (_, Ones(..)) => {}
763+
(
764+
Ones(self_chunk_domain_size),
765+
Zeros(other_chunk_domain_size) | Mixed(other_chunk_domain_size, ..),
766+
)
767+
| (Mixed(self_chunk_domain_size, ..), Zeros(other_chunk_domain_size)) => {
768+
debug_assert_eq!(self_chunk_domain_size, other_chunk_domain_size);
769+
changed = true;
770+
*self_chunk = other_chunk.clone();
771+
}
772+
(
773+
Mixed(
774+
self_chunk_domain_size,
775+
ref mut self_chunk_count,
776+
ref mut self_chunk_words,
777+
),
778+
Mixed(_other_chunk_domain_size, _other_chunk_count, other_chunk_words),
779+
) => {
780+
// See [`<Self as BitRelations<ChunkedBitSet<T>>>::union`] for the explanation
781+
let op = |a, b| a & b;
782+
let num_words = num_words(*self_chunk_domain_size as usize);
783+
if bitwise_changes(
784+
&self_chunk_words[0..num_words],
785+
&other_chunk_words[0..num_words],
786+
op,
787+
) {
788+
let self_chunk_words = Rc::make_mut(self_chunk_words);
789+
let has_changed = bitwise(
790+
&mut self_chunk_words[0..num_words],
791+
&other_chunk_words[0..num_words],
792+
op,
793+
);
794+
debug_assert!(has_changed);
795+
*self_chunk_count = self_chunk_words[0..num_words]
796+
.iter()
797+
.map(|w| w.count_ones() as ChunkSize)
798+
.sum();
799+
if *self_chunk_count == 0 {
800+
*self_chunk = Zeros(*self_chunk_domain_size);
801+
}
802+
changed = true;
803+
}
804+
}
805+
}
806+
}
807+
808+
changed
677809
}
678810
}
679811

compiler/rustc_lint/messages.ftl

-3
Original file line numberDiff line numberDiff line change
@@ -772,9 +772,6 @@ lint_suspicious_double_ref_clone =
772772
lint_suspicious_double_ref_deref =
773773
using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type
774774
775-
lint_tail_expr_drop_order = these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021
776-
.label = these values have significant drop implementation and will observe changes in drop order under Edition 2024
777-
778775
lint_trailing_semi_macro = trailing semicolon in macro used in expression position
779776
.note1 = macro invocations at the end of a block are treated as expressions
780777
.note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}`

compiler/rustc_lint/src/lib.rs

-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ mod redundant_semicolon;
7575
mod reference_casting;
7676
mod shadowed_into_iter;
7777
mod static_mut_refs;
78-
mod tail_expr_drop_order;
7978
mod traits;
8079
mod types;
8180
mod unit_bindings;
@@ -116,7 +115,6 @@ use rustc_middle::ty::TyCtxt;
116115
use shadowed_into_iter::ShadowedIntoIter;
117116
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
118117
use static_mut_refs::*;
119-
use tail_expr_drop_order::TailExprDropOrder;
120118
use traits::*;
121119
use types::*;
122120
use unit_bindings::*;
@@ -240,7 +238,6 @@ late_lint_methods!(
240238
AsyncFnInTrait: AsyncFnInTrait,
241239
NonLocalDefinitions: NonLocalDefinitions::default(),
242240
ImplTraitOvercaptures: ImplTraitOvercaptures,
243-
TailExprDropOrder: TailExprDropOrder,
244241
IfLetRescope: IfLetRescope::default(),
245242
StaticMutRefs: StaticMutRefs,
246243
UnqualifiedLocalImports: UnqualifiedLocalImports,

0 commit comments

Comments
 (0)