Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
Expand All @@ -13,6 +15,7 @@
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
Expand All @@ -30,6 +33,7 @@
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.framework.util.AnnotationMirrorMap;
import org.checkerframework.framework.util.AnnotationMirrorSet;
import org.checkerframework.framework.util.QualifierPolymorphismUtil;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
Expand Down Expand Up @@ -184,6 +188,41 @@ public void resolve(MethodInvocationTree tree, AnnotatedExecutableType type) {
atypeFactory.getReceiverType(tree), type.getReceiverType()));
}

// receiver-sensitive poly resolution: resolve return value with assignment context l-value
TreePath path = atypeFactory.getPath(tree);
if (path != null) {
AnnotatedTypeMirror assignmentContext =
QualifierPolymorphismUtil.assignedTo(atypeFactory, path);

if (assignmentContext != null) {
// get erased subtype relation
TypeMirror javaLhstype =
atypeFactory.types.erasure(assignmentContext.getUnderlyingType());
TypeMirror javaReturntype =
atypeFactory.types.erasure(type.getReturnType().getUnderlyingType());
// primitive types need special care
boolean polyIsSub =
javaReturntype instanceof Type.JCPrimitiveType
|| atypeFactory.types.isSubtype(javaReturntype, javaLhstype);

boolean polyIsSuper =
!(javaReturntype instanceof Type.ArrayType)
&& // isSubtype cannot handle this
atypeFactory.types.isSubtype(javaLhstype, javaReturntype);

// only perform receiver-sensitive poly resolution when return type is erased
// subtype of lhs
if (polyIsSub || polyIsSuper) { // lhs and return type are comparable
instantiationMapping =
collector.reduceWithUpperBounds(
instantiationMapping,
collector.visit(
// Actual assignment lhs type
assignmentContext, type.getReturnType(), polyIsSub));
}
}
}

if (instantiationMapping != null && !instantiationMapping.isEmpty()) {
replacer.visit(type, instantiationMapping);
} else {
Expand Down Expand Up @@ -353,6 +392,10 @@ protected abstract AnnotationMirror combine(
protected abstract void replace(
AnnotatedTypeMirror type, AnnotationMirrorMap<AnnotationMirror> replacements);

private interface PolyCollectorAnnotationCombiner {
AnnotationMirror combine(AnnotationMirror poly, AnnotationMirror a1, AnnotationMirror a2);
}

/**
* A helper class that resolves the polymorphic qualifiers with the most restrictive qualifier.
* It returns a mapping from the polymorphic qualifier to the substitution for that qualifier.
Expand Down Expand Up @@ -385,10 +428,10 @@ protected AnnotationMirrorMap<AnnotationMirror> scanWithNull(
return new AnnotationMirrorMap<>();
}

@Override
public AnnotationMirrorMap<AnnotationMirror> reduce(
private AnnotationMirrorMap<AnnotationMirror> genericReduce(
AnnotationMirrorMap<AnnotationMirror> r1,
AnnotationMirrorMap<AnnotationMirror> r2) {
AnnotationMirrorMap<AnnotationMirror> r2,
PolyCollectorAnnotationCombiner combiner) {

if (r1 == null || r1.isEmpty()) {
return r2;
Expand All @@ -408,7 +451,7 @@ public AnnotationMirrorMap<AnnotationMirror> reduce(
if (a2Annos == null) {
res.put(polyQual, a1Annos);
} else {
res.put(polyQual, combine(polyQual, a1Annos, a2Annos));
res.put(polyQual, combiner.combine(polyQual, a1Annos, a2Annos));
}
r2remain.remove(polyQual);
}
Expand All @@ -418,6 +461,43 @@ public AnnotationMirrorMap<AnnotationMirror> reduce(
return res;
}

@Override
protected AnnotationMirrorMap<AnnotationMirror> reduce(
AnnotationMirrorMap<AnnotationMirror> r1,
AnnotationMirrorMap<AnnotationMirror> r2) {
return genericReduce(r1, r2, AbstractQualifierPolymorphism.this::combine);
}

/**
* Reduces lower bounds r1 with upper bounds r2.
*
* @param r1 a map from {@link AnnotationMirror} to {@link AnnotationMirror}
* @param r2 a map from {@link AnnotationMirror} to {@link AnnotationMirror}
* @return the reduced {@link AnnotationMirrorMap}
*/
private AnnotationMirrorMap<AnnotationMirror> reduceWithUpperBounds(
AnnotationMirrorMap<AnnotationMirror> r1,
AnnotationMirrorMap<AnnotationMirror> r2) {

// Note: a different logic is needed to reduce with assignment context.
// For arguments, @Poly is on l-value of pseudo assignment.
// But when resolving with assignment, @Poly is on r-value of assignment.
return genericReduce(
r1,
r2,
(poly, a1, a2) -> {
AnnotationMirror subres = null;
for (AnnotationMirror top : topQuals) {
if (qualHierarchy.isSubtype(a1, top)) {
subres = a1;
} else if (qualHierarchy.isSubtype(a2, top)) {
subres = a2;
}
}
return subres;
});
}

/**
* Calls {@link #visit(AnnotatedTypeMirror, AnnotatedTypeMirror)} for each type in {@code
* types}.
Expand Down Expand Up @@ -458,10 +538,12 @@ private AnnotationMirrorMap<AnnotationMirror> visit(
*
* @param type AnnotateTypeMirror used to find instantiations
* @param polyType AnnotatedTypeMirror that may have polymorphic qualifiers
* @param polyIsSub boolean indicates whether {@code polyType} is the subtype of {@code
* type}
* @return a mapping of polymorphic qualifiers to their instantiations
*/
private AnnotationMirrorMap<AnnotationMirror> visit(
AnnotatedTypeMirror type, AnnotatedTypeMirror polyType) {
AnnotatedTypeMirror type, AnnotatedTypeMirror polyType, boolean polyIsSub) {
if (type.getKind() == TypeKind.NULL) {
return mapQualifierToPoly(type, polyType);
}
Expand All @@ -477,19 +559,39 @@ private AnnotationMirrorMap<AnnotationMirror> visit(

switch (polyType.getKind()) {
case WILDCARD:
AnnotatedTypeMirror asSuper =
AnnotatedTypes.asSuper(atypeFactory, wildcardType, polyType);
return visit(asSuper, polyType, null);
case TYPEVAR:
return mapQualifierToPoly(wildcardType.getExtendsBound(), polyType);
if (polyIsSub) {
return visit(
wildcardType,
AnnotatedTypes.asSuper(atypeFactory, polyType, wildcardType),
null);
} else {
return visit(
AnnotatedTypes.asSuper(atypeFactory, wildcardType, polyType),
polyType,
null);
}
default:
return mapQualifierToPoly(wildcardType.getExtendsBound(), polyType);
}
}

AnnotatedTypeMirror asSuper = AnnotatedTypes.asSuper(atypeFactory, type, polyType);
if (polyIsSub) {
return visit(type, AnnotatedTypes.asSuper(atypeFactory, polyType, type), null);
} else {
return visit(AnnotatedTypes.asSuper(atypeFactory, type, polyType), polyType, null);
}
}

return visit(asSuper, polyType, null);
/**
* A helper method of {@link #visit(AnnotatedTypeMirror, AnnotatedTypeMirror, boolean)}.
*
* @param type AnnotateTypeMirror used to find instantiations
* @param polyType AnnotatedTypeMirror that may have polymorphic qualifiers
* @return {@code visit(type, polyType, false)}
*/
private AnnotationMirrorMap<AnnotationMirror> visit(
AnnotatedTypeMirror type, AnnotatedTypeMirror polyType) {
return visit(type, polyType, false);
}

@Override
Expand Down
Loading