@@ -223,11 +223,23 @@ impl AbstractConst<'tcx> {
223223 }
224224}
225225
226+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
227+ struct WorkNode < ' tcx > {
228+ node : Node < ' tcx > ,
229+ span : Span ,
230+ used : bool ,
231+ }
232+
226233struct AbstractConstBuilder < ' a , ' tcx > {
227234 tcx : TyCtxt < ' tcx > ,
228235 body : & ' a mir:: Body < ' tcx > ,
229236 /// 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 > > ,
231243 locals : IndexVec < mir:: Local , NodeId > ,
232244 /// We only allow field accesses if they access
233245 /// the result of a checked operation.
@@ -274,6 +286,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
274286 Ok ( Some ( builder) )
275287 }
276288
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+
277310 fn place_to_local (
278311 & mut self ,
279312 span : Span ,
@@ -311,7 +344,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
311344 let local = self . place_to_local ( span, p) ?;
312345 Ok ( self . locals [ local] )
313346 }
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 ) ) ,
315348 }
316349 }
317350
@@ -336,38 +369,38 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
336369
337370 fn build_statement ( & mut self , stmt : & mir:: Statement < ' tcx > ) -> Result < ( ) , ErrorReported > {
338371 debug ! ( "AbstractConstBuilder: stmt={:?}" , stmt) ;
372+ let span = stmt. source_info . span ;
339373 match stmt. kind {
340374 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) ?;
342376 match * rvalue {
343377 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) ?;
346379 Ok ( ( ) )
347380 }
348381 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 ) ;
352385 if op. is_checkable ( ) {
353386 bug ! ( "unexpected unchecked checkable binary operation" ) ;
354387 } else {
355388 Ok ( ( ) )
356389 }
357390 }
358391 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 ) ;
362395 self . checked_op_locals . insert ( local) ;
363396 Ok ( ( ) )
364397 }
365398 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 ) ;
368401 Ok ( ( ) )
369402 }
370- _ => self . error ( Some ( stmt . source_info . span ) , "unsupported rvalue" ) ?,
403+ _ => self . error ( Some ( span) , "unsupported rvalue" ) ?,
371404 }
372405 }
373406 // These are not actually relevant for us here, so we can ignore them.
@@ -415,13 +448,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
415448 . map ( |arg| self . operand_to_node ( terminator. source_info . span , arg) )
416449 . collect :: < Result < Vec < NodeId > , _ > > ( ) ?,
417450 ) ;
418- self . locals [ local] = self . nodes . push ( Node :: FunctionCall ( func, args) ) ;
451+ self . locals [ local] = self . add_node ( Node :: FunctionCall ( func, args) , fn_span ) ;
419452 Ok ( Some ( target) )
420453 }
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.
425454 TerminatorKind :: Assert { ref cond, expected : false , target, .. } => {
426455 let p = match cond {
427456 mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => p,
@@ -430,7 +459,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
430459
431460 const ONE_FIELD : mir:: Field = mir:: Field :: from_usize ( 1 ) ;
432461 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 ( ) {
434471 // Only allow asserts checking the result of a checked operation.
435472 if self . checked_op_locals . contains ( p. local ) {
436473 return Ok ( Some ( target) ) ;
@@ -457,7 +494,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
457494 if let Some ( next) = self . build_terminator ( block. terminator ( ) ) ? {
458495 block = & self . body . basic_blocks ( ) [ next] ;
459496 } 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 ) ) ) ;
461504 }
462505 }
463506 }
0 commit comments