diff --git a/README.md b/README.md
index d3bfb368..a30803cc 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ Maven dependency:
org.microbean
microbean-lang
- 0.0.19
+ 0.0.20
```
diff --git a/lang/src/main/java/org/microbean/lang/Lang.java b/lang/src/main/java/org/microbean/lang/Lang.java
index 4fbde8a9..953e3628 100644
--- a/lang/src/main/java/org/microbean/lang/Lang.java
+++ b/lang/src/main/java/org/microbean/lang/Lang.java
@@ -2127,8 +2127,8 @@ private static final TypeMirror[] typeArray(final Type[] ts) {
/**
* Asynchronously and idempotently initializes the {@link Lang} class for use.
*
- *
This method is automatically called when appropriate, but is {@code public} to support eager initialization use
- * cases.
+ * This method is automatically called by the internals of this class when appropriate, but is {@code public} to
+ * support eager initialization use cases.
*/
// Idempotent.
public static final void initialize() {
@@ -2292,6 +2292,11 @@ public final boolean sameType(final TypeMirror t, final TypeMirror s) {
return t == s || Lang.sameType(t, s);
}
+ @Override
+ public final boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return t == s || Lang.subtype(t, s);
+ }
+
@Override
public final TypeElement typeElement(final CharSequence canonicalName) {
return Lang.typeElement(canonicalName);
@@ -2466,8 +2471,10 @@ public final void run() {
options.add("-verbose");
}
- final DiagnosticLogger diagnosticLogger = new DiagnosticLogger(Locale.getDefault());
- final StandardJavaFileManager sjfm = jc.getStandardFileManager(diagnosticLogger, Locale.getDefault(), Charset.defaultCharset());
+ final Locale defaultLocale = Locale.getDefault();
+
+ final DiagnosticLogger diagnosticLogger = new DiagnosticLogger(defaultLocale);
+ final StandardJavaFileManager sjfm = jc.getStandardFileManager(diagnosticLogger, defaultLocale, Charset.defaultCharset());
// (Any "loading" is actually performed by, e.g. com.sun.tools.javac.jvm.ClassReader.fillIn(), not reflective
// machinery. Once a class has been so loaded, com.sun.tools.javac.code.Symtab#getClass(ModuleSymbol, Name) will
@@ -2480,7 +2487,7 @@ public final void run() {
List.of("java.lang.annotation.RetentionPolicy"), // arbitrary, but loads the least amount of stuff up front
null); // compilation units; null means we aren't actually compiling anything
task.setProcessors(List.of(new P()));
- task.setLocale(Locale.getDefault());
+ task.setLocale(defaultLocale);
task.addModules(additionalRootModuleNames);
if (LOGGER.isLoggable(DEBUG)) {
diff --git a/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java b/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java
index 5a313ffd..4ac5b863 100644
--- a/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java
+++ b/lang/src/main/java/org/microbean/lang/TypeAndElementSource.java
@@ -74,6 +74,8 @@ public DeclaredType declaredType(final TypeElement typeElement,
public boolean sameType(final TypeMirror t, final TypeMirror s);
+ public boolean subtype(final TypeMirror t, final TypeMirror s);
+
public TypeElement typeElement(final CharSequence canonicalName);
// Note that Elements#getTypeElement(ModuleElement, CharSequence), to which this basically ultimately delegates, says
@@ -91,49 +93,48 @@ public DeclaredType declaredType(final TypeElement typeElement,
*/
- public default ArrayType arrayType(final Class> c) {
- if (!c.isArray()) {
- throw new IllegalArgumentException("c: " + c);
- }
- return this.arrayTypeOf(this.type(c.getComponentType()));
- }
-
- public default ArrayType arrayType(final GenericArrayType g) {
- return this.arrayTypeOf(this.type(g.getGenericComponentType()));
+ public default ArrayType arrayType(final Type t) {
+ return switch (t) {
+ case null -> throw new NullPointerException("t");
+ case Class> c when c.isArray() -> this.arrayTypeOf(this.type(c.getComponentType()));
+ case GenericArrayType g -> this.arrayTypeOf(this.type(g.getGenericComponentType()));
+ default -> throw new IllegalArgumentException("t: " + t);
+ };
}
public default DeclaredType declaredType(final CharSequence canonicalName) {
return this.declaredType(this.typeElement(canonicalName));
}
- public default DeclaredType declaredType(final Class> c) {
- if (c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()) {
- throw new IllegalArgumentException("c: " + c);
+ public default DeclaredType declaredType(final Type t) {
+ return switch (t) {
+ case null -> throw new NullPointerException("t");
+ case Class> c when c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()
+ -> throw new IllegalArgumentException("t: " + t);
+ case Class> c -> {
+ final Class> ec = c.getEnclosingClass();
+ yield ec == null ? this.declaredType(this.typeElement(c)) : this.declaredType(this.declaredType(ec), this.typeElement(c));
}
- final Class> ec = c.getEnclosingClass();
- return this.declaredType(ec == null ? null : this.declaredType(ec), this.typeElement(c));
- }
-
- public default DeclaredType declaredType(final ParameterizedType p) {
- return
- this.declaredType(switch (p.getOwnerType()) {
- case null -> null;
- case Class> c -> this.declaredType(c);
- case ParameterizedType pt -> this.declaredType(pt);
- default -> throw new IllegalArgumentException("p: " + p);
- },
- this.typeElement((Class>)p.getRawType()),
- this.typeArray(p.getActualTypeArguments()));
+ case ParameterizedType p -> this.declaredType(switch (p.getOwnerType()) {
+ case null -> null;
+ case Class> c -> this.declaredType(c);
+ case ParameterizedType pt -> this.declaredType(pt);
+ default -> throw new IllegalArgumentException("t: " + t);
+ },
+ this.typeElement(p.getRawType()),
+ this.typeArray(p.getActualTypeArguments()));
+ default -> throw new IllegalArgumentException("t: " + t);
+ };
}
public default Optional extends ConstantDesc> describeConstable(final AnnotatedConstruct a) {
return switch (a) {
- case null -> Optional.of(NULL);
- case Constable c -> c.describeConstable();
+ case null -> Optional.of(NULL);
+ case Constable c -> c.describeConstable();
case ConstantDesc cd -> Optional.of(cd); // future proofing
- case TypeMirror t -> Optional.empty();
- case Element e -> Optional.empty();
- default -> throw new IllegalArgumentException("a: " + a);
+ case TypeMirror t -> Optional.empty();
+ case Element e -> Optional.empty();
+ default -> throw new IllegalArgumentException("a: " + a);
};
}
@@ -146,16 +147,17 @@ public default PrimitiveType primitiveType(final Class> c) {
public default TypeMirror type(final Type t) {
return switch (t) {
- case null -> throw new NullPointerException();
- case Class> c when c == void.class -> this.noType(TypeKind.VOID);
- case Class> c when c.isArray() -> this.arrayType(c);
- case Class> c when c.isPrimitive() -> this.primitiveType(c);
- case Class> c -> this.declaredType(c);
- case ParameterizedType p -> this.declaredType(p);
- case GenericArrayType g -> this.arrayType(g);
- case java.lang.reflect.TypeVariable> tv -> this.typeVariable(tv);
- case java.lang.reflect.WildcardType w -> this.wildcardType(w);
- default -> throw new IllegalArgumentException("t: " + t);
+ case null -> throw new NullPointerException();
+ case Class> c when c == void.class -> this.noType(TypeKind.VOID);
+ case Class> c when c.isArray() -> this.arrayType(c);
+ case Class> c when c.isPrimitive() -> this.primitiveType(c);
+ case Class> c when c.isLocalClass() || c.isAnonymousClass() -> throw new IllegalArgumentException("t: " + t);
+ case Class> c -> this.declaredType(c);
+ case ParameterizedType p -> this.declaredType(p);
+ case GenericArrayType g -> this.arrayType(g);
+ case java.lang.reflect.TypeVariable> tv -> this.typeVariable(tv);
+ case java.lang.reflect.WildcardType w -> this.wildcardType(w);
+ default -> throw new IllegalArgumentException("t: " + t);
};
}
@@ -167,11 +169,15 @@ public default TypeMirror[] typeArray(final Type[] ts) {
return rv;
}
- public default TypeElement typeElement(final Class> c) {
- if (c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()) {
- throw new IllegalArgumentException("c: " + c);
- }
- return this.typeElement(c.getCanonicalName());
+ public default TypeElement typeElement(final Type t) {
+ return switch (t) {
+ case null -> throw new NullPointerException("t");
+ case Class> c when c.isArray() || c.isPrimitive() || c.isLocalClass() || c.isAnonymousClass()
+ -> throw new IllegalArgumentException("t: " + t);
+ case Class> c -> this.typeElement(c.getCanonicalName());
+ case ParameterizedType p -> this.typeElement(p.getRawType());
+ default -> throw new IllegalArgumentException("t: " + t);
+ };
}
public default WildcardType wildcardType(final java.lang.reflect.WildcardType t) {
diff --git a/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java b/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java
index 646c55b9..dbf9fd04 100644
--- a/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java
+++ b/lang/src/main/java/org/microbean/lang/visitor/AdaptingVisitor.java
@@ -41,7 +41,7 @@
import static org.microbean.lang.type.Types.asElement;
/**
- * Does something adapting-like.
+ * Does something adapting-like. Looks to be related to type variable resolution.
*
* Usage: call {@link #adapt(DeclaredType, DeclaredType)}, not {@link #visit(TypeMirror)}.
*
@@ -67,6 +67,7 @@ final class AdaptingVisitor extends SimpleTypeVisitor14 {
private final Types types;
+ // The compiler calls this "mapping"; the class is called "Adapting"; I think they are the same thing
private final Map mapping;
private final SameTypeVisitor sameTypeVisitor;
@@ -92,6 +93,10 @@ final class AdaptingVisitor extends SimpleTypeVisitor14 {
this.to = Objects.requireNonNull(to, "to");
}
+ final void adaptSelf(final DeclaredType target) {
+ this.adapt((DeclaredType)target.asElement().asType(), target);
+ }
+
final void adapt(final DeclaredType source, final DeclaredType target) {
this.visitDeclared(source, target);
final int fromSize = this.from.size();
@@ -103,10 +108,29 @@ final void adapt(final DeclaredType source, final DeclaredType target) {
}
}
- final void adaptSelf(final DeclaredType target) {
- this.adapt((DeclaredType)target.asElement().asType(), target);
+ private final void adaptRecursive(final Collection extends TypeMirror> source, final Collection extends TypeMirror> target) {
+ if (source.size() == target.size()) {
+ final Iterator extends TypeMirror> sourceIterator = source.iterator();
+ final Iterator extends TypeMirror> targetIterator = target.iterator();
+ while (sourceIterator.hasNext()) {
+ assert targetIterator.hasNext();
+ this.adaptRecursive(sourceIterator.next(), targetIterator.next());
+ }
+ }
}
+ private final void adaptRecursive(final TypeMirror source, final TypeMirror target) {
+ final TypeMirrorPair pair = new TypeMirrorPair(this.sameTypeVisitor, source, target);
+ if (this.cache.add(pair)) {
+ try {
+ this.visit(source, target);
+ } finally {
+ this.cache.remove(pair);
+ }
+ }
+ }
+
+ // Adapts the component type of source.
@Override
public final Void visitArray(final ArrayType source, final TypeMirror target) {
assert source.getKind() == TypeKind.ARRAY;
@@ -116,6 +140,8 @@ public final Void visitArray(final ArrayType source, final TypeMirror target) {
return null;
}
+ // Adapts type arguments of source. "Recursive" means something like "go into type parameters etc."
+ // Adapting a type argument takes effect on wildcard-extending type variables only, it looks like.
@Override
public final Void visitDeclared(final DeclaredType source, final TypeMirror target) {
assert source.getKind() == TypeKind.DECLARED;
@@ -135,6 +161,7 @@ public final Void visitTypeVariable(final TypeVariable source, final TypeMirror
this.from.add(source);
this.to.add(target);
} else if (val.getKind() == TypeKind.WILDCARD && target.getKind() == TypeKind.WILDCARD) {
+ // This block is the only place in this entire class where the magic happens.
final WildcardType valWc = (WildcardType)val;
final TypeMirror valSuperBound = valWc.getSuperBound();
final TypeMirror valExtendsBound = valWc.getExtendsBound();
@@ -172,6 +199,7 @@ public final Void visitTypeVariable(final TypeVariable source, final TypeMirror
return null;
}
+ // Adapts the bounds of a wildcard.
@Override
public final Void visitWildcard(final WildcardType source, final TypeMirror target) {
assert source.getKind() == TypeKind.WILDCARD;
@@ -191,26 +219,5 @@ public final Void visitWildcard(final WildcardType source, final TypeMirror targ
return null;
}
- private final void adaptRecursive(final TypeMirror source, final TypeMirror target) {
- final TypeMirrorPair pair = new TypeMirrorPair(this.sameTypeVisitor, source, target);
- if (this.cache.add(pair)) {
- try {
- this.visit(source, target);
- } finally {
- this.cache.remove(pair);
- }
- }
- }
-
- private final void adaptRecursive(final Collection extends TypeMirror> source, final Collection extends TypeMirror> target) {
- if (source.size() == target.size()) {
- final Iterator extends TypeMirror> sourceIterator = source.iterator();
- final Iterator extends TypeMirror> targetIterator = target.iterator();
- while (sourceIterator.hasNext()) {
- assert targetIterator.hasNext();
- this.adaptRecursive(sourceIterator.next(), targetIterator.next());
- }
- }
- }
}
diff --git a/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java b/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java
index e58bca73..56b46cf8 100644
--- a/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java
+++ b/lang/src/main/java/org/microbean/lang/visitor/PrecedesPredicate.java
@@ -61,7 +61,7 @@ public final boolean test(final Element e, final Element f) {
case DECLARED:
switch (s.getKind()) {
case DECLARED:
- if (this.equality.equals(e, f)) { // in place of ==
+ if (this.equality.equals(e, f)) { // already checked e == f
// Both are completely interchangeable DeclaredTypes; can't say which comes first.
return false;
}
@@ -77,7 +77,7 @@ public final boolean test(final Element e, final Element f) {
case TYPEVAR:
switch (s.getKind()) {
case TYPEVAR:
- if (this.equality.equals(e, f)) { // in place of ==
+ if (this.equality.equals(e, f)) { // already checked e == f
// Both are completely interchangeable TypeVariables; can't say which comes first.
return false;
}
diff --git a/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java b/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java
index 246af148..bd27d313 100644
--- a/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java
+++ b/lang/src/main/java/org/microbean/lang/visitor/SupertypeVisitor.java
@@ -151,7 +151,15 @@ public final TypeMirror visitDeclared(final DeclaredType t, final Void x) {
@Override // SimpleTypeVisitor14
public final TypeMirror visitIntersection(final IntersectionType t, final Void x) {
assert t.getKind() == TypeKind.INTERSECTION;
- return t.getBounds().get(0); // TODO: presumes first bound will be the supertype; see https://github.com/openjdk/jdk/blob/jdk-19+25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1268
+ // Since an intersection type is always ordered from most specialized to least, it follows that the direct supertype
+ // of an intersection type is its first bound. See
+ // https://docs.oracle.com/javase/specs/jls/se22/html/jls-4.html#jls-4.10.2
+ //
+ // TODO: wait, no, this isn't correct at all. The JLS says: "The direct supertypes of an intersection type T1 &
+ // ... & Tn are Ti (1 ≤ i ≤ n)." But javac's Types class sets the supertype_field (singular) field to the first
+ // element of the list. See
+ // https://github.com/openjdk/jdk/blob/jdk-19+25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1268.
+ return t.getBounds().get(0);
}
@Override // SimpleTypeVisitor14
diff --git a/lang/src/test/java/org/microbean/lang/TestErase.java b/lang/src/test/java/org/microbean/lang/TestErase.java
index 86dcf5b0..8eeea1ae 100644
--- a/lang/src/test/java/org/microbean/lang/TestErase.java
+++ b/lang/src/test/java/org/microbean/lang/TestErase.java
@@ -151,12 +151,10 @@ public final PrimitiveType primitiveType(final TypeKind k) {
public boolean sameType(final TypeMirror t, final TypeMirror s) {
return javacModelTypes.isSameType(t, s);
}
- /*
@Override
- public final TypeElement typeElement(final CharSequence m, final CharSequence n) {
- return elements.getTypeElement(elements.getModuleElement(m), n);
+ public boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return javacModelTypes.isSubtype(t, s);
}
- */
@Override
public final TypeElement typeElement(final CharSequence n) {
return elements.getTypeElement(n);
diff --git a/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java b/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java
index 9727fe98..690b6dfe 100644
--- a/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java
+++ b/lang/src/test/java/org/microbean/lang/TestSameTypeVisitor.java
@@ -117,12 +117,10 @@ public final PrimitiveType primitiveType(final TypeKind k) {
public boolean sameType(final TypeMirror t, final TypeMirror s) {
return javacModelTypes.isSameType(t, s);
}
- /*
@Override
- public final TypeElement typeElement(final CharSequence m, final CharSequence n) {
- return elements.getTypeElement(elements.getModuleElement(m), n);
+ public boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return javacModelTypes.isSubtype(t, s);
}
- */
@Override
public final TypeElement typeElement(final CharSequence n) {
return elements.getTypeElement(n);
diff --git a/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java b/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java
index 108db893..fbbcda85 100644
--- a/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java
+++ b/lang/src/test/java/org/microbean/lang/TestSupertypeVisitor.java
@@ -137,12 +137,10 @@ public final PrimitiveType primitiveType(final TypeKind k) {
public boolean sameType(final TypeMirror t, final TypeMirror s) {
return javacModelTypes.isSameType(t, s);
}
- /*
@Override
- public final TypeElement typeElement(final CharSequence m, final CharSequence n) {
- return elements.getTypeElement(elements.getModuleElement(m), n);
+ public boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return javacModelTypes.isSubtype(t, s);
}
- */
@Override
public final TypeElement typeElement(final CharSequence n) {
return elements.getTypeElement(n);
diff --git a/lang/src/test/java/org/microbean/lang/TestTypeClosure.java b/lang/src/test/java/org/microbean/lang/TestTypeClosure.java
index 865c21d3..ee9e09cd 100644
--- a/lang/src/test/java/org/microbean/lang/TestTypeClosure.java
+++ b/lang/src/test/java/org/microbean/lang/TestTypeClosure.java
@@ -132,12 +132,10 @@ public final PrimitiveType primitiveType(final TypeKind k) {
public boolean sameType(final TypeMirror t, final TypeMirror s) {
return javacModelTypes.isSameType(t, s);
}
- /*
@Override
- public final TypeElement typeElement(final CharSequence m, final CharSequence n) {
- return elements.getTypeElement(elements.getModuleElement(m), n);
+ public boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return javacModelTypes.isSubtype(t, s);
}
- */
@Override
public final TypeElement typeElement(final CharSequence n) {
return elements.getTypeElement(n);
@@ -248,6 +246,10 @@ public final PrimitiveType primitiveType(final TypeKind k) {
public boolean sameType(final TypeMirror t, final TypeMirror s) {
return javacModelTypes.isSameType(t, s);
}
+ @Override
+ public boolean subtype(final TypeMirror t, final TypeMirror s) {
+ return javacModelTypes.isSubtype(t, s);
+ }
/*
@Override
public final TypeElement typeElement(final CharSequence m, final CharSequence n) {
diff --git a/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java b/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java
index f366a8b9..0dd8ad83 100644
--- a/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java
+++ b/lang/src/test/java/org/microbean/lang/visitor/TestTypeClosureVisitor.java
@@ -95,7 +95,7 @@ final void testArrayTypeClosure() {
final TypeMirror t = Lang.arrayTypeOf(Lang.declaredType("java.lang.Integer"));
assertSame(TypeKind.ARRAY, t.getKind());
- final Type integerArrayType = (Type)unwrap(Lang.arrayTypeOf(Lang.declaredType("java.lang.Integer")));
+ final Type integerArrayType = (Type)unwrap(t);
assertSame(TypeKind.ARRAY, integerArrayType.getKind());
final List extends TypeMirror> closure = javacCodeTypes.closure(integerArrayType);
// Perhaps surprisingly, the closure (as implemented by javac) of any type, including array types, that is not a