Skip to content

Commit a349bed

Browse files
d367wangtxiang61
andauthored
Create refinement slots for variable declarations with initialization (#287)
Co-authored-by: txiang61 <[email protected]>
1 parent b797dbe commit a349bed

File tree

5 files changed

+102
-30
lines changed

5 files changed

+102
-30
lines changed

src/checkers/inference/InferenceVisitor.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -448,11 +448,17 @@ protected void commonAssignmentCheck(Tree varTree, ExpressionTree valueExp,
448448
return;
449449
}
450450

451-
// commonAssignmentCheck eventually create an equality constraint between varTree and valueExp.
452-
// For inference, we need this constraint to be between the RefinementVariable and the value.
453-
// Refinement variables come from flow inference, so we need to call getAnnotatedType instead of getDefaultedAnnotatedType
454451
AnnotatedTypeMirror var;
455-
if (this.infer) {
452+
if (infer && varTree.getKind() == Kind.TYPE_PARAMETER) {
453+
// When the lhs is a type variable, due to the partially resolved issue
454+
// https://github.com/opprop/checker-framework-inference/issues/316,
455+
// currently it is still "commonAssignmentCheck" who creates the refinement constraint.
456+
// We need this constraint to be between the refinement variable and the rhs value.
457+
// Since refinement variables come from flow inference, we must call "getAnnotatedType"
458+
// instead of "getAnnotatedTypeLhs".
459+
// TODO: use "getAnnotatedTypeLhs" uniformly when issue 316 is completely resolved.
460+
// (In that way, the refinement constraints are uniformly created during dataflow analysis, so
461+
// "commonAssignmentCheck" only needs to enforce the general type rule regarding assignment.)
456462
var = atypeFactory.getAnnotatedType(varTree);
457463
} else {
458464
var = atypeFactory.getAnnotatedTypeLhs(varTree);

src/checkers/inference/dataflow/InferenceTransfer.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ public TransferResult<CFValue, CFStore> visitAssignment(AssignmentNode assignmen
137137
assert false;
138138
}
139139

140+
if (assignmentNode.getTarget() instanceof LocalVariableNode
141+
&& atm.getKind() != TypeKind.TYPEVAR) {
142+
// Get the rhs value and pass it to slot manager to generate the equality constraint
143+
// as "refinement variable == rhs value"
144+
Tree valueTree = assignmentNode.getExpression().getTree();
145+
AnnotatedTypeMirror valueType = typeFactory.getAnnotatedType(valueTree);
146+
return createRefinementVar(assignmentNode.getTarget(), assignmentNode.getTree(), store, atm, valueType);
147+
}
148+
140149
return storeDeclaration(lhs, (VariableTree) assignmentNode.getTree(), store, typeFactory);
141150

142151
} else if (lhs.getTree().getKind() == Tree.Kind.IDENTIFIER
@@ -217,6 +226,14 @@ private TransferResult<CFValue, CFStore> createRefinementVar(Node lhs,
217226

218227
SlotManager slotManager = getInferenceAnalysis().getSlotManager();
219228
Slot slotToRefine = slotManager.getSlot(atm);
229+
230+
// Make sure the refinement slot is created on the declared type
231+
if (slotToRefine instanceof RefinementVariableSlot) {
232+
// Getting the declared type of a RefinementVariableSlot
233+
// getRefined() always returns the slot of the declared type value
234+
slotToRefine = ((RefinementVariableSlot)slotToRefine).getRefined();
235+
}
236+
220237
Slot refineTo = slotManager.getSlot(valueAtm);
221238

222239
logger.fine("Creating refinement variable for tree: " + assignmentTree);

src/checkers/inference/dataflow/InferenceValue.java

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -119,31 +119,54 @@ public CFValue mostSpecific(CFValue other, CFValue backup) {
119119
*
120120
*/
121121
public CFValue mostSpecificFromSlot(final Slot thisSlot, final Slot otherSlot, final CFValue other, final CFValue backup) {
122-
// TODO: refactor this method
123-
if (thisSlot instanceof VariableSlot && otherSlot instanceof VariableSlot) {
124-
if (thisSlot.isMergedTo(otherSlot)) {
125-
return other;
126-
} else if (otherSlot.isMergedTo(thisSlot)) {
127-
return this;
128-
} else if (thisSlot instanceof RefinementVariableSlot
129-
&& ((RefinementVariableSlot) thisSlot).getRefined().equals(otherSlot)) {
130-
131-
return this;
132-
} else if (otherSlot instanceof RefinementVariableSlot
133-
&& ((RefinementVariableSlot) otherSlot).getRefined().equals(thisSlot)) {
134-
135-
return other;
136-
} else {
137-
// Check if one of these has refinement variables that were merged to the other.
138-
for (RefinementVariableSlot slot : ((VariableSlot) thisSlot).getRefinedToSlots()) {
139-
if (slot.isMergedTo(otherSlot)) {
140-
return other;
141-
}
122+
if (thisSlot.isMergedTo(otherSlot)) {
123+
return other;
124+
}
125+
126+
if (otherSlot.isMergedTo(thisSlot)) {
127+
return this;
128+
}
129+
130+
if (thisSlot instanceof RefinementVariableSlot
131+
&& ((RefinementVariableSlot) thisSlot).getRefined().equals(otherSlot)) {
132+
return this;
133+
}
134+
135+
if (otherSlot instanceof RefinementVariableSlot
136+
&& ((RefinementVariableSlot) otherSlot).getRefined().equals(thisSlot)) {
137+
return other;
138+
}
139+
140+
if (thisSlot instanceof RefinementVariableSlot
141+
&& otherSlot instanceof RefinementVariableSlot
142+
&& ((RefinementVariableSlot) thisSlot).getRefined().equals(((RefinementVariableSlot) otherSlot).getRefined())) {
143+
// This happens when a local variable is declared with initializer, and is reassigned afterwards. E.g.
144+
// Object obj = null;
145+
// obj = new Object();
146+
// return obj;
147+
// Suppose RefinementVar(1) is created at variable declaration, RefinementVar(2) is created at re-assignment.
148+
// Then at the return point, when getting the most specific type of obj,
149+
// "thisSlot" is RefinementVar(1), coming from "getValueFromFactory".
150+
// "otherSlot" is RefinementVar(2), coming from the store value.
151+
// The store value is more precise, so we choose "other" as the most specific type.
152+
assert thisSlot.getId() <= otherSlot.getId();
153+
return other;
154+
}
155+
156+
if (thisSlot instanceof VariableSlot) {
157+
// Check if one of these has refinement variables that were merged to the other.
158+
for (RefinementVariableSlot slot : ((VariableSlot) thisSlot).getRefinedToSlots()) {
159+
if (slot.isMergedTo(otherSlot)) {
160+
return other;
142161
}
143-
for (RefinementVariableSlot slot : ((VariableSlot) otherSlot).getRefinedToSlots()) {
144-
if (slot.isMergedTo(thisSlot)) {
145-
return this;
146-
}
162+
}
163+
}
164+
165+
if (otherSlot instanceof VariableSlot) {
166+
// Same as above
167+
for (RefinementVariableSlot slot : ((VariableSlot) otherSlot).getRefinedToSlots()) {
168+
if (slot.isMergedTo(thisSlot)) {
169+
return this;
147170
}
148171
}
149172
}

src/checkers/inference/solver/backend/AbstractFormatTranslator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@
5353
* For example, {@link checkers.inference.solver.backend.maxsat.encoder.MaxSATConstraintEncoderFactory}
5454
* depends on {@link checkers.inference.solver.backend.maxsat.MaxSatFormatTranslator#typeToInt typeToInt}
5555
* filed in {@link checkers.inference.solver.backend.maxsat.MaxSatFormatTranslator}. So only after those
56-
* dependant fields are initialized in subclasses constructors, encoders can be then initialized.
56+
* dependent fields are initialized in subclasses constructors, encoders can be then initialized.
5757
* Calling {@link #finishInitializingEncoders()} at the last step of initialization makes sure all the
58-
* dependant fields are already initialized.
58+
* dependent fields are already initialized.
5959
* <p>
6060
* In terms of "last step of initialization", different {@code FormatTranslator}s have different definitions.
6161
* For {@link checkers.inference.solver.backend.maxsat.MaxSatFormatTranslator} and
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import ostrusted.qual.OsUntrusted;
2+
import ostrusted.qual.OsTrusted;
3+
4+
public class Refinement {
5+
6+
void foo(Object in1, Object in2) {
7+
Object o = in1;
8+
// :: fixable-error: (argument.type.incompatible)
9+
bar(o);
10+
11+
o = in2;
12+
// :: fixable-error: (argument.type.incompatible)
13+
bar(o);
14+
}
15+
16+
void bar(@OsTrusted Object in) {}
17+
18+
19+
@OsTrusted Object m(@OsUntrusted Object untrusted, Object trusted) {
20+
Object obj = untrusted;
21+
obj = trusted;
22+
// :: fixable-error: (return.type.incompatible)
23+
return obj;
24+
}
25+
}
26+

0 commit comments

Comments
 (0)