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