Skip to content

Commit de56949

Browse files
Use a fresh receiver for a call on a call result
In a.get().m(), when get() has no return refinement its TARGET metadata is its own receiver (a), not the value it returns, and m() was checked against a: "Argument Mismatch Error ... expected Door, got Holder". The receiver is now a fresh value of get()'s type, in an unknown state, created once per call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 5827a5b commit de56949

3 files changed

Lines changed: 78 additions & 0 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.StateRefinement;
4+
import liquidjava.specification.StateSet;
5+
6+
@StateSet({"open", "closed"})
7+
class CorrectCallResultDoor {
8+
@StateRefinement(to = "open(this)")
9+
public CorrectCallResultDoor() {}
10+
11+
@StateRefinement(from = "open(this)", to = "closed(this)")
12+
public void close() {}
13+
14+
@StateRefinement(to = "closed(this)")
15+
public void slam() {}
16+
}
17+
18+
class CorrectCallResultHolder {
19+
private final CorrectCallResultDoor door = new CorrectCallResultDoor();
20+
21+
CorrectCallResultDoor get() {
22+
return door;
23+
}
24+
}
25+
26+
public class CorrectCallResultReceiver {
27+
static void use(CorrectCallResultHolder holder) {
28+
holder.get().slam();
29+
}
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.StateRefinement;
4+
import liquidjava.specification.StateSet;
5+
6+
@StateSet({"open", "closed"})
7+
class ErrorCallResultDoor {
8+
@StateRefinement(to = "open(this)")
9+
public ErrorCallResultDoor() {}
10+
11+
@StateRefinement(from = "open(this)", to = "closed(this)")
12+
public void close() {}
13+
}
14+
15+
class ErrorCallResultHolder {
16+
private final ErrorCallResultDoor door = new ErrorCallResultDoor();
17+
18+
ErrorCallResultDoor get() {
19+
return door;
20+
}
21+
}
22+
23+
public class ErrorCallResultReceiver {
24+
static void use(ErrorCallResultHolder holder) {
25+
holder.get().close(); // Expect: State Refinement Error
26+
}
27+
}

‎liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/object_checkers/AuxStateHandler.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,21 @@ public static String prepareInvocationTarget(TypeChecker tc, CtElement target2,
848848
newCall.putMetadata(Keys.RECEIVER, receiver);
849849
invocation.putMetadata(Keys.TARGET, receiver);
850850
return name;
851+
} else if (target2 instanceof CtInvocation<?> call
852+
&& call.getMetadata(Keys.RECEIVER)instanceof RefinedVariable receiver) {
853+
invocation.putMetadata(Keys.TARGET, receiver);
854+
return receiver.getName();
855+
} else if (target2 instanceof CtInvocation<?> call && call.getMetadata(Keys.TARGET) != null
856+
&& !isReturnValue(call)) {
857+
// `a.get().method()` where get() has no return refinement: the call's TARGET is its own receiver (a), not
858+
// the value it returns, so the receiver is a fresh value of the call's type, in a state not known here
859+
String name = String.format(Formats.FRESH, tc.getContext().getCounter());
860+
CtTypeReference<?> type = call.getType();
861+
RefinedVariable receiver = tc.getContext().addInstanceToContext(name, type, new Predicate(), call);
862+
receiver.addSuperTypes(type.getSuperclass(), type.getSuperInterfaces());
863+
call.putMetadata(Keys.RECEIVER, receiver);
864+
invocation.putMetadata(Keys.TARGET, receiver);
865+
return name;
851866
} else if (target2.getMetadata(Keys.TARGET) != null) {
852867
VariableInstance target2Vi = (VariableInstance) target2.getMetadata(Keys.TARGET);
853868
Optional<Variable> v = target2Vi.getParent();
@@ -857,6 +872,12 @@ public static String prepareInvocationTarget(TypeChecker tc, CtElement target2,
857872
return null;
858873
}
859874

875+
/** Whether a call's TARGET metadata is the value it returns (set when its return refinement is applied). */
876+
private static boolean isReturnValue(CtInvocation<?> call) {
877+
return call.getMetadata(Keys.TARGET)instanceof VariableInstance vi
878+
&& vi.getName().equals(call.getMetadata(Keys.RETURN_VAR_NAME));
879+
}
880+
860881
static VariableInstance getTarget(CtElement invocation) {
861882
if (invocation.getMetadata(Keys.TARGET) != null) {
862883
return (VariableInstance) invocation.getMetadata(Keys.TARGET);

0 commit comments

Comments
 (0)