Skip to content

Commit efa5a0f

Browse files
Get Refinements needs to visit the variable first (#252)
## Description Closes #241 ## Example Examples added ```java float readAverageStoredCondition(Connection conn) throws SQLException { Statement parentstmt = conn.createStatement(); ResultSet parentMessage = parentstmt.executeQuery("SELECT SUM(IMPORTANCE) AS IMPAVG FROM MAIL"); boolean b = parentMessage.next(); if( b ) { // was not getting the correct refinement before float avgsum = parentMessage.getFloat("IMPAVG"); return avgsum; } else { return 0.0f; } } ``` ## Type of change - [x] Bug fix - [ ] New feature - [ ] Documentation update - [ ] Code refactoring ## Checklist - [x] Added/updated tests under `liquidjava-example/src/main/java/testSuite/` (`Correct*` / `Error*`) - [x] `mvn test` passes locally - [ ] Updated docs/README if behavior or API changed
1 parent d33af55 commit efa5a0f

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

liquidjava-example/src/main/java/testSuite/classes/resultset_forward_correct/ResultSetTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,20 @@ float readAverage(Connection conn) throws SQLException {
6262
return 0.0f;
6363
}
6464
}
65+
66+
float readAverageStoredCondition(Connection conn) throws SQLException {
67+
Statement parentstmt = conn.createStatement();
68+
ResultSet parentMessage =
69+
parentstmt.executeQuery("SELECT SUM(IMPORTANCE) AS IMPAVG FROM MAIL");
70+
// Regression for issue #241: next()'s result is stored in a boolean before the `if`.
71+
// The transition (_ ? onRow : endRows) must still flow through `b`, so the guarded
72+
// getFloat() is on a row (onRow) and verifies — exactly as the inline readAverage above.
73+
boolean b = parentMessage.next();
74+
if( b ) {
75+
float avgsum = parentMessage.getFloat("IMPAVG");
76+
return avgsum;
77+
} else {
78+
return 0.0f;
79+
}
80+
}
6581
}

liquidjava-example/src/main/java/testSuite/classes/resultset_forward_error/ResultSetTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,29 @@ float readAverage(Connection conn) throws SQLException {
6767
float avgsum = parentMessage.getFloat("IMPAVG"); // State Refinement Error
6868
return avgsum;
6969
}
70+
71+
// Issue #241 soundness: storing next()'s result in a boolean must NOT make getFloat()
72+
// unconditionally legal. With no guard the state is `next ? onRow : endRows`, so onRow is
73+
// not guaranteed and getFloat() must still be rejected.
74+
float readAverageStoredNoGuard(Connection conn) throws SQLException {
75+
Statement parentstmt = conn.createStatement();
76+
ResultSet parentMessage = parentstmt.executeQuery("SELECT SUM(IMPORTANCE) AS IMPAVG FROM MAIL");
77+
boolean b = parentMessage.next();
78+
float avgsum = parentMessage.getFloat("IMPAVG"); // State Refinement Error
79+
return avgsum;
80+
}
81+
82+
// Issue #241 branch-sensitivity: legal in the then-branch (onRow), illegal in the else-branch (endRows).
83+
float readAverageVarElse(Connection conn) throws SQLException {
84+
Statement parentstmt = conn.createStatement();
85+
ResultSet parentMessage = parentstmt.executeQuery("SELECT SUM(IMPORTANCE) AS IMPAVG FROM MAIL");
86+
float avgsum = 0.0f;
87+
boolean b = parentMessage.next();
88+
if (b) {
89+
avgsum = parentMessage.getFloat("IMPAVG");
90+
} else {
91+
avgsum = parentMessage.getFloat("IMPAVG"); // State Refinement Error
92+
}
93+
return avgsum;
94+
}
7095
}

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/RefinementTypeChecker.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ private <T> boolean tryStaticFinalConstantRefinement(CtFieldRead<T> fieldRead) {
321321
public <T> void visitCtVariableRead(CtVariableRead<T> variableRead) {
322322
super.visitCtVariableRead(variableRead);
323323
CtVariable<T> varDecl = variableRead.getVariable().getDeclaration();
324+
// Some CtVariableRead forms have no resolvable declaration (e.g. accesses to symbols outside the
325+
// model); with no name there is no context entry to attach, so leave the metadata as-is.
326+
if (varDecl == null)
327+
return;
324328
getPutVariableMetadata(variableRead, varDecl.getSimpleName());
325329
}
326330

@@ -538,8 +542,11 @@ private Predicate getAssignmentRefinement(String name, CtExpression<?> assignmen
538542
}
539543

540544
private Predicate getExpressionRefinements(CtExpression<?> element) throws LJError {
541-
if (element instanceof CtVariableRead<?>) {
542-
// CtVariableRead<?> elemVar = (CtVariableRead<?>) element;
545+
if (element instanceof CtFieldRead<?> fieldRead) {
546+
visitCtFieldRead(fieldRead);
547+
return getRefinement(element);
548+
} else if (element instanceof CtVariableRead<?> varRead) {
549+
visitCtVariableRead(varRead);
543550
return getRefinement(element);
544551
} else if (element instanceof CtBinaryOperator<?>) {
545552
CtBinaryOperator<?> binop = (CtBinaryOperator<?>) element;

0 commit comments

Comments
 (0)