@@ -52,35 +52,49 @@ object Nullables:
5252    val  hiTree  =  if (hiTpe eq hi.typeOpt) hi else  TypeTree (hiTpe)
5353    TypeBoundsTree (lo, hiTree, alias)
5454
55-   /**  A set of val or var references that are known to be not null, plus a set of 
56-    *  variable references that are not known (anymore) to be not null 
55+   /**  A set of val or var references that are known to be not null, 
56+    *  a set of variable references that are not known (anymore) to be not null, 
57+    *  plus a set of variables that are known to be not null at any point. 
5758   */  
58-   case  class  NotNullInfo (asserted : Set [TermRef ], retracted : Set [TermRef ]): 
59+   case  class  NotNullInfo (asserted : Set [TermRef ], retracted : Set [TermRef ],  onceRetracted :  Set [ TermRef ] ): 
5960    assert((asserted &  retracted).isEmpty)
61+     assert(retracted.subsetOf(onceRetracted))
6062
6163    def  isEmpty  =  this  eq NotNullInfo .empty
6264
63-     def  retractedInfo  =  NotNullInfo (Set (), retracted)
65+     def  retractedInfo  =  NotNullInfo (Set (), retracted, onceRetracted)
66+ 
67+     def  onceRetractedInfo  =  NotNullInfo (Set (), onceRetracted, onceRetracted)
6468
6569    /**  The sequential combination with another not-null info */  
6670    def  seq (that : NotNullInfo ):  NotNullInfo  = 
6771      if  this .isEmpty then  that
6872      else  if  that.isEmpty then  this 
6973      else  NotNullInfo (
7074        this .asserted.union(that.asserted).diff(that.retracted),
71-         this .retracted.union(that.retracted).diff(that.asserted))
75+         this .retracted.union(that.retracted).diff(that.asserted),
76+         this .onceRetracted.union(that.onceRetracted))
7277
7378    /**  The alternative path combination with another not-null info. Used to merge 
7479     *  the nullability info of the two branches of an if. 
7580     */  
7681    def  alt (that : NotNullInfo ):  NotNullInfo  = 
77-       NotNullInfo (this .asserted.intersect(that.asserted), this .retracted.union(that.retracted))
82+       NotNullInfo (
83+         this .asserted.intersect(that.asserted),
84+         this .retracted.union(that.retracted),
85+         this .onceRetracted.union(that.onceRetracted))
86+ 
87+     def  withOnceRetracted (that : NotNullInfo ):  NotNullInfo  = 
88+       if  that.isEmpty then  this 
89+       else  NotNullInfo (this .asserted, this .retracted, this .onceRetracted.union(that.onceRetracted))
7890
7991  object  NotNullInfo : 
80-     val  empty  =  new  NotNullInfo (Set (), Set ())
92+     val  empty  =  new  NotNullInfo (Set (), Set (),  Set () )
8193    def  apply (asserted : Set [TermRef ], retracted : Set [TermRef ]):  NotNullInfo  = 
82-       if  asserted.isEmpty &&  retracted.isEmpty then  empty
83-       else  new  NotNullInfo (asserted, retracted)
94+       apply(asserted, retracted, retracted)
95+     def  apply (asserted : Set [TermRef ], retracted : Set [TermRef ], onceRetracted : Set [TermRef ]):  NotNullInfo  = 
96+       if  asserted.isEmpty &&  onceRetracted.isEmpty then  empty
97+       else  new  NotNullInfo (asserted, retracted, onceRetracted)
8498  end  NotNullInfo 
8599
86100  /**  A pair of not-null sets, depending on whether a condition is `true` or `false` */  
0 commit comments