@@ -223,11 +223,23 @@ impl AbstractConst<'tcx> {
223
223
}
224
224
}
225
225
226
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
227
+ struct WorkNode < ' tcx > {
228
+ node : Node < ' tcx > ,
229
+ span : Span ,
230
+ used : bool ,
231
+ }
232
+
226
233
struct AbstractConstBuilder < ' a , ' tcx > {
227
234
tcx : TyCtxt < ' tcx > ,
228
235
body : & ' a mir:: Body < ' tcx > ,
229
236
/// The current WIP node tree.
230
- nodes : IndexVec < NodeId , Node < ' tcx > > ,
237
+ ///
238
+ /// We require all nodes to be used in the final abstract const,
239
+ /// so we store this here. Note that we also consider nodes as used
240
+ /// if they are mentioned in an assert, so some used nodes are never
241
+ /// actually reachable by walking the [`AbstractConst`].
242
+ nodes : IndexVec < NodeId , WorkNode < ' tcx > > ,
231
243
locals : IndexVec < mir:: Local , NodeId > ,
232
244
/// We only allow field accesses if they access
233
245
/// the result of a checked operation.
@@ -274,6 +286,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
274
286
Ok ( Some ( builder) )
275
287
}
276
288
289
+ fn add_node ( & mut self , node : Node < ' tcx > , span : Span ) -> NodeId {
290
+ // Mark used nodes.
291
+ match node {
292
+ Node :: Leaf ( _) => ( ) ,
293
+ Node :: Binop ( _, lhs, rhs) => {
294
+ self . nodes [ lhs] . used = true ;
295
+ self . nodes [ rhs] . used = true ;
296
+ }
297
+ Node :: UnaryOp ( _, input) => {
298
+ self . nodes [ input] . used = true ;
299
+ }
300
+ Node :: FunctionCall ( func, nodes) => {
301
+ self . nodes [ func] . used = true ;
302
+ nodes. iter ( ) . for_each ( |& n| self . nodes [ n] . used = true ) ;
303
+ }
304
+ }
305
+
306
+ // Nodes start as unused.
307
+ self . nodes . push ( WorkNode { node, span, used : false } )
308
+ }
309
+
277
310
fn place_to_local (
278
311
& mut self ,
279
312
span : Span ,
@@ -311,7 +344,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
311
344
let local = self . place_to_local ( span, p) ?;
312
345
Ok ( self . locals [ local] )
313
346
}
314
- mir:: Operand :: Constant ( ct) => Ok ( self . nodes . push ( Node :: Leaf ( ct. literal ) ) ) ,
347
+ mir:: Operand :: Constant ( ct) => Ok ( self . add_node ( Node :: Leaf ( ct. literal ) , span ) ) ,
315
348
}
316
349
}
317
350
@@ -336,38 +369,38 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
336
369
337
370
fn build_statement ( & mut self , stmt : & mir:: Statement < ' tcx > ) -> Result < ( ) , ErrorReported > {
338
371
debug ! ( "AbstractConstBuilder: stmt={:?}" , stmt) ;
372
+ let span = stmt. source_info . span ;
339
373
match stmt. kind {
340
374
StatementKind :: Assign ( box ( ref place, ref rvalue) ) => {
341
- let local = self . place_to_local ( stmt . source_info . span , place) ?;
375
+ let local = self . place_to_local ( span, place) ?;
342
376
match * rvalue {
343
377
Rvalue :: Use ( ref operand) => {
344
- self . locals [ local] =
345
- self . operand_to_node ( stmt. source_info . span , operand) ?;
378
+ self . locals [ local] = self . operand_to_node ( span, operand) ?;
346
379
Ok ( ( ) )
347
380
}
348
381
Rvalue :: BinaryOp ( op, ref lhs, ref rhs) if Self :: check_binop ( op) => {
349
- let lhs = self . operand_to_node ( stmt . source_info . span , lhs) ?;
350
- let rhs = self . operand_to_node ( stmt . source_info . span , rhs) ?;
351
- self . locals [ local] = self . nodes . push ( Node :: Binop ( op, lhs, rhs) ) ;
382
+ let lhs = self . operand_to_node ( span, lhs) ?;
383
+ let rhs = self . operand_to_node ( span, rhs) ?;
384
+ self . locals [ local] = self . add_node ( Node :: Binop ( op, lhs, rhs) , span ) ;
352
385
if op. is_checkable ( ) {
353
386
bug ! ( "unexpected unchecked checkable binary operation" ) ;
354
387
} else {
355
388
Ok ( ( ) )
356
389
}
357
390
}
358
391
Rvalue :: CheckedBinaryOp ( op, ref lhs, ref rhs) if Self :: check_binop ( op) => {
359
- let lhs = self . operand_to_node ( stmt . source_info . span , lhs) ?;
360
- let rhs = self . operand_to_node ( stmt . source_info . span , rhs) ?;
361
- self . locals [ local] = self . nodes . push ( Node :: Binop ( op, lhs, rhs) ) ;
392
+ let lhs = self . operand_to_node ( span, lhs) ?;
393
+ let rhs = self . operand_to_node ( span, rhs) ?;
394
+ self . locals [ local] = self . add_node ( Node :: Binop ( op, lhs, rhs) , span ) ;
362
395
self . checked_op_locals . insert ( local) ;
363
396
Ok ( ( ) )
364
397
}
365
398
Rvalue :: UnaryOp ( op, ref operand) if Self :: check_unop ( op) => {
366
- let operand = self . operand_to_node ( stmt . source_info . span , operand) ?;
367
- self . locals [ local] = self . nodes . push ( Node :: UnaryOp ( op, operand) ) ;
399
+ let operand = self . operand_to_node ( span, operand) ?;
400
+ self . locals [ local] = self . add_node ( Node :: UnaryOp ( op, operand) , span ) ;
368
401
Ok ( ( ) )
369
402
}
370
- _ => self . error ( Some ( stmt . source_info . span ) , "unsupported rvalue" ) ?,
403
+ _ => self . error ( Some ( span) , "unsupported rvalue" ) ?,
371
404
}
372
405
}
373
406
// These are not actually relevant for us here, so we can ignore them.
@@ -415,13 +448,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
415
448
. map ( |arg| self . operand_to_node ( terminator. source_info . span , arg) )
416
449
. collect :: < Result < Vec < NodeId > , _ > > ( ) ?,
417
450
) ;
418
- self . locals [ local] = self . nodes . push ( Node :: FunctionCall ( func, args) ) ;
451
+ self . locals [ local] = self . add_node ( Node :: FunctionCall ( func, args) , fn_span ) ;
419
452
Ok ( Some ( target) )
420
453
}
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
454
TerminatorKind :: Assert { ref cond, expected : false , target, .. } => {
426
455
let p = match cond {
427
456
mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => p,
@@ -430,7 +459,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
430
459
431
460
const ONE_FIELD : mir:: Field = mir:: Field :: from_usize ( 1 ) ;
432
461
debug ! ( "proj: {:?}" , p. projection) ;
433
- if let & [ mir:: ProjectionElem :: Field ( ONE_FIELD , _) ] = p. projection . as_ref ( ) {
462
+ if let Some ( p) = p. as_local ( ) {
463
+ debug_assert ! ( !self . checked_op_locals. contains( p) ) ;
464
+ // Mark locals directly used in asserts as used.
465
+ //
466
+ // This is needed because division does not use `CheckedBinop` but instead
467
+ // adds an explicit assert for `divisor != 0`.
468
+ self . nodes [ self . locals [ p] ] . used = true ;
469
+ return Ok ( Some ( target) ) ;
470
+ } else if let & [ mir:: ProjectionElem :: Field ( ONE_FIELD , _) ] = p. projection . as_ref ( ) {
434
471
// Only allow asserts checking the result of a checked operation.
435
472
if self . checked_op_locals . contains ( p. local ) {
436
473
return Ok ( Some ( target) ) ;
@@ -457,7 +494,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
457
494
if let Some ( next) = self . build_terminator ( block. terminator ( ) ) ? {
458
495
block = & self . body . basic_blocks ( ) [ next] ;
459
496
} else {
460
- return Ok ( self . tcx . arena . alloc_from_iter ( self . nodes ) ) ;
497
+ assert_eq ! ( self . locals[ mir:: RETURN_PLACE ] , self . nodes. last( ) . unwrap( ) ) ;
498
+ self . nodes [ self . locals [ mir:: RETURN_PLACE ] ] . used = true ;
499
+ if let Some ( & unused) = self . nodes . iter ( ) . find ( |n| !n. used ) {
500
+ self . error ( Some ( unused. span ) , "dead code" ) ?;
501
+ }
502
+
503
+ return Ok ( self . tcx . arena . alloc_from_iter ( self . nodes . into_iter ( ) . map ( |n| n. node ) ) ) ;
461
504
}
462
505
}
463
506
}
0 commit comments