Skip to content

Commit

Permalink
8202469: (ann) Type annotations on type variable bounds that are also…
Browse files Browse the repository at this point in the history
… type variables are lost

Reviewed-by: jfranck
  • Loading branch information
raphw committed Mar 18, 2020
1 parent af28093 commit 9d7f8bc
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2018, 2020, 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
Expand Down Expand Up @@ -265,21 +265,27 @@ private static <D extends GenericDeclaration> AnnotatedType[] parseAnnotatedBoun
int startIndex = 0;
AnnotatedType[] res = new AnnotatedType[bounds.length];

// Adjust bounds index
// According to JVMS 4.3.4, the first bound of a parameterized type is
// taken to be Object, if no explicit class bound is specified. As a
// consequence, the first interface's bound is always 1. To account for
// a potential mismatch between the indices of the bounds array that only
// contains explicit bounds and the actual bound's index, the startIndex
// is set to 1 if no explicit class type bound was set.
//
// Figure out if the type annotations for this bound starts with 0
// or 1. The spec says within a bound the 0:th type annotation will
// always be on an bound of a Class type (not Interface type). So
// if the programmer starts with an Interface type for the first
// (and following) bound(s) the implicit Object bound is considered
// the first (that is 0:th) bound and type annotations start on
// index 1.
// This is achieved by examining the first element of the bound to be a
// class or an interface, if such a bound exists. Since a bound can itself
// be a parameterized type, the bound's raw type must be investigated,
// if applicable.
if (bounds.length > 0) {
Type b0 = bounds[0];
if (!(b0 instanceof Class<?>)) {
startIndex = 1;
} else {
Class<?> c = (Class<?>)b0;
if (b0 instanceof Class<?>) {
Class<?> c = (Class<?>) b0;
if (c.isInterface()) {
startIndex = 1;
}
} else if (b0 instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) b0;
Class<?> c = (Class<?>) p.getRawType();
if (c.isInterface()) {
startIndex = 1;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2020, 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 8202469
* @summary Test adjustment of type bound index if no explicit class bound is defined
* @compile ParameterizedBoundIndex.java
* @run main ParameterizedBoundIndex
*/

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;

/*
* According to JVMS 4.3.4, the first bound of a parameterized type is
* taken to be Object, if no explicit class bound is specified. As a
* consequence, the first interface's bound is always 1, independently
* of an explicit class bound.
*
* This test investigates if this mismatch between explicit and actual
* type bound index is accounted for.
*/
public class ParameterizedBoundIndex {

public static void main(String[] args) throws Exception {
List<Class<?>> failed = new ArrayList<>();

if (!TypeClassBound.class.getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(TypeClassBound.class);
}
if (!TypeInterfaceBound.class.getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(TypeInterfaceBound.class);
}
if (!TypeParameterizedInterfaceBound.class.getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(TypeParameterizedInterfaceBound.class);
}
if (!TypeParameterizedClassBound.class.getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(TypeParameterizedClassBound.class);
}
if (!TypeVariableBound.class.getTypeParameters()[1].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(TypeVariableBound.class);
}

if (!MethodClassBound.class.getDeclaredMethod("m").getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(MethodClassBound.class);
}
if (!MethodInterfaceBound.class.getDeclaredMethod("m").getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(MethodInterfaceBound.class);
}
if (!MethodParameterizedInterfaceBound.class.getDeclaredMethod("m").getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(MethodParameterizedInterfaceBound.class);
}
if (!MethodParameterizedClassBound.class.getDeclaredMethod("m").getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(MethodParameterizedClassBound.class);
}
if (!MethodVariableBound.class.getDeclaredMethod("m").getTypeParameters()[1].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(MethodVariableBound.class);
}

if (!ConstructorClassBound.class.getDeclaredConstructor().getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(ConstructorClassBound.class);
}
if (!ConstructorInterfaceBound.class.getDeclaredConstructor().getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(ConstructorInterfaceBound.class);
}
if (!ConstructorParameterizedInterfaceBound.class.getDeclaredConstructor().getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(ConstructorParameterizedInterfaceBound.class);
}
if (!ConstructorParameterizedClassBound.class.getDeclaredConstructor().getTypeParameters()[0].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(ConstructorParameterizedClassBound.class);
}
if (!ConstructorVariableBound.class.getDeclaredConstructor().getTypeParameters()[1].getAnnotatedBounds()[0].isAnnotationPresent(TypeAnnotation.class)) {
failed.add(ConstructorVariableBound.class);
}

if (!failed.isEmpty()) {
throw new RuntimeException("Failed: " + failed);
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface TypeAnnotation { }

static class TypeClassBound<T extends @TypeAnnotation Void> { }
static class TypeInterfaceBound<T extends @TypeAnnotation Runnable> { }
static class TypeParameterizedInterfaceBound<T extends @TypeAnnotation List<?>> { }
static class TypeParameterizedClassBound<T extends @TypeAnnotation ArrayList<?>> { }
static class TypeVariableBound<T, S extends @TypeAnnotation T> { }

static class MethodClassBound<T extends @TypeAnnotation Void> {
<T extends @TypeAnnotation Void> void m() { }
}
static class MethodInterfaceBound {
<T extends @TypeAnnotation Runnable> void m() { }
}
static class MethodParameterizedInterfaceBound<T extends @TypeAnnotation List<?>> {
<T extends @TypeAnnotation List<?>> void m() { }
}
static class MethodParameterizedClassBound {
<T extends @TypeAnnotation ArrayList<?>> void m() { }
}
static class MethodVariableBound<T, S extends @TypeAnnotation T> {
<T, S extends @TypeAnnotation T> void m() { }
}

static class ConstructorClassBound<T extends @TypeAnnotation Void> {
<T extends @TypeAnnotation Void> ConstructorClassBound() { }
}
static class ConstructorInterfaceBound {
<T extends @TypeAnnotation Runnable> ConstructorInterfaceBound() { }
}
static class ConstructorParameterizedInterfaceBound<T extends @TypeAnnotation List<?>> {
<T extends @TypeAnnotation List<?>> ConstructorParameterizedInterfaceBound() { }
}
static class ConstructorParameterizedClassBound {
<T extends @TypeAnnotation ArrayList<?>> ConstructorParameterizedClassBound() { }
}
static class ConstructorVariableBound<T, S extends @TypeAnnotation T> {
<T, S extends @TypeAnnotation T> ConstructorVariableBound() { }
}
}

0 comments on commit 9d7f8bc

Please sign in to comment.