Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WPI: decision on whether to infer a type for a type variable should be made based on explicit annotations #5618

Merged
merged 11 commits into from
Mar 1, 2023
Merged
1 change: 1 addition & 0 deletions checker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ task ainferNullnessGenerateJaifs(type: Test) {

// JAIF-based WPI does not infer annotations on uses of type variables correctly.
delete('tests/ainfer-nullness/annotated/TwoCtorGenericAbstract.java')
delete('tests/ainfer-nullness/annotated/TypeVarReturnAnnotated.java')

// Inserting annotations from .jaif files in-place.
String jaifsDir = 'tests/ainfer-nullness/inference-output';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public AinferNullnessAjavaTest(List<File> testFiles) {
NullnessChecker.class,
"ainfer-nullness/non-annotated",
"-Ainfer=ajava",
// "-Aajava=tests/ainfer-nullness/input-annotation-files/",
"-Aajava=tests/ainfer-nullness/input-annotation-files/",
"-Awarns");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ public class AinferNullnessJaifsTest extends CheckerFrameworkPerDirectoryTest {
* @param testFiles the files containing test code, which will be type-checked
*/
public AinferNullnessJaifsTest(List<File> testFiles) {
super(testFiles, NullnessChecker.class, "nullness", "-Ainfer=jaifs", "-Awarns");
super(
testFiles,
NullnessChecker.class,
"nullness",
"-Ainfer=jaifs",
"-Awarns",
"-Aajava=tests/ainfer-nullness/input-annotation-files/");
}

@Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Code like this caused WPI to loop infinitely, because the annotation on the return type
// was only sometimes inferred. Based on an example from
// https://github.com/dd482IT/cache2k-wpi/blob/0eaa156bdecd617b2aa4c745d0f8844a32609697/cache2k-api/src/main/java/org/cache2k/config/ToggleFeature.java#L73
@org.checkerframework.framework.qual.AnnotatedFor("org.checkerframework.checker.nullness.NullnessChecker")
public class TypeVarReturnAnnotated {

public static <T extends TypeVarReturnAnnotated> @org.checkerframework.checker.initialization.qual.FBCBottom @org.checkerframework.checker.nullness.qual.Nullable T extract() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Code like this caused WPI to loop infinitely, because the annotation on the return type
// was only sometimes inferred. Based on an example from
// https://github.com/dd482IT/cache2k-wpi/blob/0eaa156bdecd617b2aa4c745d0f8844a32609697/cache2k-api/src/main/java/org/cache2k/config/ToggleFeature.java#L73

public class TypeVarReturnAnnotated {
public static <T extends TypeVarReturnAnnotated> T extract() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -857,10 +857,19 @@ protected void updateAnnotationSet(

// For type variables, infer primary annotations for field type use locations, but
// for other locations only infer primary annotations if they are a super type of the upper
// bound.
// bound of declaration of the type variable.
if (defLoc != TypeUseLocation.FIELD && lhsATM instanceof AnnotatedTypeVariable) {
AnnotatedTypeVariable lhsTV = (AnnotatedTypeVariable) lhsATM;
AnnotationMirrorSet upperAnnos =
((AnnotatedTypeVariable) lhsATM).getUpperBound().getEffectiveAnnotations();
new AnnotationMirrorSet(lhsTV.getUpperBound().getEffectiveAnnotations());
// If the type variable has a primary annotation then it is copied to the upper bound of the
// use of the type variable, so remove any annotations that are the same as a primary
// annotations.
// TODO: this is kind of a hack. It would be better to look up the type variable declaration
// to find the upper bound when there are primary annotations.
for (AnnotationMirror primary : lhsTV.getAnnotations()) {
upperAnnos.remove(primary);
}
// If the inferred type is a subtype of the upper bounds of the
// current type in the source code, do nothing.
if (upperAnnos.size() == rhsATM.getAnnotations().size()
Expand Down