@@ -227,7 +227,12 @@ struct AbstractConstBuilder<'a, 'tcx> {
227
227
tcx : TyCtxt < ' tcx > ,
228
228
body : & ' a mir:: Body < ' tcx > ,
229
229
/// The current WIP node tree.
230
- nodes : IndexVec < NodeId , Node < ' tcx > > ,
230
+ ///
231
+ /// We require all nodes to be used in the final abstract const,
232
+ /// so we store this here. Note that we also consider nodes as used
233
+ /// if they are mentioned in an assert, so some used nodes are never
234
+ /// actually reachable by walking the [`AbstractConst`].
235
+ nodes : IndexVec < NodeId , ( Node < ' tcx > , bool ) > ,
231
236
locals : IndexVec < mir:: Local , NodeId > ,
232
237
/// We only allow field accesses if they access
233
238
/// the result of a checked operation.
@@ -274,6 +279,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
274
279
Ok ( Some ( builder) )
275
280
}
276
281
282
+ fn add_node ( & mut self , n : Node < ' tcx > ) -> NodeId {
283
+ // Mark used nodes.
284
+ match n {
285
+ Node :: Leaf ( _) => ( ) ,
286
+ Node :: Binop ( _, lhs, rhs) => {
287
+ self . nodes [ lhs] . 1 = true ;
288
+ self . nodes [ rhs] . 1 = true ;
289
+ }
290
+ Node :: UnaryOp ( _, input) => {
291
+ self . nodes [ input] . 1 = true ;
292
+ }
293
+ Node :: FunctionCall ( func, nodes) => {
294
+ self . nodes [ func] . 1 = true ;
295
+ nodes. iter ( ) . for_each ( |& n| self . nodes [ n] . 1 = true ) ;
296
+ }
297
+ }
298
+
299
+ // Nodes start as unused.
300
+ self . nodes . push ( ( n, false ) )
301
+ }
302
+
277
303
fn place_to_local (
278
304
& mut self ,
279
305
span : Span ,
@@ -311,7 +337,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
311
337
let local = self . place_to_local ( span, p) ?;
312
338
Ok ( self . locals [ local] )
313
339
}
314
- mir:: Operand :: Constant ( ct) => Ok ( self . nodes . push ( Node :: Leaf ( ct. literal ) ) ) ,
340
+ mir:: Operand :: Constant ( ct) => Ok ( self . add_node ( Node :: Leaf ( ct. literal ) ) ) ,
315
341
}
316
342
}
317
343
@@ -348,7 +374,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
348
374
Rvalue :: BinaryOp ( op, ref lhs, ref rhs) if Self :: check_binop ( op) => {
349
375
let lhs = self . operand_to_node ( stmt. source_info . span , lhs) ?;
350
376
let rhs = self . operand_to_node ( stmt. source_info . span , rhs) ?;
351
- self . locals [ local] = self . nodes . push ( Node :: Binop ( op, lhs, rhs) ) ;
377
+ self . locals [ local] = self . add_node ( Node :: Binop ( op, lhs, rhs) ) ;
352
378
if op. is_checkable ( ) {
353
379
bug ! ( "unexpected unchecked checkable binary operation" ) ;
354
380
} else {
@@ -358,13 +384,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
358
384
Rvalue :: CheckedBinaryOp ( op, ref lhs, ref rhs) if Self :: check_binop ( op) => {
359
385
let lhs = self . operand_to_node ( stmt. source_info . span , lhs) ?;
360
386
let rhs = self . operand_to_node ( stmt. source_info . span , rhs) ?;
361
- self . locals [ local] = self . nodes . push ( Node :: Binop ( op, lhs, rhs) ) ;
387
+ self . locals [ local] = self . add_node ( Node :: Binop ( op, lhs, rhs) ) ;
362
388
self . checked_op_locals . insert ( local) ;
363
389
Ok ( ( ) )
364
390
}
365
391
Rvalue :: UnaryOp ( op, ref operand) if Self :: check_unop ( op) => {
366
392
let operand = self . operand_to_node ( stmt. source_info . span , operand) ?;
367
- self . locals [ local] = self . nodes . push ( Node :: UnaryOp ( op, operand) ) ;
393
+ self . locals [ local] = self . add_node ( Node :: UnaryOp ( op, operand) ) ;
368
394
Ok ( ( ) )
369
395
}
370
396
_ => self . error ( Some ( stmt. source_info . span ) , "unsupported rvalue" ) ?,
@@ -415,13 +441,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
415
441
. map ( |arg| self . operand_to_node ( terminator. source_info . span , arg) )
416
442
. collect :: < Result < Vec < NodeId > , _ > > ( ) ?,
417
443
) ;
418
- self . locals [ local] = self . nodes . push ( Node :: FunctionCall ( func, args) ) ;
444
+ self . locals [ local] = self . add_node ( Node :: FunctionCall ( func, args) ) ;
419
445
Ok ( Some ( target) )
420
446
}
421
- // We only allow asserts for checked operations.
422
- //
423
- // These asserts seem to all have the form `!_local.0` so
424
- // we only allow exactly that.
425
447
TerminatorKind :: Assert { ref cond, expected : false , target, .. } => {
426
448
let p = match cond {
427
449
mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => p,
@@ -430,7 +452,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
430
452
431
453
const ONE_FIELD : mir:: Field = mir:: Field :: from_usize ( 1 ) ;
432
454
debug ! ( "proj: {:?}" , p. projection) ;
433
- if let & [ mir:: ProjectionElem :: Field ( ONE_FIELD , _) ] = p. projection . as_ref ( ) {
455
+ if let Some ( p) = p. as_local ( ) {
456
+ debug_assert ! ( !self . checked_op_locals. contains( p) ) ;
457
+ // Mark locals directly used in asserts as used.
458
+ //
459
+ // This is needed because division does not use `CheckedBinop` but instead
460
+ // adds an explicit assert for `divisor != 0`.
461
+ self . nodes [ self . locals [ p] ] . 1 = true ;
462
+ return Ok ( Some ( target) ) ;
463
+ } else if let & [ mir:: ProjectionElem :: Field ( ONE_FIELD , _) ] = p. projection . as_ref ( ) {
434
464
// Only allow asserts checking the result of a checked operation.
435
465
if self . checked_op_locals . contains ( p. local ) {
436
466
return Ok ( Some ( target) ) ;
@@ -457,7 +487,16 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
457
487
if let Some ( next) = self . build_terminator ( block. terminator ( ) ) ? {
458
488
block = & self . body . basic_blocks ( ) [ next] ;
459
489
} else {
460
- return Ok ( self . tcx . arena . alloc_from_iter ( self . nodes ) ) ;
490
+ assert_eq ! ( self . locals[ mir:: Local :: from_usize( 0 ) ] , self . nodes. last( ) . unwrap( ) ) ;
491
+ self . nodes [ self . locals [ mir:: Local :: from_usize ( 0 ) ] ] . 1 = true ;
492
+ if !self . nodes . iter ( ) . all ( |n| n. 1 ) {
493
+ self . error ( None , "unused node" ) ?;
494
+ }
495
+
496
+ return Ok ( self
497
+ . tcx
498
+ . arena
499
+ . alloc_from_iter ( self . nodes . into_iter ( ) . map ( |( n, _used) | n) ) ) ;
461
500
}
462
501
}
463
502
}
0 commit comments