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}