From f6d1ba1d11d1997a44b23a7d5128ead535a001f3 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 13 Jan 2025 10:53:47 -0800 Subject: [PATCH] `getLowerBound()` should return `NULL` instead of `NONE` for variables without a lower bound Also fix handling of `null` in assignment and subtype tests, and update string representation to match javac. https://docs.oracle.com/en/java/javase/23/docs/api/java.compiler/javax/lang/model/type/TypeVariable.html#getLowerBound() PiperOrigin-RevId: 715032555 --- .../model/TurbineConstantTypeKind.java | 2 +- .../turbine/processing/TurbineTypeMirror.java | 2 +- .../turbine/processing/TurbineTypes.java | 19 ++++++++++++++++--- java/com/google/turbine/type/Type.java | 2 ++ .../processing/AbstractTurbineTypesTest.java | 4 ++++ .../processing/TurbineTypeMirrorTest.java | 2 +- 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/java/com/google/turbine/model/TurbineConstantTypeKind.java b/java/com/google/turbine/model/TurbineConstantTypeKind.java index bb5bea15..8968a4db 100644 --- a/java/com/google/turbine/model/TurbineConstantTypeKind.java +++ b/java/com/google/turbine/model/TurbineConstantTypeKind.java @@ -27,7 +27,7 @@ public enum TurbineConstantTypeKind { BOOLEAN("boolean"), BYTE("byte"), STRING("String"), - NULL("null"); + NULL(""); private final String name; diff --git a/java/com/google/turbine/processing/TurbineTypeMirror.java b/java/com/google/turbine/processing/TurbineTypeMirror.java index 8352850f..a0ee3ffa 100644 --- a/java/com/google/turbine/processing/TurbineTypeMirror.java +++ b/java/com/google/turbine/processing/TurbineTypeMirror.java @@ -527,7 +527,7 @@ public TypeMirror getUpperBound() { public TypeMirror getLowerBound() { return info().lowerBound() != null ? factory.asTypeMirror(info().lowerBound()) - : factory.noType(); + : factory.nullType(); } @Override diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java index d8f0c85c..1493dade 100644 --- a/java/com/google/turbine/processing/TurbineTypes.java +++ b/java/com/google/turbine/processing/TurbineTypes.java @@ -413,7 +413,8 @@ private boolean isArraySubtype(ArrayTy a, Type b, boolean strict) { // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.1 private static boolean isPrimSubtype(PrimTy a, Type other) { if (other.tyKind() != TyKind.PRIM_TY) { - return false; + // The null reference can always be assigned or cast to any reference type, see JLS 4.1 + return a.primkind() == TurbineConstantTypeKind.NULL && isReferenceType(other); } PrimTy b = (PrimTy) other; switch (a.primkind()) { @@ -483,7 +484,7 @@ private static boolean isPrimSubtype(PrimTy a, Type other) { case BOOLEAN: return a.primkind() == b.primkind(); case NULL: - break; + return isReferenceType(other); } throw new AssertionError(a.primkind()); } @@ -672,8 +673,12 @@ public boolean isAssignable(TypeMirror a1, TypeMirror a2) { private boolean isAssignable(Type t1, Type t2) { switch (t1.tyKind()) { case PRIM_TY: + TurbineConstantTypeKind primkind = ((PrimTy) t1).primkind(); + if (primkind == TurbineConstantTypeKind.NULL) { + return isReferenceType(t2); + } if (t2.tyKind() == TyKind.CLASS_TY) { - ClassSymbol boxed = boxedClass(((PrimTy) t1).primkind()); + ClassSymbol boxed = boxedClass(primkind); t1 = ClassTy.asNonParametricClassTy(boxed); } break; @@ -700,6 +705,14 @@ private static boolean isObjectType(Type type) { return type.tyKind() == TyKind.CLASS_TY && ((ClassTy) type).sym().equals(ClassSymbol.OBJECT); } + private static boolean isReferenceType(Type type) { + return switch (type.tyKind()) { + case CLASS_TY, ARRAY_TY, TY_VAR, WILD_TY, INTERSECTION_TY, ERROR_TY -> true; + case PRIM_TY -> ((PrimTy) type).primkind() == TurbineConstantTypeKind.NULL; + case NONE_TY, METHOD_TY, VOID_TY -> false; + }; + } + @Override public boolean contains(TypeMirror a, TypeMirror b) { return contains(asTurbineType(a), asTurbineType(b), /* strict= */ true); diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java index 41d8b7b4..51b76c17 100644 --- a/java/com/google/turbine/type/Type.java +++ b/java/com/google/turbine/type/Type.java @@ -308,6 +308,8 @@ public final String toString() { } /** A primitive type. */ + // TODO: cushon - consider renaming this, since it models things like String and null that can + // appear as constants and not primitives. @AutoValue abstract class PrimTy implements Type { diff --git a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java index 307b71ad..822283b3 100644 --- a/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java +++ b/javatests/com/google/turbine/processing/AbstractTurbineTypesTest.java @@ -462,6 +462,9 @@ public CharSequence getCharContent(boolean ignoreEncodingErrors) { /** * Discover all types contained in the given element, keyed by their immediate enclosing element. + * + *

This method is executed for both javac and Turbine and is expected to produce the same + * results in each case. */ private static void getTypes( Types typeUtils, Element element, Multimap types) { @@ -508,6 +511,7 @@ public Void visitTypeVariable(TypeVariable t, Void aVoid) { if (t.getUpperBound() != null) { types.put(key(e), t.getUpperBound()); } + types.put(String.format("getLowerBound(%s)", key(e)), t.getLowerBound()); return null; } }, diff --git a/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java index bf08f899..787e9cf9 100644 --- a/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java +++ b/javatests/com/google/turbine/processing/TurbineTypeMirrorTest.java @@ -226,7 +226,7 @@ public void tyVar() { .getTypeParameters()) .asType(); assertThat(t.getKind()).isEqualTo(TypeKind.TYPEVAR); - assertThat(t.getLowerBound().getKind()).isEqualTo(TypeKind.NONE); + assertThat(t.getLowerBound().getKind()).isEqualTo(TypeKind.NULL); assertThat(t.getUpperBound().toString()).isEqualTo("java.lang.Comparable"); }