Description
Entering a nested class resets the variable context, which wipes the outer class's fields. Any method of the outer class declared after a nested class loses the refinements of the outer class's own fields.
Minimal reproducer
import liquidjava.specification.Refinement;
public class OuterWiped {
@Refinement("_ > 0") int x = 1;
static class Inner { int y; }
@Refinement("_ > 0")
public int get() { return x; }
}
Expected
Correct! Passed Verification.
Actual
Refinement Error: true is not a subtype of #ret¹ > 0
6 | public int get() { return x; }
| ^^^^^^^^^
--> Not enough information to prove the expected refinement. Add a refinement or condition to constrain it.
Removing Inner (or moving it below get) makes it pass. Writes such as x = -1 are still rejected correctly, because the expected refinement is read from the field's annotation.
Cause
RefinementTypeChecker.visitCtClass (and MethodsFirstChecker.visitCtClass) call context.reinitializeContext(), which clears ctxVars. Spoon visits nested classes as children of the outer class, so visiting Inner clears this#x. The opposite also happens: the nested class's fields stay in the context after it returns. That leak is why declaring a nested class before its use works today (see #308).
Possible fix
When a class is entered, register all of its fields (and those of its nested types) before visiting any methods, and keep nested classes from clearing the enclosing class's context. This would probably also fix the read case above.
Reproduced on main at fbfb4e2 (liquidjava-verifier 0.0.35).
Description
Entering a nested class resets the variable context, which wipes the outer class's fields. Any method of the outer class declared after a nested class loses the refinements of the outer class's own fields.
Minimal reproducer
Expected
Correct! Passed Verification.Actual
Removing
Inner(or moving it belowget) makes it pass. Writes such asx = -1are still rejected correctly, because the expected refinement is read from the field's annotation.Cause
RefinementTypeChecker.visitCtClass(andMethodsFirstChecker.visitCtClass) callcontext.reinitializeContext(), which clearsctxVars. Spoon visits nested classes as children of the outer class, so visitingInnerclearsthis#x. The opposite also happens: the nested class's fields stay in the context after it returns. That leak is why declaring a nested class before its use works today (see #308).Possible fix
When a class is entered, register all of its fields (and those of its nested types) before visiting any methods, and keep nested classes from clearing the enclosing class's context. This would probably also fix the read case above.
Reproduced on
mainat fbfb4e2 (liquidjava-verifier 0.0.35).