Skip to content

Commit

Permalink
Wildcards are only not equal if they are provably distinct
Browse files Browse the repository at this point in the history
  • Loading branch information
smillst authored Jul 1, 2023
1 parent dd74b9b commit 1823e66
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 26 deletions.
20 changes: 20 additions & 0 deletions checker/tests/tainting/TaintingIssue6025.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import org.checkerframework.checker.tainting.qual.Untainted;

public class TaintingIssue6025 {

public interface A<T> {}

private static final class B<T> {}

public interface C<T1 extends A<?>, T2 extends A<?>> {}

private final B<C<? extends A<?>, ? extends A<?>>> one = new B<>();
private final B<C<? extends A<?>, ? extends A<?>>> two = new B<>();

void f(boolean b) {
// :: error: (assignment)
B<C<@Untainted ?, ?>> three1 = one;
// :: error: (assignment)
B<C<?, @Untainted ?>> three = b ? two : one;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,37 +206,56 @@ public Boolean visitDeclared_Declared(
if (!arePrimeAnnosEqual(type1, type2)) {
return false;
}

// Prevent infinite recursion e.g. in Issue1587b
visitHistory.put(type1, type2, currentTop, true);

boolean result = visitTypeArgs(type1, type2);
visitHistory.put(type1, type2, currentTop, result);
return result;
}

/**
* A helper class for visitDeclared_Declared. There are subtypes of DefaultTypeHierarchy that need
* to customize the handling of type arguments. This method provides a convenient extension point.
*/
protected boolean visitTypeArgs(AnnotatedDeclaredType type1, AnnotatedDeclaredType type2) {

// TODO: ANYTHING WITH RAW TYPES? SHOULD WE HANDLE THEM LIKE DefaultTypeHierarchy, i.e. use
// ignoreRawTypes
List<? extends AnnotatedTypeMirror> type1Args = type1.getTypeArguments();
List<? extends AnnotatedTypeMirror> type2Args = type2.getTypeArguments();

if (type1Args.isEmpty() && type2Args.isEmpty()) {
return true;
List<AnnotatedTypeMirror> type1Args = type1.getTypeArguments();
List<AnnotatedTypeMirror> type2Args = type2.getTypeArguments();

// Capture the types because the wildcards are only not equal if they are provably distinct.
// Provably distinct is computed using the captured and erased upper bounds of wildcards.
// See JLS 4.5.1. Type Arguments of Parameterized Types.
AnnotatedTypeFactory atypeFactory = type1.atypeFactory;
AnnotatedDeclaredType capturedType1 =
(AnnotatedDeclaredType) atypeFactory.applyCaptureConversion(type1);
AnnotatedDeclaredType capturedType2 =
(AnnotatedDeclaredType) atypeFactory.applyCaptureConversion(type2);
visitHistory.put(capturedType1, capturedType2, currentTop, true);

List<AnnotatedTypeMirror> capturedType1Args = capturedType1.getTypeArguments();
List<AnnotatedTypeMirror> capturedType2Args = capturedType2.getTypeArguments();
boolean result = true;
for (int i = 0; i < type1.getTypeArguments().size(); i++) {
AnnotatedTypeMirror type1Arg = type1Args.get(i);
AnnotatedTypeMirror type2Arg = type2Args.get(i);
Boolean pastResultTA = visitHistory.get(type1Arg, type2Arg, currentTop);
if (pastResultTA != null) {
result = pastResultTA;
} else {
if (type1Arg.getKind() != TypeKind.WILDCARD || type2Arg.getKind() != TypeKind.WILDCARD) {
result = areEqual(type1Arg, type2Arg);
} else {
AnnotatedWildcardType wildcardType1 = (AnnotatedWildcardType) type1Arg;
AnnotatedWildcardType wildcardType2 = (AnnotatedWildcardType) type2Arg;
if (type1.atypeFactory.ignoreUninferredTypeArguments
&& (wildcardType1.isUninferredTypeArgument()
|| wildcardType2.isUninferredTypeArgument())) {
result = true;
} else {
AnnotatedTypeMirror capturedType1Arg = capturedType1Args.get(i);
AnnotatedTypeMirror capturedType2Arg = capturedType2Args.get(i);
result = areEqual(capturedType1Arg.getErased(), capturedType2Arg.getErased());
}
}
}
if (!result) {
break;
}
}

if (type1Args.size() == type2Args.size()) {
return areAllEqual(type1Args, type2Args);
} else {
throw new BugInCF(
"Mismatching type argument sizes:%n type 1: %s (%d)%n type 2: %s (%d)",
type1, type1Args.size(), type2, type2Args.size());
}
visitHistory.put(capturedType1, capturedType2, currentTop, result);
visitHistory.put(type1, type2, currentTop, result);
return result;
}

/**
Expand Down
16 changes: 16 additions & 0 deletions framework/tests/all-systems/Issue6025.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
public class Issue6025 {

public interface A<T> {}

private static final class B<T> {}

public interface C<T1 extends A<?>, T2 extends A<?>> {}

private final B<C<? extends A<?>, ? extends A<?>>> one = new B<>();
private final B<C<? extends A<?>, ? extends A<?>>> two = new B<>();

void f(boolean b) {
B<C<?, ?>> three1 = one;
B<C<?, ?>> three = b ? two : one;
}
}

0 comments on commit 1823e66

Please sign in to comment.