Skip to content

Commit f3331cb

Browse files
authored
Rollup merge of #71330 - ecstatic-morse:const-qualif-lazy, r=oli-obk
Only run dataflow for const qualification if type-based check would fail This is the optimization discussed in #49146 (comment). We wait for `Qualif::in_any_value_of_ty` to return `true` before running dataflow. For bodies that deal mostly with primitive types, this will avoid running dataflow at all during const qualification. This also removes the `BitSet` used to cache `in_any_value_of_ty` for each local, which was only necessary for an old version of #64470 that also handled promotability.
2 parents 2e2080d + 15f95b1 commit f3331cb

File tree

6 files changed

+189
-99
lines changed

6 files changed

+189
-99
lines changed

src/librustc_mir/transform/check_consts/validation.rs

+83-72
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
use rustc_errors::struct_span_err;
44
use rustc_hir::lang_items;
55
use rustc_hir::{def_id::DefId, HirId};
6-
use rustc_index::bit_set::BitSet;
76
use rustc_infer::infer::TyCtxtInferExt;
87
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
98
use rustc_middle::mir::*;
@@ -28,70 +27,100 @@ use crate::dataflow::{self, Analysis};
2827
// We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
2928
// through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
3029
// kills locals upon `StorageDead` because a local will never be used after a `StorageDead`.
31-
pub type IndirectlyMutableResults<'mir, 'tcx> =
30+
type IndirectlyMutableResults<'mir, 'tcx> =
3231
dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>;
3332

34-
struct QualifCursor<'a, 'mir, 'tcx, Q: Qualif> {
35-
cursor: dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>>,
36-
in_any_value_of_ty: BitSet<Local>,
37-
}
38-
39-
impl<Q: Qualif> QualifCursor<'a, 'mir, 'tcx, Q> {
40-
pub fn new(q: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
41-
let cursor = FlowSensitiveAnalysis::new(q, ccx)
42-
.into_engine(ccx.tcx, ccx.body, ccx.def_id)
43-
.iterate_to_fixpoint()
44-
.into_results_cursor(ccx.body);
45-
46-
let mut in_any_value_of_ty = BitSet::new_empty(ccx.body.local_decls.len());
47-
for (local, decl) in ccx.body.local_decls.iter_enumerated() {
48-
if Q::in_any_value_of_ty(ccx, decl.ty) {
49-
in_any_value_of_ty.insert(local);
50-
}
51-
}
33+
type QualifResults<'mir, 'tcx, Q> =
34+
dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
5235

53-
QualifCursor { cursor, in_any_value_of_ty }
54-
}
36+
#[derive(Default)]
37+
pub struct Qualifs<'mir, 'tcx> {
38+
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
39+
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
40+
indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
5541
}
5642

57-
pub struct Qualifs<'a, 'mir, 'tcx> {
58-
has_mut_interior: QualifCursor<'a, 'mir, 'tcx, HasMutInterior>,
59-
needs_drop: QualifCursor<'a, 'mir, 'tcx, NeedsDrop>,
60-
indirectly_mutable: IndirectlyMutableResults<'mir, 'tcx>,
61-
}
62-
63-
impl Qualifs<'a, 'mir, 'tcx> {
64-
fn indirectly_mutable(&mut self, local: Local, location: Location) -> bool {
65-
self.indirectly_mutable.seek_before(location);
66-
self.indirectly_mutable.get().contains(local)
43+
impl Qualifs<'mir, 'tcx> {
44+
fn indirectly_mutable(
45+
&mut self,
46+
ccx: &'mir ConstCx<'mir, 'tcx>,
47+
local: Local,
48+
location: Location,
49+
) -> bool {
50+
let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| {
51+
let ConstCx { tcx, body, def_id, param_env, .. } = *ccx;
52+
53+
// We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not
54+
// allowed in a const.
55+
//
56+
// FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this
57+
// without breaking stable code?
58+
MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env)
59+
.unsound_ignore_borrow_on_drop()
60+
.into_engine(tcx, &body, def_id)
61+
.iterate_to_fixpoint()
62+
.into_results_cursor(&body)
63+
});
64+
65+
indirectly_mutable.seek_before(location);
66+
indirectly_mutable.get().contains(local)
6767
}
6868

6969
/// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
7070
///
7171
/// Only updates the cursor if absolutely necessary
72-
fn needs_drop(&mut self, local: Local, location: Location) -> bool {
73-
if !self.needs_drop.in_any_value_of_ty.contains(local) {
72+
fn needs_drop(
73+
&mut self,
74+
ccx: &'mir ConstCx<'mir, 'tcx>,
75+
local: Local,
76+
location: Location,
77+
) -> bool {
78+
let ty = ccx.body.local_decls[local].ty;
79+
if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
7480
return false;
7581
}
7682

77-
self.needs_drop.cursor.seek_before(location);
78-
self.needs_drop.cursor.get().contains(local) || self.indirectly_mutable(local, location)
83+
let needs_drop = self.needs_drop.get_or_insert_with(|| {
84+
let ConstCx { tcx, body, def_id, .. } = *ccx;
85+
86+
FlowSensitiveAnalysis::new(NeedsDrop, ccx)
87+
.into_engine(tcx, &body, def_id)
88+
.iterate_to_fixpoint()
89+
.into_results_cursor(&body)
90+
});
91+
92+
needs_drop.seek_before(location);
93+
needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location)
7994
}
8095

8196
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
8297
///
8398
/// Only updates the cursor if absolutely necessary.
84-
fn has_mut_interior(&mut self, local: Local, location: Location) -> bool {
85-
if !self.has_mut_interior.in_any_value_of_ty.contains(local) {
99+
fn has_mut_interior(
100+
&mut self,
101+
ccx: &'mir ConstCx<'mir, 'tcx>,
102+
local: Local,
103+
location: Location,
104+
) -> bool {
105+
let ty = ccx.body.local_decls[local].ty;
106+
if !HasMutInterior::in_any_value_of_ty(ccx, ty) {
86107
return false;
87108
}
88109

89-
self.has_mut_interior.cursor.seek_before(location);
90-
self.has_mut_interior.cursor.get().contains(local)
91-
|| self.indirectly_mutable(local, location)
110+
let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| {
111+
let ConstCx { tcx, body, def_id, .. } = *ccx;
112+
113+
FlowSensitiveAnalysis::new(HasMutInterior, ccx)
114+
.into_engine(tcx, &body, def_id)
115+
.iterate_to_fixpoint()
116+
.into_results_cursor(&body)
117+
});
118+
119+
has_mut_interior.seek_before(location);
120+
has_mut_interior.get().contains(local) || self.indirectly_mutable(ccx, local, location)
92121
}
93122

94-
fn in_return_place(&mut self, ccx: &ConstCx<'_, 'tcx>) -> ConstQualifs {
123+
fn in_return_place(&mut self, ccx: &'mir ConstCx<'mir, 'tcx>) -> ConstQualifs {
95124
// Find the `Return` terminator if one exists.
96125
//
97126
// If no `Return` terminator exists, this MIR is divergent. Just return the conservative
@@ -114,49 +143,31 @@ impl Qualifs<'a, 'mir, 'tcx> {
114143
let return_loc = ccx.body.terminator_loc(return_block);
115144

116145
ConstQualifs {
117-
needs_drop: self.needs_drop(RETURN_PLACE, return_loc),
118-
has_mut_interior: self.has_mut_interior(RETURN_PLACE, return_loc),
146+
needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
147+
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
119148
}
120149
}
121150
}
122151

123-
pub struct Validator<'a, 'mir, 'tcx> {
124-
ccx: &'a ConstCx<'mir, 'tcx>,
125-
qualifs: Qualifs<'a, 'mir, 'tcx>,
152+
pub struct Validator<'mir, 'tcx> {
153+
ccx: &'mir ConstCx<'mir, 'tcx>,
154+
qualifs: Qualifs<'mir, 'tcx>,
126155

127156
/// The span of the current statement.
128157
span: Span,
129158
}
130159

131-
impl Deref for Validator<'_, 'mir, 'tcx> {
160+
impl Deref for Validator<'mir, 'tcx> {
132161
type Target = ConstCx<'mir, 'tcx>;
133162

134163
fn deref(&self) -> &Self::Target {
135164
&self.ccx
136165
}
137166
}
138167

139-
impl Validator<'a, 'mir, 'tcx> {
140-
pub fn new(ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
141-
let ConstCx { tcx, body, def_id, param_env, .. } = *ccx;
142-
143-
let needs_drop = QualifCursor::new(NeedsDrop, ccx);
144-
let has_mut_interior = QualifCursor::new(HasMutInterior, ccx);
145-
146-
// We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not
147-
// allowed in a const.
148-
//
149-
// FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this
150-
// without breaking stable code?
151-
let indirectly_mutable = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
152-
.unsound_ignore_borrow_on_drop()
153-
.into_engine(tcx, body, def_id)
154-
.iterate_to_fixpoint()
155-
.into_results_cursor(body);
156-
157-
let qualifs = Qualifs { needs_drop, has_mut_interior, indirectly_mutable };
158-
159-
Validator { span: ccx.body.span, ccx, qualifs }
168+
impl Validator<'mir, 'tcx> {
169+
pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
170+
Validator { span: ccx.body.span, ccx, qualifs: Default::default() }
160171
}
161172

162173
pub fn check_body(&mut self) {
@@ -239,7 +250,7 @@ impl Validator<'a, 'mir, 'tcx> {
239250
}
240251
}
241252

242-
impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
253+
impl Visitor<'tcx> for Validator<'mir, 'tcx> {
243254
fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) {
244255
trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
245256

@@ -345,7 +356,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
345356
| Rvalue::AddressOf(Mutability::Not, ref place) => {
346357
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
347358
&self.ccx,
348-
&mut |local| self.qualifs.has_mut_interior(local, location),
359+
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
349360
place.as_ref(),
350361
);
351362

@@ -571,7 +582,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
571582
let needs_drop = if let Some(local) = dropped_place.as_local() {
572583
// Use the span where the local was declared as the span of the drop error.
573584
err_span = self.body.local_decls[local].source_info.span;
574-
self.qualifs.needs_drop(local, location)
585+
self.qualifs.needs_drop(self.ccx, local, location)
575586
} else {
576587
true
577588
};

src/test/ui/issues/issue-17252.stderr

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
error[E0391]: cycle detected when const checking `FOO`
2-
--> $DIR/issue-17252.rs:1:20
1+
error[E0391]: cycle detected when normalizing `FOO`
2+
|
3+
note: ...which requires const-evaluating + checking `FOO`...
4+
--> $DIR/issue-17252.rs:1:1
5+
|
6+
LL | const FOO: usize = FOO;
7+
| ^^^^^^^^^^^^^^^^^^^^^^^
8+
note: ...which requires const-evaluating + checking `FOO`...
9+
--> $DIR/issue-17252.rs:1:1
310
|
411
LL | const FOO: usize = FOO;
5-
| ^^^
12+
| ^^^^^^^^^^^^^^^^^^^^^^^
13+
note: ...which requires const-evaluating `FOO`...
14+
--> $DIR/issue-17252.rs:1:1
615
|
7-
= note: ...which again requires const checking `FOO`, completing the cycle
8-
note: cycle used when const checking `main::{{constant}}#0`
16+
LL | const FOO: usize = FOO;
17+
| ^^^^^^^^^^^^^^^^^^^^^^^
18+
= note: ...which again requires normalizing `FOO`, completing the cycle
19+
note: cycle used when const-evaluating `main::{{constant}}#0`
920
--> $DIR/issue-17252.rs:4:18
1021
|
1122
LL | let _x: [u8; FOO]; // caused stack overflow prior to fix

src/test/ui/issues/issue-23302-1.stderr

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
error[E0391]: cycle detected when const checking `X::A::{{constant}}#0`
1+
error[E0391]: cycle detected when const-evaluating + checking `X::A::{{constant}}#0`
22
--> $DIR/issue-23302-1.rs:4:9
33
|
44
LL | A = X::A as isize,
55
| ^^^^^^^^^^^^^
66
|
7-
= note: ...which again requires const checking `X::A::{{constant}}#0`, completing the cycle
8-
note: cycle used when processing `X::A::{{constant}}#0`
7+
note: ...which requires const-evaluating + checking `X::A::{{constant}}#0`...
98
--> $DIR/issue-23302-1.rs:4:9
109
|
1110
LL | A = X::A as isize,
1211
| ^^^^^^^^^^^^^
12+
note: ...which requires const-evaluating `X::A::{{constant}}#0`...
13+
--> $DIR/issue-23302-1.rs:4:9
14+
|
15+
LL | A = X::A as isize,
16+
| ^^^^^^^^^^^^^
17+
= note: ...which requires normalizing `X::A as isize`...
18+
= note: ...which again requires const-evaluating + checking `X::A::{{constant}}#0`, completing the cycle
19+
note: cycle used when collecting item types in top-level module
20+
--> $DIR/issue-23302-1.rs:3:1
21+
|
22+
LL | enum X {
23+
| ^^^^^^
1324

1425
error: aborting due to previous error
1526

src/test/ui/issues/issue-23302-2.stderr

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
error[E0391]: cycle detected when const checking `Y::A::{{constant}}#0`
1+
error[E0391]: cycle detected when const-evaluating + checking `Y::A::{{constant}}#0`
22
--> $DIR/issue-23302-2.rs:4:9
33
|
44
LL | A = Y::B as isize,
55
| ^^^^^^^^^^^^^
66
|
7-
= note: ...which again requires const checking `Y::A::{{constant}}#0`, completing the cycle
8-
note: cycle used when processing `Y::A::{{constant}}#0`
7+
note: ...which requires const-evaluating + checking `Y::A::{{constant}}#0`...
98
--> $DIR/issue-23302-2.rs:4:9
109
|
1110
LL | A = Y::B as isize,
1211
| ^^^^^^^^^^^^^
12+
note: ...which requires const-evaluating `Y::A::{{constant}}#0`...
13+
--> $DIR/issue-23302-2.rs:4:9
14+
|
15+
LL | A = Y::B as isize,
16+
| ^^^^^^^^^^^^^
17+
= note: ...which requires normalizing `Y::B as isize`...
18+
= note: ...which again requires const-evaluating + checking `Y::A::{{constant}}#0`, completing the cycle
19+
note: cycle used when collecting item types in top-level module
20+
--> $DIR/issue-23302-2.rs:3:1
21+
|
22+
LL | enum Y {
23+
| ^^^^^^
1324

1425
error: aborting due to previous error
1526

src/test/ui/issues/issue-23302-3.stderr

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
1-
error[E0391]: cycle detected when const checking `A`
2-
--> $DIR/issue-23302-3.rs:1:16
1+
error[E0391]: cycle detected when const-evaluating + checking `A`
2+
--> $DIR/issue-23302-3.rs:1:1
33
|
44
LL | const A: i32 = B;
5-
| ^
5+
| ^^^^^^^^^^^^^^^^^
66
|
7-
note: ...which requires const checking `B`...
8-
--> $DIR/issue-23302-3.rs:3:16
7+
note: ...which requires const-evaluating + checking `A`...
8+
--> $DIR/issue-23302-3.rs:1:1
99
|
10-
LL | const B: i32 = A;
11-
| ^
12-
= note: ...which again requires const checking `A`, completing the cycle
13-
note: cycle used when processing `A`
10+
LL | const A: i32 = B;
11+
| ^^^^^^^^^^^^^^^^^
12+
note: ...which requires const-evaluating `A`...
1413
--> $DIR/issue-23302-3.rs:1:1
1514
|
1615
LL | const A: i32 = B;
1716
| ^^^^^^^^^^^^^^^^^
17+
= note: ...which requires normalizing `B`...
18+
note: ...which requires const-evaluating + checking `B`...
19+
--> $DIR/issue-23302-3.rs:3:1
20+
|
21+
LL | const B: i32 = A;
22+
| ^^^^^^^^^^^^^^^^^
23+
note: ...which requires const-evaluating + checking `B`...
24+
--> $DIR/issue-23302-3.rs:3:1
25+
|
26+
LL | const B: i32 = A;
27+
| ^^^^^^^^^^^^^^^^^
28+
note: ...which requires const-evaluating `B`...
29+
--> $DIR/issue-23302-3.rs:3:1
30+
|
31+
LL | const B: i32 = A;
32+
| ^^^^^^^^^^^^^^^^^
33+
= note: ...which requires normalizing `A`...
34+
= note: ...which again requires const-evaluating + checking `A`, completing the cycle
35+
= note: cycle used when running analysis passes on this crate
1836

1937
error: aborting due to previous error
2038

0 commit comments

Comments
 (0)