1f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettipackage com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
2f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
3f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.ast.expr.*;
4f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.ast.stmt.*;
5f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.ast.type.UnknownType;
6e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassettiimport com.github.javaparser.resolution.types.ResolvedType;
7e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassettiimport com.github.javaparser.resolution.types.ResolvedTypeVariable;
8f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
9f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
10f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
11f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.symbolsolver.resolution.typeinference.*;
12f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport com.github.javaparser.utils.Pair;
13f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
14a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassettiimport java.util.HashMap;
15f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport java.util.LinkedList;
16f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport java.util.List;
17a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassettiimport java.util.Map;
18f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
19f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isPolyExpression;
20f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression;
21f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
22f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettiimport static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
23b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggenimport static java.util.stream.Collectors.*;
24f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
25f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti/**
26f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti * An expression is compatible in a loose invocation context with type T
27f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti *
28f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti * @author Federico Tomassetti
29f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti */
30f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassettipublic class ExpressionCompatibleWithType extends ConstraintFormula {
31f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    private TypeSolver typeSolver;
32f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    private Expression expression;
33e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti    private ResolvedType T;
34f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
35e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti    public ExpressionCompatibleWithType(TypeSolver typeSolver, Expression expression, ResolvedType T) {
36f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        this.typeSolver = typeSolver;
37f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        this.expression = expression;
38f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        this.T = T;
39f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
40f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
41f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    @Override
42f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    public ReductionResult reduce(BoundSet currentBoundSet) {
43f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // If T is a proper type, the constraint reduces to true if the expression is compatible in a loose
44f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // invocation context with T (§5.3), and false otherwise.
45f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
46f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (isProperType(T)) {
47f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (isCompatibleInALooseInvocationContext(typeSolver, expression, T)) {
48f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return ReductionResult.trueResult();
49f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            } else {
50f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return ReductionResult.falseResult();
51f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
52f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        }
53f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
54f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // Otherwise, if the expression is a standalone expression (§15.2) of type S, the constraint reduces
55f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // to ‹S → T›.
56f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
57f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (isStandaloneExpression(expression)) {
58e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti            ResolvedType s = JavaParserFacade.get(typeSolver).getType(expression, false);
59f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            return ReductionResult.empty().withConstraint(new TypeCompatibleWithType(typeSolver, s, T));
60f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        }
61f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
62f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // Otherwise, the expression is a poly expression (§15.2). The result depends on the form of the expression:
63f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
64f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (isPolyExpression(expression)) {
65f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
66f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // - If the expression is a parenthesized expression of the form ( Expression' ), the constraint reduces
67f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   to ‹Expression' → T›.
68f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
69f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof EnclosedExpr) {
70f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                EnclosedExpr enclosedExpr = (EnclosedExpr)expression;
71f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return ReductionResult.oneConstraint(new ExpressionCompatibleWithType(typeSolver, enclosedExpr.getInner(), T));
72f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
73f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
74f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // - If the expression is a class instance creation expression or a method invocation expression, the
75f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   constraint reduces to the bound set B3 which would be used to determine the expression's invocation
76f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   type when targeting T, as defined in §18.5.2. (For a class instance creation expression, the
77f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   corresponding "method" used for inference is defined in §15.9.3).
78f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //
79f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   This bound set may contain new inference variables, as well as dependencies between these new
80f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   variables and the inference variables in T.
81f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
82f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof ObjectCreationExpr) {
83d85a40f2ffa200bbb0b11adc979e69f3c9410c5fFederico Tomassetti                BoundSet B3 = new TypeInference(typeSolver).invocationTypeInferenceBoundsSetB3();
84d85a40f2ffa200bbb0b11adc979e69f3c9410c5fFederico Tomassetti                return ReductionResult.bounds(B3);
85f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
86f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
87f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof MethodCallExpr) {
88f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                throw new UnsupportedOperationException();
89f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
90f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
91f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // - If the expression is a conditional expression of the form e1 ? e2 : e3, the constraint reduces to two
92f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   constraint formulas, ‹e2 → T› and ‹e3 → T›.
93f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
94f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof ConditionalExpr) {
95f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                ConditionalExpr conditionalExpr = (ConditionalExpr)expression;
96f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return ReductionResult.withConstraints(
97f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getThenExpr(), T),
98f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getElseExpr(), T));
99f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
100f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
101f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // - If the expression is a lambda expression or a method reference expression, the result is specified
102f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            //   below.
103f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
104f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // A constraint formula of the form ‹LambdaExpression → T›, where T mentions at least one inference variable, is reduced as follows:
105f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
106f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof LambdaExpr) {
107f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                LambdaExpr lambdaExpr = (LambdaExpr)expression;
108f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
109f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - If T is not a functional interface type (§9.8), the constraint reduces to false.
110f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
111f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (!FunctionalInterfaceLogic.isFunctionalInterfaceType(T)) {
112f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    return ReductionResult.falseResult();
113f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
114f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
115f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - Otherwise, let T' be the ground target type derived from T, as specified in §15.27.3. If §18.5.3
116f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   is used to derive a functional interface type which is parameterized, then the test that
117f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   F<A'1, ..., A'm> is a subtype of F<A1, ..., Am> is not performed (instead, it is asserted with a
118f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   constraint formula below). Let the target function type for the lambda expression be the
119f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   function type of T'. Then:
120f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
121e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti                Pair<ResolvedType, Boolean> result = TypeHelper.groundTargetTypeOfLambda(lambdaExpr, T, typeSolver);
122e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti                ResolvedType TFirst = result.a;
123f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                MethodType targetFunctionType = TypeHelper.getFunctionType(TFirst);
124a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti                targetFunctionType = replaceTypeVariablesWithInferenceVariables(targetFunctionType);
125f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (result.b) {
126f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    throw new UnsupportedOperationException();
127f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
128f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
129f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - If no valid function type can be found, the constraint reduces to false.
130f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
131f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     Federico: THIS SHOULD NOT HAPPEN, IN CASE WE WILL THROW AN EXCEPTION
132f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
133f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - Otherwise, the congruence of LambdaExpression with the target function type is asserted as
134f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     follows:
135f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
136f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - If the number of lambda parameters differs from the number of parameter types of the function
137f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       type, the constraint reduces to false.
138f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
139f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (targetFunctionType.getFormalArgumentTypes().size() != lambdaExpr.getParameters().size()) {
140f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    return ReductionResult.falseResult();
141f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
142f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
143f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - If the lambda expression is implicitly typed and one or more of the function type's parameter
144f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       types is not a proper type, the constraint reduces to false.
145f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
146f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       This condition never arises in practice, due to the handling of implicitly typed lambda
147f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       expressions in §18.5.1 and the substitution applied to the target type in §18.5.2.
148f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
149f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - If the function type's result is void and the lambda body is neither a statement expression
150f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       nor a void-compatible block, the constraint reduces to false.
151f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
152f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (targetFunctionType.getReturnType().isVoid()) {
153f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    throw new UnsupportedOperationException();
154f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
155f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
156f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - If the function type's result is not void and the lambda body is a block that is not
157f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       value-compatible, the constraint reduces to false.
158f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
159f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (!targetFunctionType.getReturnType().isVoid() && lambdaExpr.getBody() instanceof BlockStmt
160f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        && !isValueCompatibleBlock(lambdaExpr.getBody())) {
161f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    return ReductionResult.falseResult();
162f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
163f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
164f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - Otherwise, the constraint reduces to all of the following constraint formulas:
165f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                List<ConstraintFormula> constraints = new LinkedList<>();
166f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
167f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       - If the lambda parameters have explicitly declared types F1, ..., Fn and the function type
168f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //         has parameter types G1, ..., Gn, then i) for all i (1 ≤ i ≤ n), ‹Fi = Gi›, and ii) ‹T' <: T›.
169f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
170f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                boolean hasExplicitlyDeclaredTypes = lambdaExpr.getParameters().stream().anyMatch(p -> !(p.getType() instanceof UnknownType));
171f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (hasExplicitlyDeclaredTypes) {
172f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    throw new UnsupportedOperationException();
173f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
174f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
175f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //       - If the function type's return type is a (non-void) type R, assume the lambda's parameter
176f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //         types are the same as the function type's parameter types. Then:
177f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
178f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                if (!targetFunctionType.getReturnType().isVoid()) {
179f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
180e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti                    ResolvedType R = targetFunctionType.getReturnType();
181f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
182f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    if (TypeHelper.isProperType(R)) {
183f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
184f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        //         - If R is a proper type, and if the lambda body or some result expression in the lambda body
185f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        //           is not compatible in an assignment context with R, then false.
186f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
187f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        if (lambdaExpr.getBody() instanceof BlockStmt) {
188f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            List<Expression> resultExpressions = ExpressionHelper.getResultExpressions((BlockStmt)lambdaExpr.getBody());
189f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            for (Expression e : resultExpressions) {
190f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                                if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
191f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                                    return ReductionResult.falseResult();
192f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                                }
193f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            }
194f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        } else {
195f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
196f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
197f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                                return ReductionResult.falseResult();
198f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            }
199f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        }
200f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    } else {
201f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        //         - Otherwise, if R is not a proper type, then where the lambda body has the form Expression,
202f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        //           the constraint ‹Expression → R›; or where the lambda body is a block with result
203f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        //           expressions e1, ..., em, for all i (1 ≤ i ≤ m), ‹ei → R›.
204f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
205f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        if (lambdaExpr.getBody() instanceof BlockStmt) {
206b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggen                            getAllReturnExpressions((BlockStmt)lambdaExpr.getBody()).forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R)));
207f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        } else {
208f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            // FEDERICO: Added - Start
209f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            for (int i=0;i<lambdaExpr.getParameters().size();i++) {
210e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti                                ResolvedType paramType = targetFunctionType.getFormalArgumentTypes().get(i);
211f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                                TypeInferenceCache.record(typeSolver, lambdaExpr, lambdaExpr.getParameter(i).getNameAsString(), paramType);
212f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            }
213f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            // FEDERICO: Added - End
214f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
215f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                            constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R));
216f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                        }
217f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                    }
218f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                }
219f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
220f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return ReductionResult.withConstraints(constraints);
221f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
222f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
223f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            // A constraint formula of the form ‹MethodReference → T›, where T mentions at least one inference variable, is reduced as follows:
224f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
225f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            if (expression instanceof MethodReferenceExpr) {
226f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
227f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - 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.
228f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
229f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - Otherwise, if there does not exist a potentially applicable method for the method reference when targeting T, the constraint reduces to false.
230f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
231f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - 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:
232f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
233f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - 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›.
234f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
235f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     In all other cases, n = k, and the constraint reduces to, for all i (1 ≤ i ≤ n), ‹Pi → Fi›.
236f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
237f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - 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.
238f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
239f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                // - Otherwise, the method reference is inexact, and:
240f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
241f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - If one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
242f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
243f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     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.
244f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
245f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //   - 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:
246f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
247f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - If the result of the function type is void, the constraint reduces to true.
248f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
249f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - 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.
250f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //
251f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                //     - 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›.
252f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
253f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                throw new UnsupportedOperationException();
254f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
255f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
256f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            throw new RuntimeException("This should not happen");
257f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        }
258f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
259f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        throw new RuntimeException("This should not happen");
260f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
261f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
262f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    private List<Expression> getAllReturnExpressions(BlockStmt blockStmt) {
263b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggen        return blockStmt.findAll(ReturnStmt.class).stream()
264f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                .filter(r -> r.getExpression().isPresent())
265f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                .map(r -> r.getExpression().get())
266b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggen                .collect(toList());
267f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
268f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
269f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    private boolean isValueCompatibleBlock(Statement statement) {
270f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // A block lambda body is value-compatible if it cannot complete normally (§14.21) and every return statement
271f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        // in the block has the form return Expression;.
272f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
273f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (statement instanceof BlockStmt) {
274d86f152abcaf45b9ad37a1341ab46299b8e99497Federico Tomassetti            if (!ControlFlowLogic.getInstance().canCompleteNormally(statement)) {
275f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                return true;
276f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            }
277b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggen            List<ReturnStmt> returnStmts = statement.findAll(ReturnStmt.class);
278f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti            return returnStmts.stream().allMatch(r -> r.getExpression().isPresent());
279f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        }
280b54617e765d73b3ce0d187cf12ad8da382bce439Danny van Bruggen        return false;
281f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
282f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
283f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    @Override
284f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    public boolean equals(Object o) {
285f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (this == o) return true;
286f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (o == null || getClass() != o.getClass()) return false;
287f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
288f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        ExpressionCompatibleWithType that = (ExpressionCompatibleWithType) o;
289f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
290f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (!typeSolver.equals(that.typeSolver)) return false;
291f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        if (!expression.equals(that.expression)) return false;
292f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        return T.equals(that.T);
293f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
294f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
295f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    @Override
296f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    public int hashCode() {
297f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        int result = typeSolver.hashCode();
298f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        result = 31 * result + expression.hashCode();
299f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        result = 31 * result + T.hashCode();
300f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        return result;
301f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
302f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti
303f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    @Override
304f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    public String toString() {
305f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti        return "ExpressionCompatibleWithType{" +
306f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                "typeSolver=" + typeSolver +
307f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                ", expression=" + expression +
308f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                ", T=" + T +
309f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti                '}';
310f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti    }
311a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti
312a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti    private MethodType replaceTypeVariablesWithInferenceVariables(MethodType methodType) {
313a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        // Find all type variable
314e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti        Map<ResolvedTypeVariable, InferenceVariable> correspondences = new HashMap<>();
315e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti        List<ResolvedType> newFormalArgumentTypes = new LinkedList<>();
316e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti        for (ResolvedType formalArg : methodType.getFormalArgumentTypes()) {
317a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti            newFormalArgumentTypes.add(replaceTypeVariablesWithInferenceVariables(formalArg, correspondences));
318a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        }
319e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti        ResolvedType newReturnType = replaceTypeVariablesWithInferenceVariables(methodType.getReturnType(), correspondences);
320a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        return new MethodType(methodType.getTypeParameters(), newFormalArgumentTypes, newReturnType, methodType.getExceptionTypes());
321a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti    }
322a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti
323e8007447885c3ee0a462444cbc7fa83e80297f4aFederico Tomassetti    private ResolvedType replaceTypeVariablesWithInferenceVariables(ResolvedType originalType, Map<ResolvedTypeVariable, InferenceVariable> correspondences) {
324a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        if (originalType.isTypeVariable()) {
325a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti            if (!correspondences.containsKey(originalType.asTypeVariable())) {
326a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti                correspondences.put(originalType.asTypeVariable(), InferenceVariable.unnamed(originalType.asTypeVariable().asTypeParameter()));
327a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti            }
328a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti            return correspondences.get(originalType.asTypeVariable());
329a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        }
330a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        if (originalType.isPrimitive()) {
331a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti            return originalType;
332a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        }
333a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti        throw new UnsupportedOperationException(originalType.toString());
334a65cadbf0b6914f061968057a80680a5d25a042bFederico Tomassetti    }
335f30c3694b1c6dd5b3db5dfbbc1dddcbffbf6cb95Federico Tomassetti}
336