Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c479361
fix viewpoint adaptation
AndrewShf Oct 27, 2023
0b71144
add comments
AndrewShf Nov 13, 2023
3f4b065
remove getannotationsfield().clear()
AndrewShf Nov 13, 2023
7d78a7d
reset annotated type variables in viewpoint adaptation
AndrewShf Nov 13, 2023
06c15e5
add comments
AndrewShf Nov 18, 2023
584d98f
Refine the comment
aosen-xiong Jul 3, 2024
371a623
Test case with expected failure
aosen-xiong Jul 3, 2024
e15a909
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Jul 3, 2024
eb4a8bd
Empty commit for CI
aosen-xiong Jul 3, 2024
48ef745
Assign @B to effectively @A should fail
aosen-xiong Jul 4, 2024
5db3b4c
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Aug 29, 2024
46f8fba
Clear comment how the code change fix the problem
aosen-xiong Sep 6, 2024
6c579aa
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Sep 6, 2024
b35a4ab
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Sep 8, 2024
d1220fd
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
wmdietl Nov 8, 2024
8ef1c18
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
wmdietl Nov 13, 2024
ed7cfbd
Also add unannotated type variable use in test
aosen-xiong Dec 18, 2024
cb70b07
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
wmdietl Jan 3, 2025
9f994a9
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Oct 21, 2025
5977355
Preserve type variable use annotations during viewpoint adaptation
aosen-xiong Jun 15, 2026
b88be07
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Jun 15, 2026
e598c25
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Jun 15, 2026
c61001f
Trigger CI
aosen-xiong Jun 15, 2026
8721ef9
Implement explicit type-variable use semantics
aosen-xiong Jun 23, 2026
58bc0b3
Consolidate type-variable use tests
aosen-xiong Jun 23, 2026
2581b3c
Refine type-variable use requalification
aosen-xiong Jun 23, 2026
34b82d2
Merge branch 'master' into haifeng-viewpointAdpatOnGenerics
aosen-xiong Jun 23, 2026
c8aa74f
Reset type variable mode when clearing stub annotations
aosen-xiong Jun 23, 2026
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 @@ -1560,6 +1560,9 @@ private void clearAnnotations(AnnotatedTypeMirror atype, Type typeDef) {
// Clear existing annotations, which only makes a difference for
// type variables, but doesn't hurt in other cases.
atype.clearAnnotations();
if (atype instanceof AnnotatedTypeVariable) {
((AnnotatedTypeVariable) atype).markAsSubTypeVariableUse();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedNullType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedPrimitiveType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable.TypeVariableUseKind;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedWildcardType;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.plumelib.util.IPair;
Expand Down Expand Up @@ -275,6 +277,10 @@ protected AnnotatedTypeMirror combineAnnotationWithType(
apt.replaceAnnotation(resultAnnotation);
return apt;
} else if (declared.getKind() == TypeKind.TYPEVAR) {
AnnotatedTypeVariable declaredTypeVariable = (AnnotatedTypeVariable) declared;
if (declaredTypeVariable.getTypeVariableUseKind() == TypeVariableUseKind.SUB) {
return declared;
}
if (!isTypeVarExtends) {
isTypeVarExtends = true;
AnnotatedTypeVariable atv = (AnnotatedTypeVariable) declared.shallowCopy();
Expand All @@ -292,6 +298,11 @@ protected AnnotatedTypeMirror combineAnnotationWithType(

AnnotatedTypeMirror result =
AnnotatedTypeCopierWithReplacement.replace(atv, mappings);
AnnotationMirror resultAnnotation =
combineAnnotationWithAnnotation(
receiverAnnotation, extractAnnotationMirror(atv));
result.replaceAnnotation(resultAnnotation);
((AnnotatedTypeVariable) result).markAsConcreteTypeVariableUse(resultAnnotation);

isTypeVarExtends = false;
return result;
Expand Down Expand Up @@ -520,6 +531,12 @@ private AnnotatedTypeMirror getTypeVariableSubstitution(
List<AnnotatedTypeMirror> tas = decltype.getTypeArguments();
// return a copy, as we want to modify the type later.
AnnotatedTypeMirror result = tas.get(foundindex).shallowCopy(true);
if (var.getTypeVariableUseKind() == TypeVariableUseKind.CONCRETE) {
AnnotationMirrorSet concreteAnnotations = var.getConcreteTypeVariableUseAnnotations();
if (!concreteAnnotations.isEmpty()) {
result.replaceAnnotations(concreteAnnotations);
}
}
if (result.getKind() == TypeKind.WILDCARD) {
AnnotatedWildcardType wildcard = (AnnotatedWildcardType) result;
// When substituting an unbounded wildcard for a bounded type variable, the shallow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ public AnnotatedTypeMirror visitTypeVariable(
}

AnnotatedTypeVariable copy = makeOrReturnCopy(original, originalToCopy);
copy.setTypeVariableUseKind(original.getTypeVariableUseKind());
copy.setConcreteTypeVariableUseAnnotations(
original.getConcreteTypeVariableUseAnnotations());

if (original.getUpperBoundField() != null) {
copy.setUpperBound(visit(original.getUpperBoundField(), originalToCopy));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public AnnotatedTypeMirror visitTypeVariable(
original.atypeFactory,
original.isDeclaration());
maybeCopyPrimaryAnnotations(original, copy);
copy.setTypeVariableUseKind(original.getTypeVariableUseKind());
copy.setConcreteTypeVariableUseAnnotations(
original.getConcreteTypeVariableUseAnnotations());
originalToCopy.put(original, copy);

if (original.getUpperBoundField() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -2151,6 +2152,29 @@ private static void checkBound(
*/
public static class AnnotatedTypeVariable extends AnnotatedTypeMirror {

/** How this type-variable use should be substituted. */
public enum TypeVariableUseKind {
/**
* Substitute the full actual type argument. This is the internal representation of
* {@code @Sub E}.
*/
SUB,

/**
* Substitute the actual type argument, but replace its top-level annotations with the
* annotations on this type-variable use. This is the internal representation of
* {@code @Concrete q E}.
*/
CONCRETE
}

/**
* Creates a new {@link AnnotatedTypeVariable}.
*
* @param type the underlying type
* @param atypeFactory the type factory
* @param declaration whether this represents a type-variable declaration
*/
private AnnotatedTypeVariable(
TypeVariable type, AnnotatedTypeFactory atypeFactory, boolean declaration) {
super(type, atypeFactory);
Expand All @@ -2168,16 +2192,133 @@ public TypeKind getKind() {
/** The upper bound of the type variable. */
private AnnotatedTypeMirror upperBound;

/** Whether this represents a type-variable declaration. */
private boolean declaration;

/** How this type-variable use should be substituted. */
private TypeVariableUseKind useKind = TypeVariableUseKind.SUB;

/** The explicitly written annotations for a {@link TypeVariableUseKind#CONCRETE} use. */
private AnnotationMirrorSet concreteTypeVariableUseAnnotations = new AnnotationMirrorSet();

@Override
public boolean isDeclaration() {
return declaration;
}

/**
* Sets whether this type-variable use should be substituted as {@code @Sub E} or
* {@code @Concrete q E}.
*
* @param useKind how this type-variable use should be substituted
*/
public void setTypeVariableUseKind(TypeVariableUseKind useKind) {
this.useKind = useKind;
if (useKind == TypeVariableUseKind.SUB) {
concreteTypeVariableUseAnnotations.clear();
}
}

/**
* Returns whether this type-variable use should be substituted as {@code @Sub E} or
* {@code @Concrete q E}.
*
* @return how this type-variable use should be substituted
*/
public TypeVariableUseKind getTypeVariableUseKind() {
return useKind;
}

/** Marks this type-variable use as {@code @Sub E}. */
public void markAsSubTypeVariableUse() {
setTypeVariableUseKind(TypeVariableUseKind.SUB);
}

/**
* Marks this type-variable use as {@code @Concrete q E}.
*
* @param annotations the explicitly written annotations that are {@code q}
*/
public void markAsConcreteTypeVariableUse(
Collection<? extends AnnotationMirror> annotations) {
AnnotationMirrorSet supportedAnnotations = new AnnotationMirrorSet();
for (AnnotationMirror annotation : annotations) {
AnnotationMirror supportedAnnotation =
canonicalSupportedTypeVariableUseAnnotation(annotation);
if (supportedAnnotation != null) {
supportedAnnotations.add(supportedAnnotation);
}
}
if (!supportedAnnotations.isEmpty()) {
useKind = TypeVariableUseKind.CONCRETE;
concreteTypeVariableUseAnnotations = supportedAnnotations;
}
}

/**
* Marks this type-variable use as {@code @Concrete q E}.
*
* @param annotation the explicitly written annotation that is {@code q}
*/
public void markAsConcreteTypeVariableUse(AnnotationMirror annotation) {
AnnotationMirror supportedAnnotation =
canonicalSupportedTypeVariableUseAnnotation(annotation);
if (supportedAnnotation == null) {
return;
}
useKind = TypeVariableUseKind.CONCRETE;
AnnotationMirror previous =
atypeFactory
.getQualifierHierarchy()
.findAnnotationInSameHierarchy(
concreteTypeVariableUseAnnotations, supportedAnnotation);
if (previous != null) {
concreteTypeVariableUseAnnotations.remove(previous);
}
concreteTypeVariableUseAnnotations.add(supportedAnnotation);
}

/**
* Returns the supported canonical form of {@code annotation}, or null if it is not a
* qualifier for this type factory.
*
* @param annotation an annotation
* @return the supported canonical form of {@code annotation}, or null
*/
private @Nullable AnnotationMirror canonicalSupportedTypeVariableUseAnnotation(
AnnotationMirror annotation) {
if (atypeFactory.isSupportedQualifier(annotation)) {
return annotation;
}
AnnotationMirror canonical = atypeFactory.canonicalAnnotation(annotation);
return atypeFactory.isSupportedQualifier(canonical) ? canonical : null;
}

/**
* Sets the explicitly written annotations for a {@link TypeVariableUseKind#CONCRETE} use.
*
* @param annotations the explicitly written annotations
*/
public void setConcreteTypeVariableUseAnnotations(AnnotationMirrorSet annotations) {
concreteTypeVariableUseAnnotations = new AnnotationMirrorSet(annotations);
}

/**
* Returns the explicitly written annotations for a {@link TypeVariableUseKind#CONCRETE}
* use.
*
* @return the explicitly written annotations for a {@link TypeVariableUseKind#CONCRETE} use
*/
public AnnotationMirrorSet getConcreteTypeVariableUseAnnotations() {
return concreteTypeVariableUseAnnotations;
}

@Override
public void addAnnotation(AnnotationMirror annotation) {
super.addAnnotation(annotation);
if (!isDeclaration()) {
markAsConcreteTypeVariableUse(annotation);
}
fixupBoundAnnotations();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedDeclaredType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.util.AnnotatedTypes;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -111,6 +113,10 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp
} else {
// Add the primary annotation from the variableTree.getModifiers();
AnnotatedTypeMirror innerType = AnnotatedTypes.innerMostType(result);
List<AnnotationMirror> explicitTypeVariableUseAnnotations =
innerType.getKind() == TypeKind.TYPEVAR
? new ArrayList<>()
: Collections.emptyList();
for (AnnotationMirror anno : modifierAnnos) {
// The code here is similar to
// org.checkerframework.framework.util.element.ElementAnnotationUtil.addDeclarationAnnotationsFromElement.
Expand All @@ -120,11 +126,18 @@ public AnnotatedTypeMirror visitVariable(VariableTree variableTree, AnnotatedTyp
.startsWith("org.checkerframework")) {
// Type annotations apply to the innermost type.
innerType.addAnnotation(anno);
if (innerType.getKind() == TypeKind.TYPEVAR) {
explicitTypeVariableUseAnnotations.add(anno);
}
} else {
// Declaration annotations apply to the outer type.
result.addAnnotation(anno);
}
}
if (!explicitTypeVariableUseAnnotations.isEmpty()) {
((AnnotatedTypeVariable) innerType)
.markAsConcreteTypeVariableUse(explicitTypeVariableUseAnnotations);
}
}

AnnotatedTypeMirror lambdaParamType = inferLambdaParamAnnotations(f, result, elt);
Expand Down Expand Up @@ -154,6 +167,17 @@ public AnnotatedTypeMirror visitMethod(MethodTree tree, AnnotatedTypeFactory f)
// This would be similar to
// org.checkerframework.framework.util.element.ElementAnnotationUtil.addDeclarationAnnotationsFromElement.
ElementAnnotationApplier.apply(result, elt, f);
if (result.getReturnType().getKind() == TypeKind.TYPEVAR && tree.getReturnType() != null) {
List<? extends AnnotationTree> explicitAnnotationTrees =
TreeUtils.getExplicitAnnotationTrees(
tree.getModifiers().getAnnotations(), tree.getReturnType());
if (!explicitAnnotationTrees.isEmpty()) {
((AnnotatedTypeVariable) result.getReturnType())
.markAsConcreteTypeVariableUse(
TreeUtils.annotationsFromTypeAnnotationTrees(
explicitAnnotationTrees));
}
}
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ public AnnotatedTypeMirror visitAnnotatedType(AnnotatedTypeTree tree, AnnotatedT
}
} else {
type.addAnnotations(annos);
if (type.getKind() == TypeKind.TYPEVAR && !annos.isEmpty()) {
((AnnotatedTypeVariable) type).markAsConcreteTypeVariableUse(annos);
}
}

return type;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.checkerframework.framework.type;

import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable.TypeVariableUseKind;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.TypesUtils;

import java.util.ArrayList;
Expand Down Expand Up @@ -59,8 +61,9 @@ public AnnotatedTypeMirror substituteWithoutCopyingTypeArguments(
* correct annotations.
*
* <p>To determine what primary annotations are correct for the substitute the following rules
* are used: If the type variable use has a primary annotation then apply that primary
* annotation to the substitute. Otherwise, use the annotations of the argument.
* are used: if the type variable use represents {@code @Sub E}, use the annotations of the
* argument. If it represents {@code @Concrete q E}, apply the type variable use's primary
* annotations to the substitute.
*
* @param argument the argument to declaration (this will be a value in typeParamToArg)
* @param use the use that is being replaced
Expand All @@ -69,8 +72,11 @@ public AnnotatedTypeMirror substituteWithoutCopyingTypeArguments(
protected AnnotatedTypeMirror substituteTypeVariable(
AnnotatedTypeMirror argument, AnnotatedTypeVariable use) {
AnnotatedTypeMirror substitute = argument.deepCopy(true);
if (!use.getAnnotationsField().isEmpty()) {
substitute.replaceAnnotations(use.getAnnotationsField());
if (use.getTypeVariableUseKind() == TypeVariableUseKind.CONCRETE) {
AnnotationMirrorSet concreteAnnotations = use.getConcreteTypeVariableUseAnnotations();
if (!concreteAnnotations.isEmpty()) {
substitute.replaceAnnotations(concreteAnnotations);
}
}
return substitute;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,19 @@ protected boolean shouldBeAnnotated(AnnotatedTypeMirror type) {
protected void addAnnotation(AnnotatedTypeMirror type, AnnotationMirror qual) {
// Add the default annotation, but only if no other annotation is present.
if (type.getKind() != TypeKind.EXECUTABLE) {
type.addMissingAnnotation(qual);
if (type instanceof AnnotatedTypeVariable) {
AnnotatedTypeVariable typeVariable = (AnnotatedTypeVariable) type;
AnnotatedTypeVariable.TypeVariableUseKind useKind =
typeVariable.getTypeVariableUseKind();
AnnotationMirrorSet concreteAnnotations =
new AnnotationMirrorSet(
typeVariable.getConcreteTypeVariableUseAnnotations());
type.addMissingAnnotation(qual);
typeVariable.setTypeVariableUseKind(useKind);
typeVariable.setConcreteTypeVariableUseAnnotations(concreteAnnotations);
} else {
type.addMissingAnnotation(qual);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ protected void handleTargeted(List<TypeCompound> targeted)
methodType.getReceiverType(), targetTypeToAnno.get(TargetType.METHOD_RECEIVER));
ElementAnnotationUtil.annotateViaTypeAnnoPosition(
methodType.getReturnType(), targetTypeToAnno.get(TargetType.METHOD_RETURN));
if (methodType.getReturnType() instanceof AnnotatedTypeVariable
&& !targetTypeToAnno.get(TargetType.METHOD_RETURN).isEmpty()) {
((AnnotatedTypeVariable) methodType.getReturnType())
.markAsConcreteTypeVariableUse(targetTypeToAnno.get(TargetType.METHOD_RETURN));
}
applyThrowsAnnotations(targetTypeToAnno.get(TargetType.THROWS));

if (!unmatched.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public void extractAndApply() throws UnexpectedAnnotationLocationException {
for (Attribute.TypeCompound annotation : typeVarAnnotations) {
typeVariable.replaceAnnotation(annotation);
}
if (!typeVarAnnotations.isEmpty()) {
typeVariable.markAsConcreteTypeVariableUse(typeVarAnnotations);
}
}

/**
Expand Down
Loading
Loading