001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023–2025 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.util.ArrayList;
019import java.util.Collections;
020import java.util.List;
021import java.util.Objects;
022
023import java.util.function.Predicate;
024
025import javax.lang.model.element.Element;
026import javax.lang.model.element.ElementKind;
027import javax.lang.model.element.Parameterizable;
028import javax.lang.model.element.QualifiedNameable;
029
030import javax.lang.model.type.ArrayType;
031import javax.lang.model.type.DeclaredType;
032import javax.lang.model.type.IntersectionType;
033import javax.lang.model.type.ReferenceType;
034import javax.lang.model.type.TypeKind;
035import javax.lang.model.type.TypeMirror;
036import javax.lang.model.type.TypeVariable;
037import javax.lang.model.type.WildcardType;
038
039import org.microbean.assign.AbstractTypeMatcher;
040
041import org.microbean.construct.Domain;
042
043/**
044 * An {@link AbstractTypeMatcher} encapsulating <a
045 * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">CDI-compatible
046 * type matching rules</a>.
047 *
048 * @author <a href="https://about.me/lairdnelson/" target="_top">Laird Nelson</a>
049 *
050 * @see #test(TypeMirror, TypeMirror)
051 */
052public final class BeanTypeMatcher extends AbstractTypeMatcher {
053
054
055  /*
056   * Static fields.
057   */
058
059
060  private static final Logger LOGGER = System.getLogger(BeanTypeMatcher.class.getName());
061
062
063  /*
064   * Constructors.
065   */
066
067
068  /**
069   * Creates a new {@link BeanTypeMatcher}.
070   *
071   * @param domain a {@link Domain}; must not be {@code null}
072   *
073   * @exception NullPointerException if {@code domain} is {@code null}
074   *
075   * @see Domain
076   */
077  public BeanTypeMatcher(final Domain domain) {
078    super(domain);
079  }
080
081
082  /*
083   * Instance methods.
084   */
085
086
087  /*
088   * Public methods.
089   */
090
091  /**
092   * Returns {@code true} if and only if the supplied {@code payload} argument <a
093   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution"><em>matches</em></a>
094   * the supplied {@code receiver} argument, according to the rules defined by <a
095   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section
096   * 2.4.2.1 of the CDI specification</a>.
097   *
098   * @param receiver the left hand side of a type assignment; must not be {@code null}
099   *
100   * @param payload the right hand side of a type assignment; must not be {@code null}
101   *
102   * @return {@code true} if and only if the supplied {@code payload} argument <a
103   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">matches</a>
104   * the supplied {@code receiver} argument, according to the rules defined by <a
105   * href="https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution">section
106   * 2.4.2.1 of the CDI specification</a>; {@code false} otherwise
107   *
108   * @exception NullPointerException if either argument is {@code null}
109   *
110   * @exception IllegalArgumentException if either type is any type other than an {@linkplain TypeKind#ARRAY array
111   * type}, a {@link TypeKind#isPrimitive() primitive type}, or a {@linkplain TypeKind#DECLARED declared type}
112   */
113  // Is the payload assignable to the receiver? That is, does the payload "match the receiver", in CDI parlance?
114  @Override // Matcher<TypeMirror, TypeMirror>
115  public final boolean test(final TypeMirror receiver, final TypeMirror payload) {
116    // "A bean is assignable to a given injection point if:
117    //
118    // "The bean has a [legal] bean type [payload] that matches the [legal] required type [receiver]. For this
119    // purpose..."
120    return receiver == Objects.requireNonNull(payload, "payload") || switch (receiver.getKind()) {
121
122      case ARRAY    -> switch (payload.getKind()) {
123        // "...array types are considered to match only if their element types [note: not component types] are
124        // identical ['identical' is actually undefined in the specification]..."
125        case ARRAY                                                -> this.identical(this.domain().elementType((ArrayType)receiver), this.domain().elementType((ArrayType)payload));
126        case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false;
127        case DECLARED                                             -> false;
128        default                                                   -> throw illegalPayload(payload);
129      };
130
131      // "...primitive types are considered to match their corresponding wrapper types in java.lang..."
132      case BOOLEAN  -> switch (payload.getKind()) {
133        case ARRAY                                                -> false;
134        case BOOLEAN                                              -> true;
135        case          BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false;
136        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Boolean");
137        default                                                   -> throw illegalPayload(payload);
138      };
139
140      case BYTE     -> switch (payload.getKind()) {
141        case ARRAY                                                -> false;
142        case BYTE                                                 -> true;
143        case BOOLEAN,       CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false;
144        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Byte");
145        default                                                   -> throw illegalPayload(payload);
146      };
147
148      case CHAR     -> switch (payload.getKind()) {
149        case ARRAY                                                -> false;
150        case CHAR                                                 -> true;
151        case BOOLEAN, BYTE,       DOUBLE, FLOAT, INT, LONG, SHORT -> false;
152        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Character");
153        default                                                   -> throw illegalPayload(payload);
154      };
155
156      case DOUBLE   -> switch (payload.getKind()) {
157        case ARRAY                                                -> false;
158        case DOUBLE                                               -> true;
159        case BOOLEAN, BYTE, CHAR,         FLOAT, INT, LONG, SHORT -> false;
160        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Double");
161        default                                                   -> throw illegalPayload(payload);
162      };
163
164      case FLOAT    -> switch (payload.getKind()) {
165        case ARRAY                                                -> false;
166        case FLOAT                                                -> true;
167        case BOOLEAN, BYTE, CHAR, DOUBLE,        INT, LONG, SHORT -> false;
168        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Float");
169        default                                                   -> throw illegalPayload(payload);
170      };
171
172      case INT      -> switch (payload.getKind()) {
173        case ARRAY                                                -> false;
174        case INT                                                  -> true;
175        case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT,      LONG, SHORT -> false;
176        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Integer");
177        default                                                   -> throw illegalPayload(payload);
178      };
179
180      case LONG     -> switch (payload.getKind()) {
181        case ARRAY                                                -> false;
182        case LONG                                                 -> true;
183        case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT,       SHORT -> false;
184        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Long");
185        default                                                   -> throw illegalPayload(payload);
186      };
187
188      case SHORT    -> switch (payload.getKind()) {
189        case ARRAY                                                -> false;
190        case SHORT                                                -> true;
191        case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG        -> false;
192        case DECLARED                                             -> named((DeclaredType)payload, "java.lang.Short");
193        default                                                   -> throw illegalPayload(payload);
194      };
195
196      case DECLARED -> switch (payload.getKind()) {
197        // Implied.
198        case ARRAY    -> false;
199
200        // "...primitive types are considered to match their corresponding wrapper types in java.lang..."
201        case BOOLEAN  -> named((DeclaredType)receiver, "java.lang.Boolean");
202        case BYTE     -> named((DeclaredType)receiver, "java.lang.Byte");
203        case CHAR     -> named((DeclaredType)receiver, "java.lang.Character");
204        case DOUBLE   -> named((DeclaredType)receiver, "java.lang.Double");
205        case FLOAT    -> named((DeclaredType)receiver, "java.lang.Float");
206        case INT      -> named((DeclaredType)receiver, "java.lang.Integer");
207        case LONG     -> named((DeclaredType)receiver, "java.lang.Long");
208        case SHORT    -> named((DeclaredType)receiver, "java.lang.Short");
209
210        // "Parameterized and raw types [and non-generic classes, and non-array types]..."
211        case DECLARED ->
212          // "...are considered to match if they are identical [undefined]..."
213          this.identical(receiver, payload) ||
214          // "...or if the bean type [payload] is assignable to [see #assignable(TypeMirror, TypeMirror)] the required type
215          // [receiver]...."
216          this.assignable((DeclaredType)receiver, (DeclaredType)payload);
217
218        default       -> throw illegalPayload(payload);
219      };
220
221      default       -> throw illegalReceiver(receiver);
222    };
223  }
224
225  /*
226   * Private methods.
227   */
228
229  private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) {
230    assert receiver.getKind() == TypeKind.DECLARED;
231    assert payload.getKind() == TypeKind.DECLARED;
232    return switch (payload) {
233
234      case DeclaredType parameterizedPayload when this.domain().parameterized(payload) -> switch (receiver) {
235        // "A parameterized bean type [parameterizedPayload] is considered assignable..."
236
237        case DeclaredType nonGenericOrRawReceiver when !this.domain().generic(receiver) || this.domain().raw(receiver) ->
238          // "...to a [non-generic class or] raw required type [nonGenericOrRawReceiver] if the [non-generic class or]
239          // raw types are identical [undefined] and all type parameters [type arguments] of the bean type
240          // [parameterizedPayload] are either unbounded type variables [undefined] or java.lang.Object."
241          this.identical(this.nonGenericClassOrRawType(nonGenericOrRawReceiver),
242                         this.nonGenericClassOrRawType(parameterizedPayload)) &&
243          allAre(parameterizedPayload.getTypeArguments(),
244                 ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(this.domain()::javaLangObject));
245
246        case DeclaredType parameterizedReceiver -> {
247          // "...to a parameterized required type [parameterizedReceiver]..."
248
249          if (this.identical(this.domain().rawType(parameterizedReceiver), this.domain().rawType(parameterizedPayload))) {
250            // "...if they have identical raw type [really if their declarations/elements are 'identical']..."
251
252            final List<? extends TypeMirror> rtas = parameterizedReceiver.getTypeArguments();
253            final List<? extends TypeMirror> ptas = parameterizedPayload.getTypeArguments();
254            assert rtas.size() == ptas.size();
255            for (int i = 0; i < rtas.size(); i++) {
256              final TypeMirror rta = rtas.get(i);
257              final TypeMirror pta = ptas.get(i);
258              // "...and for each parameter [type argument pair] the required type parameter [receiver type argument,
259              // rta] and the bean type parameter [payload type argument, pta]..."
260
261              switch (rta.getKind()) {
262              case ARRAY: // rta
263              case DECLARED: // rta
264                switch (pta.getKind()) {
265
266                case ARRAY: // pta
267                case DECLARED: // pta
268                  // "...are actual types [non-type variable, non-wildcard reference types]..."
269                  //
270                  // CDI mentions actual types but does not define what they are. This method attempts to divine and
271                  // implement the intent.
272                  //
273                  // A comment in a closed bug report (CDI-502)
274                  // (https://issues.redhat.com/browse/CDI-502?focusedId=13036118&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13036118)
275                  // by one of the reference implementation's authors (Jozef Hartinger) provides the only definition:
276                  //
277                  // "An actual type is a type that is not a wildcard nor [sic] an unresolved [sic] type variable."
278                  //
279                  // The Java Language Specification does not mention anything about "actual types" or type variable
280                  // "resolution". CDI does not mention anything about type variable "resolution".
281                  //
282                  // (Clearly type variable "resolution" must simply be the process of supplying a type argument for a
283                  // type parameter.)
284                  //
285                  // Presumably the null type is not intended to be an actual type either.
286                  //
287                  // More strictly, therefore, the intent seems to be that an actual type is an array, declared or
288                  // primitive type, and none other.
289                  //
290                  // See also:
291                  // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L213
292
293                  if (this.identical(this.nonGenericClassOrRawType(rta), this.nonGenericClassOrRawType(pta))) {
294                    // "...with identical [non-generic classes or] raw types[s], and, if the type [?] is parameterized
295                    // [?]..."
296                    //
297                    // For a maximally illustrative example, let rta and pta be array types with parameterized element
298                    // types, such as List<Number>[] and List<String>[]. Then per the Java Language Specification (JLS)
299                    // their raw types are List[] and List[], and their parameterized element types are List<Number> and
300                    // List<String>. According to the JLS, neither List<Number>[] nor List<String>[] is parameterized.
301                    //
302                    // In this example, if we get here, we have only proven that List[] is "identical to" List[].
303                    //
304                    // The "if the type is parameterized" clause is tough. Which type is under discussion? "the type"
305                    // seems to refer to the "identical raw type". But a raw type by definition is not parameterized, so
306                    // this clause would seem to be superfluous and thus never apply, and so List<Number>[] :=
307                    // List<String>[] is OK. Oops. So not that interpretation.
308                    //
309                    // What if instead the "if the type is parameterized" clause means the receiver type itself and
310                    // somehow loosely the payload type as well? Well, clearly it cannot correctly do this, since an
311                    // array type is never a parameterized type. Same bad result. Oops. So not that interpretation.
312                    //
313                    // Or what if "identical raw type" really means "identical raw type (or identical component type if
314                    // they are array types)"? That would be most efficient, since it would rule out List<Number>[] :=
315                    // List<String>[] right off the bat: we wouldn't even get here. But that really doesn't seem to be
316                    // what is meant. So probably not that interpretation.
317                    //
318                    // What if the "if the type is parameterized" clause really means "if the element type declaration
319                    // used by both the receiver and payload types is generic"? That would work. That's also
320                    // semantically equivalent to something like: "...if at least one of the two arguments is a
321                    // parameterized type [e.g. List<Number>, not List<Number>[], not String, not List], or at least one
322                    // of the two types is an array type with a parameterized element type [e.g. List<Number>[], not
323                    // List[], not String[]]..."
324                    //
325                    // That is the interpretation we apply here. So:
326                    //
327                    //   "...and, if the type [?] is parameterized [?]..."
328                    //
329                    // becomes:
330                    //
331                    //   "...and, if at least one of the two type arguments is a parameterized type, or if at least one
332                    //   of the two types is an array type with a parameterized element type..."
333                    //
334                    // That, in turn, designates any type capable of properly yielding a raw type, while ruling out
335                    // those that can't! That means it is exactly equal to the yieldsRawType(TypeMirror) method,
336                    if (yieldsRawType(rta)) {
337                      assert yieldsRawType(pta); // ...because otherwise their raw types would not have been "identical"
338                      // "...the bean type parameter [type argument] is assignable to the required type parameter [type
339                      // argument] according to [all of] these rules [including 'matching']..."
340                      if (test(rta, pta)) { // note recursion
341                        continue; // or break
342                      }
343                    } else {
344                      assert !yieldsRawType(pta);
345                      continue; // yes, trust me; vetted (or break)
346                    }
347                  }
348                  yield false;
349
350                case TYPEVAR: // pta
351                  // "...the required type parameter [receiver type argument, rta] is an actual type [an array or
352                  // declared type], the bean type parameter [payload type argument, pta] is a type variable, and the
353                  // actual type [rta] is assignable to the upper bound, if any, of the type variable [pta]..."
354                  /*
355                  if (assignableToCondensedTypeVariableBounds((TypeVariable)pta, rta)) {
356                    //
357                    // (This is weird. Yes, this is "backwards"; the *receiver* type argument is being tested to see if
358                    // it is assignable to the *payload* type argument.)
359                    //
360                    // TODO: I think this is fully handled by covariantlyAssignable(pta, rta)
361                    continue;
362                  }
363                  */
364                  if (this.covariantlyAssignable(pta, rta)) {
365                    continue;
366                  }
367                  yield false;
368
369                case WILDCARD: // pta
370                  throw illegalPayload(payload); // bean types can't have wildcard type arguments
371
372                default: // pta
373                  throw new AssertionError();
374                }
375
376              case TYPEVAR: // rta
377                switch (pta.getKind()) {
378
379                case ARRAY: // pta
380                case DECLARED: // pta
381                  yield false; // implied
382
383                case TYPEVAR: // pta
384                  // "...the required type parameter [receiver type argument, rta] and the bean type parameter [payload
385                  // type argument, pta] are both type variables, and the upper bound of the required type parameter
386                  // [rta] [a type variable has many bounds?] is assignable to the upper bound [a type variable has many
387                  // bounds?], if any, of the bean type parameter [pta]..."
388                  /*
389                  if (condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds((TypeVariable)pta, (TypeVariable)rta)) {
390                    //
391                    // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to
392                    // the *payload* type argument.)
393                    //
394                    // TODO: I think this is fully handled by covariantlyAssignable(pta, rta).
395                    continue;
396                  }
397                  */
398                  if (this.covariantlyAssignable(pta, rta)) {
399                    continue;
400                  }
401                  yield false;
402
403                case WILDCARD: // pta
404                  throw illegalPayload(payload); // bean types can't have wildcard type arguments
405
406                default: // pta
407                  throw new AssertionError();
408                }
409
410              case WILDCARD: // rta
411                // "...the required type parameter [receiver type argument, rta] is a wildcard..."
412                switch (pta.getKind()) {
413
414                case ARRAY: // pta
415                case DECLARED: // pta
416                  // "...the bean type parameter [payload type argument, pta] is an actual type [a non-type variable,
417                  // non-wildcard reference type], and the actual type [pta] is assignable to the upper bound, if any,
418                  // of the wildcard [receiver type argument, rta] and assignable from the lower bound, if any of the
419                  // wildcard [rta]..."
420                  /*
421                  if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) &&
422                      assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) {
423                    // TODO: is this simply the contains() relationship? So basically: does rta contain pta?
424                    continue; // (or break)
425                  }
426                  */
427                  if (this.domain().contains(rta, pta)) {
428                    continue;
429                  }
430                  yield false;
431
432                case TYPEVAR: // pta
433                  // "...the required type parameter [receiver type argument, rta] is a wildcard, the bean type
434                  // parameter [payload type argument, pta] is a type variable, and the upper bound of the type variable
435                  // [a type variable has many bounds!] [pta] is assignable to or assignable from the upper bound, if
436                  // any, of the wildcard [rta] and assignable from the lower bound, if any, of the wildcard [rta]"
437                  /*
438                  if ((condensedTypeVariableBoundsAssignableToCondensedExtendsBound((WildcardType)rta, (TypeVariable)pta) ||
439                       condensedTypeVariableBoundsAssignableFromCondensedExtendsBound((TypeVariable)pta, (WildcardType)rta)) &&
440                      condensedTypeVariableBoundsAssignableFromCondensedSuperBound((TypeVariable)pta, (WildcardType)rta)) {
441                    // TODO: is this simply the contains() relationship? So basically: does rta contain pta?
442                    continue;
443                  }
444                  */
445                  if (this.domain().contains(rta, pta)) {
446                    continue;
447                  }
448                  yield false;
449
450                case WILDCARD: // pta
451                  throw illegalPayload(payload); // bean types can't have wildcard type arguments
452
453                default: // pta
454                  throw new AssertionError();
455                }
456
457              default: // rta
458                throw new AssertionError();
459              }
460
461            } // end type argument for loop
462            yield true; // we passed all the type argument assignability tests
463
464          } // end this.identical() check
465          yield false; // the type arguments' non-generic classes or raw types were not identical
466        }
467      };
468
469      case DeclaredType nonGenericOrRawPayload -> switch (receiver) {
470        // "A [non-generic or] raw bean type [nonGenericOrRawPayload] is considered assignable..."
471
472        case DeclaredType parameterizedReceiver when this.domain().parameterized(receiver) ->
473          // "...to a parameterized required type [parameterizedReceiver] if the[ir] [non-generic classes or] raw types
474          // are identical and all type parameters [type arguments] of the required type [parameterizedReceiver] are
475          // either unbounded type variables [undefined] or java.lang.Object."
476          this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), nonGenericOrRawPayload) &&
477          allAre(parameterizedReceiver.getTypeArguments(),
478                 ((Predicate<TypeMirror>)this::unboundedTypeVariable).or(this.domain()::javaLangObject));
479
480        // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in
481        // test(), not here.]
482        case DeclaredType nonGenericOrRawReceiver -> false; // or this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload);
483      };
484    };
485  }
486
487  // Are payload's condensed bounds assignable to receiver's condensed extends bound (upper bound)?
488  /*
489  private final boolean condensedTypeVariableBoundsAssignableToCondensedExtendsBound(final WildcardType receiver, final TypeVariable payload) {
490    assert receiver.getKind() == TypeKind.WILDCARD;
491    assert payload.getKind() == TypeKind.TYPEVAR;
492    // "...the upper bound of the type variable [a type variable has many bounds?] is assignable TO [...] the upper
493    // bound, if any, of the wildcard..."
494    final TypeMirror extendsBound = receiver.getExtendsBound();
495    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
496    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
497    return extendsBound == null || this.covariantlyAssignable(List.of((ReferenceType)extendsBound), List.of(payload));
498  }
499  */
500
501  // Is payload's condensed extends bound (upper bound) covariantly assignable to receiver's condensed bounds?
502  /*
503  private final boolean condensedTypeVariableBoundsAssignableFromCondensedExtendsBound(final TypeVariable receiver, final WildcardType payload) {
504    assert receiver.getKind() == TypeKind.TYPEVAR;
505    assert payload.getKind() == TypeKind.WILDCARD;
506    // "...the upper bound of the type variable [a type variable has many bounds?] is assignable [...] FROM the upper
507    // bound, if any, of the wildcard..."
508    final TypeMirror extendsBound = payload.getExtendsBound();
509    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
510    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
511    return extendsBound == null || this.covariantlyAssignable(List.of(receiver), List.of((ReferenceType)extendsBound));
512  }
513  */
514
515  // Is payload's super bound (lower bound) covariantly assignable to receiver's condensed bounds?
516  /*
517  private final boolean condensedTypeVariableBoundsAssignableFromCondensedSuperBound(final TypeVariable receiver, final WildcardType payload) {
518    assert receiver.getKind() == TypeKind.TYPEVAR;
519    assert payload.getKind() == TypeKind.WILDCARD;
520    final TypeMirror superBound = payload.getSuperBound();
521    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
522    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
523    return superBound == null || this.covariantlyAssignable(List.of(receiver), List.of(superBound));
524  }
525  */
526
527  // Are payload's condensed bounds covariantly assignable to receiver's condensed bounds?
528  /*
529  private final boolean condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeVariable payload) {
530    assert receiver.getKind() == TypeKind.TYPEVAR;
531    assert payload.getKind() == TypeKind.TYPEVAR;
532    // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant
533    // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already.
534    return this.covariantlyAssignable(List.of(receiver), List.of(payload));
535  }
536  */
537
538
539  /*
540   * Static methods.
541   */
542
543  
544  // Returns a new IllegalArgumentException describing an illegal payload type.
545  private static IllegalArgumentException illegalPayload(final TypeMirror payload) {
546    return new IllegalArgumentException("Illegal payload kind: " + payload.getKind() + "; payload: " + payload);
547  }
548
549  // Returns a new IllegalArgumentException describing an illegal receiver type.
550  private static IllegalArgumentException illegalReceiver(final TypeMirror receiver) {
551    return new IllegalArgumentException("Illegal receiver kind: " + receiver.getKind() + "; receiver: " + receiver);
552  }
553
554  
555}