Description
Suppose we have f
like:
f { if(nondet) return new A(); else throw new B(); }
On the first call to f
, the two arms of the if
produce value sets { f#return_value -> { dynamic_object1 }, @inflight_exception -> { null } }
and { @inflight_exception -> { dynamic_object2 } }
respectively. These merge "cleanly" to give { f#return_value -> { dynamic_object1 }, @inflight_exception -> { null, dynamic_object2 } }
allowing the caller to proceed as if the return value is certainly a pointer to DO1. However, the caller then runs
DEAD f#return_value
This leads to a value-set f#return_value -> { f#return_value$object }
, which is different to the value-set we had when the program started before it was referenced at all.
Then when we call f
again, our two value sets are:
{ f#return_value -> { dynamic_object1 }, @inflight_exception -> { null } }
and { f#return_value -> { f#return_value$object }, @inflight_exception -> { dynamic_object2 } }
These merge to produce { f#return_value -> { dynamic_object1, f#return_value$object }, @inflight_exception -> { null, dynamic_object2 } }
, and the caller's certainty that f#return_value
has a single determined referee is suddenly gone.
One of these behaviours is wrong (the two calls should behave the same): either f#return_value
should have been initialised with f#return_value$object
, or the DEAD
should delete it from the value-set completely. But which?