Skip to content

Commit f5370fa

Browse files
Add CheckLiveDrops pass
1 parent a77f046 commit f5370fa

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

src/librustc_mir/transform/check_consts/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
1212
pub use self::qualifs::Qualif;
1313

1414
mod ops;
15+
pub mod post_drop_elaboration;
1516
pub mod qualifs;
1617
mod resolver;
1718
pub mod validation;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use rustc_hir::def_id::LocalDefId;
2+
use rustc_middle::mir::visit::Visitor;
3+
use rustc_middle::mir::{self, BasicBlock, Location};
4+
use rustc_middle::ty::TyCtxt;
5+
use rustc_span::Span;
6+
7+
use super::ops;
8+
use super::qualifs::{NeedsDrop, Qualif};
9+
use super::validation::Qualifs;
10+
use super::ConstCx;
11+
12+
/// Returns `true` if we should use the more precise live drop checker that runs after drop
13+
/// elaboration.
14+
pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool {
15+
tcx.features().const_precise_live_drops
16+
}
17+
18+
/// Look for live drops in a const context.
19+
///
20+
/// This is separate from the rest of the const checking logic because it must run after drop
21+
/// elaboration.
22+
pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) {
23+
let const_kind = tcx.hir().body_const_context(def_id);
24+
if const_kind.is_none() {
25+
return;
26+
}
27+
28+
if !checking_enabled(tcx) {
29+
return;
30+
}
31+
32+
let ccx = ConstCx {
33+
body,
34+
tcx,
35+
def_id: def_id.to_def_id(),
36+
const_kind,
37+
param_env: tcx.param_env(def_id),
38+
};
39+
40+
let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() };
41+
42+
visitor.visit_body(body);
43+
}
44+
45+
struct CheckLiveDrops<'mir, 'tcx> {
46+
ccx: &'mir ConstCx<'mir, 'tcx>,
47+
qualifs: Qualifs<'mir, 'tcx>,
48+
}
49+
50+
// So we can access `body` and `tcx`.
51+
impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> {
52+
type Target = ConstCx<'mir, 'tcx>;
53+
54+
fn deref(&self) -> &Self::Target {
55+
&self.ccx
56+
}
57+
}
58+
59+
impl CheckLiveDrops<'mir, 'tcx> {
60+
fn check_live_drop(&self, span: Span) {
61+
ops::non_const(self.ccx, ops::LiveDrop, span);
62+
}
63+
}
64+
65+
impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
66+
fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) {
67+
trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
68+
69+
// Ignore drop terminators in cleanup blocks.
70+
if block.is_cleanup {
71+
return;
72+
}
73+
74+
self.super_basic_block_data(bb, block);
75+
}
76+
77+
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
78+
trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
79+
80+
match &terminator.kind {
81+
mir::TerminatorKind::Drop { location: dropped_place, .. } => {
82+
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
83+
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
84+
return;
85+
}
86+
87+
if dropped_place.is_indirect() {
88+
self.check_live_drop(terminator.source_info.span);
89+
return;
90+
}
91+
92+
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
93+
// Use the span where the dropped local was declared for the error.
94+
let span = self.body.local_decls[dropped_place.local].source_info.span;
95+
self.check_live_drop(span);
96+
}
97+
}
98+
99+
mir::TerminatorKind::DropAndReplace { .. } => span_bug!(
100+
terminator.source_info.span,
101+
"`DropAndReplace` should be removed by drop elaboration",
102+
),
103+
104+
mir::TerminatorKind::Abort
105+
| mir::TerminatorKind::Call { .. }
106+
| mir::TerminatorKind::Assert { .. }
107+
| mir::TerminatorKind::FalseEdge { .. }
108+
| mir::TerminatorKind::FalseUnwind { .. }
109+
| mir::TerminatorKind::GeneratorDrop
110+
| mir::TerminatorKind::Goto { .. }
111+
| mir::TerminatorKind::InlineAsm { .. }
112+
| mir::TerminatorKind::Resume
113+
| mir::TerminatorKind::Return
114+
| mir::TerminatorKind::SwitchInt { .. }
115+
| mir::TerminatorKind::Unreachable
116+
| mir::TerminatorKind::Yield { .. } => {}
117+
}
118+
}
119+
}

src/librustc_mir/transform/check_consts/validation.rs

+6
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,12 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
562562
// projections that cannot be `NeedsDrop`.
563563
TerminatorKind::Drop { location: dropped_place, .. }
564564
| TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
565+
// If we are checking live drops after drop-elaboration, don't emit duplicate
566+
// errors here.
567+
if super::post_drop_elaboration::checking_enabled(self.tcx) {
568+
return;
569+
}
570+
565571
let mut err_span = self.span;
566572

567573
// Check to see if the type of this place can ever have a drop impl. If not, this

0 commit comments

Comments
 (0)