diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Types.java b/src/java.compiler/share/classes/javax/lang/model/util/Types.java
index 8c3cc7ba5d9..93b43c777be 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/Types.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/Types.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,10 @@ import javax.lang.model.type.*;
/**
* Utility methods for operating on types.
*
+ * Where a method returns a type mirror or a collection of type
+ * mirrors, any type mirrors represent types with no type annotations,
+ * unless otherwise indicated.
+ *
*
Compatibility Note: Methods may be added to this interface
* in future releases of the platform.
*
@@ -153,6 +157,8 @@ public interface Types {
* the direct supertypes of a type mirror representing {@code
* java.lang.Object}.
*
+ * Annotations on the direct super types are preserved.
+ *
* @param t the type being examined
* @return the direct supertypes, or an empty list if none
* @throws IllegalArgumentException if given a type for an executable, package, or module
@@ -235,6 +241,8 @@ public interface Types {
/**
* {@return an array type with the specified component type}
*
+ * Annotations on the component type are preserved.
+ *
* @param componentType the component type
* @throws IllegalArgumentException if the component type is not valid for
* an array
@@ -245,6 +253,8 @@ public interface Types {
* {@return a new wildcard type} Either of the wildcard's
* bounds may be specified, or neither, but not both.
*
+ * Annotations on the bounds are preserved.
+ *
* @param extendsBound the extends (upper) bound, or {@code null} if none
* @param superBound the super (lower) bound, or {@code null} if none
* @throws IllegalArgumentException if bounds are not valid
@@ -260,6 +270,8 @@ public interface Types {
* for example, this method may be used to get the
* parameterized type {@code Set}.
*
+ * Annotations on the type arguments are preserved.
+ *
* The number of type arguments must either equal the
* number of the type element's formal type parameters, or must be
* zero. If zero, and if the type element is generic,
@@ -291,6 +303,8 @@ public interface Types {
* to get the type {@code Outer}, and then invoking
* this method.
*
+ * Annotations on the type arguments are preserved.
+ *
* If the containing type is a parameterized type,
* the number of type arguments must equal the
* number of {@code typeElem}'s formal type parameters.
@@ -324,4 +338,29 @@ public interface Types {
* for the given type
*/
TypeMirror asMemberOf(DeclaredType containing, Element element);
+
+ /**
+ * {@return a type mirror equivalent to the argument, but with no annotations}
+ * If the type mirror is a composite type, such as an array type
+ * or a wildcard type, any constitute types, such as the
+ * component type of an array and the type of the bounds of a
+ * wildcard type, also have no annotations, recursively.
+ *
+ *
For most kinds of type mirrors, the result of
+ * {@snippet lang="java" :
+ * types.isSameType(typeMirror, types.stripAnnotations(typeMirror))
+ * }
+ * is {@code true}. The predicate is {@code false} on wildcard
+ * types for {@linkplain #isSameType(TypeMirror, TypeMirror)
+ * reasons discussed elsewhere}.
+ *
+ * @param t the type mirror
+ * @param the specific type of type mirror
+ * @implSpec
+ * The default implementation throws {@code UnsupportedOperationException}.
+ * @since 23
+ */
+ default T stripAnnotations(T t) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
index 533d756d641..1715dfa1451 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java
@@ -340,7 +340,7 @@ public abstract class Type extends AnnoConstruct implements TypeMirror, PoolCons
* it should not be used outside this class.
*/
protected Type typeNoMetadata() {
- return metadata.isEmpty() ? this : baseType();
+ return metadata.isEmpty() ? this : stripMetadata();
}
/**
@@ -426,25 +426,42 @@ public abstract class Type extends AnnoConstruct implements TypeMirror, PoolCons
return accept(stripMetadata, null);
}
//where
+ /**
+ * Note: this visitor only needs to handle cases where
+ * 'contained' types can be annotated. These cases are
+ * described in JVMS 4.7.20.2 and are : classes (for type
+ * parameters and enclosing types), wildcards, and arrays.
+ */
private static final TypeMapping stripMetadata = new StructuralTypeMapping() {
@Override
public Type visitClassType(ClassType t, Void aVoid) {
- return super.visitClassType((ClassType)t.typeNoMetadata(), aVoid);
+ return super.visitClassType((ClassType) dropMetadata(t), aVoid);
}
@Override
public Type visitArrayType(ArrayType t, Void aVoid) {
- return super.visitArrayType((ArrayType)t.typeNoMetadata(), aVoid);
- }
-
- @Override
- public Type visitTypeVar(TypeVar t, Void aVoid) {
- return super.visitTypeVar((TypeVar)t.typeNoMetadata(), aVoid);
+ return super.visitArrayType((ArrayType) dropMetadata(t), aVoid);
}
@Override
public Type visitWildcardType(WildcardType wt, Void aVoid) {
- return super.visitWildcardType((WildcardType)wt.typeNoMetadata(), aVoid);
+ return super.visitWildcardType((WildcardType) dropMetadata(wt), aVoid);
+ }
+
+ @Override
+ public Type visitType(Type t, Void aVoid) {
+ return dropMetadata(t);
+ }
+
+ private static Type dropMetadata(Type t) {
+ if (t.getMetadata().isEmpty()) {
+ return t;
+ }
+ Type baseType = t.baseType();
+ if (baseType.getMetadata().isEmpty()) {
+ return baseType;
+ }
+ return baseType.cloneWithMetadata(List.nil());
}
};
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java
index 08d5e785a3c..1bc5de7f73a 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java
@@ -134,7 +134,7 @@ public class JavacTypes implements javax.lang.model.util.Types {
TypeKind kind = t.getKind();
if (kind == TypeKind.PACKAGE || kind == TypeKind.MODULE)
throw new IllegalArgumentException(t.toString());
- return types.erasure((Type)t).stripMetadataIfNeeded();
+ return types.erasure((Type)t).stripMetadata();
}
@DefinedBy(Api.LANGUAGE_MODEL)
@@ -155,7 +155,7 @@ public class JavacTypes implements javax.lang.model.util.Types {
@DefinedBy(Api.LANGUAGE_MODEL)
public TypeMirror capture(TypeMirror t) {
validateTypeNotIn(t, EXEC_OR_PKG_OR_MOD);
- return types.capture((Type)t).stripMetadataIfNeeded();
+ return types.capture((Type)t).stripMetadata();
}
@DefinedBy(Api.LANGUAGE_MODEL)
@@ -304,6 +304,13 @@ public class JavacTypes implements javax.lang.model.util.Types {
}
+ @DefinedBy(Api.LANGUAGE_MODEL)
+ @SuppressWarnings("unchecked")
+ public T stripAnnotations(T t) {
+ return (T)((Type) t).stripMetadata();
+ }
+
+
private static final Set EXEC_OR_PKG_OR_MOD =
EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE, TypeKind.MODULE);
diff --git a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
index a4f47d0ff2f..cf4de078e9e 100644
--- a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
+++ b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@ import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
+import javax.lang.model.type.*;
import javax.lang.model.util.*;
import static javax.lang.model.SourceVersion.*;
@@ -322,4 +323,73 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
@Override
public boolean isFunctionalInterface(TypeElement type) {return false;}
}
+
+ /**
+ * Vacuous implementation of javax.lang.model.util.Types to aid
+ * in test development. Methods with defaults in the interface are
+ * *not* overridden to allow them to be tested.
+ */
+ public static class VacuousTypes implements Types {
+ public VacuousTypes() {}
+
+ @Override
+ public Element asElement(TypeMirror t) {return null;}
+
+ @Override
+ public boolean isSameType(TypeMirror t1, TypeMirror t2) {return false;}
+
+ @Override
+ public boolean isSubtype(TypeMirror t1, TypeMirror t2) {return false;};
+
+ @Override
+ public boolean isAssignable(TypeMirror t1, TypeMirror t2) {return false;};
+
+ @Override
+ public boolean contains(TypeMirror t1, TypeMirror t2) {return false;};
+
+ @Override
+ public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {return false;}
+
+ @Override
+ public List extends TypeMirror> directSupertypes(TypeMirror t) {return null;}
+
+ @Override
+ public TypeMirror erasure(TypeMirror t) {return null;}
+
+ @Override
+ public TypeElement boxedClass(PrimitiveType p) {return null;}
+
+ @Override
+ public PrimitiveType unboxedType(TypeMirror t) {return null;}
+
+ @Override
+ public TypeMirror capture(TypeMirror t) {return null;}
+
+ @Override
+ public PrimitiveType getPrimitiveType(TypeKind kind) {return null;}
+
+ @Override
+ public NullType getNullType() {return null;}
+
+ @Override
+ public NoType getNoType(TypeKind kind) {return null;}
+
+ @Override
+ public ArrayType getArrayType(TypeMirror componentType) {return null;}
+
+ @Override
+ public WildcardType getWildcardType(TypeMirror extendsBound,
+ TypeMirror superBound) {return null;}
+
+ @Override
+ public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {return null;}
+
+
+ @Override
+ public DeclaredType getDeclaredType(DeclaredType containing,
+ TypeElement typeElem, TypeMirror... typeArgs) {return null;}
+
+ @Override
+ public TypeMirror asMemberOf(DeclaredType containing, Element element) {return null;}
+ }
}
diff --git a/test/langtools/tools/javac/processing/model/util/types/TestAnnotationStripping.java b/test/langtools/tools/javac/processing/model/util/types/TestAnnotationStripping.java
new file mode 100644
index 00000000000..79e970d7173
--- /dev/null
+++ b/test/langtools/tools/javac/processing/model/util/types/TestAnnotationStripping.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8042981
+ * @summary Test if annotations are stripped from the results of Types' methods
+ * @library /tools/javac/lib
+ * @modules java.compiler
+ * jdk.compiler
+ * @build JavacTestingAbstractProcessor TestAnnotationStripping
+ * @compile -processor TestAnnotationStripping -proc:only TestAnnotationStripping.java
+ */
+
+import java.lang.annotation.*;
+import java.util.*;
+import static java.util.Objects.*;
+import javax.annotation.processing.*;
+import javax.lang.model.SourceVersion;
+import static javax.lang.model.SourceVersion.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+import static javax.lang.model.util.ElementFilter.*;
+import static javax.tools.Diagnostic.Kind.*;
+import static javax.tools.StandardLocation.*;
+
+/**
+ * Test if annotations are stripped from the results of Types' methods
+ */
+public class TestAnnotationStripping extends JavacTestingAbstractProcessor {
+ private Types vacuousTypes = new VacuousTypes();
+
+ /**
+ * Check expected behavior on classes and packages.
+ */
+ public boolean process(Set extends TypeElement> annotations,
+ RoundEnvironment roundEnv) {
+ if (!roundEnv.processingOver()) {
+ TypeElement juSetElt = eltUtils.getTypeElement("java.util.Set");
+ TypeElement testElt = elements.getTypeElement("TestAnnotationStripping");
+ TypeElement boxElt = elements.getTypeElement("TestAnnotationStripping.Box");
+
+ TypeMirror expectedAnnotation = eltUtils.getTypeElement("TestTypeAnnotation").asType();
+
+ for (ExecutableElement m :
+ methodsIn(eltUtils.getTypeElement("HostClass").getEnclosedElements())) {
+ /*
+ * The kinds of types include:
+ *
+ * arrays
+ * declared types (classes, interfaces, etc.)
+ * error types
+ * executable types
+ * intersection types
+ * no-type
+ * null type
+ * primitive types
+ * type variable
+ * union type
+ * wildcards
+ *
+ * A subset of these can appear at the return type of
+ * a method. The general methodology is to verify that
+ * types that can appear as return types when
+ * annotated with type annotations appear as specified
+ * as the result of type operations or when new types
+ * are constructed.
+ */
+
+ TypeMirror returnType = m.getReturnType();
+
+ System.err.println("Checking " + returnType);
+
+ testVacuous(returnType);
+ checkDeepEmptyAnnotations(typeUtils.stripAnnotations(returnType));
+
+ checkExpectedTypeAnnotations(returnType, expectedAnnotation);
+
+ // Note: the result of Types.asElement is *not*
+ // checked for its annotations since the return value
+ // is an Element and not a TypeMirror.
+
+ System.err.print("\tcapture()");
+ checkDeepEmptyAnnotations(typeUtils.capture(returnType));
+
+ System.err.print("\terasure()");
+ checkDeepEmptyAnnotations(typeUtils.erasure(returnType));
+
+ System.err.print("\tgetArrayType()");
+ ArrayType arrayType = typeUtils.getArrayType(returnType);
+ checkEmptyAnnotations(arrayType);
+ /*
+ * "Annotations on the component type are preserved."
+ */
+ checkEqualTypeAndAnnotations(returnType, arrayType.getComponentType());
+
+ if (!returnType.getKind().isPrimitive()) {
+ /*
+ * For getWildcardType()
+ * "Annotations on the bounds are preserved."
+ */
+ WildcardType wcType;
+ checkEmptyAnnotations(wcType = typeUtils.getWildcardType(returnType, null));
+ checkEqualTypeAndAnnotations(returnType, wcType.getExtendsBound());
+
+ checkEmptyAnnotations(wcType = typeUtils.getWildcardType(null, returnType));
+ checkEqualTypeAndAnnotations(returnType, wcType.getSuperBound());
+
+ /*
+ * For getDeclaredType()
+ * "Annotations on the type arguments are preserved."
+ */
+ DeclaredType declaredType = typeUtils.getDeclaredType(juSetElt, returnType);
+ checkEqualTypeAndAnnotations(returnType, declaredType.getTypeArguments().get(0));
+
+ // Check both overloads
+ declaredType = typeUtils.getDeclaredType(typeUtils.getDeclaredType(testElt), // outer type
+ boxElt,
+ returnType);
+ checkEqualTypeAndAnnotations(returnType, declaredType.getTypeArguments().get(0));
+ }
+
+ System.out.println(returnType.getAnnotation(TestTypeAnnotation.class));
+ System.out.println(returnType.getAnnotationsByType(TestTypeAnnotation.class).length);
+ TestTypeAnnotation ta = requireNonNull(returnType.getAnnotation(TestTypeAnnotation.class),
+ returnType.toString());
+
+ System.err.println();
+ System.err.println();
+ }
+
+ if (failures > 0)
+ throw new RuntimeException(failures + " failures occured.");
+ }
+ return true;
+ }
+
+ void testVacuous(TypeMirror tm ) {
+ try {
+ var result = vacuousTypes.stripAnnotations(tm);
+ messager.printError("Unexpected non-exceptional result returned " + result);
+ } catch(UnsupportedOperationException uoe) {
+ ; // Expected
+ }
+ }
+
+ private int failures = 0;
+
+ void checkExpectedTypeAnnotations(AnnotatedConstruct ac, TypeMirror expectedAnnotation) {
+ List extends AnnotationMirror> annotations = ac.getAnnotationMirrors();
+ if (annotations.size() != 1) {
+ failures++;
+ System.err.println("\t\t\tUnexpected annotations size: " + annotations.size());
+ } else if (!typeUtils.isSameType(annotations.get(0).getAnnotationType(), expectedAnnotation)) {
+ failures++;
+ System.err.println("\t\t\tUnexpected annotations type: " + annotations);
+ }
+ }
+
+ void checkEmptyAnnotations(AnnotatedConstruct ac) {
+ System.err.println("\t" + ac);
+ if (ac == null)
+ return;
+ else {
+ List extends AnnotationMirror> annotations = ac.getAnnotationMirrors();
+ int count = annotations.size();
+ if (count != 0) {
+ failures++;
+ System.err.println(ac.getClass());
+ System.err.println("\t\t\tUnexpected nonzero annotations size: " + annotations);
+ }
+ }
+ }
+
+ void checkDeepEmptyAnnotations(TypeMirror ac) {
+ System.err.println("\t" + ac);
+ if (ac == null) {
+ return;
+ }
+ new SimpleTypeVisitor14() {
+ @Override
+ protected Void defaultAction(TypeMirror t, Void o) {
+ checkEmptyAnnotations(t);
+ return null;
+ }
+
+ @Override
+ public Void visitArray(ArrayType t, Void o) {
+ scan(t.getComponentType());
+ return super.visitArray(t, o);
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType t, Void o) {
+ scan(t.getEnclosingType());
+ t.getTypeArguments().stream().forEach(this::scan);
+ return super.visitDeclared(t, o);
+ }
+
+ @Override
+ public Void visitTypeVariable(TypeVariable t, Void o) {
+ // the bounds correspond to the type variable declaration, not its use
+ // scan(t.getUpperBound());
+ // scan(t.getLowerBound());
+ return super.visitTypeVariable(t, o);
+ }
+
+ @Override
+ public Void visitWildcard(WildcardType t, Void o) {
+ scan(t.getExtendsBound());
+ scan(t.getSuperBound());
+ return super.visitWildcard(t, o);
+ }
+
+ private void scan(TypeMirror t) {
+ if (t != null) {
+ visit(t);
+ }
+ }
+ }.visit(ac);
+ }
+
+ void checkEqualTypeAndAnnotations(TypeMirror tm1, TypeMirror tm2) {
+ if (!typeUtils.isSameType(tm1, tm2)) {
+ failures++;
+ System.err.printf("Unequal types %s and %s.%n", tm1, tm2);
+ }
+
+ if (!Objects.equals(tm1.getAnnotationMirrors(), tm1.getAnnotationMirrors())) {
+ failures++;
+ System.err.printf("Unequal annotations on and %s.%n", tm1, tm2);
+ }
+ }
+
+ // Nested class to test getDeclaredType overload.
+ class Box {
+ private T contents;
+
+ public Box(T t){
+ contents = t;
+ }
+
+ T value() { return contents;};
+ }
+}
+
+/*
+ * Class to host annotations for testing
+ */
+class HostClass {
+ // Declared type Integer
+ public static @TestTypeAnnotation("foo") Integer foo() {return null;}
+
+ // Primitive type int
+ public static @TestTypeAnnotation("foo2") int foo2() {return 0;}
+
+ public static @TestTypeAnnotation("foo3") String foo3() {return null;}
+
+ // Declared raw type Set
+ public static java.util.@TestTypeAnnotation("foo4")Set foo4() {return null;}
+
+ // Array type
+ public static String @TestTypeAnnotation("foo5")[] foo5() {return null;}
+
+ // Declared type Set with instantiated type parameter
+ public static java.util. @TestTypeAnnotation("foo6") Set < @TestTypeAnnotation("foo7") String> foo6() {return null;}
+
+ // Type variable
+ public static <@TestTypeAnnotation("foo8") T extends @TestTypeAnnotation("foo9") String> @TestTypeAnnotation("foo10") T foo7() {return null;}
+
+ // Declared type including wildcard
+ public static java.util. @TestTypeAnnotation("foo11") Set < @TestTypeAnnotation("foo12") ? extends @TestTypeAnnotation("foo13") Number> foo8() {return null;}
+
+ // Type variable with intersection type
+ public static <@TestTypeAnnotation("foo14") S extends Number & Runnable> @TestTypeAnnotation("foo15") S foo9() {return null;}
+
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE_USE)
+@interface TestTypeAnnotation {
+ String value() default "";
+}