blob: 49902116045aa30f554072cf5aefa07e6833d116 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2014 GK Software AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
/**
* Constraint formula expressing that a given expression must have an exception type.
* <ul>
* <li>Expression contains<sub>throws</sub> T</li>
* </ul>
*/
public class ConstraintExceptionFormula extends ConstraintFormula {
FunctionalExpression left;
public ConstraintExceptionFormula(FunctionalExpression left, TypeBinding type) {
this.left = left;
this.right = type;
this.relation = EXCEPTIONS_CONTAINED;
}
public Object reduce(InferenceContext18 inferenceContext) {
// JLS 18.2.5
Scope scope = inferenceContext.scope;
if (!this.right.isFunctionalInterface(scope))
return FALSE;
MethodBinding sam = this.right.getSingleAbstractMethod(scope, true);
if (sam == null)
return FALSE;
if (this.left instanceof LambdaExpression) {
if (((LambdaExpression)this.left).argumentsTypeElided()) {
int nParam = sam.parameters.length;
for (int i = 0; i < nParam; i++)
if (!sam.parameters[i].isProperType(true))
return FALSE;
}
if (sam.returnType != TypeBinding.VOID && !sam.returnType.isProperType(true))
return FALSE;
} else { // reference expression
if (!((ReferenceExpression)this.left).isExactMethodReference()) {
int nParam = sam.parameters.length;
for (int i = 0; i < nParam; i++)
if (!sam.parameters[i].isProperType(true))
return FALSE;
if (sam.returnType != TypeBinding.VOID && !sam.returnType.isProperType(true))
return FALSE;
}
}
TypeBinding[] thrown = sam.thrownExceptions;
InferenceVariable[] e = new InferenceVariable[thrown.length];
int n = 0;
for (int i = 0; i < thrown.length; i++)
if (!thrown[i].isProperType(true))
e[n++] = (InferenceVariable) thrown[i]; // thrown[i] is not a proper type, since it's an exception it must be an inferenceVariable, right?
/* If throw specification does not encode any type parameters, there are no constraints to be gleaned/gathered from the throw sites.
See also that thrown exceptions are not allowed to influence compatibility and overload resolution.
*/
if (n == 0)
return TRUE;
TypeBinding[] ePrime = null;
if (this.left instanceof LambdaExpression) {
LambdaExpression lambda = ((LambdaExpression) this.left).getResolvedCopyForInferenceTargeting(this.right);
if (lambda == null)
return TRUE; // cannot make use of this buggy constraint
Set<TypeBinding> ePrimeSet = lambda.getThrownExceptions();
ePrime = ePrimeSet.toArray(new TypeBinding[ePrimeSet.size()]);
} else {
ReferenceExpression referenceExpression = (ReferenceExpression)this.left;
MethodBinding method = referenceExpression.findCompileTimeMethodTargeting(this.right, scope);
if (method != null)
ePrime = method.thrownExceptions;
}
if (ePrime == null)
return TRUE;
int m = ePrime.length;
List<ConstraintFormula> result = new ArrayList<ConstraintFormula>();
actual: for (int i = 0; i < m; i++) {
if (ePrime[i].isUncheckedException(false))
continue;
for (int j = 0; j < thrown.length; j++)
if (thrown[j].isProperType(true) && ePrime[i].isCompatibleWith(thrown[j]))
continue actual;
for (int j = 0; j < n; j++)
result.add(ConstraintTypeFormula.create(ePrime[i], e[j], SUBTYPE));
}
for (int j = 0; j < n; j++)
inferenceContext.currentBounds.inThrows.add(e[j]);
return result.toArray(new ConstraintFormula[result.size()]);
}
Collection<InferenceVariable> inputVariables(final InferenceContext18 context) {
// from 18.5.2.
if (this.left instanceof LambdaExpression) {
if (this.right instanceof InferenceVariable) {
return Collections.singletonList((InferenceVariable)this.right);
}
if (this.right.isFunctionalInterface(context.scope)) {
LambdaExpression lambda = (LambdaExpression) this.left;
MethodBinding sam = this.right.getSingleAbstractMethod(context.scope, true); // TODO derive with target type?
final Set<InferenceVariable> variables = new HashSet<InferenceVariable>();
if (lambda.argumentsTypeElided()) {
// i)
int len = sam.parameters.length;
for (int i = 0; i < len; i++) {
sam.parameters[i].collectInferenceVariables(variables);
}
}
if (sam.returnType != TypeBinding.VOID) {
// ii)
sam.returnType.collectInferenceVariables(variables);
}
return variables;
}
} else if (this.left instanceof ReferenceExpression) {
if (this.right instanceof InferenceVariable) {
return Collections.singletonList((InferenceVariable)this.right);
}
if (this.right.isFunctionalInterface(context.scope)) { // TODO: && this.left is inexact
MethodBinding sam = this.right.getSingleAbstractMethod(context.scope, true); // TODO derive with target type?
final Set<InferenceVariable> variables = new HashSet<InferenceVariable>();
int len = sam.parameters.length;
for (int i = 0; i < len; i++) {
sam.parameters[i].collectInferenceVariables(variables);
}
sam.returnType.collectInferenceVariables(variables);
return variables;
}
}
return EMPTY_VARIABLE_LIST;
}
public String toString() {
StringBuffer buf = new StringBuffer().append(LEFT_ANGLE_BRACKET);
this.left.printExpression(4, buf);
buf.append(" \u2286throws "); //$NON-NLS-1$
appendTypeName(buf, this.right);
buf.append(RIGHT_ANGLE_BRACKET);
return buf.toString();
}
}