001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023–2024 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License.  You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.bean;
015
016import java.lang.System.Logger;
017
018import java.lang.constant.ClassDesc;
019import java.lang.constant.Constable;
020import java.lang.constant.ConstantDesc;
021import java.lang.constant.DynamicConstantDesc;
022import java.lang.constant.MethodHandleDesc;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027import java.util.Objects;
028import java.util.Optional;
029
030import java.util.function.Predicate;
031
032import javax.lang.model.element.Element;
033import javax.lang.model.element.ElementKind;
034import javax.lang.model.element.Parameterizable;
035import javax.lang.model.element.QualifiedNameable;
036
037import javax.lang.model.type.ArrayType;
038import javax.lang.model.type.DeclaredType;
039import javax.lang.model.type.IntersectionType;
040import javax.lang.model.type.ReferenceType;
041import javax.lang.model.type.TypeKind;
042import javax.lang.model.type.TypeMirror;
043import javax.lang.model.type.TypeVariable;
044import javax.lang.model.type.WildcardType;
045
046import org.microbean.lang.TypeAndElementSource;
047
048import static java.lang.constant.ConstantDescs.BSM_INVOKE;
049
050import static org.microbean.lang.ConstantDescs.CD_TypeAndElementSource;
051
052/**
053 * A {@link Matcher} encapsulating <a
054 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">CDI-compatible
055 * type matching rules</a>.
056 *
057 * @author <a href="https://about.me/lairdnelson/" target="_top">Laird Nelson</a>
058 *
059 * @see #test(TypeMirror, TypeMirror)
060 */
061public final class TypeMatcher implements Constable, Matcher<TypeMirror, TypeMirror> {
062
063
064  /*
065   * Static fields.
066   */
067
068
069  private static final Logger LOGGER = System.getLogger(TypeMatcher.class.getName());
070
071
072  /*
073   * Instance fields.
074   */
075
076
077  private final TypeAndElementSource tes;
078
079
080  /*
081   * Constructors.
082   */
083
084
085  /**
086   * Creates a new {@link TypeMatcher}.
087   *
088   * @param tes a {@link TypeAndElementSource}; must not be {@code null}
089   *
090   * @exception NullPointerException if {@code tes} is {@code null}
091   *
092   * @see TypeAndElementSource
093   */
094  public TypeMatcher(final TypeAndElementSource tes) {
095    super();
096    this.tes = Objects.requireNonNull(tes, "tes");
097  }
098
099
100  /*
101   * Instance methods.
102   */
103
104
105  /*
106   * Public methods.
107   */
108
109  /**
110   * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}.
111   *
112   * <p>This method never returns {@code null}.</p>
113   *
114   * <p>The default implementation of this method relies on the presence of a {@code public} constructor that accepts a
115   * single {@link TypeAndElementSource}-typed argument.</p>
116   *
117   * <p>The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain
118   * Optional#isEmpty() empty}.</p>
119   *
120   * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}; never {@code
121   * null}
122   *
123   * @see Constable#describeConstable()
124   */
125  @Override // Constable
126  public final Optional<? extends ConstantDesc> describeConstable() {
127    return (this.tes instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty())
128      .map(tesDesc -> DynamicConstantDesc.of(BSM_INVOKE,
129                                             MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()),
130                                                                            CD_TypeAndElementSource),
131                                             tesDesc));
132  }
133
134  /**
135   * Returns {@code true} if and only if the supplied {@code payload} argument <a
136   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution"><em>matches</em></a>
137   * the supplied {@code receiver} argument, according to the rules defined by <a
138   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section
139   * 2.4.2.1 of the CDI specification</a>.
140   *
141   * @param receiver the left hand side of a type assignment; must not be {@code null}
142   *
143   * @param payload the right hand side of a type assignment; must not be {@code null}
144   *
145   * @return {@code true} if and only if the supplied {@code payload} argument <a
146   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">matches</a>
147   * the supplied {@code receiver} argument, according to the rules defined by <a
148   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section
149   * 2.4.2.1 of the CDI specification</a>; {@code false} otherwise
150   *
151   * @exception NullPointerException if either argument is {@code null}
152   *
153   * @exception IllegalArgumentException if either type is any type other than an {@linkplain TypeKind#ARRAY array
154   * type}, a {@link TypeKind#isPrimitive() primitive type}, or a {@linkplain TypeKind#DECLARED declared type}
155   */
156  // Is the payload assignable to the receiver? That is, does the payload "match the receiver", in CDI parlance?
157  @Override
158  public final boolean test(final TypeMirror receiver, final TypeMirror payload) {
159    // "A bean is assignable to a given injection point if:
160    //
161    // "The bean has a bean type [payload] that matches the required type [receiver]. For this purpose..."
162    return receiver == Objects.requireNonNull(payload, "payload") || switch (receiver.getKind()) {
163      // "...primitive types are considered to match their corresponding wrapper types in java.lang..."
164      case BOOLEAN  -> payload.getKind() == TypeKind.BOOLEAN || declaredTypeNamed(payload, "java.lang.Boolean");
165      case BYTE     -> payload.getKind() == TypeKind.BYTE    || declaredTypeNamed(payload, "java.lang.Byte");
166      case CHAR     -> payload.getKind() == TypeKind.CHAR    || declaredTypeNamed(payload, "java.lang.Character");
167      case DOUBLE   -> payload.getKind() == TypeKind.DOUBLE  || declaredTypeNamed(payload, "java.lang.Double");
168      case FLOAT    -> payload.getKind() == TypeKind.FLOAT   || declaredTypeNamed(payload, "java.lang.Float");
169      case INT      -> payload.getKind() == TypeKind.INT     || declaredTypeNamed(payload, "java.lang.Integer");
170      case LONG     -> payload.getKind() == TypeKind.LONG    || declaredTypeNamed(payload, "java.lang.Long");
171      case SHORT    -> payload.getKind() == TypeKind.SHORT   || declaredTypeNamed(payload, "java.lang.Short");
172      // "...and array types are considered to match only if their element types [note: not component types] are
173      // identical ['identical' is actually undefined in the specification]..."
174      case ARRAY    -> payload.getKind() == TypeKind.ARRAY   && this.identical(elementType(receiver), elementType(payload));
175      case DECLARED -> switch (payload.getKind()) {
176        // "...primitive types are considered to match their corresponding wrapper types in java.lang..."
177        case BOOLEAN  -> named((DeclaredType)receiver, "java.lang.Boolean");
178        case BYTE     -> named((DeclaredType)receiver, "java.lang.Byte");
179        case CHAR     -> named((DeclaredType)receiver, "java.lang.Character");
180        case DOUBLE   -> named((DeclaredType)receiver, "java.lang.Double");
181        case FLOAT    -> named((DeclaredType)receiver, "java.lang.Float");
182        case INT      -> named((DeclaredType)receiver, "java.lang.Integer");
183        case LONG     -> named((DeclaredType)receiver, "java.lang.Long");
184        case SHORT    -> named((DeclaredType)receiver, "java.lang.Short");
185        // "Parameterized and raw types [and non-generic classes, and non-array types]..."
186        case DECLARED ->
187          // "...are considered to match if they are identical [undefined]..."
188          this.identical(receiver, payload) ||
189          // "...or if the bean type [payload] is assignable to [see #assignable(TypeMirror, TypeMirror)] the required type
190          // [receiver]...."
191          this.assignable((DeclaredType)receiver, (DeclaredType)payload);
192        default       -> throw new IllegalArgumentException("Illegal payload kind: " + payload.getKind() + "; payload: " + payload);
193      };
194      default       -> throw new IllegalArgumentException("Illegal receiver kind: " + receiver.getKind() + "; receiver: " + receiver);
195    };
196  }
197
198  /**
199   * Returns the {@link TypeAndElementSource} used by this {@link TypeMatcher}.
200   *
201   * @return the {@link TypeAndElementSource} used by this {@link TypeMatcher}; never {@code null}
202   *
203   * @see #TypeMatcher(TypeAndElementSource)
204   *
205   * @see TypeAndElementSource
206   */
207  public final TypeAndElementSource typeAndElementSource() {
208    return this.tes;
209  }
210
211  /*
212   * Private methods.
213   */
214
215  private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) {
216    assert receiver.getKind() == TypeKind.DECLARED;
217    assert payload.getKind() == TypeKind.DECLARED;
218    return switch (payload) {
219      // "A parameterized bean type [payload] is considered assignable..."
220      case DeclaredType parameterizedPayload when parameterized(payload) -> switch (receiver) {
221        // "...to a [non-generic class or] raw required type [receiver]..."
222        case TypeMirror rawReceiver when !generic(receiver) || raw(receiver) -> {
223          // "...if the [non-generic class or] raw types are identical [undefined] and all type parameters [type
224          // arguments] of the bean type [payload] are either unbounded type variables [undefined] or java.lang.Object."
225          yield
226            this.identical(this.nonGenericClassOrRawType(rawReceiver), this.nonGenericClassOrRawType(parameterizedPayload)) &&
227            allTypeArgumentsAre(parameterizedPayload.getTypeArguments(),
228                                ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject));
229        }
230        // "...to a parameterized required type [receiver]..."
231        case DeclaredType parameterizedReceiver when parameterized(receiver) -> {
232          // "...if they have identical raw type [really if their declarations/elements are 'identical']..."
233          if (this.identical(this.rawType(parameterizedReceiver), this.rawType(parameterizedPayload))) {
234            // "...and for each parameter [type argument pair]..."
235            final List<? extends TypeMirror> rtas = parameterizedReceiver.getTypeArguments();
236            final List<? extends TypeMirror> ptas = parameterizedPayload.getTypeArguments();
237            assert rtas.size() == ptas.size();
238            for (int i = 0; i < rtas.size(); i++) {
239              final TypeMirror rta = rtas.get(i);
240              final TypeMirror pta = ptas.get(i);
241              // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter [type
242              // argument] are actual types [non-type variable, non-wildcard reference types]..."
243              if (actual(rta)) {
244                if (actual(pta)) {
245                  // "...with identical [non-generic classes or] raw type[s]..."
246                  if (this.identical(this.nonGenericClassOrRawType(rta), this.nonGenericClassOrRawType(pta))) {
247                    // "...and, if the type [?] is parameterized [?]..."
248                    //
249                    // Let rta and pta be array types with parameterized element types, such as List<Number>[] and
250                    // List<String>[]. Then their raw types are List[] and List[]. Their parameterized element types are
251                    // List<Number> and List<String>. According to the JLS, neither List<Number>[] nor List<String>[] is
252                    // parameterized.
253                    //
254                    // In this example, if we get here, we have only proven that List[] is "identical to" List[].
255                    //
256                    // The "if the type is parameterized" clause is tough. Which type is under discussion? "the type"
257                    // seems to refer to the "identical raw type". But a raw type by definition is not parameterized, so
258                    // this clause would seem to be superfluous and thus never apply, and so List<Number>[] :=
259                    // List<String>[] is OK. Oops. So not that interpretation.
260                    //
261                    // What if instead the "if the type is parameterized" clause means the receiver type itself and
262                    // somehow loosely the payload type as well? Well, clearly it cannot correctly do this, since an
263                    // array type is never a parameterized type. Same bad result. Oops. So not that interpretation.
264                    //
265                    // Or what if "identical raw type" really means "identical raw type (or identical component type if
266                    // they are array types)"? That would be most efficient, since it would rule out List<Number>[] :=
267                    // List<String>[] right off the bat: we wouldn't even get here. But that really doesn't seem to be
268                    // what is meant. So probably not that interpretation.
269                    //
270                    // What if the "if the type is parameterized" clause really means "if the element type declaration
271                    // used by both the receiver and payload types is generic"? That would work. That's also
272                    // semantically equivalent to something like: "...if at least one of the two arguments is a
273                    // parameterized type [e.g. List<Number>, not List<Number>[], not String, not List], or at least one
274                    // of the two types is an array type with a parameterized element type [e.g. List<Number>[], not
275                    // List[], not String[]]..."
276                    //
277                    // That is the interpretation we apply here. So:
278                    //
279                    //   "...and, if the type [?] is parameterized [?]..."
280                    //
281                    // becomes:
282                    //
283                    //   "...and, if at least one of the two type arguments is a parameterized type, or if at least one
284                    //   of the two types is an array type with a parameterized element type..."
285                    //
286                    // That, in turn, designates any type capable of properly yielding a raw type, while ruling out
287                    // those that can't! That means it is exactly equal to our yieldsRawType(TypeMirror) method, and so
288                    // that's what cdiParameterized(TypeMirror) returns (go see for yourself).
289                    if (cdiParameterized(rta)) { // really just yieldsRawType(rta)
290                      assert cdiParameterized(pta); // ...because otherwise their raw types would not have been "identical"
291                      // "...the bean type parameter [type argument] is assignable to the required type parameter [type
292                      // argument] according to [all of] these rules [including 'matching']..."
293                      if (test(rta, pta)) { // note recursion
294                        continue;
295                      }
296                      yield false;
297                    } else {
298                      assert !cdiParameterized(pta);
299                      continue; // yes, trust me; vetted
300                    }
301                  }
302                  yield false;
303                } else if (pta.getKind() == TypeKind.TYPEVAR) {
304                  // "...the required [receiver] type parameter [type argument] is an actual type [a non-type variable,
305                  // non-wildcard reference type], the bean [payload] type parameter [type argument] is a type variable and
306                  // the actual type [non-type variable, non-wildcard reference type] [required type argument, receiver] is
307                  // assignable to the upper bound, if any, of the type variable [bean type argument, payload] [type
308                  // variables have multiple bounds]..."
309                  //
310                  // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the
311                  // *payload* type argument.)
312                  if (assignableToCondensedTypeVariableBounds((TypeVariable)pta, (ReferenceType)rta)) {
313                    continue;
314                  }
315                  yield false;
316                }
317                throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind());
318              } else if (rta.getKind() == TypeKind.WILDCARD) {
319                // "...the required type parameter [type argument] is a wildcard, the bean type parameter [type
320                // argument] is an actual type [a non-type variable, non-wildcard reference type]..."
321                if (actual(pta)) {
322                  // "...and the actual type [non-type variable, non-wildcard reference type] is assignable to the upper
323                  // bound, if any, of the wildcard and assignable from the lower bound, if any of the wildcard"
324                  if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) &&
325                      assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) {
326                    continue;
327                  }
328                  yield false;
329                } else if (pta.getKind() == TypeKind.TYPEVAR) {
330                  // "...the required [receiver] type parameter [type argument] is a wildcard, the bean [payload] type
331                  // parameter [type argument] is a type variable and the upper bound of the type variable [a type
332                  // variable has many bounds?] is assignable to or assignable from the upper bound, if any, of the
333                  // wildcard and assignable from the lower bound, if any, of the wildcard"
334                  if ((condensedTypeVariableBoundsAssignableToCondensedExtendsBound((WildcardType)rta, (TypeVariable)pta) ||
335                       condensedTypeVariableBoundsAssignableFromCondensedExtendsBound((TypeVariable)pta, (WildcardType)rta)) &&
336                      condensedTypeVariableBoundsAssignableFromCondensedSuperBound((TypeVariable)pta, (WildcardType)rta)) {
337                    continue;
338                  }
339                  yield false;
340                }
341                throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind());
342              } else if (rta.getKind() == TypeKind.TYPEVAR) {
343                if (pta.getKind() == TypeKind.TYPEVAR) {
344                  // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter
345                  // [type argument] are both type variables and the upper bound of the required [receiver] type
346                  // parameter [type argument] [a type variable has many bounds?] is assignable to the upper bound [a
347                  // type variable has many bounds?], if any, of the bean [payload] type parameter [type argument]"
348                  //
349                  // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the
350                  // *payload* type argument.)
351                  if (condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds((TypeVariable)pta, (TypeVariable)rta)) {
352                    continue;
353                  }
354                  yield false;
355                }
356                throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind());
357              }
358              throw new AssertionError("Unexpected receiver type argument kind: " + rta.getKind());
359            }
360            yield true; // we passed all the type argument assignability tests
361          }
362          yield false; // the type arguments' non-generic classes or raw types were not identical
363        }
364        default -> throw new AssertionError("Unexpected receiver kind: " + receiver.getKind());
365      };
366      // "A [non-generic or] raw bean type [payload] is considered assignable..."
367      case TypeMirror nonGenericOrRawPayload -> switch (receiver) {
368        // "...to a parameterized required type [receiver]..."
369        case DeclaredType parameterizedReceiver when parameterized(receiver) -> {
370          // "...if the[ir] [non-generic classes or] raw types are identical and all type parameters [type arguments] of
371          // the required type [receiver] are either unbounded type variables or java.lang.Object."
372          yield
373            this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), nonGenericOrRawPayload) &&
374            allTypeArgumentsAre(parameterizedReceiver.getTypeArguments(),
375                                ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject));
376        }
377        // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in
378        // matches(), not here.]
379        case DeclaredType nonGenericOrRawReceiver when receiver.getKind() == TypeKind.DECLARED -> {
380          yield false; // or yield this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload);
381        }
382        default -> throw new AssertionError("Unexpected payload kind: " + payload.getKind() + "; receiver: " + receiver + "; payload: " + payload);
383      };
384    };
385  }
386
387  // Is payload "identical to" receiver, following the intent of CDI? The relation "identical to" is not defined in the
388  // specification. Does it mean ==? Does it mean equals()? Does it mean
389  // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)? Something else?
390  //
391  // This implementation chooses TypeAndElementSource#sameType(TypeMirror, TypeMirror), but with one
392  // change. TypeAndElementSource#sameType(TypeMirror, TypeMirror) is usually backed by the
393  // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror) method. That method will return false if either
394  // argument is a wildcard type. This method first checks to see if the arguments are the same Java references (==),
395  // regardless of type.
396  private final boolean identical(final TypeMirror receiver, final TypeMirror payload) {
397    // CDI has an undefined notion of "identical to". This method attempts to divine and implement the intent. Recall
398    // that javax.lang.model.* compares types with "sameType" semantics.
399    return receiver == payload || this.tes.sameType(receiver, payload);
400  }
401
402  // Is the "actual" type argument represented by payload assignable to all of the receiver's condensed bounds?
403  //
404  // Pay close attention to how this is called, i.e. what is payload and what is receiver is often "backwards". For
405  // example:
406  //
407  //   @Inject Foo<String> foo; <-- Bean<T extends CharSequence> // is the String "actual type" (payload) assignable to T's (receiver's) CharSequence bound?
408  //   //          ^ payload!            ^ receiver!
409  //
410  // Recall that after condensing "T extends CharSequence" you get CharSequence.
411  //
412  // Recall that if, instead, you had T extends S and S extends CharSequence, condensing T still yields CharSequence.
413  private final boolean assignableToCondensedTypeVariableBounds(final TypeVariable receiver, final ReferenceType payload) {
414    assert receiver.getKind() == TypeKind.TYPEVAR;
415    assert actual(payload); // We can assert this only because of where the method is called.
416    return covariantlyAssignable(List.of(receiver), List.of(payload)); // deliberately List.of(payload) and not condense(payload)
417  }
418
419  // Is candidate covariantly assignable to w's condensed extends (upper) bound?
420  private final boolean assignableToCondensedExtendsBound(final WildcardType w, final ReferenceType candidate) {
421    assert w.getKind() == TypeKind.WILDCARD;
422    assert actual(candidate); // We can assert this only because of where this method is called from.
423    final ReferenceType extendsBound = (ReferenceType)w.getExtendsBound();
424    return extendsBound == null || switch (extendsBound.getKind()) {
425    case ARRAY, DECLARED -> covariantlyAssignable(extendsBound, candidate);
426    case TYPEVAR -> covariantlyAssignable((ReferenceType)this.condense(extendsBound), List.of(candidate));
427    default -> throw new AssertionError();
428    };
429  }
430
431  // It's not immediately clear what CDI means by a type variable's upper bound. In javax.lang.model.type parlance, the
432  // upper bound of a TypeVariable could be an IntersectionType, which java.lang.reflect.TypeVariable represents as a
433  // collection of bounds. CDI blundered into this earlier: https://issues.redhat.com/browse/CDI-440 and
434  // https://github.com/jakartaee/cdi/issues/682
435  //
436  // "the upper bound of the required type parameter [receiver type argument] is assignable to the upper bound, if any,
437  // of the bean type parameter [payload type argument]" (when both arguments are type variables) should *actually*
438  // read:
439  //
440  // "for each bound, PTA, of the bean type parameter [payload type argument], there is at least one bound, RTA, of the
441  // required type parameter [receiver type argument], which is assignable to PTA."
442  //
443  // The TCK enforces this, even though it's not in the specification (!).
444  //
445  // Weld's implementation confuses type parameters with arguments, just like the specification. They have a series of
446  // methods implemented as part of PR 614 (https://github.com/weld/core/pull/614) named "parametersMatch" [arguments
447  // match].
448  //
449  // Weld also has methods named things like "getUppermostTypeVariableBounds" and "getUppermostBounds". These appear to
450  // "condense" useless type variable extensions to "get to" the "real" types involved. So T extends S extends String
451  // becomes String.
452  //
453  // Digging deeper, you (I) might think getUppermostTypeVariableBounds(tv) is just erase(tv) applied recursively. But I
454  // think you would be wrong.
455  //
456  // Start with:
457  // https://github.com/openjdk/jdk/blob/181845ae46157a9bb3bf8e2a328fa59eddc0273a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2450
458  //
459  // Compare vs.:
460  // https://github.com/weld/core/blob/e894d1699ff1c91332605f5ecae5f53410effb81/impl/src/main/java/org/jboss/weld/resolution/AbstractAssignabilityRules.java#L57-L62
461  //
462  // To illustrate the difference between the two operations, recursive erasure (according to the Java Language
463  // Specification) of the pseudo-declaration T extends S extends List<String> & Serializable would yield, simply, List
464  // (T would erease to "the erasure of its leftmost bound", which would be the erasure of S, which would be "the
465  // erasure of its leftmost bound", which would be the erasure of List<String>, which would be List. Serializable just
466  // gets dropped.)
467  //
468  // By contrast, Weld's getUppermostTypeVariableBounds(T) operation would yield [List<String>, Serializable] (have not
469  // tested this with code, just reading).
470  //
471  // I think this is what the javac compiler calls, somewhat confusingly, "classBound":
472  // https://github.com/openjdk/jdk/blob/jdk-24%2B7/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2760-L2796
473  //
474  // So then:
475  //
476  // For every bound in (condensed) receiverBounds, is there a bound in (condensed) payloadBounds that is covariantly
477  // assignable to it?
478  //
479  // (Is there one bound in (condensed) payloadBounds that matches all bounds in (condensed) receiver bounds?)
480  //
481  // Throws ClassCastException if, after condensing, any encountered bound is not either an ArrayType or a DeclaredType.
482  private final boolean covariantlyAssignable(final List<? extends TypeMirror> receiverBounds, List<? extends TypeMirror> payloadBounds) {
483    payloadBounds = this.condense(payloadBounds);
484    for (final TypeMirror condensedReceiverBound : this.condense(receiverBounds)) {
485      if (!covariantlyAssignable((ReferenceType)condensedReceiverBound, payloadBounds)) {
486        return false;
487      }
488    }
489    return true;
490  }
491
492  // Is there a DeclaredType-or-ArrayType bound in condensedPayloadBounds that is assignable to classOrArrayTypeReceiver
493  // using Java, not CDI, assignability semantics?
494  //
495  // It is assumed condensedPayloadBounds is the result of a condense() call.
496  //
497  // Throws ClassCastException or IllegalArgumentException if any encountered type is neither an ArrayType nor a
498  // DeclaredType.
499  private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final List<? extends TypeMirror> condensedPayloadBounds) {
500    assert actual(classOrArrayTypeReceiver); // We can assert this only because of where this method is called.
501    return switch (classOrArrayTypeReceiver.getKind()) {
502    case ARRAY, DECLARED -> {
503      for (final TypeMirror condensedPayloadBound : condensedPayloadBounds) {
504        assert actual(condensedPayloadBound);
505        if (covariantlyAssignable(classOrArrayTypeReceiver, (ReferenceType)condensedPayloadBound)) {
506          yield true;
507        }
508      }
509      yield false;
510    }
511    default -> throw new IllegalArgumentException("classOrArrayTypeReceiver: " + classOrArrayTypeReceiver + "; kind: " + classOrArrayTypeReceiver.getKind());
512    };
513  }
514
515  // Is classOrArrayTypePayload assignable to classOrArrayTypeReceiver following the rules of Java assignability
516  // (i.e. covariance)?
517  //
518  // The types are ReferenceTypes because this is only ever invoked in the context of type arguments.
519  private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final ReferenceType classOrArrayTypePayload) {
520    assert actual(classOrArrayTypeReceiver); // Based on where and how this method is called.
521    assert actual(classOrArrayTypePayload); // Based on where and how this method is called.
522    return
523      classOrArrayTypeReceiver == classOrArrayTypePayload || // Optimization
524      // Note that TypeAndElementSource#assignable(TypeMirror, TypeMirror) follows the lead of
525      // javax.lang.model.util.Types#isAssignable(TypeMirror, TypeMirror) where the "payload" is the *first* parameter
526      // and the "receiver" is the *second* argument.
527      this.tes.assignable(classOrArrayTypePayload, classOrArrayTypeReceiver); // yes, "backwards"
528  }
529
530  // Are payload's condensed bounds assignable to receiver's condensed extends bound (upper bound)?
531  private final boolean condensedTypeVariableBoundsAssignableToCondensedExtendsBound(final WildcardType receiver, final TypeVariable payload) {
532    assert receiver.getKind() == TypeKind.WILDCARD;
533    assert payload.getKind() == TypeKind.TYPEVAR;
534    // "...the upper bound of the type variable [a type variable has many bounds?] is assignable TO [...] the upper
535    // bound, if any, of the wildcard..."
536    final TypeMirror extendsBound = receiver.getExtendsBound();
537    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
538    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
539    return extendsBound == null || covariantlyAssignable(List.of((ReferenceType)extendsBound), List.of(payload));
540  }
541
542  // Is payload's condensed extends bound (upper bound) covariantly assignable to receiver's condensed bounds?
543  private final boolean condensedTypeVariableBoundsAssignableFromCondensedExtendsBound(final TypeVariable receiver, final WildcardType payload) {
544    assert receiver.getKind() == TypeKind.TYPEVAR;
545    assert payload.getKind() == TypeKind.WILDCARD;
546    // "...the upper bound of the type variable [a type variable has many bounds?] is assignable [...] FROM the upper
547    // bound, if any, of the wildcard..."
548    final TypeMirror extendsBound = payload.getExtendsBound();
549    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
550    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
551    return extendsBound == null || covariantlyAssignable(List.of(receiver), List.of((ReferenceType)extendsBound));
552  }
553
554  // Is payload's super bound (lower bound) covariantly assignable to receiver's condensed bounds?
555  private final boolean condensedTypeVariableBoundsAssignableFromCondensedSuperBound(final TypeVariable receiver, final WildcardType payload) {
556    assert receiver.getKind() == TypeKind.TYPEVAR;
557    assert payload.getKind() == TypeKind.WILDCARD;
558    final TypeMirror superBound = payload.getSuperBound();
559    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
560    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
561    return superBound == null || covariantlyAssignable(List.of(receiver), List.of(superBound));
562  }
563
564  // Are payload's condensed bounds covariantly assignable to receiver's condensed bounds?
565  private final boolean condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeVariable payload) {
566    assert receiver.getKind() == TypeKind.TYPEVAR;
567    assert payload.getKind() == TypeKind.TYPEVAR;
568    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
569    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
570    return covariantlyAssignable(List.of(receiver), List.of(payload));
571  }
572
573  // Is payload's condensed super bound (lower bound) covariantly assignable to receiver?
574  //
575  // Since either of a wildcard type's bounds may be a type variable, and since a type variable's bound may be an
576  // intersection type, it follows that after condensation either of a wildcard type's bounds may be a list of actual
577  // types.
578  //
579  // Therefore this is really: Is there one bound among payload's actual super bounds that is assignable to receiver?
580  private final boolean assignableFromCondensedSuperBound(final ReferenceType receiver, final WildcardType payload) {
581    assert payload.getKind() == TypeKind.WILDCARD;
582    assert actual(receiver); // We can assert this only because of where this method is called from.
583    final ReferenceType superBound = (ReferenceType)payload.getSuperBound();
584    return superBound == null || switch (superBound.getKind()) {
585    case ARRAY, DECLARED -> covariantlyAssignable(receiver, superBound);
586    case TYPEVAR -> covariantlyAssignable(receiver, (ReferenceType)this.condense(superBound));
587    default -> throw new AssertionError();
588    };
589  }
590
591  // Get the raw type yielded by t, assuming t is the sort of type that can yield a raw type.
592  //
593  // An array type with a parameterized element type can yield a raw type.
594  //
595  // A declared type that is parameterized can yield a raw type.
596  //
597  // No other type yields a raw type.
598  //
599  // t cannot be null.
600  private final TypeMirror rawType(final TypeMirror t) {
601    return switch (t.getKind()) {
602    case ARRAY -> {
603      final TypeMirror et = elementType(t);
604      if (!parameterized(et)) {
605        throw new IllegalArgumentException("t is an array type whose element type is not parameterized so cannot yield a raw type");
606      }
607      yield this.arrayTypeOf(this.erasure(et));
608    }
609    case DECLARED -> {
610      if (!parameterized(t)) {
611        throw new IllegalArgumentException("t is a declared type that is not parameterized so cannot yield a raw type");
612      }
613      yield this.erasure(t);
614    }
615    default -> throw new IllegalArgumentException("t is a " + t.getKind() + " type and so cannot yield a raw type");
616    };
617  }
618
619  // Return t if its element declares a non-generic class, or if it is the raw type usage of a generic class.
620  private final TypeMirror nonGenericClassOrRawType(final TypeMirror t) {
621    return yieldsRawType(t) ? this.rawType(t) : t;
622  }
623
624  // Erase t and return its erasure. Erasing is a complex business that can involve the creation of new types.
625  private final TypeMirror erasure(final TypeMirror t) {
626    return this.tes.erasure(t);
627  }
628
629  // Return a possibly new ArrayType whose component type is the supplied componentType.
630  private final ArrayType arrayTypeOf(final TypeMirror componentType) {
631    return this.tes.arrayTypeOf(componentType);
632  }
633
634  // "Condenses" t, according to the following rules.
635  //
636  // If t is null, returns List.of().
637  //
638  // If t is a type variable, returns the result of condensing its upper bound (thus eliminating the type variable).
639  //
640  // If t is an intersection type, returns the result of condensing its bounds (thus eliminating the intersection type).
641  //
642  // If t is a wildcard type, returns the result of condensing either its super or extends bound (thus eliminating the
643  // wildcard type). The result of condensing a wildcard type that declares neither bound (?) is the result of
644  // condensing the type declared by java.lang.Object.
645  //
646  // In all other cases returns List.of(t).
647  //
648  // Note especially this is not type erasure, though it has some similarities.
649  //
650  // See #condense(List) below.
651  private final List<? extends TypeMirror> condense(final TypeMirror t) {
652    return t == null ? List.of() : switch (t.getKind()) {
653    case INTERSECTION -> this.condense(((IntersectionType)t).getBounds());
654    case TYPEVAR      -> this.condense(((TypeVariable)t).getUpperBound());
655    case WILDCARD     -> {
656      final WildcardType w = (WildcardType)t;
657      final TypeMirror s = w.getSuperBound();
658      final TypeMirror e = w.getExtendsBound();
659      if (s == null) {
660        yield e == null ? List.of(this.tes.declaredType("java.lang.Object")) : this.condense(e);
661      } else if (e == null) {
662        yield this.condense(s);
663      } else {
664        throw new AssertionError();
665      }
666    }
667    default           -> List.of(t); // t's bounds are defined solely by itself, or it's a wildcard and we didn't say which bounds
668    };
669  }
670
671  // If ts is empty, returns an empty unmodifiable List.
672  //
673  // If ts consists of a single element, returns the result of condensing t (see #condense(TypeMirror) above).
674  //
675  // Otherwise creates a new list, condenses each element of ts, and adds each element of the condensation to the new
676  // list, and returns an unmodifiable view of the new list.
677  //
678  // Note that deliberately duplicates are not eliminated from the new list.
679  private final List<? extends TypeMirror> condense(final List<? extends TypeMirror> ts) {
680    final int size = ts.isEmpty() ? 0 : ts.size();
681    if (size == 0) {
682      return List.of();
683    }
684    // We take care not to allocate/copy new lists needlessly.
685    ArrayList<TypeMirror> newBounds = null;
686    for (int i = 0; i < size; i++) {
687      final TypeMirror t = ts.get(i);
688      switch (t.getKind()) {
689      case INTERSECTION:
690      case TYPEVAR:
691      case WILDCARD:
692        // These are the only types where condensing does anything. In these cases alone we need to make a new list.
693        if (newBounds == null) {
694          newBounds = new ArrayList<>(size * 2);
695          if (i > 0) {
696            // All ts up to this point have been non-condensable types, so catch up and add them as-is.
697            newBounds.addAll(ts.subList(0, i));
698          }
699        }
700        newBounds.addAll(condense(t));
701        break;
702      default:
703        if (newBounds != null) {
704          newBounds.add(t);
705        }
706        break;
707      }
708    }
709    if (newBounds == null) {
710      return ts;
711    }
712    newBounds.trimToSize();
713    return Collections.unmodifiableList(newBounds);
714  }
715
716  // Is t an unbounded type variable?
717  //
718  // CDI does not define what an "unbounded type variable" is. This method attempts to divine and implement the intent.
719  //
720  // Since according to the Java Language Specification, all type variables have an upper bound, which in the
721  // pathological case is java.lang.Object, it would seem that "unbounded" means "has java.lang.Object as its upper
722  // bound".
723  //
724  // It is not clear in the case of T extends S whether T is an unbounded TypeVariable or not. This interpretation
725  // behaves as if it is.
726  //
727  // Weld seems to take the position that an unbounded type variable is one that has java.lang.Object as its sole upper
728  // bound; see
729  // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L258. Under this
730  // interpretation T extends S would not be considered an unbounded type variable. Type variable bounds are erased in
731  // every other situation in CDI.
732  private final boolean unboundedTypeVariable(final TypeMirror t) {
733    return switch (t.getKind()) {
734    case TYPEVAR -> {
735      final List<? extends TypeMirror> condensedBounds = this.condense(((TypeVariable)t).getUpperBound());
736      yield condensedBounds.size() == 1 && isJavaLangObject(condensedBounds.get(0));
737    }
738    default -> false;
739    };
740  }
741
742
743  /*
744   * Static methods.
745   */
746
747
748  // Does e represent a generic declaration?
749  //
750  // A declaration is generic if it declares one or more type parameters.
751  //
752  // Since an annotation interface cannot declare type parameters, it follows that TypeElements representing annotation
753  // instances are never generic.
754  private static final boolean generic(final Element e) {
755    return switch (e.getKind()) {
756    case CLASS, CONSTRUCTOR, ENUM, INTERFACE, METHOD, RECORD -> !((Parameterizable)e).getTypeParameters().isEmpty();
757    default -> false;
758    };
759  }
760
761  // Is t the usage of a generic class, i.e. a usage (whether raw or parameterized) of a generic class declaration?
762  //
763  // t is deemed to be generic if it is a declared type whose defining element (its type declaration) is generic.
764  //
765  // Array types are never generic.
766  private static final boolean generic(final TypeMirror t) {
767    return
768      t.getKind() == TypeKind.DECLARED &&
769      generic(((DeclaredType)t).asElement());
770  }
771
772  // Is t a parameterized type (and not a raw type) strictly according to the rules of the Java Language Specification?
773  //
774  // A type is parameterized if it is a declared type with a non-empty list of type arguments. No other type is
775  // parameterized.
776  private static final boolean parameterized(final TypeMirror t) {
777    return
778      t.getKind() == TypeKind.DECLARED &&
779      !((DeclaredType)t).getTypeArguments().isEmpty();
780  }
781
782  // Is t a type that CDI considers to be "parameterized"?
783  //
784  // There are some cases, but not all, where CDI (incorrectly) considers an array type to be something that can be
785  // parameterized, or else "bean type parameter" resolution would never work. See
786  // https://stackoverflow.com/questions/76493672/when-cdi-speaks-of-a-parameterized-type-does-it-also-incorrectly-mean-array-typ.
787  //
788  // The semantics CDI wants to express are really: can t *yield* a raw type, for a certain definition of "yield"? See
789  // #yieldsRawType(TypeMirror) below.
790  private static final boolean cdiParameterized(final TypeMirror t) {
791    return yieldsRawType(t);
792  }
793
794  // Can t *yield* a raw type?
795  //
796  // We say that to yield a raw type, t must be either:
797  //
798  // * an array type with a parameterized element type
799  // * a declared type with at least one type argument
800  private static final boolean yieldsRawType(final TypeMirror t) {
801    return parameterized(t) || t.getKind() == TypeKind.ARRAY && parameterized(elementType(t));
802  }
803
804  // Is t a raw type, following the rules of the Java Language Specification, not CDI?
805  //
806  // A raw type is either "the erasure of a parameterized type" (so List<String>'s raw type is List, clearly not
807  // List<?>, and not List<E>) or "an array type whose element type is a raw type" (so List<String>[]'s raw type is
808  // List[]). (String is not a raw type; its element defines no type parameters.)
809  //
810  // No other type is a raw type.
811  //
812  // CDI gets confused and uses "raw" in different senses. The sense used here is that of the Java Language
813  // Specification.
814  private static final boolean raw(final TypeMirror t) {
815    return switch (t.getKind()) {
816    case ARRAY -> raw(elementType((ArrayType)t));
817    case DECLARED -> generic(t) && ((DeclaredType)t).getTypeArguments().isEmpty();
818    case TYPEVAR -> false; // called out explicitly just so you realize it
819    default -> false;
820    };
821  }
822
823  // Is t a declared type that bears the supplied fully qualified name?
824  private static final boolean declaredTypeNamed(final TypeMirror t, final CharSequence n) {
825    return
826      t.getKind() == TypeKind.DECLARED &&
827      named(((DeclaredType)t), n);
828  }
829
830  // Regardless of its reported TypeKind, does t's declaring TypeElement bear the supplied fully qualified name?
831  //
832  // Throws ClassCastException if the return value of t.asElement() is not a TypeElement.
833  private static final boolean named(final DeclaredType t, final CharSequence n) {
834    // (No getKind() check on purpose.)
835    return ((QualifiedNameable)t.asElement()).getQualifiedName().contentEquals(n);
836  }
837
838  // Do all supplied TypeMirrors, when used as type arguments, pass the test represented by the supplied Predicate?
839  private static final boolean allTypeArgumentsAre(final Iterable<? extends TypeMirror> typeArguments, final Predicate<? super TypeMirror> p) {
840    for (final TypeMirror t : typeArguments) {
841      if (!p.test(t)) {
842        return false;
843      }
844    }
845    return true;
846  }
847
848  // Is e a TypeElement representing java.lang.Object?
849  private static final boolean isJavaLangObject(final Element e) {
850    return
851      e.getKind() == ElementKind.CLASS &&
852      ((QualifiedNameable)e).getQualifiedName().contentEquals("java.lang.Object");
853  }
854
855  // Is t a DeclaredType whose asElement() method returns an Element representing java.lang.Object?
856  private static final boolean isJavaLangObject(final TypeMirror t) {
857    return
858      t.getKind() == TypeKind.DECLARED &&
859      isJavaLangObject(((DeclaredType)t).asElement());
860  }
861
862  // Is t an "actual type"?
863  //
864  // CDI mentions actual types but does not define what they are. This method attempts to divine and implement the
865  // intent.
866  //
867  // A comment in a closed bug report (CDI-502)
868  // (https://issues.redhat.com/browse/CDI-502?focusedId=13036118&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13036118)
869  // by one of the reference implementation's authors (Jozef Hartinger) provides the only definition:
870  //
871  // "An actual type is a type that is not a wildcard nor [sic] an unresolved [sic] type variable."
872  //
873  // The Java Language Specification does not mention anything about "actual types" or type variable "resolution". CDI
874  // does not mention anything about type variable "resolution".
875  //
876  // (Clearly type variable "resolution" must simply be the process of supplying a type argument for a type parameter.)
877  //
878  // Presumably the null type is not intended to be an actual type either.
879  //
880  // More strictly, therefore, the intent seems to be that an actual type is an array, declared or primitive type, and
881  // none other.
882  //
883  // See also: https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L213
884  private static final boolean actual(final TypeMirror t) {
885    return switch (t.getKind()) {
886    case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT -> true;
887    default -> false;
888    };
889  }
890
891  // Returns the element type of t.
892  //
893  // The element type of an array type is the element type of its component type.
894  //
895  // The element type of every other kind of type is the type itself.
896  private static final TypeMirror elementType(final TypeMirror t) {
897    return t.getKind() == TypeKind.ARRAY ? elementType(((ArrayType)t).getComponentType()) : t;
898  }
899
900}