Skip to content

Commit 35c1268

Browse files
committed
Generalize Qualif to prepare for a qualification that needs to track multiple levels of qualification
1 parent 1832bdd commit 35c1268

File tree

7 files changed

+188
-89
lines changed

7 files changed

+188
-89
lines changed

compiler/rustc_mir/src/dataflow/framework/lattice.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,35 @@ impl JoinSemiLattice for bool {
8989
}
9090
}
9191

92+
/// Just for completion, we implement `JoinSemiLattice` for `()`, where top and bottom are
93+
/// the same value.
94+
impl JoinSemiLattice for () {
95+
fn join(&mut self, _: &Self) -> bool {
96+
false
97+
}
98+
}
99+
100+
/// An `Option` is a "two-point" lattice with `Some as the top element and `None` as the bottom:
101+
///
102+
/// ```text
103+
/// Some(T)
104+
/// |
105+
/// None
106+
/// ```
107+
impl<T: JoinSemiLattice + Clone> JoinSemiLattice for Option<T> {
108+
fn join(&mut self, other: &Self) -> bool {
109+
match (self, other) {
110+
(None, None) |
111+
(Some(_), None) => false,
112+
(this @ None, Some(val)) => {
113+
*this = Some(val.clone());
114+
true
115+
}
116+
(Some(a), Some(b)) => a.join(b),
117+
}
118+
}
119+
}
120+
92121
impl MeetSemiLattice for bool {
93122
fn meet(&mut self, other: &Self) -> bool {
94123
if let (true, false) = (*self, *other) {

compiler/rustc_mir/src/transform/check_consts/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::mir;
1111
use rustc_middle::ty::{self, TyCtxt};
1212
use rustc_span::Symbol;
1313

14-
pub use self::qualifs::Qualif;
14+
pub(super) use self::qualifs::Qualif;
1515

1616
mod ops;
1717
pub mod post_drop_elaboration;

compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
7878
match &terminator.kind {
7979
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
8080
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
81-
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
81+
if NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty).is_none() {
8282
return;
8383
}
8484

@@ -87,7 +87,7 @@ impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> {
8787
return;
8888
}
8989

90-
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
90+
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location).is_some() {
9191
// Use the span where the dropped local was declared for the error.
9292
let span = self.body.local_decls[dropped_place.local].source_info.span;
9393
self.check_live_drop(span);

compiler/rustc_mir/src/transform/check_consts/qualifs.rs

Lines changed: 117 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use rustc_middle::mir::*;
77
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
88
use rustc_span::DUMMY_SP;
99
use rustc_trait_selection::traits;
10+
use rustc_index::vec::IndexVec;
11+
use rustc_index::bit_set::BitSet;
12+
use crate::dataflow::JoinSemiLattice;
1013

1114
use super::ConstCx;
1215

@@ -16,9 +19,9 @@ pub fn in_any_value_of_ty(
1619
error_occured: Option<ErrorReported>,
1720
) -> ConstQualifs {
1821
ConstQualifs {
19-
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
20-
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
21-
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
22+
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty).is_some(),
23+
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty).is_some(),
24+
custom_eq: CustomEq::in_any_value_of_ty(cx, ty).is_some(),
2225
error_occured,
2326
}
2427
}
@@ -34,15 +37,21 @@ pub fn in_any_value_of_ty(
3437
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
3538
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
3639
/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed.
37-
pub trait Qualif {
40+
pub(crate) trait Qualif {
3841
/// The name of the file used to debug the dataflow analysis that computes this qualif.
3942
const ANALYSIS_NAME: &'static str;
4043

44+
/// The dataflow result type. If it's just qualified/not qualified, then
45+
/// you can just use a `()` (most qualifs do that). But if you need more state, use a
46+
/// custom enum.
47+
type Result: SetChoice = ();
48+
type Set: QualifsPerLocal<Self::Result> = <Self::Result as SetChoice>::Set;
49+
4150
/// Whether this `Qualif` is cleared when a local is moved from.
4251
const IS_CLEARED_ON_MOVE: bool = false;
4352

4453
/// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`.
45-
fn in_qualifs(qualifs: &ConstQualifs) -> bool;
54+
fn in_qualifs(qualifs: &ConstQualifs) -> Option<Self::Result>;
4655

4756
/// Returns `true` if *any* value of the given type could possibly have this `Qualif`.
4857
///
@@ -51,7 +60,7 @@ pub trait Qualif {
5160
/// from a call to another function.
5261
///
5362
/// It also determines the `Qualif`s for primitive types.
54-
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool;
63+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<Self::Result>;
5564

5665
/// Returns `true` if this `Qualif` is inherent to the given struct or enum.
5766
///
@@ -65,7 +74,61 @@ pub trait Qualif {
6574
cx: &ConstCx<'_, 'tcx>,
6675
adt: &'tcx AdtDef,
6776
substs: SubstsRef<'tcx>,
68-
) -> bool;
77+
) -> Option<Self::Result>;
78+
}
79+
80+
pub(crate) trait SetChoice: Sized + Clone + JoinSemiLattice {
81+
type Set: QualifsPerLocal<Self> = IndexVec<Local, Option<Self>>;
82+
}
83+
84+
impl SetChoice for () {
85+
type Set = BitSet<Local>;
86+
}
87+
88+
pub(crate) trait QualifsPerLocal<Value>: Sized + Clone + JoinSemiLattice {
89+
fn new_empty(n: usize) -> Self;
90+
fn insert(&mut self, local: Local, val: Value);
91+
fn remove(&mut self, local: Local);
92+
fn clear(&mut self);
93+
fn get(&self, local: Local) -> Option<Value>;
94+
}
95+
96+
impl QualifsPerLocal<()> for BitSet<Local> {
97+
fn new_empty(n: usize) -> Self {
98+
BitSet::new_empty(n)
99+
}
100+
fn insert(&mut self, local: Local, _: ()) {
101+
BitSet::insert(self, local);
102+
}
103+
fn remove(&mut self, local: Local) {
104+
BitSet::remove(self, local);
105+
}
106+
fn clear(&mut self) {
107+
BitSet::clear(self)
108+
}
109+
fn get(&self, local: Local) -> Option<()> {
110+
self.contains(local).then_some(())
111+
}
112+
}
113+
114+
impl<T: Clone + Eq + JoinSemiLattice> QualifsPerLocal<T> for IndexVec<Local, Option<T>> {
115+
fn new_empty(n: usize) -> Self {
116+
IndexVec::from_elem_n(None, n)
117+
}
118+
fn insert(&mut self, local: Local, val: T) {
119+
self[local] = Some(val);
120+
}
121+
fn remove(&mut self, local: Local) {
122+
self[local] = None;
123+
}
124+
fn clear(&mut self) {
125+
for elem in self.iter_mut() {
126+
*elem = None;
127+
}
128+
}
129+
fn get(&self, local: Local) -> Option<T> {
130+
self[local].clone()
131+
}
69132
}
70133

71134
/// Constant containing interior mutability (`UnsafeCell<T>`).
@@ -78,18 +141,18 @@ pub struct HasMutInterior;
78141
impl Qualif for HasMutInterior {
79142
const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
80143

81-
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
82-
qualifs.has_mut_interior
144+
fn in_qualifs(qualifs: &ConstQualifs) -> Option<()> {
145+
qualifs.has_mut_interior.then_some(())
83146
}
84147

85-
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
86-
!ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
148+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<()> {
149+
(!ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)).then_some(())
87150
}
88151

89-
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
152+
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> Option<()> {
90153
// Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
91154
// It arises structurally for all other types.
92-
Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type()
155+
(Some(adt.did) == cx.tcx.lang_items().unsafe_cell_type()).then_some(())
93156
}
94157
}
95158

@@ -103,16 +166,16 @@ impl Qualif for NeedsDrop {
103166
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
104167
const IS_CLEARED_ON_MOVE: bool = true;
105168

106-
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
107-
qualifs.needs_drop
169+
fn in_qualifs(qualifs: &ConstQualifs) -> Option<()> {
170+
qualifs.needs_drop.then_some(())
108171
}
109172

110-
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
111-
ty.needs_drop(cx.tcx, cx.param_env)
173+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<()> {
174+
ty.needs_drop(cx.tcx, cx.param_env).then_some(())
112175
}
113176

114-
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> bool {
115-
adt.has_dtor(cx.tcx)
177+
fn in_adt_inherently(cx: &ConstCx<'_, 'tcx>, adt: &'tcx AdtDef, _: SubstsRef<'tcx>) -> Option<()> {
178+
adt.has_dtor(cx.tcx).then_some(())
116179
}
117180
}
118181

@@ -122,37 +185,37 @@ pub struct CustomEq;
122185
impl Qualif for CustomEq {
123186
const ANALYSIS_NAME: &'static str = "flow_custom_eq";
124187

125-
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
126-
qualifs.custom_eq
188+
fn in_qualifs(qualifs: &ConstQualifs) -> Option<()> {
189+
qualifs.custom_eq.then_some(())
127190
}
128191

129-
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
192+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<()> {
130193
// If *any* component of a composite data type does not implement `Structural{Partial,}Eq`,
131194
// we know that at least some values of that type are not structural-match. I say "some"
132195
// because that component may be part of an enum variant (e.g.,
133196
// `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
134197
// structural-match (`Option::None`).
135198
let id = cx.tcx.hir().local_def_id_to_hir_id(cx.def_id());
136-
traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).is_some()
199+
traits::search_for_structural_match_violation(id, cx.body.span, cx.tcx, ty).map(drop)
137200
}
138201

139202
fn in_adt_inherently(
140203
cx: &ConstCx<'_, 'tcx>,
141204
adt: &'tcx AdtDef,
142205
substs: SubstsRef<'tcx>,
143-
) -> bool {
206+
) -> Option<()> {
144207
let ty = cx.tcx.mk_ty(ty::Adt(adt, substs));
145-
!ty.is_structural_eq_shallow(cx.tcx)
208+
(!ty.is_structural_eq_shallow(cx.tcx)).then_some(())
146209
}
147210
}
148211

149212
// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return.
150213

151214
/// Returns `true` if this `Rvalue` contains qualif `Q`.
152-
pub fn in_rvalue<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool
215+
pub(crate) fn in_rvalue<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> Option<Q::Result>
153216
where
154217
Q: Qualif,
155-
F: FnMut(Local) -> bool,
218+
F: FnMut(Local) -> Option<Q::Result>,
156219
{
157220
match rvalue {
158221
Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(..) => {
@@ -169,7 +232,9 @@ where
169232
| Rvalue::Cast(_, operand, _) => in_operand::<Q, _>(cx, in_local, operand),
170233

171234
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
172-
in_operand::<Q, _>(cx, in_local, lhs) || in_operand::<Q, _>(cx, in_local, rhs)
235+
let mut res = in_operand::<Q, _>(cx, in_local, lhs);
236+
res.join(&in_operand::<Q, _>(cx, in_local, rhs));
237+
res
173238
}
174239

175240
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
@@ -189,57 +254,61 @@ where
189254
}
190255

191256
Rvalue::Aggregate(kind, operands) => {
192-
// Return early if we know that the struct or enum being constructed is always
193-
// qualified.
257+
// Check if we know that the struct or enum being constructed is always qualified.
258+
let mut result = None;
194259
if let AggregateKind::Adt(def, _, substs, ..) = **kind {
195-
if Q::in_adt_inherently(cx, def, substs) {
196-
return true;
197-
}
260+
result.join(&Q::in_adt_inherently(cx, def, substs));
198261
}
199262

200263
// Otherwise, proceed structurally...
201-
operands.iter().any(|o| in_operand::<Q, _>(cx, in_local, o))
264+
for o in operands {
265+
result.join(&in_operand::<Q, _>(cx, in_local, o));
266+
}
267+
result
202268
}
203269
}
204270
}
205271

206272
/// Returns `true` if this `Place` contains qualif `Q`.
207-
pub fn in_place<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool
273+
pub(crate) fn in_place<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> Option<Q::Result>
208274
where
209275
Q: Qualif,
210-
F: FnMut(Local) -> bool,
276+
F: FnMut(Local) -> Option<Q::Result>,
211277
{
212278
let mut projection = place.projection;
279+
let mut result = None;
213280
while let &[ref proj_base @ .., proj_elem] = projection {
214281
match proj_elem {
215-
ProjectionElem::Index(index) if in_local(index) => return true,
282+
ProjectionElem::Index(index) => {
283+
result.join(&in_local(index));
284+
},
216285

217286
ProjectionElem::Deref
218287
| ProjectionElem::Field(_, _)
219288
| ProjectionElem::ConstantIndex { .. }
220289
| ProjectionElem::Subslice { .. }
221-
| ProjectionElem::Downcast(_, _)
222-
| ProjectionElem::Index(_) => {}
290+
| ProjectionElem::Downcast(_, _) => {}
223291
}
224292

225293
let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx);
226294
let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty;
227-
if !Q::in_any_value_of_ty(cx, proj_ty) {
228-
return false;
295+
if Q::in_any_value_of_ty(cx, proj_ty).is_none() {
296+
return result;
229297
}
230298

231299
projection = proj_base;
232300
}
233301

234302
assert!(projection.is_empty());
235-
in_local(place.local)
303+
result.join(&in_local(place.local));
304+
result
236305
}
237306

238307
/// Returns `true` if this `Operand` contains qualif `Q`.
239-
pub fn in_operand<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool
308+
pub(crate) fn in_operand<Q, F>(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> Option<Q::Result>
240309
where
241310
Q: Qualif,
242-
F: FnMut(Local) -> bool,
311+
F: FnMut(Local) -> Option<Q::Result>,
243312
{
244313
let constant = match operand {
245314
Operand::Copy(place) | Operand::Move(place) => {
@@ -260,9 +329,10 @@ where
260329
cx.tcx.at(constant.span).mir_const_qualif(def.did)
261330
};
262331

263-
if !Q::in_qualifs(&qualifs) {
264-
return false;
265-
}
332+
// Since this comes from a constant's qualifs, there can only
333+
// be `Option<()>` style qualifs, so we are allowed to early
334+
// return here and not try to join the results.
335+
Q::in_qualifs(&qualifs)?;
266336

267337
// Just in case the type is more specific than
268338
// the definition, e.g., impl associated const

0 commit comments

Comments
 (0)