Skip to content

Commit 084edc4

Browse files
committed
Auto merge of #63812 - eddyb:promo-sanity, r=oli-obk
rustc_mir: double-check const-promotion candidates for sanity. Previously, const promotion involved tracking information about the value in a MIR local (or any part of the computation leading up to that value), aka "qualifs", in a quite stateful manner, which is hard to extend to arbitrary CFGs without a dataflow pass. However, the nature of the promotion we do is that it's effectively an SSA-like "tree" (or DAG, really), of assigned-once locals - which is how we can take them from the original MIR in the first place. This structure means that the subset of the MIR responsible for computing any given part of a const-promoted value is readily analyzable by walking that tree/DAG. This PR implements such an analysis in `promote_consts`, reusing the `HasMutInterior` / `NeedsDrop` computation from `qualify_consts`, but reimplementing the equivalent of `IsNotPromotable` / `IsNotImplicitlyPromotable`. Eventually we should be able to remove `IsNotPromotable` / `IsNotImplicitlyPromotable` from `qualify_consts`, which will simplify @ecstatic-morse's dataflow-based const-checking efforts. But currently this is mainly for a crater check-only run - it will compare the results from the old promotion collection and the new promotion validation and ICE if they don't match. r? @oli-obk
2 parents 246be7e + f2c8628 commit 084edc4

File tree

6 files changed

+789
-57
lines changed

6 files changed

+789
-57
lines changed

src/librustc_mir/transform/check_consts/mod.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc::ty::{self, TyCtxt};
1111
pub use self::qualifs::Qualif;
1212

1313
pub mod ops;
14-
mod qualifs;
14+
pub mod qualifs;
1515
mod resolver;
1616
pub mod validation;
1717

@@ -23,6 +23,7 @@ pub struct Item<'mir, 'tcx> {
2323
def_id: DefId,
2424
param_env: ty::ParamEnv<'tcx>,
2525
mode: validation::Mode,
26+
for_promotion: bool,
2627
}
2728

2829
impl Item<'mir, 'tcx> {
@@ -41,6 +42,28 @@ impl Item<'mir, 'tcx> {
4142
def_id,
4243
param_env,
4344
mode,
45+
for_promotion: false,
46+
}
47+
}
48+
49+
// HACK(eddyb) this is to get around the panic for a runtime fn from `Item::new`.
50+
// Also, it allows promoting `&mut []`.
51+
pub fn for_promotion(
52+
tcx: TyCtxt<'tcx>,
53+
def_id: DefId,
54+
body: &'mir mir::Body<'tcx>,
55+
) -> Self {
56+
let param_env = tcx.param_env(def_id);
57+
let mode = validation::Mode::for_item(tcx, def_id)
58+
.unwrap_or(validation::Mode::ConstFn);
59+
60+
Item {
61+
body,
62+
tcx,
63+
def_id,
64+
param_env,
65+
mode,
66+
for_promotion: true,
4467
}
4568
}
4669
}

src/librustc_mir/transform/check_consts/qualifs.rs

+28-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
use rustc::mir::*;
44
use rustc::mir::interpret::ConstValue;
55
use rustc::ty::{self, Ty};
6-
use rustc_index::bit_set::BitSet;
76
use syntax_pos::DUMMY_SP;
87

98
use super::Item as ConstCx;
@@ -44,7 +43,7 @@ pub trait Qualif {
4443

4544
fn in_projection_structurally(
4645
cx: &ConstCx<'_, 'tcx>,
47-
per_local: &BitSet<Local>,
46+
per_local: &impl Fn(Local) -> bool,
4847
place: PlaceRef<'_, 'tcx>,
4948
) -> bool {
5049
if let [proj_base @ .., elem] = place.projection {
@@ -65,7 +64,7 @@ pub trait Qualif {
6564
ProjectionElem::ConstantIndex { .. } |
6665
ProjectionElem::Downcast(..) => qualif,
6766

68-
ProjectionElem::Index(local) => qualif || per_local.contains(*local),
67+
ProjectionElem::Index(local) => qualif || per_local(*local),
6968
}
7069
} else {
7170
bug!("This should be called if projection is not empty");
@@ -74,22 +73,22 @@ pub trait Qualif {
7473

7574
fn in_projection(
7675
cx: &ConstCx<'_, 'tcx>,
77-
per_local: &BitSet<Local>,
76+
per_local: &impl Fn(Local) -> bool,
7877
place: PlaceRef<'_, 'tcx>,
7978
) -> bool {
8079
Self::in_projection_structurally(cx, per_local, place)
8180
}
8281

8382
fn in_place(
8483
cx: &ConstCx<'_, 'tcx>,
85-
per_local: &BitSet<Local>,
84+
per_local: &impl Fn(Local) -> bool,
8685
place: PlaceRef<'_, 'tcx>,
8786
) -> bool {
8887
match place {
8988
PlaceRef {
9089
base: PlaceBase::Local(local),
9190
projection: [],
92-
} => per_local.contains(*local),
91+
} => per_local(*local),
9392
PlaceRef {
9493
base: PlaceBase::Static(box Static {
9594
kind: StaticKind::Promoted(..),
@@ -112,7 +111,7 @@ pub trait Qualif {
112111

113112
fn in_operand(
114113
cx: &ConstCx<'_, 'tcx>,
115-
per_local: &BitSet<Local>,
114+
per_local: &impl Fn(Local) -> bool,
116115
operand: &Operand<'tcx>,
117116
) -> bool {
118117
match *operand {
@@ -143,7 +142,7 @@ pub trait Qualif {
143142

144143
fn in_rvalue_structurally(
145144
cx: &ConstCx<'_, 'tcx>,
146-
per_local: &BitSet<Local>,
145+
per_local: &impl Fn(Local) -> bool,
147146
rvalue: &Rvalue<'tcx>,
148147
) -> bool {
149148
match *rvalue {
@@ -185,13 +184,17 @@ pub trait Qualif {
185184
}
186185
}
187186

188-
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
187+
fn in_rvalue(
188+
cx: &ConstCx<'_, 'tcx>,
189+
per_local: &impl Fn(Local) -> bool,
190+
rvalue: &Rvalue<'tcx>,
191+
) -> bool {
189192
Self::in_rvalue_structurally(cx, per_local, rvalue)
190193
}
191194

192195
fn in_call(
193196
cx: &ConstCx<'_, 'tcx>,
194-
_per_local: &BitSet<Local>,
197+
_per_local: &impl Fn(Local) -> bool,
195198
_callee: &Operand<'tcx>,
196199
_args: &[Operand<'tcx>],
197200
return_ty: Ty<'tcx>,
@@ -216,7 +219,11 @@ impl Qualif for HasMutInterior {
216219
!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
217220
}
218221

219-
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
222+
fn in_rvalue(
223+
cx: &ConstCx<'_, 'tcx>,
224+
per_local: &impl Fn(Local) -> bool,
225+
rvalue: &Rvalue<'tcx>,
226+
) -> bool {
220227
match *rvalue {
221228
// Returning `true` for `Rvalue::Ref` indicates the borrow isn't
222229
// allowed in constants (and the `Checker` will error), and/or it
@@ -231,12 +238,11 @@ impl Qualif for HasMutInterior {
231238
// Inside a `static mut`, &mut [...] is also allowed.
232239
ty::Array(..) | ty::Slice(_) if cx.mode == Mode::StaticMut => {},
233240

234-
// FIXME(ecstaticmorse): uncomment the following match arm to stop marking
235-
// `&mut []` as `HasMutInterior`.
236-
/*
237-
ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
238-
=> {},
239-
*/
241+
// FIXME(eddyb) the `cx.for_promotion` condition
242+
// seems unnecessary, given that this is merely a ZST.
243+
ty::Array(_, len)
244+
if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
245+
&& cx.for_promotion => {},
240246

241247
_ => return true,
242248
}
@@ -275,7 +281,11 @@ impl Qualif for NeedsDrop {
275281
ty.needs_drop(cx.tcx, cx.param_env)
276282
}
277283

278-
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, per_local: &BitSet<Local>, rvalue: &Rvalue<'tcx>) -> bool {
284+
fn in_rvalue(
285+
cx: &ConstCx<'_, 'tcx>,
286+
per_local: &impl Fn(Local) -> bool,
287+
rvalue: &Rvalue<'tcx>,
288+
) -> bool {
279289
if let Rvalue::Aggregate(ref kind, _) = *rvalue {
280290
if let AggregateKind::Adt(def, ..) = **kind {
281291
if def.has_dtor(cx.tcx) {

src/librustc_mir/transform/check_consts/resolver.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,13 @@ where
8181
return_place: &mir::Place<'tcx>,
8282
) {
8383
let return_ty = return_place.ty(self.item.body, self.item.tcx).ty;
84-
let qualif = Q::in_call(self.item, &mut self.qualifs_per_local, func, args, return_ty);
84+
let qualif = Q::in_call(
85+
self.item,
86+
&|l| self.qualifs_per_local.contains(l),
87+
func,
88+
args,
89+
return_ty,
90+
);
8591
if !return_place.is_indirect() {
8692
self.assign_qualif_direct(return_place, qualif);
8793
}
@@ -114,7 +120,7 @@ where
114120
rvalue: &mir::Rvalue<'tcx>,
115121
location: Location,
116122
) {
117-
let qualif = Q::in_rvalue(self.item, self.qualifs_per_local, rvalue);
123+
let qualif = Q::in_rvalue(self.item, &|l| self.qualifs_per_local.contains(l), rvalue);
118124
if !place.is_indirect() {
119125
self.assign_qualif_direct(place, qualif);
120126
}
@@ -129,7 +135,7 @@ where
129135
// here; that occurs in `apply_call_return_effect`.
130136

131137
if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind {
132-
let qualif = Q::in_operand(self.item, self.qualifs_per_local, value);
138+
let qualif = Q::in_operand(self.item, &|l| self.qualifs_per_local.contains(l), value);
133139
if !dest.is_indirect() {
134140
self.assign_qualif_direct(dest, qualif);
135141
}

src/librustc_mir/transform/check_consts/validation.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,10 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369369
// it depends on `HasMutInterior` being set for mutable borrows as well as values with
370370
// interior mutability.
371371
if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
372-
let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
373-
&self.item,
374-
self.qualifs.has_mut_interior.get(),
375-
rvalue,
376-
);
372+
let rvalue_has_mut_interior = {
373+
let has_mut_interior = self.qualifs.has_mut_interior.get();
374+
HasMutInterior::in_rvalue(&self.item, &|l| has_mut_interior.contains(l), rvalue)
375+
};
377376

378377
if rvalue_has_mut_interior {
379378
let is_derived_from_illegal_borrow = match borrowed_place.as_local() {

0 commit comments

Comments
 (0)