diff --git a/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavaEnvironment.java b/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavaEnvironment.java index 8e249d376a..236c437b38 100644 --- a/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavaEnvironment.java +++ b/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavaEnvironment.java @@ -101,6 +101,7 @@ import javax.lang.model.type.IntersectionType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.WildcardType; /** Utility functions to interact with JavaC internal representations. */ class JavaEnvironment { @@ -317,8 +318,7 @@ private TypeDescriptor createTypeDescriptorWithNullability( } if (typeMirror.getKind() == TypeKind.WILDCARD) { - return createWildcardTypeVariable( - ((javax.lang.model.type.WildcardType) typeMirror).getExtendsBound()); + return createWildcardTypeVariable(((WildcardType) typeMirror).getExtendsBound()); } boolean isNullable = isNullable(typeMirror, elementAnnotations, inNullMarkedScope); @@ -398,7 +398,7 @@ private TypeVariable createTypeVariable(javax.lang.model.type.TypeVariable typeV .build(); } - private TypeVariable createWildcardTypeVariable(TypeMirror bound) { + private TypeVariable createWildcardTypeVariable(@Nullable TypeMirror bound) { return TypeVariable.newBuilder() .setUpperBoundTypeDescriptorFactory(() -> createTypeDescriptor(bound)) .setWildcard(true) @@ -994,13 +994,47 @@ private DeclaredTypeDescriptor createDeclaredType( return cachedTypeDescriptor; } + TypeDeclaration typeDeclaration = createDeclarationForType((TypeElement) classType.asElement()); DeclaredTypeDescriptor typeDescriptor = - createDeclarationForType((TypeElement) classType.asElement()) - .toDescriptor(createTypeDescriptors(getTypeArguments(classType), inNullMarkedScope)); + typeDeclaration.toDescriptor( + createTypeArgumentDescriptors( + getTypeArguments(classType), typeDeclaration, inNullMarkedScope)); putTypeDescriptorInCache(inNullMarkedScope, classType, typeDescriptor); return typeDescriptor; } + public ImmutableList createTypeArgumentDescriptors( + List typeMirrors, + TypeDeclaration typeDeclaration, + boolean inNullMarkedScope) { + // TODO(b/246332093): Consider doing this in our type model after cleanup. Currently results in + // an infinite recursion. + return Streams.zip( + typeMirrors.stream(), + typeDeclaration.getTypeParameterDescriptors().stream(), + (typeMirror, declaredTypeVariable) -> { + if (typeMirror.getKind() == TypeKind.WILDCARD + && isNullOrJavaLangObject(((WildcardType) typeMirror).getExtendsBound()) + && declaredTypeVariable != null) { + // If this is a wildcard but the bound is not specified (or is Object), we might be + // able to get a tighter bound from the declaration. + return TypeVariable.createWildcardWithUpperBound( + declaredTypeVariable.getUpperBoundTypeDescriptor()); + } + return createTypeDescriptor(typeMirror, inNullMarkedScope); + }) + .collect(toImmutableList()); + } + + private boolean isNullOrJavaLangObject(@Nullable TypeMirror typeMirror) { + if (typeMirror == null) { + return true; + } + Element element = asElement(typeMirror); + return element instanceof TypeElement + && ((TypeElement) element).getQualifiedName().contentEquals("java.lang.Object"); + } + private final Map cachedDeclaredTypeDescriptorByDeclaredTypeInNullMarkedScope = new HashMap<>();