1 package com.github.javaparser.symbolsolver.resolution.typeinference; 2 3 import com.github.javaparser.ast.expr.*; 4 import com.github.javaparser.ast.type.UnknownType; 5 import com.github.javaparser.resolution.MethodUsage; 6 import com.github.javaparser.resolution.declarations.ResolvedInterfaceDeclaration; 7 import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; 8 import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration; 9 import com.github.javaparser.resolution.types.ResolvedType; 10 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; 11 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 12 import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl; 13 import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SubtypeOfBound; 14 import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.ThrowsBound; 15 import com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas.ExpressionCompatibleWithType; 16 17 import java.util.LinkedList; 18 import java.util.List; 19 import java.util.Optional; 20 21 import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression; 22 23 /** 24 * The API exposed by the TypeInference subsystem. 25 * 26 * @author Federico Tomassetti 27 */ 28 public class TypeInference { 29 30 private final ResolvedType object; 31 private TypeSolver typeSolver; 32 TypeInference(TypeSolver typeSolver)33 public TypeInference(TypeSolver typeSolver) { 34 if (typeSolver == null) { 35 throw new NullPointerException(); 36 } 37 this.typeSolver = typeSolver; 38 this.object = new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver); 39 } 40 41 /// 42 /// Public static methods 43 /// 44 toMethodUsage(MethodCallExpr call, ResolvedMethodDeclaration methodDeclaration, TypeSolver typeSolver)45 public static MethodUsage toMethodUsage(MethodCallExpr call, ResolvedMethodDeclaration methodDeclaration, TypeSolver typeSolver) { 46 TypeInference typeInference = new TypeInference(typeSolver); 47 Optional<InstantiationSet> instantiationSetOpt = typeInference.instantiationInference(call, methodDeclaration); 48 if (instantiationSetOpt.isPresent()) { 49 return instantiationSetToMethodUsage(methodDeclaration, instantiationSetOpt.get()); 50 } else { 51 throw new IllegalArgumentException(); 52 } 53 } 54 55 /// 56 /// Public instance methods 57 /// 58 instantiationInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration)59 public Optional<InstantiationSet> instantiationInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration) { 60 return instantiationInference(methodCallExpr.getArguments(), methodDeclaration); 61 } 62 instantiationInference(List<Expression> argumentExpressions, ResolvedMethodDeclaration methodDeclaration)63 public Optional<InstantiationSet> instantiationInference(List<Expression> argumentExpressions, ResolvedMethodDeclaration methodDeclaration) { 64 // if (methodCallExpr.getTypeArguments().isPresent()) { 65 // throw new IllegalArgumentException("Type inference unnecessary as type arguments have been specified"); 66 // } 67 68 // Given a method invocation that provides no explicit type arguments, the process to determine whether a 69 // potentially applicable generic method m is applicable is as follows: 70 71 // - Where P1, ..., Pp (p ≥ 1) are the type parameters of m, let α1, ..., αp be inference variables, and 72 // let θ be the substitution [P1:=α1, ..., Pp:=αp]. 73 74 List<ResolvedTypeParameterDeclaration> Ps = methodDeclaration.getTypeParameters(); 75 List<InferenceVariable> alphas = InferenceVariable.instantiate(Ps); 76 Substitution theta = Substitution.empty(); 77 for (int i=0;i<Ps.size();i++) { 78 theta = theta.withPair(Ps.get(0), alphas.get(0)); 79 } 80 81 // - An initial bound set, B0, is constructed from the declared bounds of P1, ..., Pp, as described in §18.1.3. 82 83 BoundSet B0 = boundSetup(Ps, alphas); 84 85 // - For all i (1 ≤ i ≤ p), if Pi appears in the throws clause of m, then the bound throws αi is implied. 86 // These bounds, if any, are incorporated with B0 to produce a new bound set, B1. 87 88 BoundSet B1 = B0; 89 for (int i=0;i<Ps.size();i++) { 90 ResolvedTypeParameterDeclaration Pi = Ps.get(i); 91 if (appearInThrowsClause(Pi, methodDeclaration)) { 92 B1 = B1.withBound(new ThrowsBound(alphas.get(i))); 93 } 94 } 95 96 // - A set of constraint formulas, C, is constructed as follows. 97 // 98 // Let F1, ..., Fn be the formal parameter types of m, and let e1, ..., ek be the actual argument expressions 99 // of the invocation. Then: 100 101 List<ResolvedType> Fs = formalParameterTypes(methodDeclaration); 102 List<Expression> es = argumentExpressions; 103 104 Optional<ConstraintFormulaSet> C = Optional.empty(); 105 106 // - To test for applicability by strict invocation: 107 108 if (!C.isPresent()) { 109 C = testForApplicabilityByStrictInvocation(Fs, es, theta); 110 } 111 112 // - To test for applicability by loose invocation: 113 114 if (!C.isPresent()) { 115 C = testForApplicabilityByLooseInvocation(Fs, es, theta); 116 } 117 118 // - To test for applicability by variable arity invocation: 119 120 if (!C.isPresent()) { 121 C = testForApplicabilityByVariableArityInvocation(Fs, es, theta); 122 } 123 124 if (!C.isPresent()) { 125 return Optional.empty(); 126 } 127 128 // - C is reduced (§18.2) and the resulting bounds are incorporated with B1 to produce a new bound set, B2. 129 130 BoundSet resultingBounds = C.get().reduce(typeSolver); 131 BoundSet B2 = B1.incorporate(resultingBounds, typeSolver); 132 133 // - Finally, the method m is applicable if B2 does not contain the bound false and resolution of all the 134 // inference variables in B2 succeeds (§18.4). 135 136 if (B2.containsFalse()) { 137 return Optional.empty(); 138 } 139 140 Optional<InstantiationSet> instantiation = B2.performResolution(alphas, typeSolver); 141 return instantiation; 142 } 143 144 /** 145 * Determine whether a potentially applicable generic method m is applicable for a method invocation that 146 * provides no explicit type arguments. 147 */ invocationApplicabilityInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration)148 public boolean invocationApplicabilityInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration) { 149 if (!methodCallExpr.getNameAsString().equals(methodDeclaration.getName())) { 150 throw new IllegalArgumentException(); 151 } 152 Optional<InstantiationSet> partial = instantiationInference(methodCallExpr, methodDeclaration); 153 if (!partial.isPresent()) { 154 return false; 155 } 156 int nActualParams = methodCallExpr.getArguments().size(); 157 int nFormalParams = methodDeclaration.getNumberOfParams(); 158 if (nActualParams != nFormalParams) { 159 if (methodDeclaration.hasVariadicParameter()) { 160 if (nActualParams < (nFormalParams - 1)) { 161 return false; 162 } 163 } else { 164 return false; 165 } 166 } 167 //MethodUsage methodUsage = instantiationSetToMethodUsage(methodDeclaration, partial.get()); 168 // for (int i=0;i<nActualParams;i++) { 169 // int formalIndex = i >= nFormalParams ? nFormalParams - 1 : i; 170 // Type formalType = methodDeclaration.getParam(formalIndex).getType(); 171 // Type actualType = JavaParserFacade.get(typeSolver).getType(methodCallExpr.getArgument(i)); 172 // //if (!formalType.isAssignableBy(actualType)) { 173 // // return false; 174 // //} 175 // } 176 return true; 177 } 178 invocationTypeInferenceBoundsSetB3()179 public BoundSet invocationTypeInferenceBoundsSetB3() { 180 // Given a method invocation that provides no explicit type arguments, and a corresponding most specific 181 // applicable generic method m, the process to infer the invocation type (§15.12.2.6) of the chosen method is 182 // as follows: 183 // 184 // - Let θ be the substitution [P1:=α1, ..., Pp:=αp] defined in §18.5.1 to replace the type parameters of m with inference variables. 185 // 186 // - Let B2 be the bound set produced by reduction in order to demonstrate that m is applicable in §18.5.1. (While it was necessary in §18.5.1 to demonstrate that the inference variables in B2 could be resolved, in order to establish applicability, the instantiations produced by this resolution step are not considered part of B2.) 187 // 188 // - If the invocation is not a poly expression, let the bound set B3 be the same as B2. 189 // 190 // If the invocation is a poly expression, let the bound set B3 be derived from B2 as follows. Let R be the 191 // return type of m, let T be the invocation's target type, and then: 192 // 193 // - If unchecked conversion was necessary for the method to be applicable during constraint set reduction 194 // in §18.5.1, the constraint formula ‹|R| → T› is reduced and incorporated with B2. 195 // 196 // - Otherwise, if R θ is a parameterized type, G<A1, ..., An>, and one of A1, ..., An is a wildcard, then, 197 // for fresh inference variables β1, ..., βn, the constraint formula ‹G<β1, ..., βn> → T› is reduced and 198 // incorporated, along with the bound G<β1, ..., βn> = capture(G<A1, ..., An>), with B2. 199 // 200 // - Otherwise, if R θ is an inference variable α, and one of the following is true: 201 // 202 // - T is a reference type, but is not a wildcard-parameterized type, and either i) B2 contains a bound of 203 // one of the forms α = S or S <: α, where S is a wildcard-parameterized type, or ii) B2 contains two 204 // bounds of the forms S1 <: α and S2 <: α, where S1 and S2 have supertypes that are two different 205 // parameterizations of the same generic class or interface. 206 // 207 // - T is a parameterization of a generic class or interface, G, and B2 contains a bound of one of the 208 // forms α = S or S <: α, where there exists no type of the form G<...> that is a supertype of S, but the 209 // raw type |G<...>| is a supertype of S. 210 // 211 // - T is a primitive type, and one of the primitive wrapper classes mentioned in §5.1.7 is an 212 // instantiation, upper bound, or lower bound for α in B2. 213 // 214 // then α is resolved in B2, and where the capture of the resulting instantiation of α is U, the constraint 215 // formula ‹U → T› is reduced and incorporated with B2. 216 // 217 // - Otherwise, the constraint formula ‹R θ → T› is reduced and incorporated with B2. 218 throw new UnsupportedOperationException(); 219 } 220 invocationTypeInference()221 public void invocationTypeInference() { 222 BoundSet B3 = invocationTypeInferenceBoundsSetB3(); 223 // 224 //A set of constraint formulas, C, is constructed as follows. 225 // 226 // Let e1, ..., ek be the actual argument expressions of the invocation. If m is applicable by strict or loose invocation, let F1, ..., Fk be the formal parameter types of m; if m is applicable by variable arity invocation, let F1, ..., Fk the first k variable arity parameter types of m (§15.12.2.4). Then: 227 // 228 //For all i (1 ≤ i ≤ k), if ei is not pertinent to applicability, C contains ‹ei → Fi θ›. 229 // 230 //For all i (1 ≤ i ≤ k), additional constraints may be included, depending on the form of ei: 231 // 232 //If ei is a LambdaExpression, C contains ‹LambdaExpression →throws Fi θ›. 233 // 234 //In addition, the lambda body is searched for additional constraints: 235 // 236 //For a block lambda body, the search is applied recursively to each result expression. 237 // 238 //For a poly class instance creation expression (§15.9) or a poly method invocation expression (§15.12), C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type. 239 // 240 //For a parenthesized expression, the search is applied recursively to the contained expression. 241 // 242 //For a conditional expression, the search is applied recursively to the second and third operands. 243 // 244 //For a lambda expression, the search is applied recursively to the lambda body. 245 // 246 //If ei is a MethodReference, C contains ‹MethodReference →throws Fi θ›. 247 // 248 //If ei is a poly class instance creation expression (§15.9) or a poly method invocation expression (§15.12), C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type. 249 // 250 //If ei is a parenthesized expression, these rules are applied recursively to the contained expression. 251 // 252 //If ei is a conditional expression, these rules are applied recursively to the second and third operands. 253 // 254 //While C is not empty, the following process is repeated, starting with the bound set B3 and accumulating new bounds into a "current" bound set, ultimately producing a new bound set, B4: 255 // 256 //A subset of constraints is selected in C, satisfying the property that, for each constraint, no input variable can influence an output variable of another constraint in C. The terms input variable and output variable are defined below. An inference variable α can influence an inference variable β if α depends on the resolution of β (§18.4), or vice versa; or if there exists a third inference variable γ such that α can influence γ and γ can influence β. 257 // 258 //If this subset is empty, then there is a cycle (or cycles) in the graph of dependencies between constraints. In this case, all constraints are considered that participate in a dependency cycle (or cycles) and do not depend on any constraints outside of the cycle (or cycles). A single constraint is selected from the considered constraints, as follows: 259 // 260 //If any of the considered constraints have the form ‹Expression → T›, then the selected constraint is the considered constraint of this form that contains the expression to the left (§3.5) of the expression of every other considered constraint of this form. 261 // 262 // If no considered constraint has the form ‹Expression → T›, then the selected constraint is the considered constraint that contains the expression to the left of the expression of every other considered constraint. 263 // 264 // The selected constraint(s) are removed from C. 265 // 266 // The input variables α1, ..., αm of all the selected constraint(s) are resolved. 267 // 268 // Where T1, ..., Tm are the instantiations of α1, ..., αm, the substitution [α1:=T1, ..., αm:=Tm] is applied to every constraint. 269 // 270 // The constraint(s) resulting from substitution are reduced and incorporated with the current bound set. 271 // 272 //Finally, if B4 does not contain the bound false, the inference variables in B4 are resolved. 273 // 274 //If resolution succeeds with instantiations T1, ..., Tp for inference variables α1, ..., αp, let θ' be the substitution [P1:=T1, ..., Pp:=Tp]. Then: 275 // 276 //If unchecked conversion was necessary for the method to be applicable during constraint set reduction in §18.5.1, then the parameter types of the invocation type of m are obtained by applying θ' to the parameter types of m's type, and the return type and thrown types of the invocation type of m are given by the erasure of the return type and thrown types of m's type. 277 // 278 //If unchecked conversion was not necessary for the method to be applicable, then the invocation type of m is obtained by applying θ' to the type of m. 279 // 280 //If B4 contains the bound false, or if resolution fails, then a compile-time error occurs. 281 // 282 //Invocation type inference may require carefully sequencing the reduction of constraint formulas of the forms ‹Expression → T›, ‹LambdaExpression →throws T›, and ‹MethodReference →throws T›. To facilitate this sequencing, the input variables of these constraints are defined as follows: 283 // 284 //For ‹LambdaExpression → T›: 285 // 286 //If T is an inference variable, it is the (only) input variable. 287 // 288 // If T is a functional interface type, and a function type can be derived from T (§15.27.3), then the input variables include i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and ii) if the function type's return type, R, is not void, then for each result expression e in the lambda body (or for the body itself if it is an expression), the input variables of ‹e → R›. 289 // 290 //Otherwise, there are no input variables. 291 // 292 //For ‹LambdaExpression →throws T›: 293 // 294 //If T is an inference variable, it is the (only) input variable. 295 // 296 // If T is a functional interface type, and a function type can be derived, as described in §15.27.3, the input variables include i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and ii) the inference variables mentioned by the function type's return type. 297 // 298 // Otherwise, there are no input variables. 299 // 300 // For ‹MethodReference → T›: 301 // 302 //If T is an inference variable, it is the (only) input variable. 303 // 304 // If T is a functional interface type with a function type, and if the method reference is inexact (§15.13.1), the input variables are the inference variables mentioned by the function type's parameter types. 305 // 306 //Otherwise, there are no input variables. 307 // 308 //For ‹MethodReference →throws T›: 309 // 310 //If T is an inference variable, it is the (only) input variable. 311 // 312 // If T is a functional interface type with a function type, and if the method reference is inexact (§15.13.1), the input variables are the inference variables mentioned by the function type's parameter types and the function type's return type. 313 // 314 // Otherwise, there are no input variables. 315 // 316 // For ‹Expression → T›, if Expression is a parenthesized expression: 317 // 318 //Where the contained expression of Expression is Expression', the input variables are the input variables of ‹Expression' → T›. 319 // 320 //For ‹ConditionalExpression → T›: 321 // 322 //Where the conditional expression has the form e1 ? e2 : e3, the input variables are the input variables of ‹e2 → T› and ‹e3 → T›. 323 // 324 //For all other constraint formulas, there are no input variables. 325 // 326 //The output variables of these constraints are all inference variables mentioned by the type on the right-hand side of the constraint, T, that are not input variables. 327 328 throw new UnsupportedOperationException(); 329 } 330 functionalInterfaceParameterizationInference(LambdaExpr lambdaExpr, ResolvedInterfaceDeclaration interfaceDeclaration)331 public void functionalInterfaceParameterizationInference(LambdaExpr lambdaExpr, 332 ResolvedInterfaceDeclaration interfaceDeclaration) { 333 // Where a lambda expression with explicit parameter types P1, ..., Pn targets a functional interface 334 // type F<A1, ..., Am> with at least one wildcard type argument, then a parameterization of F may be derived 335 // as the ground target type of the lambda expression as follows. 336 337 int n = lambdaExpr.getParameters().size(); 338 339 if (interfaceDeclaration.getTypeParameters().isEmpty()) { 340 throw new IllegalArgumentException("Functional Interface without type arguments"); 341 } 342 343 // Let Q1, ..., Qk be the parameter types of the function type of the type F<α1, ..., αm>, 344 // where α1, ..., αm are fresh inference variables. 345 346 int k = interfaceDeclaration.getTypeParameters().size(); 347 List<InferenceVariable> alphas = InferenceVariable.instantiate(interfaceDeclaration.getTypeParameters()); 348 349 TypeInferenceCache.recordInferenceVariables(typeSolver, lambdaExpr, alphas); 350 351 // If n ≠ k, no valid parameterization exists. 352 353 if (n != k) { 354 throw new IllegalArgumentException("No valida parameterization can exist has n=" + " and k=" + k); 355 } 356 357 // Otherwise, a set of constraint formulas is formed with, for 358 // all i (1 ≤ i ≤ n), ‹Pi = Qi›. This constraint formula set is reduced to form the bound set B. 359 360 ConstraintFormulaSet constraintFormulaSet = ConstraintFormulaSet.empty(); 361 for (int i=0; i<n; i++) { 362 throw new UnsupportedOperationException(); 363 //Type pi = JavaParserFacade.get(typeSolver).convertToUsage(lambdaExpr.getParameters().get(i).getType(), lambdaExpr); 364 //Type qi = JavaParserFacade.get(typeSolver).convertToUsage(interfaceDeclaration.getm.get(i).getType(), lambdaExpr); 365 //constraintFormulaSet = constraintFormulaSet.withConstraint(new TypeSameAsType(pi, qi)); 366 } 367 BoundSet B = constraintFormulaSet.reduce(typeSolver); 368 369 // If B contains the bound false, no valid parameterization exists. Otherwise, a new parameterization of the 370 // functional interface type, F<A'1, ..., A'm>, is constructed as follows, for 1 ≤ i ≤ m: 371 // 372 // - If B contains an instantiation (§18.1.3) for αi, T, then A'i = T. 373 // 374 // - Otherwise, A'i = Ai. 375 // 376 // If F<A'1, ..., A'm> is not a well-formed type (that is, the type arguments are not within their bounds), or if F<A'1, ..., A'm> is not a subtype of F<A1, ..., Am>, no valid parameterization exists. Otherwise, the inferred parameterization is either F<A'1, ..., A'm>, if all the type arguments are types, or the non-wildcard parameterization (§9.9) of F<A'1, ..., A'm>, if one or more type arguments are still wildcards. 377 378 throw new UnsupportedOperationException(); 379 } 380 381 /** 382 * Return if m2 is more specific than m1 383 * @param methodCall 384 * @param m1 385 * @param m2 386 */ moreSpecificMethodInference(MethodCallExpr methodCall, ResolvedMethodDeclaration m1, ResolvedMethodDeclaration m2)387 public boolean moreSpecificMethodInference(MethodCallExpr methodCall, ResolvedMethodDeclaration m1, ResolvedMethodDeclaration m2) { 388 // When testing that one applicable method is more specific than another (§15.12.2.5), where the second method 389 // is generic, it is necessary to test whether some instantiation of the second method's type parameters can be 390 // inferred to make the first method more specific than the second. 391 392 if (!m2.isGeneric()) { 393 throw new IllegalArgumentException("M2 is not generic (m2: " + m2 + ")"); 394 } 395 396 // Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, 397 // let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp]. 398 // 399 // Let e1, ..., ek be the argument expressions of the corresponding invocation. Then: 400 // 401 // - If m1 and m2 are applicable by strict or loose invocation (§15.12.2.2, §15.12.2.3), then let S1, ..., Sk be the formal parameter types of m1, and let T1, ..., Tk be the result of θ applied to the formal parameter types of m2. 402 // 403 // - If m1 and m2 are applicable by variable arity invocation (§15.12.2.4), then let S1, ..., Sk be the first k variable arity parameter types of m1, and let T1, ..., Tk be the result of θ applied to the first k variable arity parameter types of m2. 404 // 405 // Note that no substitution is applied to S1, ..., Sk; even if m1 is generic, the type parameters of m1 are treated as type variables, not inference variables. 406 // 407 // The process to determine if m1 is more specific than m2 is as follows: 408 // 409 // - First, an initial bound set, B, is constructed from the declared bounds of P1, ..., Pp, as specified in §18.1.3. 410 // 411 // - Second, for all i (1 ≤ i ≤ k), a set of constraint formulas or bounds is generated. 412 // 413 // If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.) 414 // 415 // Otherwise, if Ti is not a functional interface type, the constraint formula ‹Si <: Ti› is generated. 416 // 417 // Otherwise, Ti is a parameterization of a functional interface, I. It must be determined whether Si satisfies the following five conditions: 418 // 419 // 1. Si is a functional interface type. 420 // 421 // 2. Si is not a superinterface of I, nor a parameterization of a superinterface of I. 422 // 423 // 3. Si is not a subinterface of I, nor a parameterization of a subinterface of I. 424 // 425 // 4. If Si is an intersection type, at least one element of the intersection is not a superinterface of I, nor a parameterization of a superinterface of I. 426 // 427 // 5. If Si is an intersection type, no element of the intersection is a subinterface of I, nor a parameterization of a subinterface of I. 428 // 429 // If all five conditions are true, then the following constraint formulas or bounds are generated (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of Si, and V1 ... Vk and R2 are the parameter types and return type of the function type of Ti): 430 // 431 // - If ei is an explicitly typed lambda expression: 432 // 433 // - For all j (1 ≤ j ≤ k), ‹Uj = Vj›. 434 // 435 // - If R2 is void, true. 436 // 437 // - Otherwise, if R1 and R2 are functional interface types, and neither interface is a subinterface of the other, and ei has at least one result expression, then these rules are applied recursively to R1 and R2, for each result expression in ei. 438 // 439 // - Otherwise, if R1 is a primitive type and R2 is not, and ei has at least one result expression, and each result expression of ei is a standalone expression (§15.2) of a primitive type, true. 440 // 441 // - Otherwise, if R2 is a primitive type and R1 is not, and ei has at least one result expression, and each result expression of ei is either a standalone expression of a reference type or a poly expression, true. 442 // 443 // - Otherwise, ‹R1 <: R2›. 444 // 445 // - If ei is an exact method reference: 446 // 447 // - For all j (1 ≤ j ≤ k), ‹Uj = Vj›. 448 // 449 // - If R2 is void, true. 450 // 451 // - Otherwise, if R1 is a primitive type and R2 is not, and the compile-time declaration for ei has a primitive return type, true. 452 // 453 // - Otherwise if R2 is a primitive type and R1 is not, and the compile-time declaration for ei has a reference return type, true. 454 // 455 // - Otherwise, ‹R1 <: R2›. 456 // 457 // - If ei is a parenthesized expression, these rules are applied recursively to the contained expression. 458 // 459 // - If ei is a conditional expression, these rules are applied recursively to each of the second and third operands. 460 // 461 // - Otherwise, false. 462 // 463 // If the five constraints on Si are not satisfied, the constraint formula ‹Si <: Ti› is generated instead. 464 // 465 // - Third, if m2 is applicable by variable arity invocation and has k+1 parameters, then where Sk+1 is the k+1'th variable arity parameter type of m1 and Tk+1 is the result of θ applied to the k+1'th variable arity parameter type of m2, the constraint ‹Sk+1 <: Tk+1› is generated. 466 // 467 // - Fourth, the generated bounds and constraint formulas are reduced and incorporated with B to produce a bound set B'. 468 // 469 // If B' does not contain the bound false, and resolution of all the inference variables in B' succeeds, then m1 is more specific than m2. 470 // 471 // Otherwise, m1 is not more specific than m2. 472 473 throw new UnsupportedOperationException(); 474 } 475 476 477 /// 478 /// Private static methods 479 /// 480 instantiationSetToMethodUsage(ResolvedMethodDeclaration methodDeclaration, InstantiationSet instantiationSet)481 private static MethodUsage instantiationSetToMethodUsage(ResolvedMethodDeclaration methodDeclaration, InstantiationSet instantiationSet) { 482 if (instantiationSet.isEmpty()) { 483 return new MethodUsage(methodDeclaration); 484 } 485 List<ResolvedType> paramTypes = new LinkedList<>(); 486 for (int i=0;i<methodDeclaration.getNumberOfParams();i++) { 487 paramTypes.add(instantiationSet.apply(methodDeclaration.getParam(i).getType())); 488 } 489 ResolvedType returnType = instantiationSet.apply(methodDeclaration.getReturnType()); 490 return new MethodUsage(methodDeclaration, paramTypes, returnType); 491 } 492 493 /// 494 /// Private instance methods 495 /// 496 497 /** 498 * When inference begins, a bound set is typically generated from a list of type parameter declarations P1, ..., Pp 499 * and associated inference variables α1, ..., αp 500 * 501 * @param typeParameterDeclarations 502 * @param inferenceVariables 503 * @return 504 */ boundSetup(List<ResolvedTypeParameterDeclaration> typeParameterDeclarations, List<InferenceVariable> inferenceVariables)505 private BoundSet boundSetup(List<ResolvedTypeParameterDeclaration> typeParameterDeclarations, List<InferenceVariable> inferenceVariables) { 506 if (typeParameterDeclarations.size() != inferenceVariables.size()) { 507 throw new IllegalArgumentException(); 508 } 509 510 // When inference begins, a bound set is typically generated from a list of 511 // type parameter declarations P1, ..., Pp and associated inference variables α1, ..., αp. 512 // Such a bound set is constructed as follows. For each l (1 ≤ l ≤ p): 513 514 BoundSet boundSet = BoundSet.empty(); 515 516 for (int l=0;l<typeParameterDeclarations.size();l++) { 517 ResolvedTypeParameterDeclaration Pl = typeParameterDeclarations.get(l); 518 InferenceVariable alphaL = inferenceVariables.get(l); 519 520 // - If Pl has no TypeBound, the bound αl <: Object appears in the set. 521 522 if (Pl.getBounds().isEmpty()) { 523 boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, object)); 524 } else { 525 526 // - Otherwise, for each type T delimited by & in the TypeBound, the bound αl <: T[P1:=α1, ..., Pp:=αp] appears 527 // in the set; if this results in no proper upper bounds for αl (only dependencies), then the 528 // bound αl <: Object also appears in the set. 529 530 for (ResolvedTypeParameterDeclaration.Bound bound : Pl.getBounds()) { 531 ResolvedType T = bound.getType(); 532 Substitution substitution = Substitution.empty(); 533 for (int j=0;j<typeParameterDeclarations.size();j++) { 534 substitution = substitution.withPair(typeParameterDeclarations.get(j), inferenceVariables.get(j)); 535 } 536 ResolvedType TWithSubstitutions = substitution.apply(T); 537 538 boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, TWithSubstitutions)); 539 540 if (boundSet.getProperUpperBoundsFor(alphaL).isEmpty()) { 541 boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, object)); 542 } 543 } 544 } 545 } 546 547 return boundSet; 548 } 549 appearInThrowsClause(ResolvedTypeParameterDeclaration p, ResolvedMethodDeclaration methodDeclaration)550 private boolean appearInThrowsClause(ResolvedTypeParameterDeclaration p, ResolvedMethodDeclaration methodDeclaration) { 551 for (int j=0;j<methodDeclaration.getNumberOfSpecifiedExceptions();j++) { 552 ResolvedType thrownType = methodDeclaration.getSpecifiedException(j); 553 if (thrownType.isTypeVariable() && thrownType.asTypeVariable().asTypeParameter().equals(p)) { 554 return true; 555 } 556 } 557 return false; 558 } 559 formalParameterTypes(ResolvedMethodDeclaration methodDeclaration)560 private List<ResolvedType> formalParameterTypes(ResolvedMethodDeclaration methodDeclaration) { 561 List<ResolvedType> types = new LinkedList<>(); 562 for (int i=0;i<methodDeclaration.getNumberOfParams();i++) { 563 types.add(methodDeclaration.getParam(i).getType()); 564 } 565 return types; 566 } 567 isImplicitlyTyped(LambdaExpr lambdaExpr)568 private boolean isImplicitlyTyped(LambdaExpr lambdaExpr) { 569 return lambdaExpr.getParameters().stream().anyMatch(p -> p.getType() instanceof UnknownType); 570 } 571 isInexact(MethodReferenceExpr methodReferenceExpr)572 private boolean isInexact(MethodReferenceExpr methodReferenceExpr) { 573 throw new UnsupportedOperationException(); 574 } 575 isPertinentToApplicability(Expression argument)576 private boolean isPertinentToApplicability(Expression argument) { 577 // An argument expression is considered pertinent to applicability for a potentially applicable method m 578 // unless it has one of the following forms: 579 // 580 // - An implicitly typed lambda expression (§15.27.1). 581 582 if (argument instanceof LambdaExpr) { 583 LambdaExpr lambdaExpr = (LambdaExpr)argument; 584 if (isImplicitlyTyped(lambdaExpr)) { 585 return false; 586 } 587 } 588 589 // - An inexact method reference expression (§15.13.1). 590 591 if (argument instanceof MethodReferenceExpr) { 592 MethodReferenceExpr methodReferenceExpr = (MethodReferenceExpr)argument; 593 if (isInexact(methodReferenceExpr)) { 594 return false; 595 } 596 } 597 598 // - If m is a generic method and the method invocation does not provide explicit type arguments, an 599 // explicitly typed lambda expression or an exact method reference expression for which the 600 // corresponding target type (as derived from the signature of m) is a type parameter of m. 601 602 if (argument instanceof LambdaExpr) { 603 throw new UnsupportedOperationException(); 604 } 605 606 if (argument instanceof MethodReferenceExpr) { 607 throw new UnsupportedOperationException(); 608 } 609 610 // - An explicitly typed lambda expression whose body is an expression that is not pertinent to applicability. 611 612 if (argument instanceof LambdaExpr) { 613 throw new UnsupportedOperationException(); 614 } 615 616 // - An explicitly typed lambda expression whose body is a block, where at least one result expression is not 617 // pertinent to applicability. 618 619 if (argument instanceof LambdaExpr) { 620 throw new UnsupportedOperationException(); 621 } 622 623 // - A parenthesized expression (§15.8.5) whose contained expression is not pertinent to applicability. 624 625 if (argument instanceof EnclosedExpr) { 626 EnclosedExpr enclosedExpr = (EnclosedExpr)argument; 627 return isPertinentToApplicability(enclosedExpr.getInner()); 628 } 629 630 // - A conditional expression (§15.25) whose second or third operand is not pertinent to applicability. 631 632 if (argument instanceof ConditionalExpr) { 633 ConditionalExpr conditionalExpr = (ConditionalExpr)argument; 634 return isPertinentToApplicability(conditionalExpr.getThenExpr()) && 635 isPertinentToApplicability(conditionalExpr.getElseExpr()); 636 } 637 638 return true; 639 } 640 testForApplicabilityByStrictInvocation(List<ResolvedType> Fs, List<Expression> es, Substitution theta)641 private Optional<ConstraintFormulaSet> testForApplicabilityByStrictInvocation(List<ResolvedType> Fs, List<Expression> es, 642 Substitution theta) { 643 int n = Fs.size(); 644 int k = es.size(); 645 646 // If k ≠ n, or if there exists an i (1 ≤ i ≤ n) such that ei is pertinent to applicability (§15.12.2.2) 647 // and either: 648 // i) ei is a standalone expression of a primitive type but Fi is a reference type, or 649 // ii) Fi is a primitive type but ei is not a standalone expression of a primitive type; 650 if (k != n) { 651 return Optional.empty(); 652 } 653 for (int i=0;i<n;i++) { 654 Expression ei = es.get(i); 655 ResolvedType fi = Fs.get(i); 656 if (isPertinentToApplicability(ei)) { 657 if (isStandaloneExpression(ei) && JavaParserFacade.get(typeSolver).getType(ei).isPrimitive() 658 && fi.isReferenceType()) { 659 return Optional.empty(); 660 } 661 if (fi.isPrimitive() && (!isStandaloneExpression(ei) || !JavaParserFacade.get(typeSolver).getType(ei).isPrimitive())) { 662 return Optional.empty(); 663 } 664 } 665 } 666 // then the method is not applicable and there is no need to proceed with inference. 667 // 668 // Otherwise, C includes, for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → Fi θ›. 669 670 return Optional.of(constraintSetFromArgumentsSubstitution(Fs, es, theta, k)); 671 } 672 typeWithSubstitution(ResolvedType originalType, Substitution substitution)673 private ResolvedType typeWithSubstitution(ResolvedType originalType, Substitution substitution) { 674 return substitution.apply(originalType); 675 } 676 testForApplicabilityByLooseInvocation(List<ResolvedType> Fs, List<Expression> es, Substitution theta)677 private Optional<ConstraintFormulaSet> testForApplicabilityByLooseInvocation(List<ResolvedType> Fs, List<Expression> es, 678 Substitution theta) { 679 int n = Fs.size(); 680 int k = es.size(); 681 682 // If k ≠ n, the method is not applicable and there is no need to proceed with inference. 683 684 if (k != n) { 685 return Optional.empty(); 686 } 687 688 // Otherwise, C includes, for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → Fi θ›. 689 return Optional.of(constraintSetFromArgumentsSubstitution(Fs, es, theta, k)); 690 } 691 constraintSetFromArgumentsSubstitution(List<ResolvedType> Fs, List<Expression> es, Substitution theta, int k)692 private ConstraintFormulaSet constraintSetFromArgumentsSubstitution(List<ResolvedType> Fs, List<Expression> es, Substitution theta, int k) { 693 ConstraintFormulaSet constraintFormulaSet = ConstraintFormulaSet.empty(); 694 for (int i=0;i<k;i++) { 695 Expression ei = es.get(i); 696 ResolvedType fi = Fs.get(i); 697 ResolvedType fiTheta = typeWithSubstitution(fi, theta); 698 constraintFormulaSet = constraintFormulaSet.withConstraint( 699 new ExpressionCompatibleWithType(typeSolver, ei, fiTheta)); 700 } 701 return constraintFormulaSet; 702 } 703 testForApplicabilityByVariableArityInvocation(List<ResolvedType> Fs, List<Expression> es, Substitution theta)704 private Optional<ConstraintFormulaSet> testForApplicabilityByVariableArityInvocation(List<ResolvedType> Fs, List<Expression> es, 705 Substitution theta) { 706 int k = es.size(); 707 708 // Let F'1, ..., F'k be the first k variable arity parameter types of m (§15.12.2.4). C includes, 709 // for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → F'i θ›. 710 711 List<ResolvedType> FsFirst = new LinkedList<>(); 712 for (int i=0;i<k;i++) { 713 ResolvedType FFirstI = i < Fs.size() ? Fs.get(i) : Fs.get(Fs.size() - 1); 714 FsFirst.add(FFirstI); 715 } 716 717 return Optional.of(constraintSetFromArgumentsSubstitution(FsFirst, es, theta, k)); 718 } 719 } 720