aboutsummaryrefslogtreecommitdiffstats
path: root/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas
diff options
context:
space:
mode:
Diffstat (limited to 'javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas')
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java335
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java67
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java62
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java122
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java95
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java140
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java139
7 files changed, 960 insertions, 0 deletions
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java
new file mode 100644
index 000000000..30042f8a8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java
@@ -0,0 +1,335 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typeinference.*;
+import com.github.javaparser.utils.Pair;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isPolyExpression;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+import static java.util.stream.Collectors.*;
+
+/**
+ * An expression is compatible in a loose invocation context with type T
+ *
+ * @author Federico Tomassetti
+ */
+public class ExpressionCompatibleWithType extends ConstraintFormula {
+ private TypeSolver typeSolver;
+ private Expression expression;
+ private ResolvedType T;
+
+ public ExpressionCompatibleWithType(TypeSolver typeSolver, Expression expression, ResolvedType T) {
+ this.typeSolver = typeSolver;
+ this.expression = expression;
+ this.T = T;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // If T is a proper type, the constraint reduces to true if the expression is compatible in a loose
+ // invocation context with T (§5.3), and false otherwise.
+
+ if (isProperType(T)) {
+ if (isCompatibleInALooseInvocationContext(typeSolver, expression, T)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // Otherwise, if the expression is a standalone expression (§15.2) of type S, the constraint reduces
+ // to ‹S → T›.
+
+ if (isStandaloneExpression(expression)) {
+ ResolvedType s = JavaParserFacade.get(typeSolver).getType(expression, false);
+ return ReductionResult.empty().withConstraint(new TypeCompatibleWithType(typeSolver, s, T));
+ }
+
+ // Otherwise, the expression is a poly expression (§15.2). The result depends on the form of the expression:
+
+ if (isPolyExpression(expression)) {
+
+ // - If the expression is a parenthesized expression of the form ( Expression' ), the constraint reduces
+ // to ‹Expression' → T›.
+
+ if (expression instanceof EnclosedExpr) {
+ EnclosedExpr enclosedExpr = (EnclosedExpr)expression;
+ return ReductionResult.oneConstraint(new ExpressionCompatibleWithType(typeSolver, enclosedExpr.getInner(), T));
+ }
+
+ // - If the expression is a class instance creation expression or a method invocation expression, the
+ // constraint reduces to the bound set B3 which would be used to determine the expression's invocation
+ // type when targeting T, as defined in §18.5.2. (For a class instance creation expression, the
+ // corresponding "method" used for inference is defined in §15.9.3).
+ //
+ // This bound set may contain new inference variables, as well as dependencies between these new
+ // variables and the inference variables in T.
+
+ if (expression instanceof ObjectCreationExpr) {
+ BoundSet B3 = new TypeInference(typeSolver).invocationTypeInferenceBoundsSetB3();
+ return ReductionResult.bounds(B3);
+ }
+
+ if (expression instanceof MethodCallExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the expression is a conditional expression of the form e1 ? e2 : e3, the constraint reduces to two
+ // constraint formulas, ‹e2 → T› and ‹e3 → T›.
+
+ if (expression instanceof ConditionalExpr) {
+ ConditionalExpr conditionalExpr = (ConditionalExpr)expression;
+ return ReductionResult.withConstraints(
+ new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getThenExpr(), T),
+ new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getElseExpr(), T));
+ }
+
+ // - If the expression is a lambda expression or a method reference expression, the result is specified
+ // below.
+
+ // A constraint formula of the form ‹LambdaExpression → T›, where T mentions at least one inference variable, is reduced as follows:
+
+ if (expression instanceof LambdaExpr) {
+ LambdaExpr lambdaExpr = (LambdaExpr)expression;
+
+ // - If T is not a functional interface type (§9.8), the constraint reduces to false.
+
+ if (!FunctionalInterfaceLogic.isFunctionalInterfaceType(T)) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, let T' be the ground target type derived from T, as specified in §15.27.3. If §18.5.3
+ // is used to derive a functional interface type which is parameterized, then the test that
+ // F<A'1, ..., A'm> is a subtype of F<A1, ..., Am> is not performed (instead, it is asserted with a
+ // constraint formula below). Let the target function type for the lambda expression be the
+ // function type of T'. Then:
+
+ Pair<ResolvedType, Boolean> result = TypeHelper.groundTargetTypeOfLambda(lambdaExpr, T, typeSolver);
+ ResolvedType TFirst = result.a;
+ MethodType targetFunctionType = TypeHelper.getFunctionType(TFirst);
+ targetFunctionType = replaceTypeVariablesWithInferenceVariables(targetFunctionType);
+ if (result.b) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If no valid function type can be found, the constraint reduces to false.
+ //
+ // Federico: THIS SHOULD NOT HAPPEN, IN CASE WE WILL THROW AN EXCEPTION
+ //
+ // - Otherwise, the congruence of LambdaExpression with the target function type is asserted as
+ // follows:
+ //
+ // - If the number of lambda parameters differs from the number of parameter types of the function
+ // type, the constraint reduces to false.
+
+ if (targetFunctionType.getFormalArgumentTypes().size() != lambdaExpr.getParameters().size()) {
+ return ReductionResult.falseResult();
+ }
+
+ // - If the lambda expression is implicitly typed and one or more of the function type's parameter
+ // types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the handling of implicitly typed lambda
+ // expressions in §18.5.1 and the substitution applied to the target type in §18.5.2.
+
+ // - If the function type's result is void and the lambda body is neither a statement expression
+ // nor a void-compatible block, the constraint reduces to false.
+
+ if (targetFunctionType.getReturnType().isVoid()) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the function type's result is not void and the lambda body is a block that is not
+ // value-compatible, the constraint reduces to false.
+
+ if (!targetFunctionType.getReturnType().isVoid() && lambdaExpr.getBody() instanceof BlockStmt
+ && !isValueCompatibleBlock(lambdaExpr.getBody())) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, the constraint reduces to all of the following constraint formulas:
+ List<ConstraintFormula> constraints = new LinkedList<>();
+
+ // - If the lambda parameters have explicitly declared types F1, ..., Fn and the function type
+ // has parameter types G1, ..., Gn, then i) for all i (1 ≤ i ≤ n), ‹Fi = Gi›, and ii) ‹T' <: T›.
+
+ boolean hasExplicitlyDeclaredTypes = lambdaExpr.getParameters().stream().anyMatch(p -> !(p.getType() instanceof UnknownType));
+ if (hasExplicitlyDeclaredTypes) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the function type's return type is a (non-void) type R, assume the lambda's parameter
+ // types are the same as the function type's parameter types. Then:
+
+ if (!targetFunctionType.getReturnType().isVoid()) {
+
+ ResolvedType R = targetFunctionType.getReturnType();
+
+ if (TypeHelper.isProperType(R)) {
+
+ // - If R is a proper type, and if the lambda body or some result expression in the lambda body
+ // is not compatible in an assignment context with R, then false.
+
+ if (lambdaExpr.getBody() instanceof BlockStmt) {
+ List<Expression> resultExpressions = ExpressionHelper.getResultExpressions((BlockStmt)lambdaExpr.getBody());
+ for (Expression e : resultExpressions) {
+ if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
+ return ReductionResult.falseResult();
+ }
+ }
+ } else {
+ Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
+ if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
+ return ReductionResult.falseResult();
+ }
+ }
+ } else {
+ // - Otherwise, if R is not a proper type, then where the lambda body has the form Expression,
+ // the constraint ‹Expression → R›; or where the lambda body is a block with result
+ // expressions e1, ..., em, for all i (1 ≤ i ≤ m), ‹ei → R›.
+
+ if (lambdaExpr.getBody() instanceof BlockStmt) {
+ getAllReturnExpressions((BlockStmt)lambdaExpr.getBody()).forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R)));
+ } else {
+ // FEDERICO: Added - Start
+ for (int i=0;i<lambdaExpr.getParameters().size();i++) {
+ ResolvedType paramType = targetFunctionType.getFormalArgumentTypes().get(i);
+ TypeInferenceCache.record(typeSolver, lambdaExpr, lambdaExpr.getParameter(i).getNameAsString(), paramType);
+ }
+ // FEDERICO: Added - End
+ Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
+ constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R));
+ }
+ }
+ }
+
+ return ReductionResult.withConstraints(constraints);
+ }
+
+ // A constraint formula of the form ‹MethodReference → T›, where T mentions at least one inference variable, is reduced as follows:
+
+ if (expression instanceof MethodReferenceExpr) {
+
+ // - If T is not a functional interface type, or if T is a functional interface type that does not have a function type (§9.9), the constraint reduces to false.
+ //
+ // - Otherwise, if there does not exist a potentially applicable method for the method reference when targeting T, the constraint reduces to false.
+ //
+ // - Otherwise, if the method reference is exact (§15.13.1), then let P1, ..., Pn be the parameter types of the function type of T, and let F1, ..., Fk be the parameter types of the potentially applicable method. The constraint reduces to a new set of constraints, as follows:
+ //
+ // - In the special case where n = k+1, the parameter of type P1 is to act as the target reference of the invocation. The method reference expression necessarily has the form ReferenceType :: [TypeArguments] Identifier. The constraint reduces to ‹P1 <: ReferenceType› and, for all i (2 ≤ i ≤ n), ‹Pi → Fi-1›.
+ //
+ // In all other cases, n = k, and the constraint reduces to, for all i (1 ≤ i ≤ n), ‹Pi → Fi›.
+ //
+ // - If the function type's result is not void, let R be its return type. Then, if the result of the potentially applicable compile-time declaration is void, the constraint reduces to false. Otherwise, the constraint reduces to ‹R' → R›, where R' is the result of applying capture conversion (§5.1.10) to the return type of the potentially applicable compile-time declaration.
+ //
+ // - Otherwise, the method reference is inexact, and:
+ //
+ // - If one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the handling of inexact method references in §18.5.1 and the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, a search for a compile-time declaration is performed, as specified in §15.13.1. If there is no compile-time declaration for the method reference, the constraint reduces to false. Otherwise, there is a compile-time declaration, and:
+ //
+ // - If the result of the function type is void, the constraint reduces to true.
+ //
+ // - Otherwise, if the method reference expression elides TypeArguments, and the compile-time declaration is a generic method, and the return type of the compile-time declaration mentions at least one of the method's type parameters, then the constraint reduces to the bound set B3 which would be used to determine the method reference's invocation type when targeting the return type of the function type, as defined in §18.5.2. B3 may contain new inference variables, as well as dependencies between these new variables and the inference variables in T.
+ //
+ // - Otherwise, let R be the return type of the function type, and let R' be the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the compile-time declaration. If R' is void, the constraint reduces to false; otherwise, the constraint reduces to ‹R' → R›.
+
+ throw new UnsupportedOperationException();
+ }
+
+ throw new RuntimeException("This should not happen");
+ }
+
+ throw new RuntimeException("This should not happen");
+ }
+
+ private List<Expression> getAllReturnExpressions(BlockStmt blockStmt) {
+ return blockStmt.findAll(ReturnStmt.class).stream()
+ .filter(r -> r.getExpression().isPresent())
+ .map(r -> r.getExpression().get())
+ .collect(toList());
+ }
+
+ private boolean isValueCompatibleBlock(Statement statement) {
+ // A block lambda body is value-compatible if it cannot complete normally (§14.21) and every return statement
+ // in the block has the form return Expression;.
+
+ if (statement instanceof BlockStmt) {
+ if (!ControlFlowLogic.getInstance().canCompleteNormally(statement)) {
+ return true;
+ }
+ List<ReturnStmt> returnStmts = statement.findAll(ReturnStmt.class);
+ return returnStmts.stream().allMatch(r -> r.getExpression().isPresent());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ExpressionCompatibleWithType that = (ExpressionCompatibleWithType) o;
+
+ if (!typeSolver.equals(that.typeSolver)) return false;
+ if (!expression.equals(that.expression)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = typeSolver.hashCode();
+ result = 31 * result + expression.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpressionCompatibleWithType{" +
+ "typeSolver=" + typeSolver +
+ ", expression=" + expression +
+ ", T=" + T +
+ '}';
+ }
+
+ private MethodType replaceTypeVariablesWithInferenceVariables(MethodType methodType) {
+ // Find all type variable
+ Map<ResolvedTypeVariable, InferenceVariable> correspondences = new HashMap<>();
+ List<ResolvedType> newFormalArgumentTypes = new LinkedList<>();
+ for (ResolvedType formalArg : methodType.getFormalArgumentTypes()) {
+ newFormalArgumentTypes.add(replaceTypeVariablesWithInferenceVariables(formalArg, correspondences));
+ }
+ ResolvedType newReturnType = replaceTypeVariablesWithInferenceVariables(methodType.getReturnType(), correspondences);
+ return new MethodType(methodType.getTypeParameters(), newFormalArgumentTypes, newReturnType, methodType.getExceptionTypes());
+ }
+
+ private ResolvedType replaceTypeVariablesWithInferenceVariables(ResolvedType originalType, Map<ResolvedTypeVariable, InferenceVariable> correspondences) {
+ if (originalType.isTypeVariable()) {
+ if (!correspondences.containsKey(originalType.asTypeVariable())) {
+ correspondences.put(originalType.asTypeVariable(), InferenceVariable.unnamed(originalType.asTypeVariable().asTypeParameter()));
+ }
+ return correspondences.get(originalType.asTypeVariable());
+ }
+ if (originalType.isPrimitive()) {
+ return originalType;
+ }
+ throw new UnsupportedOperationException(originalType.toString());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java
new file mode 100644
index 000000000..b1c26ec68
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java
@@ -0,0 +1,67 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+/**
+ * The checked exceptions thrown by the body of the LambdaExpression are declared by the throws clause of the
+ * function type derived from T.
+ *
+ * @author Federico Tomassetti
+ */
+public class LambdaThrowsCompatibleWithType extends ConstraintFormula {
+ private LambdaExpr lambdaExpression;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹LambdaExpression →throws T› is reduced as follows:
+ //
+ // - If T is not a functional interface type (§9.8), the constraint reduces to false.
+ //
+ // - Otherwise, let the target function type for the lambda expression be determined as specified in §15.27.3. If no valid function type can be found, the constraint reduces to false.
+ //
+ // - Otherwise, if the lambda expression is implicitly typed, and one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, if the function type's return type is neither void nor a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, let E1, ..., En be the types in the function type's throws clause that are not proper types. If the lambda expression is implicitly typed, let its parameter types be the function type's parameter types. If the lambda body is a poly expression or a block containing a poly result expression, let the targeted return type be the function type's return type. Let X1, ..., Xm be the checked exception types that the lambda body can throw (§11.2). Then there are two cases:
+ //
+ // - If n = 0 (the function type's throws clause consists only of proper types), then if there exists some i (1 ≤ i ≤ m) such that Xi is not a subtype of any proper type in the throws clause, the constraint reduces to false; otherwise, the constraint reduces to true.
+ //
+ // - If n > 0, the constraint reduces to a set of subtyping constraints: for all i (1 ≤ i ≤ m), if Xi is not a subtype of any proper type in the throws clause, then the constraints include, for all j (1 ≤ j ≤ n), ‹Xi <: Ej›. In addition, for all j (1 ≤ j ≤ n), the constraint reduces to the bound throws Ej.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LambdaThrowsCompatibleWithType that = (LambdaThrowsCompatibleWithType) o;
+
+ if (!lambdaExpression.equals(that.lambdaExpression)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = lambdaExpression.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "LambdaThrowsCompatibleWithType{" +
+ "lambdaExpression=" + lambdaExpression +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java
new file mode 100644
index 000000000..a59e3fbd5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java
@@ -0,0 +1,62 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.MethodReferenceExpr;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+/**
+ * The checked exceptions thrown by the referenced method are declared by the throws clause of the function type
+ * derived from T.
+ *
+ * @author Federico Tomassetti
+ */
+public class MethodReferenceThrowsCompatibleWithType extends ConstraintFormula {
+ private MethodReferenceExpr methodReference;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹MethodReference →throws T› is reduced as follows:
+ //
+ // - If T is not a functional interface type, or if T is a functional interface type but does not have a function type (§9.9), the constraint reduces to false.
+ //
+ // - Otherwise, let the target function type for the method reference expression be the function type of T. If the method reference is inexact (§15.13.1) and one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // - Otherwise, if the method reference is inexact and the function type's result is neither void nor a proper type, the constraint reduces to false.
+ //
+ // - Otherwise, let E1, ..., En be the types in the function type's throws clause that are not proper types. Let X1, ..., Xm be the checked exceptions in the throws clause of the invocation type of the method reference's compile-time declaration (§15.13.2) (as derived from the function type's parameter types and return type). Then there are two cases:
+ //
+ // - If n = 0 (the function type's throws clause consists only of proper types), then if there exists some i (1 ≤ i ≤ m) such that Xi is not a subtype of any proper type in the throws clause, the constraint reduces to false; otherwise, the constraint reduces to true.
+ //
+ // - If n > 0, the constraint reduces to a set of subtyping constraints: for all i (1 ≤ i ≤ m), if Xi is not a subtype of any proper type in the throws clause, then the constraints include, for all j (1 ≤ j ≤ n), ‹Xi <: Ej›. In addition, for all j (1 ≤ j ≤ n), the constraint reduces to the bound throws Ej.
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodReferenceThrowsCompatibleWithType that = (MethodReferenceThrowsCompatibleWithType) o;
+
+ if (!methodReference.equals(that.methodReference)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = methodReference.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "MethodReferenceThrowsCompatibleWithType{" +
+ "methodReference=" + methodReference +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java
new file mode 100644
index 000000000..1c03064a8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java
@@ -0,0 +1,122 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type S is compatible in a loose invocation context with type T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeCompatibleWithType extends ConstraintFormula {
+ private ResolvedType s;
+ private ResolvedType t;
+ private TypeSolver typeSolver;
+
+ public TypeCompatibleWithType(TypeSolver typeSolver, ResolvedType s, ResolvedType t) {
+ this.typeSolver = typeSolver;
+ this.s = s;
+ this.t = t;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S → T› is reduced as follows:
+ //
+ // 1. If S and T are proper types, the constraint reduces to true if S is compatible in a loose invocation context with T (§5.3), and false otherwise.
+
+ if (isProperType(s) && isProperType(t)) {
+ if (isCompatibleInALooseInvocationContext(s, t)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // 2. Otherwise, if S is a primitive type, let S' be the result of applying boxing conversion (§5.1.7) to S. Then the constraint reduces to ‹S' → T›.
+
+ if (s.isPrimitive()) {
+ ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
+ ResolvedType sFirst = new ReferenceTypeImpl(typeSolver.solveType(s.asPrimitive().getBoxTypeQName()), typeSolver);
+ return ReductionResult.oneConstraint(new TypeCompatibleWithType(typeSolver, sFirst, t));
+ }
+
+ // 3. Otherwise, if T is a primitive type, let T' be the result of applying boxing conversion (§5.1.7) to T. Then the constraint reduces to ‹S = T'›.
+
+ if (t.isPrimitive()) {
+ ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
+ ResolvedType tFirst = new ReferenceTypeImpl(typeSolver.solveType(t.asPrimitive().getBoxTypeQName()), typeSolver);
+ return ReductionResult.oneConstraint(new TypeSameAsType(s, tFirst));
+ }
+
+ // The fourth and fifth cases are implicit uses of unchecked conversion (§5.1.9). These, along with any use of
+ // unchecked conversion in the first case, may result in compile-time unchecked warnings, and may influence a
+ // method's invocation type (§15.12.2.6).
+
+ // 4. Otherwise, if T is a parameterized type of the form G<T1, ..., Tn>, and there exists no type of the
+ // form G<...> that is a supertype of S, but the raw type G is a supertype of S, then the constraint reduces
+ // to true.
+
+ if (t.isReferenceType() && !t.asReferenceType().getTypeDeclaration().getTypeParameters().isEmpty()) {
+ // FIXME I really cannot understand what the specification means...
+
+ // there exists a type of the form G<...> that is a supertype of S?
+ boolean condition1 = t.isAssignableBy(s);
+
+ // the raw type G is a supertype of S
+ ResolvedType G = t.asReferenceType().toRawType();
+ boolean condition2 = G.isAssignableBy(s);
+
+ if (!condition1 && condition2) {
+ return ReductionResult.trueResult();
+ }
+
+ //throw new UnsupportedOperationException();
+ }
+
+ // 5. Otherwise, if T is an array type of the form G<T1, ..., Tn>[]k, and there exists no type of the form
+ // G<...>[]k that is a supertype of S, but the raw type G[]k is a supertype of S, then the constraint
+ // reduces to true. (The notation []k indicates an array type of k dimensions.)
+
+ if (t.isArray()) {
+ throw new UnsupportedOperationException();
+ }
+
+ // 6. Otherwise, the constraint reduces to ‹S <: T›
+
+ return ReductionResult.empty().withConstraint(new TypeSubtypeOfType(typeSolver, s, t));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeCompatibleWithType that = (TypeCompatibleWithType) o;
+
+ if (!s.equals(that.s)) return false;
+ return t.equals(that.t);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = s.hashCode();
+ result = 31 * result + t.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeCompatibleWithType{" +
+ "s=" + s +
+ ", t=" + t +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java
new file mode 100644
index 000000000..685270a4c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java
@@ -0,0 +1,95 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type argument S is contained by a type argument T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeContainedByType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S <= T›, where S and T are type arguments (§4.5.1), is reduced as follows:
+ //
+ // - If T is a type:
+
+ if (isProperType(T) && !T.isWildcard()) {
+
+ // - If S is a type, the constraint reduces to ‹S = T›.
+ //
+ // - If S is a wildcard, the constraint reduces to false.
+
+ throw new UnsupportedOperationException();
+ }
+
+ // - If T is a wildcard of the form ?, the constraint reduces to true.
+
+ if (T.isWildcard() && !T.asWildcard().isBounded()) {
+ return ReductionResult.trueResult();
+ }
+
+ // - If T is a wildcard of the form ? extends T':
+
+ if (T.isWildcard() && T.asWildcard().isExtends()) {
+
+ // - If S is a type, the constraint reduces to ‹S <: T'›.
+ //
+ // - If S is a wildcard of the form ?, the constraint reduces to ‹Object <: T'›.
+ //
+ // - If S is a wildcard of the form ? extends S', the constraint reduces to ‹S' <: T'›.
+ //
+ // - If S is a wildcard of the form ? super S', the constraint reduces to ‹Object = T'›.
+
+ throw new UnsupportedOperationException();
+ }
+
+ // - If T is a wildcard of the form ? super T':
+
+ if (T.isWildcard() && T.asWildcard().isSuper()) {
+
+ // - If S is a type, the constraint reduces to ‹T' <: S›.
+ //
+ // - If S is a wildcard of the form ? super S', the constraint reduces to ‹T' <: S'›.
+ //
+ // - Otherwise, the constraint reduces to false.
+
+ throw new UnsupportedOperationException();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeContainedByType that = (TypeContainedByType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeContainedByType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java
new file mode 100644
index 000000000..5fbdc51ae
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java
@@ -0,0 +1,140 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SameAsBound;
+
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type S is the same as a type T (§4.3.4), or a type argument S is the same as type argument T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeSameAsType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+
+ public TypeSameAsType(ResolvedType s, ResolvedType t) {
+ S = s;
+ T = t;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S = T›, where S and T are types, is reduced as follows:
+
+ if (!S.isWildcard() && !T.isWildcard()) {
+
+ // - If S and T are proper types, the constraint reduces to true if S is the same as T (§4.3.4), and false
+ // otherwise.
+
+ if (isProperType(S) && isProperType(T)) {
+ if (S.equals(T)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // - Otherwise, if S or T is the null type, the constraint reduces to false.
+
+ if (S.isNull() || T.isNull()) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, if S is an inference variable, α, and T is not a primitive type, the constraint reduces to the
+ // bound α = T.
+
+ if (isInferenceVariable(S) && !T.isPrimitive()) {
+ return ReductionResult.oneBound(new SameAsBound(S, T));
+ }
+
+ // - Otherwise, if T is an inference variable, α, and S is not a primitive type, the constraint reduces to the
+ // bound S = α.
+
+ if (isInferenceVariable(T) && !S.isPrimitive()) {
+ return ReductionResult.oneBound(new SameAsBound(S, T));
+ }
+
+ // - Otherwise, if S and T are class or interface types with the same erasure, where S has
+ // type arguments B1, ..., Bn and T has type arguments A1, ..., An, the constraint reduces to the following
+ // new constraints: for all i (1 ≤ i ≤ n), ‹Bi = Ai›.
+
+ if (S.isReferenceType() && T.isReferenceType()
+ && S.asReferenceType().toRawType().equals(T.asReferenceType().toRawType())) {
+ ReductionResult res = ReductionResult.empty();
+ List<ResolvedType> Bs = S.asReferenceType().typeParametersValues();
+ List<ResolvedType> As = T.asReferenceType().typeParametersValues();
+ for (int i = 0; i < Bs.size(); i++) {
+ res = res.withConstraint(new TypeSameAsType(Bs.get(i), As.get(i)));
+ }
+ return res;
+ }
+
+ // - Otherwise, if S and T are array types, S'[] and T'[], the constraint reduces to ‹S' = T'›.
+
+ if (S.isArray() && T.isArray()) {
+ return ReductionResult.oneConstraint(new TypeSameAsType(
+ S.asArrayType().getComponentType(),
+ T.asArrayType().getComponentType()));
+ }
+
+ // - Otherwise, the constraint reduces to false.
+
+ return ReductionResult.falseResult();
+ }
+
+ // Note that we do not address intersection types above, because it is impossible for reduction to encounter an
+ // intersection type that is not a proper type.
+
+ // A constraint formula of the form ‹S = T›, where S and T are type arguments (§4.5.1), is reduced as follows:
+ //
+ // - If S and T are types, the constraint is reduced as described above.
+ //
+ // - If S has the form ? and T has the form ?, the constraint reduces to true.
+ //
+ // - If S has the form ? and T has the form ? extends T', the constraint reduces to ‹Object = T'›.
+ //
+ // - If S has the form ? extends S' and T has the form ?, the constraint reduces to ‹S' = Object›.
+ //
+ // - If S has the form ? extends S' and T has the form ? extends T', the constraint reduces to ‹S' = T'›.
+ //
+ // - If S has the form ? super S' and T has the form ? super T', the constraint reduces to ‹S' = T'›.
+ //
+ // - Otherwise, the constraint reduces to false.
+
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeSameAsType that = (TypeSameAsType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeSameAsType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java
new file mode 100644
index 000000000..6bc7fdc81
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java
@@ -0,0 +1,139 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedIntersectionType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.NullType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SubtypeOfBound;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A reference type S is a subtype of a reference type T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeSubtypeOfType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+ private TypeSolver typeSolver;
+
+ public TypeSubtypeOfType(TypeSolver typeSolver, ResolvedType S, ResolvedType T) {
+ this.typeSolver = typeSolver;
+ this.S = S;
+ this.T = T;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S <: T› is reduced as follows:
+ //
+ // - If S and T are proper types, the constraint reduces to true if S is a subtype of T (§4.10), and false otherwise.
+
+ if (isProperType(S) && isProperType(T)) {
+ if (T.isAssignableBy(S)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // - Otherwise, if S is the null type, the constraint reduces to true.
+
+ if (S instanceof NullType) {
+ return ReductionResult.trueResult();
+ }
+
+ // - Otherwise, if T is the null type, the constraint reduces to false.
+
+ if (T instanceof NullType) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, if S is an inference variable, α, the constraint reduces to the bound α <: T.
+
+ if (isInferenceVariable(S)) {
+ return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ }
+
+ // - Otherwise, if T is an inference variable, α, the constraint reduces to the bound S <: α.
+
+ if (isInferenceVariable(T)) {
+ return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ }
+
+ // FEDERICO - Added start
+ //if (T.isTypeVariable()) {
+ // return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ //}
+ // FEDERICO - Added end
+
+ // - Otherwise, the constraint is reduced according to the form of T:
+ //
+ // - If T is a parameterized class or interface type, or an inner class type of a parameterized class or interface type (directly or indirectly), let A1, ..., An be the type arguments of T. Among the supertypes of S, a corresponding class or interface type is identified, with type arguments B1, ..., Bn. If no such type exists, the constraint reduces to false. Otherwise, the constraint reduces to the following new constraints: for all i (1 ≤ i ≤ n), ‹Bi <= Ai›.
+ //
+ // - If T is any other class or interface type, then the constraint reduces to true if T is among the supertypes of S, and false otherwise.
+ //
+ // - If T is an array type, T'[], then among the supertypes of S that are array types, a most specific type is identified, S'[] (this may be S itself). If no such array type exists, the constraint reduces to false. Otherwise:
+ //
+ // - If neither S' nor T' is a primitive type, the constraint reduces to ‹S' <: T'›.
+ //
+ // - Otherwise, the constraint reduces to true if S' and T' are the same primitive type, and false otherwise.
+ //
+ // - If T is a type variable, there are three cases:
+
+ if (T.isTypeVariable()) {
+
+ // - If S is an intersection type of which T is an element, the constraint reduces to true.
+
+ if (S instanceof ResolvedIntersectionType) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - Otherwise, if T has a lower bound, B, the constraint reduces to ‹S <: B›.
+
+ if (T.asTypeVariable().asTypeParameter().hasLowerBound()) {
+ return ReductionResult.oneConstraint(new TypeSubtypeOfType(typeSolver, S, T.asTypeVariable().asTypeParameter().getLowerBound()));
+ }
+
+ // - Otherwise, the constraint reduces to false.
+
+ return ReductionResult.falseResult();
+ }
+
+ //
+ // - If T is an intersection type, I1 & ... & In, the constraint reduces to the following new constraints: for all i (1 ≤ i ≤ n), ‹S <: Ii›.
+ //
+
+ throw new UnsupportedOperationException("S = "+ S + ", T = " + T);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeSubtypeOfType that = (TypeSubtypeOfType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeSubtypeOfType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}