1
+ //! Checking that constant values used in types can be successfully evaluated.
2
+ //!
3
+ //! For concrete constants, this is fairly simple as we can just try and evaluate it.
4
+ //!
5
+ //! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
6
+ //! this is not as easy.
7
+ //!
8
+ //! In this case we try to build an abstract representation of this constant using
9
+ //! `mir_abstract_const` which can then be checked for structural equality with other
10
+ //! generic constants mentioned in the `caller_bounds` of the current environment.
1
11
use rustc_hir:: def:: DefKind ;
2
12
use rustc_index:: bit_set:: BitSet ;
3
13
use rustc_index:: vec:: IndexVec ;
@@ -129,13 +139,19 @@ impl AbstractConst<'tcx> {
129
139
struct AbstractConstBuilder < ' a , ' tcx > {
130
140
tcx : TyCtxt < ' tcx > ,
131
141
body : & ' a mir:: Body < ' tcx > ,
142
+ /// The current WIP node tree.
132
143
nodes : IndexVec < NodeId , Node < ' tcx > > ,
133
144
locals : IndexVec < mir:: Local , NodeId > ,
145
+ /// We only allow field accesses if they access
146
+ /// the result of a checked operation.
134
147
checked_op_locals : BitSet < mir:: Local > ,
135
148
}
136
149
137
150
impl < ' a , ' tcx > AbstractConstBuilder < ' a , ' tcx > {
138
151
fn new ( tcx : TyCtxt < ' tcx > , body : & ' a mir:: Body < ' tcx > ) -> Option < AbstractConstBuilder < ' a , ' tcx > > {
152
+ // We only allow consts without control flow, so
153
+ // we check for cycles here which simplifies the
154
+ // rest of this implementation.
139
155
if body. is_cfg_cyclic ( ) {
140
156
return None ;
141
157
}
@@ -154,17 +170,21 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
154
170
checked_op_locals : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
155
171
} )
156
172
}
157
-
158
173
fn operand_to_node ( & mut self , op : & mir:: Operand < ' tcx > ) -> Option < NodeId > {
159
174
debug ! ( "operand_to_node: op={:?}" , op) ;
160
175
const ZERO_FIELD : mir:: Field = mir:: Field :: from_usize ( 0 ) ;
161
176
match op {
162
177
mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => {
178
+ // Do not allow any projections.
179
+ //
180
+ // One exception are field accesses on the result of checked operations,
181
+ // which are required to support things like `1 + 2`.
163
182
if let Some ( p) = p. as_local ( ) {
164
183
debug_assert ! ( !self . checked_op_locals. contains( p) ) ;
165
184
Some ( self . locals [ p] )
166
185
} else if let & [ mir:: ProjectionElem :: Field ( ZERO_FIELD , _) ] = p. projection . as_ref ( ) {
167
- // Only allow field accesses on the result of checked operations.
186
+ // Only allow field accesses if the given local
187
+ // contains the result of a checked operation.
168
188
if self . checked_op_locals . contains ( p. local ) {
169
189
Some ( self . locals [ p. local ] )
170
190
} else {
@@ -238,6 +258,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
238
258
}
239
259
}
240
260
261
+ /// Possible return values:
262
+ ///
263
+ /// - `None`: unsupported terminator, stop building
264
+ /// - `Some(None)`: supported terminator, finish building
265
+ /// - `Some(Some(block))`: support terminator, build `block` next
241
266
fn build_terminator (
242
267
& mut self ,
243
268
terminator : & mir:: Terminator < ' tcx > ,
@@ -250,7 +275,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
250
275
ref func,
251
276
ref args,
252
277
destination : Some ( ( ref place, target) ) ,
278
+ // We do not care about `cleanup` here. Any branch which
279
+ // uses `cleanup` will fail const-eval and they therefore
280
+ // do not matter when checking for const evaluatability.
281
+ //
282
+ // Do note that even if `panic::catch_unwind` is made const,
283
+ // we still do not have to care about this, as we do not look
284
+ // into functions.
253
285
cleanup : _,
286
+ // Do not allow overloaded operators for now,
287
+ // we probably do want to allow this in the future.
288
+ //
289
+ // This is currently fairly irrelevant as it requires `const Trait`s.
254
290
from_hir_call : true ,
255
291
fn_span : _,
256
292
} => {
@@ -264,10 +300,14 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
264
300
self . locals [ local] = self . nodes . push ( Node :: FunctionCall ( func, args) ) ;
265
301
Some ( Some ( target) )
266
302
}
303
+ // We only allow asserts for checked operations.
304
+ //
305
+ // These asserts seem to all have the form `!_local.0` so
306
+ // we only allow exactly that.
267
307
TerminatorKind :: Assert { ref cond, expected : false , target, .. } => {
268
308
let p = match cond {
269
309
mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => p,
270
- mir:: Operand :: Constant ( _) => bug ! ( "Unexpected assert" ) ,
310
+ mir:: Operand :: Constant ( _) => bug ! ( "unexpected assert" ) ,
271
311
} ;
272
312
273
313
const ONE_FIELD : mir:: Field = mir:: Field :: from_usize ( 1 ) ;
@@ -285,8 +325,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
285
325
}
286
326
}
287
327
328
+ /// Builds the abstract const by walking the mir from start to finish
329
+ /// and bailing out when encountering an unsupported operation.
288
330
fn build ( mut self ) -> Option < & ' tcx [ Node < ' tcx > ] > {
289
331
let mut block = & self . body . basic_blocks ( ) [ mir:: START_BLOCK ] ;
332
+ // We checked for a cyclic cfg above, so this should terminate.
290
333
loop {
291
334
debug ! ( "AbstractConstBuilder: block={:?}" , block) ;
292
335
for stmt in block. statements . iter ( ) {
@@ -340,6 +383,7 @@ pub(super) fn try_unify_abstract_consts<'tcx>(
340
383
false
341
384
}
342
385
386
+ /// Tries to unify two abstract constants using structural equality.
343
387
pub ( super ) fn try_unify < ' tcx > (
344
388
tcx : TyCtxt < ' tcx > ,
345
389
a : AbstractConst < ' tcx > ,
0 commit comments