package org.eclipse.jdt.internal.compiler.lookup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FunctionalExpression;
import org.eclipse.jdt.internal.compiler.ast.Invocation;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.SwitchExpression;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants.BoundCheckStatus;
import org.eclipse.jdt.internal.compiler.util.Sorting;
public class InferenceContext18 {
static final boolean SIMULATE_BUG_JDK_8026527 = true;
static final boolean SHOULD_WORKAROUND_BUG_JDK_8054721 = true;
static final boolean SHOULD_WORKAROUND_BUG_JDK_8153748 = true;
static final boolean ARGUMENT_CONSTRAINTS_ARE_SOFT = false;
InvocationSite currentInvocation;
Expression[] invocationArguments;
InferenceVariable[] inferenceVariables;
ConstraintFormula[] initialConstraints;
ConstraintExpressionFormula[] finalConstraints;
BoundSet currentBounds;
int inferenceKind;
public int stepCompleted = NOT_INFERRED;
public static final int NOT_INFERRED = 0;
public static final int APPLICABILITY_INFERRED = 1;
public static final int TYPE_INFERRED = 2;
public static final int TYPE_INFERRED_FINAL = 3;
public List<ConstraintFormula> constraintsWithUncheckedConversion;
public boolean usesUncheckedConversion;
public InferenceContext18 outerContext;
private Set<InferenceContext18> seenInnerContexts;
Scope scope;
LookupEnvironment environment;
ReferenceBinding object;
public BoundSet b2;
private BoundSet b3;
private BoundSet innerInbox;
private boolean directlyAcceptingInnerBounds = false;
private Runnable pushToOuterJob = null;
public static boolean isSameSite(InvocationSite site1, InvocationSite site2) {
if (site1 == site2)
return true;
if (site1 == null || site2 == null)
return false;
if (site1.sourceStart() == site2.sourceStart() && site1.sourceEnd() == site2.sourceEnd())
return true;
return false;
}
public static final int CHECK_UNKNOWN = 0;
public static final int CHECK_STRICT = 1;
public static final int CHECK_LOOSE = 2;
public static final int CHECK_VARARG = 3;
static class SuspendedInferenceRecord {
InvocationSite site;
Expression[] invocationArguments;
InferenceVariable[] inferenceVariables;
int inferenceKind;
boolean usesUncheckedConversion;
SuspendedInferenceRecord(InvocationSite site, Expression[] invocationArguments, InferenceVariable[] inferenceVariables, int inferenceKind, boolean usesUncheckedConversion) {
this.site = site;
this.invocationArguments = invocationArguments;
this.inferenceVariables = inferenceVariables;
this.inferenceKind = inferenceKind;
this.usesUncheckedConversion = usesUncheckedConversion;
}
}
public InferenceContext18(Scope scope, Expression[] arguments, InvocationSite site, InferenceContext18 outerContext) {
this.scope = scope;
this.environment = scope.environment();
this.object = scope.getJavaLangObject();
this.invocationArguments = arguments;
this.currentInvocation = site;
this.outerContext = outerContext;
if (site instanceof Invocation)
scope.compilationUnitScope().registerInferredInvocation((Invocation) site);
}
public InferenceContext18(Scope scope) {
this.scope = scope;
this.environment = scope.environment();
this.object = scope.getJavaLangObject();
}
public InferenceVariable[] createInitialBoundSet(TypeVariableBinding[] typeParameters) {
if (this.currentBounds == null) {
this.currentBounds = new BoundSet();
}
if (typeParameters != null) {
InferenceVariable[] newInferenceVariables = addInitialTypeVariableSubstitutions(typeParameters);
this.currentBounds.addBoundsFromTypeParameters(this, typeParameters, newInferenceVariables);
return newInferenceVariables;
}
return Binding.NO_INFERENCE_VARIABLES;
}
public TypeBinding substitute(TypeBinding type) {
InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this);
return inferenceSubstitution.substitute(inferenceSubstitution, type);
}
public void createInitialConstraintsForParameters(TypeBinding[] parameters, boolean checkVararg, TypeBinding varArgsType, MethodBinding method) {
if (this.invocationArguments == null)
return;
int len = checkVararg ? parameters.length - 1 : Math.min(parameters.length, this.invocationArguments.length);
int maxConstraints = checkVararg ? this.invocationArguments.length : len;
int numConstraints = 0;
boolean ownConstraints;
if (this.initialConstraints == null) {
this.initialConstraints = new ConstraintFormula[maxConstraints];
ownConstraints = true;
} else {
numConstraints = this.initialConstraints.length;
maxConstraints += numConstraints;
System.arraycopy(this.initialConstraints, 0,
this.initialConstraints=new ConstraintFormula[maxConstraints], 0, numConstraints);
ownConstraints = false;
}
for (int i = 0; i < len; i++) {
TypeBinding thetaF = substitute(parameters[i]);
if (this.invocationArguments[i].isPertinentToApplicability(parameters[i], method)) {
this.initialConstraints[numConstraints++] = new ConstraintExpressionFormula(this.invocationArguments[i], thetaF, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT);
} else if (!isTypeVariableOfCandidate(parameters[i], method)) {
this.initialConstraints[numConstraints++] = new ConstraintExpressionFormula(this.invocationArguments[i], thetaF, ReductionResult.POTENTIALLY_COMPATIBLE);
}
}
if (checkVararg && varArgsType instanceof ArrayBinding) {
varArgsType = ((ArrayBinding)varArgsType).elementsType();
TypeBinding thetaF = substitute(varArgsType);
for (int i = len; i < this.invocationArguments.length; i++) {
if (this.invocationArguments[i].isPertinentToApplicability(varArgsType, method)) {
this.initialConstraints[numConstraints++] = new ConstraintExpressionFormula(this.invocationArguments[i], thetaF, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT);
} else if (!isTypeVariableOfCandidate(varArgsType, method)) {
this.initialConstraints[numConstraints++] = new ConstraintExpressionFormula(this.invocationArguments[i], thetaF, ReductionResult.POTENTIALLY_COMPATIBLE);
}
}
}
if (numConstraints == 0)
this.initialConstraints = ConstraintFormula.NO_CONSTRAINTS;
else if (numConstraints < maxConstraints)
System.arraycopy(this.initialConstraints, 0, this.initialConstraints = new ConstraintFormula[numConstraints], 0, numConstraints);
if (ownConstraints) {
final int length = this.initialConstraints.length;
System.arraycopy(this.initialConstraints, 0, this.finalConstraints = new ConstraintExpressionFormula[length], 0, length);
}
}
private boolean isTypeVariableOfCandidate(TypeBinding type, MethodBinding candidate) {
if (type instanceof TypeVariableBinding) {
Binding declaringElement = ((TypeVariableBinding) type).declaringElement;
if (declaringElement == candidate)
return true;
if (candidate.isConstructor() && declaringElement == candidate.declaringClass)
return true;
}
return false;
}
private InferenceVariable[] addInitialTypeVariableSubstitutions(TypeBinding[] typeVariables) {
int len = typeVariables.length;
if (len == 0) {
if (this.inferenceVariables == null)
this.inferenceVariables = Binding.NO_INFERENCE_VARIABLES;
return Binding.NO_INFERENCE_VARIABLES;
}
InferenceVariable[] newVariables = new InferenceVariable[len];
for (int i = 0; i < len; i++)
newVariables[i] = InferenceVariable.get(typeVariables[i], i, this.currentInvocation, this.scope, this.object, true);
addInferenceVariables(newVariables);
return newVariables;
}
private void addInferenceVariables(InferenceVariable[] newVariables) {
if (this.inferenceVariables == null || this.inferenceVariables.length == 0) {
this.inferenceVariables = newVariables;
} else {
int len = newVariables.length;
int prev = this.inferenceVariables.length;
System.arraycopy(this.inferenceVariables, 0, this.inferenceVariables = new InferenceVariable[len+prev], 0, prev);
System.arraycopy(newVariables, 0, this.inferenceVariables, prev, len);
}
}
public InferenceVariable[] addTypeVariableSubstitutions(TypeBinding[] typeVariables) {
int len2 = typeVariables.length;
InferenceVariable[] newVariables = new InferenceVariable[len2];
InferenceVariable[] toAdd = new InferenceVariable[len2];
int numToAdd = 0;
for (int i = 0; i < typeVariables.length; i++) {
if (typeVariables[i] instanceof InferenceVariable)
newVariables[i] = (InferenceVariable) typeVariables[i];
else
toAdd[numToAdd++] =
newVariables[i] = InferenceVariable.get(typeVariables[i], i, this.currentInvocation, this.scope, this.object, false);
}
if (numToAdd > 0) {
int start = 0;
if (this.inferenceVariables != null) {
int len1 = this.inferenceVariables.length;
System.arraycopy(this.inferenceVariables, 0, this.inferenceVariables = new InferenceVariable[len1+numToAdd], 0, len1);
start = len1;
} else {
this.inferenceVariables = new InferenceVariable[numToAdd];
}
System.arraycopy(toAdd, 0, this.inferenceVariables, start, numToAdd);
}
return newVariables;
}
public void addThrowsContraints(TypeBinding[] parameters, InferenceVariable[] variables, ReferenceBinding[] thrownExceptions) {
for (int i = 0; i < parameters.length; i++) {
TypeBinding parameter = parameters[i];
for (int j = 0; j < thrownExceptions.length; j++) {
if (TypeBinding.equalsEquals(parameter, thrownExceptions[j])) {
this.currentBounds.inThrows.add(variables[i].prototype());
break;
}
}
}
}
public void inferInvocationApplicability(MethodBinding method, TypeBinding[] arguments, boolean isDiamond) {
ConstraintExpressionFormula.inferInvocationApplicability(this, method, arguments, isDiamond, this.inferenceKind);
}
boolean computeB3(InvocationSite invocationSite, TypeBinding targetType, MethodBinding method)
throws InferenceFailureException
{
boolean result = ConstraintExpressionFormula.inferPolyInvocationType(this, invocationSite, targetType, method);
if (result) {
mergeInnerBounds();
if (this.b3 == null)
this.b3 = this.currentBounds.copy();
}
return result;
}
public BoundSet inferInvocationType(TypeBinding expectedType, InvocationSite invocationSite, MethodBinding method) throws InferenceFailureException
{
if (expectedType == null && method.returnType != null)
substitute(method.returnType);
this.currentBounds = this.b2.copy();
int step = (expectedType == null || expectedType.isProperType(true)) ? TYPE_INFERRED_FINAL : TYPE_INFERRED;
try {
if (expectedType != null
&& expectedType != TypeBinding.VOID
&& invocationSite instanceof Expression && ((Expression) invocationSite).isTrulyExpression()
&& ((Expression)invocationSite).isPolyExpression(method))
{
if (!computeB3(invocationSite, expectedType, method)) {
return null;
}
} else {
mergeInnerBounds();
this.b3 = this.currentBounds.copy();
}
if (SHOULD_WORKAROUND_BUG_JDK_8153748) {
ReductionResult jdk8153748result = addJDK_8153748ConstraintsFromInvocation(this.invocationArguments, method, new InferenceSubstitution(this));
if (jdk8153748result != null) {
if (!this.currentBounds.incorporate(this))
return null;
}
}
pushBoundsToOuter();
this.directlyAcceptingInnerBounds = true;
Set<ConstraintFormula> c = new HashSet<ConstraintFormula>();
if (!addConstraintsToC(this.invocationArguments, c, method, this.inferenceKind, invocationSite))
return null;
List<Set<InferenceVariable>> components = this.currentBounds.computeConnectedComponents(this.inferenceVariables);
while (!c.isEmpty()) {
Set<ConstraintFormula> bottomSet = findBottomSet(c, allOutputVariables(c), components);
if (bottomSet.isEmpty()) {
bottomSet.add(pickFromCycle(c));
}
c.removeAll(bottomSet);
Set<InferenceVariable> allInputs = new HashSet<InferenceVariable>();
Iterator<ConstraintFormula> bottomIt = bottomSet.iterator();
while (bottomIt.hasNext()) {
allInputs.addAll(bottomIt.next().inputVariables(this));
}
InferenceVariable[] variablesArray = allInputs.toArray(new InferenceVariable[allInputs.size()]);
if (!this.currentBounds.incorporate(this))
return null;
BoundSet solution = resolve(variablesArray);
if (solution == null)
solution = resolve(this.inferenceVariables);
bottomIt = bottomSet.iterator();
while (bottomIt.hasNext()) {
ConstraintFormula constraint = bottomIt.next();
if (solution != null)
if (!constraint.applySubstitution(solution, variablesArray))
return null;
if (!this.currentBounds.reduceOneConstraint(this, constraint))
return null;
}
}
BoundSet solution = solve();
if (solution == null || !isResolved(solution)) {
this.currentBounds = this.b2;
return null;
}
reportUncheckedConversions(solution);
if (step == TYPE_INFERRED_FINAL)
this.currentBounds = solution;
return solution;
} finally {
this.stepCompleted = step;
}
}
private void pushBoundsToOuter() {
pushBoundsTo(this.outerContext);
}
public void pushBoundsTo(InferenceContext18 outer) {
if (outer != null && outer.stepCompleted >= APPLICABILITY_INFERRED) {
boolean deferred = outer.currentInvocation instanceof Invocation;
BoundSet toPush = deferred ? this.currentBounds.copy() : this.currentBounds;
Runnable job = () -> {
if (outer.directlyAcceptingInnerBounds) {
outer.currentBounds.addBounds(toPush, this.environment);
} else if (outer.innerInbox == null) {
outer.innerInbox = deferred ? toPush : toPush.copy();
} else {
outer.innerInbox.addBounds(toPush, this.environment);
}
};
if (deferred) {
this.pushToOuterJob = job;
} else {
job.run();
}
}
}
public void flushBoundOutbox() {
if (this.pushToOuterJob != null) {
this.pushToOuterJob.run();
this.pushToOuterJob = null;
}
}
private void mergeInnerBounds() {
if (this.innerInbox != null) {
this.currentBounds.addBounds(this.innerInbox, this.environment);
this.innerInbox = null;
}
}
interface InferenceOperation {
boolean perform() throws InferenceFailureException;
}
private boolean collectingInnerBounds(InferenceOperation operation) throws InferenceFailureException {
boolean result = operation.perform();
if (result)
mergeInnerBounds();
else
this.innerInbox = null;
return result;
}
private ReductionResult addJDK_8153748ConstraintsFromInvocation(Expression[] arguments, MethodBinding method, InferenceSubstitution substitution)
throws InferenceFailureException
{
boolean constraintAdded = false;
if (arguments != null) {
for (int i = 0; i < arguments.length; i++) {
Expression argument = arguments[i];
TypeBinding parameter = getParameter(method.parameters, i, method.isVarargs());
parameter = substitution.substitute(substitution, parameter);
ReductionResult result = addJDK_8153748ConstraintsFromExpression(argument, parameter, method, substitution);
if (result == ReductionResult.FALSE)
return ReductionResult.FALSE;
if (result == ReductionResult.TRUE)
constraintAdded = true;
}
}
return constraintAdded ? ReductionResult.TRUE : null;
}
private ReductionResult addJDK_8153748ConstraintsFromExpression(Expression argument, TypeBinding parameter, MethodBinding method,
InferenceSubstitution substitution)
throws InferenceFailureException
{
if (argument instanceof FunctionalExpression) {
return addJDK_8153748ConstraintsFromFunctionalExpr((FunctionalExpression) argument, parameter, method);
} else if (argument instanceof Invocation && argument.isPolyExpression(method)) {
Invocation invocation = (Invocation) argument;
Expression[] innerArgs = invocation.arguments();
MethodBinding innerMethod = invocation.binding();
if (innerMethod != null && innerMethod.isValidBinding()) {
substitution = enrichSubstitution(substitution, invocation, innerMethod);
return addJDK_8153748ConstraintsFromInvocation(innerArgs, innerMethod.shallowOriginal(), substitution);
}
} else if (argument instanceof ConditionalExpression) {
ConditionalExpression ce = (ConditionalExpression) argument;
if (addJDK_8153748ConstraintsFromExpression(ce.valueIfTrue, parameter, method, substitution) == ReductionResult.FALSE)
return ReductionResult.FALSE;
return addJDK_8153748ConstraintsFromExpression(ce.valueIfFalse, parameter, method, substitution);
} else if (argument instanceof SwitchExpression) {
SwitchExpression se = (SwitchExpression) argument;
ReductionResult result = ReductionResult.FALSE;
for (Expression re : se.resultExpressions) {
result = addJDK_8153748ConstraintsFromExpression(re, parameter, method, substitution);
if (result == ReductionResult.FALSE)
break;
}
return result;
}
return null;
}
private ReductionResult addJDK_8153748ConstraintsFromFunctionalExpr(FunctionalExpression functionalExpr, TypeBinding targetType, MethodBinding method) throws InferenceFailureException {
if (!functionalExpr.isPertinentToApplicability(targetType, method)) {
ConstraintFormula exprConstraint = new ConstraintExpressionFormula(functionalExpr, targetType, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT);
if (collectingInnerBounds(() -> exprConstraint.inputVariables(this).isEmpty())) {
if (!collectingInnerBounds(() -> reduceAndIncorporate(exprConstraint)))
return ReductionResult.FALSE;
ConstraintFormula excConstraint = new ConstraintExceptionFormula(functionalExpr, targetType);
if (!collectingInnerBounds(() -> reduceAndIncorporate(excConstraint)))
return ReductionResult.FALSE;
return ReductionResult.TRUE;
}
}
return null;
}
InferenceSubstitution enrichSubstitution(InferenceSubstitution substitution, Invocation innerInvocation, MethodBinding innerMethod) {
if (innerMethod instanceof ParameterizedGenericMethodBinding) {
InferenceContext18 innerContext = innerInvocation.getInferenceContext((ParameterizedMethodBinding) innerMethod);
if (innerContext != null)
return substitution.addContext(innerContext);
}
return substitution;
}
private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, InvocationSite site)
throws InferenceFailureException
{
TypeBinding[] fs;
if (exprs != null) {
int k = exprs.length;
int p = method.parameters.length;
if (method.isVarargs()) {
if (k < p - 1) return false;
} else if (k != p) {
return false;
}
switch (inferenceKindForMethod) {
case CHECK_STRICT:
case CHECK_LOOSE:
fs = method.parameters;
break;
case CHECK_VARARG:
fs = varArgTypes(method.parameters, k);
break;
default:
throw new IllegalStateException("Unexpected checkKind "+this.inferenceKind);
}
for (int i = 0; i < k; i++) {
TypeBinding fsi = fs[Math.min(i, p-1)];
InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this.environment, this.inferenceVariables, site);
TypeBinding substF = inferenceSubstitution.substitute(inferenceSubstitution,fsi);
if (!addConstraintsToC_OneExpr(exprs[i], c, fsi, substF, method))
return false;
}
}
return true;
}
private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormula> c, TypeBinding fsi, TypeBinding substF, MethodBinding method)
throws InferenceFailureException
{
boolean substFIsProperType = substF.isProperType(true);
substF = Scope.substitute(getResultSubstitution(this.b3), substF);
if (!expri.isPertinentToApplicability(fsi, method)) {
c.add(new ConstraintExpressionFormula(expri, substF, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT));
}
if (expri instanceof FunctionalExpression) {
c.add(new ConstraintExceptionFormula((FunctionalExpression) expri, substF));
if (expri instanceof LambdaExpression) {
LambdaExpression lambda = (LambdaExpression) expri;
BlockScope skope = lambda.enclosingScope;
if (substF.isFunctionalInterface(skope)) {
ReferenceBinding t = (ReferenceBinding) substF;
ParameterizedTypeBinding withWildCards = InferenceContext18.parameterizedWithWildcard(t);
if (withWildCards != null) {
t = ConstraintExpressionFormula.findGroundTargetType(this, skope, lambda, withWildCards);
}
MethodBinding functionType;
if (t != null && (functionType = t.getSingleAbstractMethod(skope, true)) != null && (lambda = lambda.resolveExpressionExpecting(t, this.scope, this)) != null) {
TypeBinding r = functionType.returnType;
Expression[] resultExpressions = lambda.resultExpressions();
for (int i = 0, length = resultExpressions == null ? 0 : resultExpressions.length; i < length; i++) {
Expression resultExpression = resultExpressions[i];
if (!addConstraintsToC_OneExpr(resultExpression, c, r.original(), r, method))
return false;
}
}
}
}
} else if (expri instanceof Invocation && expri.isPolyExpression()) {
if (substFIsProperType)
return true;
Invocation invocation = (Invocation) expri;
MethodBinding innerMethod = invocation.binding();
if (innerMethod == null)
return true;
Expression[] arguments = invocation.arguments();
TypeBinding[] argumentTypes = arguments == null ? Binding.NO_PARAMETERS : new TypeBinding[arguments.length];
for (int i = 0; i < argumentTypes.length; i++)
argumentTypes[i] = arguments[i].resolvedType;
InferenceContext18 innerContext = null;
if (innerMethod instanceof ParameterizedGenericMethodBinding)
innerContext = invocation.getInferenceContext((ParameterizedGenericMethodBinding) innerMethod);
if (innerContext != null) {
MethodBinding shallowMethod = innerMethod.shallowOriginal();
innerContext.outerContext = this;
if (innerContext.stepCompleted < InferenceContext18.APPLICABILITY_INFERRED)
innerContext.inferInvocationApplicability(shallowMethod, argumentTypes, shallowMethod.isConstructor());
if (!innerContext.computeB3(invocation, substF, shallowMethod))
return false;
if (innerContext.addConstraintsToC(arguments, c, innerMethod.genericMethod(), innerContext.inferenceKind, invocation)) {
this.currentBounds.addBounds(innerContext.currentBounds, this.environment);
return true;
}
return false;
} else {
int applicabilityKind = getInferenceKind(innerMethod, argumentTypes);
return this.addConstraintsToC(arguments, c, innerMethod.genericMethod(), applicabilityKind, invocation);
}
} else if (expri instanceof ConditionalExpression) {
ConditionalExpression ce = (ConditionalExpression) expri;
return addConstraintsToC_OneExpr(ce.valueIfTrue, c, fsi, substF, method)
&& addConstraintsToC_OneExpr(ce.valueIfFalse, c, fsi, substF, method);
} else if (expri instanceof SwitchExpression) {
SwitchExpression se = (SwitchExpression) expri;
for (Expression re : se.resultExpressions) {
if (!addConstraintsToC_OneExpr(re, c, fsi, substF, method))
return false;
}
return true;
}
return true;
}
protected int getInferenceKind(MethodBinding nonGenericMethod, TypeBinding[] argumentTypes) {
switch (this.scope.parameterCompatibilityLevel(nonGenericMethod, argumentTypes)) {
case Scope.AUTOBOX_COMPATIBLE:
return CHECK_LOOSE;
case Scope.VARARGS_COMPATIBLE:
return CHECK_VARARG;
default:
return CHECK_STRICT;
}
}
public ReferenceBinding inferFunctionalInterfaceParameterization(LambdaExpression lambda, BlockScope blockScope,
ParameterizedTypeBinding targetTypeWithWildCards)
{
TypeBinding[] q = createBoundsForFunctionalInterfaceParameterizationInference(targetTypeWithWildCards);
if (q == null || q.length != lambda.arguments().length) {
} else {
if (reduceWithEqualityConstraints(lambda.argumentTypes(), q)) {
ReferenceBinding genericType = targetTypeWithWildCards.genericType();
TypeBinding[] a = targetTypeWithWildCards.arguments;
TypeBinding[] aprime = getFunctionInterfaceArgumentSolutions(a);
ParameterizedTypeBinding ptb = blockScope.environment().createParameterizedType(genericType, aprime, targetTypeWithWildCards.enclosingType());
TypeVariableBinding[] vars = ptb.genericType().typeVariables();
ParameterizedTypeBinding captured = ptb.capture(blockScope, lambda.sourceStart, lambda.sourceEnd);
for (int i = 0; i < vars.length; i++) {
if (vars[i].boundCheck(captured, aprime[i], blockScope, lambda) == BoundCheckStatus.MISMATCH)
return null;
}
return ptb;
}
}
return targetTypeWithWildCards;
}
TypeBinding[] createBoundsForFunctionalInterfaceParameterizationInference(ParameterizedTypeBinding functionalInterface) {
if (this.currentBounds == null)
this.currentBounds = new BoundSet();
TypeBinding[] a = functionalInterface.arguments;
if (a == null)
return null;
InferenceVariable[] alpha = addInitialTypeVariableSubstitutions(a);
for (int i = 0; i < a.length; i++) {
TypeBound bound;
if (a[i].kind() == Binding.WILDCARD_TYPE) {
WildcardBinding wildcard = (WildcardBinding) a[i];
switch(wildcard.boundKind) {
case Wildcard.EXTENDS :
bound = new TypeBound(alpha[i], wildcard.allBounds(), ReductionResult.SUBTYPE);
break;
case Wildcard.SUPER :
bound = new TypeBound(alpha[i], wildcard.bound, ReductionResult.SUPERTYPE);
break;
case Wildcard.UNBOUND :
bound = new TypeBound(alpha[i], this.object, ReductionResult.SUBTYPE);
break;
default:
continue;
}
} else {
bound = new TypeBound(alpha[i], a[i], ReductionResult.SAME);
}
this.currentBounds.addBound(bound, this.environment);
}
TypeBinding falpha = substitute(functionalInterface);
return falpha.getSingleAbstractMethod(this.scope, true).parameters;
}
public boolean reduceWithEqualityConstraints(TypeBinding[] p, TypeBinding[] q) {
if (p != null) {
for (int i = 0; i < p.length; i++) {
try {
if (!this.reduceAndIncorporate(ConstraintTypeFormula.create(p[i], q[i], ReductionResult.SAME)))
return false;
} catch (InferenceFailureException e) {
return false;
}
}
}
return true;
}
public boolean isMoreSpecificThan(MethodBinding m1, MethodBinding m2, boolean isVarArgs, boolean isVarArgs2) {
if (isVarArgs != isVarArgs2) {
return isVarArgs2;
}
Expression[] arguments = this.invocationArguments;
int numInvocArgs = arguments == null ? 0 : arguments.length;
TypeVariableBinding[] p = m2.typeVariables();
TypeBinding[] s = m1.parameters;
TypeBinding[] t = new TypeBinding[m2.parameters.length];
createInitialBoundSet(p);
for (int i = 0; i < t.length; i++)
t[i] = substitute(m2.parameters[i]);
try {
for (int i = 0; i < numInvocArgs; i++) {
TypeBinding si = getParameter(s, i, isVarArgs);
TypeBinding ti = getParameter(t, i, isVarArgs);
Boolean result = moreSpecificMain(si, ti, this.invocationArguments[i]);
if (result == Boolean.FALSE)
return false;
if (result == null)
if (!reduceAndIncorporate(ConstraintTypeFormula.create(si, ti, ReductionResult.SUBTYPE)))
return false;
}
if (t.length == numInvocArgs + 1) {
TypeBinding skplus1 = getParameter(s, numInvocArgs, true);
TypeBinding tkplus1 = getParameter(t, numInvocArgs, true);
if (!reduceAndIncorporate(ConstraintTypeFormula.create(skplus1, tkplus1, ReductionResult.SUBTYPE)))
return false;
}
return solve() != null;
} catch (InferenceFailureException e) {
return false;
}
}
private Boolean moreSpecificMain(TypeBinding si, TypeBinding ti, Expression expri) throws InferenceFailureException {
if (si.isProperType(true) && ti.isProperType(true)) {
return expri.sIsMoreSpecific(si, ti, this.scope) ? Boolean.TRUE : Boolean.FALSE;
}
if (!ti.isFunctionalInterface(this.scope))
return null;
TypeBinding funcI = ti.original();
if (si.isFunctionalInterface(this.scope)) {
if (siSuperI(si, funcI) || siSubI(si, funcI))
return null;
if (si instanceof IntersectionTypeBinding18) {
TypeBinding[] elements = ((IntersectionTypeBinding18)si).intersectingTypes;
checkSuper: {
for (int i = 0; i < elements.length; i++)
if (!siSuperI(elements[i], funcI))
break checkSuper;
return null;
}
for (int i = 0; i < elements.length; i++)
if (siSubI(elements[i], funcI))
return null;
}
TypeBinding siCapture = si.capture(this.scope, expri.sourceStart, expri.sourceEnd);
MethodBinding sam = siCapture.getSingleAbstractMethod(this.scope, false);
TypeBinding[] u = sam.parameters;
TypeBinding r1 = sam.isConstructor() ? sam.declaringClass : sam.returnType;
sam = ti.getSingleAbstractMethod(this.scope, true);
TypeBinding[] v = sam.parameters;
TypeBinding r2 = sam.isConstructor() ? sam.declaringClass : sam.returnType;
return Boolean.valueOf(checkExpression(expri, u, r1, v, r2));
}
return null;
}
private boolean checkExpression(Expression expri, TypeBinding[] u, TypeBinding r1, TypeBinding[] v, TypeBinding r2)
throws InferenceFailureException {
if (expri instanceof LambdaExpression && !((LambdaExpression)expri).argumentsTypeElided()) {
for (int i = 0; i < u.length; i++) {
if (!reduceAndIncorporate(ConstraintTypeFormula.create(u[i], v[i], ReductionResult.SAME)))
return false;
}
if (r2.id == TypeIds.T_void)
return true;
LambdaExpression lambda = (LambdaExpression) expri;
Expression[] results = lambda.resultExpressions();
if (results != Expression.NO_EXPRESSIONS) {
if (r1.isFunctionalInterface(this.scope) && r2.isFunctionalInterface(this.scope)
&& !(r1.isCompatibleWith(r2) || r2.isCompatibleWith(r1))) {
for (int i = 0; i < results.length; i++) {
if (!checkExpression(results[i], u, r1, v, r2))
return false;
}
return true;
}
checkPrimitive1: if (r1.isPrimitiveType() && !r2.isPrimitiveType()) {
for (int i = 0; i < results.length; i++) {
if (results[i].isPolyExpression() || (results[i].resolvedType != null && !results[i].resolvedType.isPrimitiveType()))
break checkPrimitive1;
}
return true;
}
checkPrimitive2: if (r2.isPrimitiveType() && !r1.isPrimitiveType()) {
for (int i = 0; i < results.length; i++) {
if (!(
(!results[i].isPolyExpression() && (results[i].resolvedType != null && !results[i].resolvedType.isPrimitiveType()))
|| results[i].isPolyExpression()))
break checkPrimitive2;
}
return true;
}
}
return reduceAndIncorporate(ConstraintTypeFormula.create(r1, r2, ReductionResult.SUBTYPE));
} else if (expri instanceof ReferenceExpression && ((ReferenceExpression)expri).isExactMethodReference()) {
ReferenceExpression reference = (ReferenceExpression) expri;
for (int i = 0; i < u.length; i++) {
if (!reduceAndIncorporate(ConstraintTypeFormula.create(u[i], v[i], ReductionResult.SAME)))
return false;
}
if (r2.id == TypeIds.T_void)
return true;
MethodBinding method = reference.getExactMethod();
TypeBinding returnType = method.isConstructor() ? method.declaringClass : method.returnType;
if (r1.isPrimitiveType() && !r2.isPrimitiveType() && returnType.isPrimitiveType())
return true;
if (r2.isPrimitiveType() && !r1.isPrimitiveType() && !returnType.isPrimitiveType())
return true;
return reduceAndIncorporate(ConstraintTypeFormula.create(r1, r2, ReductionResult.SUBTYPE));
} else if (expri instanceof ConditionalExpression) {
ConditionalExpression cond = (ConditionalExpression) expri;
return checkExpression(cond.valueIfTrue, u, r1, v, r2) && checkExpression(cond.valueIfFalse, u, r1, v, r2);
} else if (expri instanceof SwitchExpression) {
SwitchExpression se = (SwitchExpression) expri;
for (Expression re : se.resultExpressions) {
if (!checkExpression(re, u, r1, v, r2))
return false;
}
return true;
} else {
return false;
}
}
private boolean siSuperI(TypeBinding si, TypeBinding funcI) {
if (TypeBinding.equalsEquals(si, funcI) || TypeBinding.equalsEquals(si.original(), funcI))
return true;
TypeBinding[] superIfcs = funcI.superInterfaces();
if (superIfcs == null) return false;
for (int i = 0; i < superIfcs.length; i++) {
if (siSuperI(si, superIfcs[i].original()))
return true;
}
return false;
}
private boolean siSubI(TypeBinding si, TypeBinding funcI) {
if (TypeBinding.equalsEquals(si, funcI) || TypeBinding.equalsEquals(si.original(), funcI))
return true;
TypeBinding[] superIfcs = si.superInterfaces();
if (superIfcs == null) return false;
for (int i = 0; i < superIfcs.length; i++) {
if (siSubI(superIfcs[i], funcI))
return true;
}
return false;
}
public BoundSet solve(boolean inferringApplicability) throws InferenceFailureException {
if (!reduce())
return null;
if (!this.currentBounds.incorporate(this))
return null;
if (inferringApplicability)
this.b2 = this.currentBounds.copy();
BoundSet solution = resolve(this.inferenceVariables);
if (inferringApplicability && solution != null && this.finalConstraints != null) {
for (ConstraintExpressionFormula constraint: this.finalConstraints) {
if (constraint.left.isPolyExpression())
continue;
constraint.applySubstitution(solution, this.inferenceVariables);
if (!this.currentBounds.reduceOneConstraint(this, constraint)) {
return null;
}
}
}
return solution;
}
public BoundSet solve() throws InferenceFailureException {
return solve(false);
}
public BoundSet solve(InferenceVariable[] toResolve) throws InferenceFailureException {
if (!reduce())
return null;
if (!this.currentBounds.incorporate(this))
return null;
return resolve(toResolve);
}
private boolean reduce() throws InferenceFailureException {
for (int i = 0; this.initialConstraints != null && i < this.initialConstraints.length; i++) {
final ConstraintFormula currentConstraint = this.initialConstraints[i];
if (currentConstraint == null)
continue;
this.initialConstraints[i] = null;
if (!this.currentBounds.reduceOneConstraint(this, currentConstraint))
return false;
}
this.initialConstraints = null;
return true;
}
public boolean isResolved(BoundSet boundSet) {
if (this.inferenceVariables != null) {
for (int i = 0; i < this.inferenceVariables.length; i++) {
if (!boundSet.isInstantiated(this.inferenceVariables[i]))
return false;
}
}
return true;
}
public TypeBinding [] getSolutions(TypeVariableBinding[] typeParameters, InvocationSite site, BoundSet boundSet) {
int len = typeParameters.length;
TypeBinding[] substitutions = new TypeBinding[len];
InferenceVariable[] outerVariables = null;
if (this.outerContext != null && this.outerContext.stepCompleted < TYPE_INFERRED)
outerVariables = this.outerContext.inferenceVariables;
for (int i = 0; i < typeParameters.length; i++) {
for (int j = 0; j < this.inferenceVariables.length; j++) {
InferenceVariable variable = this.inferenceVariables[j];
if (isSameSite(variable.site, site) && TypeBinding.equalsEquals(variable.typeParameter, typeParameters[i])) {
TypeBinding outerVar = null;
if (outerVariables != null && (outerVar = boundSet.getEquivalentOuterVariable(variable, outerVariables)) != null)
substitutions[i] = outerVar;
else
substitutions[i] = boundSet.getInstantiation(variable, this.environment);
break;
}
}
if (substitutions[i] == null)
return null;
}
return substitutions;
}
public boolean reduceAndIncorporate(ConstraintFormula constraint) throws InferenceFailureException {
return this.currentBounds.reduceOneConstraint(this, constraint);
}
private BoundSet resolve(InferenceVariable[] toResolve) throws InferenceFailureException {
this.captureId = 0;
BoundSet tmpBoundSet = this.currentBounds;
if (this.inferenceVariables != null) {
Set<InferenceVariable> variableSet;
while ((variableSet = getSmallestVariableSet(tmpBoundSet, toResolve)) != null) {
int oldNumUninstantiated = tmpBoundSet.numUninstantiatedVariables(this.inferenceVariables);
final int numVars = variableSet.size();
if (numVars > 0) {
final InferenceVariable[] variables = variableSet.toArray(new InferenceVariable[numVars]);
variables: if (!tmpBoundSet.hasCaptureBound(variableSet)) {
BoundSet prevBoundSet = tmpBoundSet;
tmpBoundSet = tmpBoundSet.copy();
for (int j = 0; j < variables.length; j++) {
InferenceVariable variable = variables[j];
TypeBinding[] lowerBounds = tmpBoundSet.lowerBounds(variable, true);
if (lowerBounds != Binding.NO_TYPES) {
TypeBinding lub = this.scope.lowerUpperBound(lowerBounds);
if (lub == TypeBinding.VOID || lub == null)
return null;
tmpBoundSet.addBound(new TypeBound(variable, lub, ReductionResult.SAME), this.environment);
} else {
TypeBinding[] upperBounds = tmpBoundSet.upperBounds(variable, true);
if (tmpBoundSet.inThrows.contains(variable.prototype()) && tmpBoundSet.hasOnlyTrivialExceptionBounds(variable, upperBounds)) {
TypeBinding runtimeException = this.scope.getType(TypeConstants.JAVA_LANG_RUNTIMEEXCEPTION, 3);
tmpBoundSet.addBound(new TypeBound(variable, runtimeException, ReductionResult.SAME), this.environment);
} else {
TypeBinding glb = this.object;
if (upperBounds != Binding.NO_TYPES) {
if (upperBounds.length == 1) {
glb = upperBounds[0];
} else {
TypeBinding[] glbs = Scope.greaterLowerBound(upperBounds, this.scope, this.environment);
if (glbs == null) {
return null;
} else if (glbs.length == 1) {
glb = glbs[0];
} else {
glb = intersectionFromGlb(glbs);
if (glb == null) {
tmpBoundSet = prevBoundSet;
break variables;
}
}
}
}
tmpBoundSet.addBound(new TypeBound(variable, glb, ReductionResult.SAME), this.environment);
}
}
}
if (tmpBoundSet.incorporate(this))
continue;
tmpBoundSet = prevBoundSet;
}
Sorting.sortInferenceVariables(variables);
final CaptureBinding18[] zs = new CaptureBinding18[numVars];
for (int j = 0; j < numVars; j++)
zs[j] = freshCapture(variables[j]);
final BoundSet kurrentBoundSet = tmpBoundSet;
Substitution theta = new Substitution() {
@Override
public LookupEnvironment environment() {
return InferenceContext18.this.environment;
}
@Override
public boolean isRawSubstitution() {
return false;
}
@Override
public TypeBinding substitute(TypeVariableBinding typeVariable) {
for (int j = 0; j < numVars; j++)
if (TypeBinding.equalsEquals(variables[j], typeVariable))
return zs[j];
if (typeVariable instanceof InferenceVariable) {
InferenceVariable inferenceVariable = (InferenceVariable) typeVariable;
TypeBinding instantiation = kurrentBoundSet.getInstantiation(inferenceVariable, null);
if (instantiation != null)
return instantiation;
}
return typeVariable;
}
};
for (int j = 0; j < numVars; j++) {
InferenceVariable variable = variables[j];
CaptureBinding18 zsj = zs[j];
TypeBinding[] lowerBounds = tmpBoundSet.lowerBounds(variable, true);
if (lowerBounds != Binding.NO_TYPES) {
TypeBinding lub = this.scope.lowerUpperBound(lowerBounds);
if (lub != TypeBinding.VOID && lub != null)
zsj.lowerBound = lub;
}
TypeBinding[] upperBounds = tmpBoundSet.upperBounds(variable, false);
if (upperBounds != Binding.NO_TYPES) {
for (int k = 0; k < upperBounds.length; k++)
upperBounds[k] = Scope.substitute(theta, upperBounds[k]);
if (!setUpperBounds(zsj, upperBounds))
continue;
}
if (tmpBoundSet == this.currentBounds)
tmpBoundSet = tmpBoundSet.copy();
Iterator<ParameterizedTypeBinding> captureKeys = tmpBoundSet.captures.keySet().iterator();
Set<ParameterizedTypeBinding> toRemove = new HashSet<ParameterizedTypeBinding>();
while (captureKeys.hasNext()) {
ParameterizedTypeBinding key = captureKeys.next();
int len = key.arguments.length;
for (int i = 0; i < len; i++) {
if (TypeBinding.equalsEquals(key.arguments[i], variable)) {
toRemove.add(key);
break;
}
}
}
captureKeys = toRemove.iterator();
while (captureKeys.hasNext())
tmpBoundSet.captures.remove(captureKeys.next());
tmpBoundSet.addBound(new TypeBound(variable, zsj, ReductionResult.SAME), this.environment);
}
if (tmpBoundSet.incorporate(this)) {
if (tmpBoundSet.numUninstantiatedVariables(this.inferenceVariables) == oldNumUninstantiated)
return null;
continue;
}
return null;
}
}
}
return tmpBoundSet;
}
private TypeBinding intersectionFromGlb(TypeBinding[] glbs) {
ReferenceBinding[] refGlbs = new ReferenceBinding[glbs.length];
for (int i = 0; i < glbs.length; i++) {
TypeBinding typeBinding = glbs[i];
if (typeBinding instanceof ReferenceBinding) {
refGlbs[i] = (ReferenceBinding) typeBinding;
} else {
return null;
}
}
IntersectionTypeBinding18 intersection = (IntersectionTypeBinding18) this.environment.createIntersectionType18(refGlbs);
if (ReferenceBinding.isConsistentIntersection(intersection.intersectingTypes))
return intersection;
return null;
}
int captureId = 0;
private CaptureBinding18 freshCapture(InferenceVariable variable) {
int id = this.captureId++;
char[] sourceName = CharOperation.concat("Z".toCharArray(), '#', String.valueOf(id).toCharArray(), '-', variable.sourceName);
int start = this.currentInvocation != null ? this.currentInvocation.sourceStart() : 0;
int end = this.currentInvocation != null ? this.currentInvocation.sourceEnd() : 0;
return new CaptureBinding18(this.scope.enclosingSourceType(), sourceName, variable.typeParameter.shortReadableName(),
start, end, id, this.environment);
}
private boolean setUpperBounds(CaptureBinding18 typeVariable, TypeBinding[] substitutedUpperBounds) {
if (substitutedUpperBounds.length == 1) {
return typeVariable.setUpperBounds(substitutedUpperBounds, this.object);
} else {
TypeBinding[] glbs = Scope.greaterLowerBound(substitutedUpperBounds, this.scope, this.environment);
if (glbs == null)
return false;
if (typeVariable.lowerBound != null) {
for (int i = 0; i < glbs.length; i++) {
if (!typeVariable.lowerBound.isCompatibleWith(glbs[i]))
return false;
}
}
sortTypes(glbs);
if (!typeVariable.setUpperBounds(glbs, this.object))
return false;
}
return true;
}
static void sortTypes(TypeBinding[] types) {
Arrays.sort(types, new Comparator<TypeBinding>() {
@Override
public int compare(TypeBinding o1, TypeBinding o2) {
int i1 = o1.id, i2 = o2.id;
return (i1<i2 ? -1 : (i1==i2 ? 0 : 1));
}
});
}
private Set<InferenceVariable> getSmallestVariableSet(BoundSet bounds, InferenceVariable[] subSet) {
Set<InferenceVariable> v = new HashSet<InferenceVariable>();
Map<InferenceVariable,Set<InferenceVariable>> dependencies = new HashMap<>();
for (InferenceVariable iv : subSet) {
Set<InferenceVariable> tmp = new HashSet<>();
addDependencies(bounds, tmp, iv);
dependencies.put(iv, tmp);
v.addAll(tmp);
}
int min = Integer.MAX_VALUE;
Set<InferenceVariable> result = null;
for (InferenceVariable currentVariable : v) {
if (!bounds.isInstantiated(currentVariable)) {
Set<InferenceVariable> set = dependencies.get(currentVariable);
if (set == null)
addDependencies(bounds, set = new HashSet<>(), currentVariable);
int cur = set.size();
if (cur == 1)
return set;
if (cur < min) {
result = set;
min = cur;
}
}
}
return result;
}
private void addDependencies(BoundSet boundSet, Set<InferenceVariable> variableSet, InferenceVariable currentVariable) {
if (boundSet.isInstantiated(currentVariable)) return;
if (!variableSet.add(currentVariable)) return;
for (int j = 0; j < this.inferenceVariables.length; j++) {
InferenceVariable nextVariable = this.inferenceVariables[j];
if (TypeBinding.equalsEquals(nextVariable, currentVariable)) continue;
if (boundSet.dependsOnResolutionOf(currentVariable, nextVariable))
addDependencies(boundSet, variableSet, nextVariable);
}
}
private ConstraintFormula pickFromCycle(Set<ConstraintFormula> c) {
HashMap<ConstraintFormula,Set<ConstraintFormula>> dependencies = new HashMap<ConstraintFormula, Set<ConstraintFormula>>();
Set<ConstraintFormula> cycles = new HashSet<ConstraintFormula>();
for (ConstraintFormula constraint : c) {
Collection<InferenceVariable> infVars = constraint.inputVariables(this);
for (ConstraintFormula other : c) {
if (other == constraint) continue;
if (dependsOn(infVars, other.outputVariables(this))) {
Set<ConstraintFormula> targetSet = dependencies.get(constraint);
if (targetSet == null)
dependencies.put(constraint, targetSet = new HashSet<ConstraintFormula>());
targetSet.add(other);
Set<ConstraintFormula> nodesInCycle = new HashSet<ConstraintFormula>();
if (isReachable(dependencies, other, constraint, new HashSet<ConstraintFormula>(), nodesInCycle)) {
cycles.addAll(nodesInCycle);
}
}
}
}
Set<ConstraintFormula> outside = new HashSet<ConstraintFormula>(c);
outside.removeAll(cycles);
Set<ConstraintFormula> candidatesII = new HashSet<ConstraintFormula>();
candidates: for (ConstraintFormula candidate : cycles) {
Collection<InferenceVariable> infVars = candidate.inputVariables(this);
for (ConstraintFormula out : outside) {
if (dependsOn(infVars, out.outputVariables(this)))
continue candidates;
}
candidatesII.add(candidate);
}
if (candidatesII.isEmpty())
candidatesII = c;
Set<ConstraintFormula> candidatesIII = new HashSet<ConstraintFormula>();
for (ConstraintFormula candidate : candidatesII) {
if (candidate instanceof ConstraintExpressionFormula)
candidatesIII.add(candidate);
}
if (candidatesIII.isEmpty()) {
candidatesIII = candidatesII;
} else {
Map<ConstraintExpressionFormula,ConstraintExpressionFormula> expressionContainedBy = new HashMap<ConstraintExpressionFormula, ConstraintExpressionFormula>();
for (ConstraintFormula one : candidatesIII) {
ConstraintExpressionFormula oneCEF = (ConstraintExpressionFormula) one;
Expression exprOne = oneCEF.left;
for (ConstraintFormula two : candidatesIII) {
if (one == two) continue;
ConstraintExpressionFormula twoCEF = (ConstraintExpressionFormula) two;
Expression exprTwo = twoCEF.left;
if (doesExpressionContain(exprOne, exprTwo)) {
ConstraintExpressionFormula previous = expressionContainedBy.get(two);
if (previous == null || doesExpressionContain(previous.left, exprOne))
expressionContainedBy.put(twoCEF, oneCEF);
}
}
}
Map<ConstraintExpressionFormula,Set<ConstraintExpressionFormula>> containmentForest = new HashMap<ConstraintExpressionFormula, Set<ConstraintExpressionFormula>>();
for (Map.Entry<ConstraintExpressionFormula, ConstraintExpressionFormula> parentRelation : expressionContainedBy.entrySet()) {
ConstraintExpressionFormula parent = parentRelation.getValue();
Set<ConstraintExpressionFormula> children = containmentForest.get(parent);
if (children == null)
containmentForest.put(parent, children = new HashSet<ConstraintExpressionFormula>());
children.add(parentRelation.getKey());
}
int bestRank = -1;
ConstraintExpressionFormula candidate = null;
for (ConstraintExpressionFormula parent : containmentForest.keySet()) {
int rank = rankNode(parent, expressionContainedBy, containmentForest);
if (rank > bestRank) {
bestRank = rank;
candidate = parent;
}
}
if (candidate != null)
return candidate;
}
if (candidatesIII.isEmpty())
throw new IllegalStateException("cannot pick constraint from cyclic set");
return candidatesIII.iterator().next();
}
private boolean dependsOn(Collection<InferenceVariable> inputsOfFirst, Collection<InferenceVariable> outputsOfOther) {
for (InferenceVariable iv : inputsOfFirst) {
for (InferenceVariable otherIV : outputsOfOther)
if (this.currentBounds.dependsOnResolutionOf(iv, otherIV))
return true;
}
return false;
}
private boolean isReachable(Map<ConstraintFormula,Set<ConstraintFormula>> deps, ConstraintFormula from, ConstraintFormula to,
Set<ConstraintFormula> nodesVisited, Set<ConstraintFormula> nodesInCycle)
{
if (from == to) {
nodesInCycle.add(from);
return true;
}
if (!nodesVisited.add(from))
return false;
Set<ConstraintFormula> targetSet = deps.get(from);
if (targetSet != null) {
for (ConstraintFormula tgt : targetSet) {
if (isReachable(deps, tgt, to, nodesVisited, nodesInCycle)) {
nodesInCycle.add(from);
return true;
}
}
}
return false;
}
private boolean doesExpressionContain(Expression exprOne, Expression exprTwo) {
if (exprTwo.sourceStart > exprOne.sourceStart) {
return exprTwo.sourceEnd <= exprOne.sourceEnd;
} else if (exprTwo.sourceStart == exprOne.sourceStart) {
return exprTwo.sourceEnd < exprOne.sourceEnd;
}
return false;
}
private int rankNode(ConstraintExpressionFormula parent,
Map<ConstraintExpressionFormula,ConstraintExpressionFormula> expressionContainedBy,
Map<ConstraintExpressionFormula, Set<ConstraintExpressionFormula>> containmentForest)
{
if (expressionContainedBy.get(parent) != null)
return -1;
Set<ConstraintExpressionFormula> children = containmentForest.get(parent);
if (children == null)
return 1;
int sum = 1;
for (ConstraintExpressionFormula child : children) {
int cRank = rankNode(child, expressionContainedBy, containmentForest);
if (cRank > 0)
sum += cRank;
}
return sum;
}
private Set<ConstraintFormula> findBottomSet(Set<ConstraintFormula> constraints,
Set<InferenceVariable> allOutputVariables, List<Set<InferenceVariable>> components)
{
Set<ConstraintFormula> result = new HashSet<ConstraintFormula>();
constraintLoop:
for (ConstraintFormula constraint : constraints) {
for (InferenceVariable in : constraint.inputVariables(this)) {
if (canInfluenceAnyOf(in, allOutputVariables, components))
continue constraintLoop;
}
result.add(constraint);
}
return result;
}
private boolean canInfluenceAnyOf(InferenceVariable in, Set<InferenceVariable> allOuts, List<Set<InferenceVariable>> components) {
for (Set<InferenceVariable> component : components) {
if (component.contains(in)) {
for (InferenceVariable out : allOuts)
if (component.contains(out))
return true;
return false;
}
}
return false;
}
Set<InferenceVariable> allOutputVariables(Set<ConstraintFormula> constraints) {
Set<InferenceVariable> result = new HashSet<InferenceVariable>();
Iterator<ConstraintFormula> it = constraints.iterator();
while (it.hasNext()) {
result.addAll(it.next().outputVariables(this));
}
return result;
}
private TypeBinding[] varArgTypes(TypeBinding[] parameters, int k) {
TypeBinding[] types = new TypeBinding[k];
int declaredLength = parameters.length-1;
System.arraycopy(parameters, 0, types, 0, declaredLength);
TypeBinding last = ((ArrayBinding)parameters[declaredLength]).elementsType();
for (int i = declaredLength; i < k; i++)
types[i] = last;
return types;
}
public SuspendedInferenceRecord enterPolyInvocation(InvocationSite invocation, Expression[] innerArguments) {
SuspendedInferenceRecord record = new SuspendedInferenceRecord(this.currentInvocation, this.invocationArguments, this.inferenceVariables, this.inferenceKind, this.usesUncheckedConversion);
this.inferenceVariables = null;
this.invocationArguments = innerArguments;
this.currentInvocation = invocation;
this.usesUncheckedConversion = false;
return record;
}
public SuspendedInferenceRecord enterLambda(LambdaExpression lambda) {
SuspendedInferenceRecord record = new SuspendedInferenceRecord(this.currentInvocation, this.invocationArguments, this.inferenceVariables, this.inferenceKind, this.usesUncheckedConversion);
this.inferenceVariables = null;
this.invocationArguments = null;
this.usesUncheckedConversion = false;
return record;
}
public void integrateInnerInferenceB2(InferenceContext18 innerCtx) {
this.currentBounds.addBounds(innerCtx.b2, this.environment);
this.inferenceVariables = innerCtx.inferenceVariables;
this.inferenceKind = innerCtx.inferenceKind;
if (!isSameSite(innerCtx.currentInvocation, this.currentInvocation))
innerCtx.outerContext = this;
this.usesUncheckedConversion = innerCtx.usesUncheckedConversion;
}
public void resumeSuspendedInference(SuspendedInferenceRecord record, InferenceContext18 innerContext) {
boolean firstTime = collectInnerContext(innerContext);
if (this.inferenceVariables == null) {
this.inferenceVariables = record.inferenceVariables;
} else if(!firstTime) {
final Set<InferenceVariable> uniqueVariables = new LinkedHashSet<>();
uniqueVariables.addAll(Arrays.asList(record.inferenceVariables));
uniqueVariables.addAll(Arrays.asList(this.inferenceVariables));
this.inferenceVariables = uniqueVariables.toArray(new InferenceVariable[uniqueVariables.size()]);
} else {
int l1 = this.inferenceVariables.length;
int l2 = record.inferenceVariables.length;
System.arraycopy(this.inferenceVariables, 0, this.inferenceVariables=new InferenceVariable[l1+l2], l2, l1);
System.arraycopy(record.inferenceVariables, 0, this.inferenceVariables, 0, l2);
}
this.currentInvocation = record.site;
this.invocationArguments = record.invocationArguments;
this.inferenceKind = record.inferenceKind;
this.usesUncheckedConversion = record.usesUncheckedConversion;
}
private boolean collectInnerContext(final InferenceContext18 innerContext) {
if(innerContext == null) {
return false;
}
if(this.seenInnerContexts == null) {
this.seenInnerContexts = new HashSet<>();
}
return this.seenInnerContexts.add(innerContext);
}
private Substitution getResultSubstitution(final BoundSet result) {
return new Substitution() {
@Override
public LookupEnvironment environment() {
return InferenceContext18.this.environment;
}
@Override
public boolean isRawSubstitution() {
return false;
}
@Override
public TypeBinding substitute(TypeVariableBinding typeVariable) {
if (typeVariable instanceof InferenceVariable) {
TypeBinding instantiation = result.getInstantiation((InferenceVariable) typeVariable, InferenceContext18.this.environment);
if (instantiation != null)
return instantiation;
}
return typeVariable;
}
};
}
public boolean isVarArgs() {
return this.inferenceKind == CHECK_VARARG;
}
public static TypeBinding getParameter(TypeBinding[] parameters, int rank, boolean isVarArgs) {
if (isVarArgs) {
if (rank >= parameters.length-1)
return ((ArrayBinding)parameters[parameters.length-1]).elementsType();
} else if (rank >= parameters.length) {
return null;
}
return parameters[rank];
}
public MethodBinding getReturnProblemMethodIfNeeded(TypeBinding expectedType, MethodBinding method) {
if (InferenceContext18.SIMULATE_BUG_JDK_8026527 && expectedType != null
&& !(method.original() instanceof SyntheticFactoryMethodBinding)
&& (method.returnType instanceof ReferenceBinding || method.returnType instanceof ArrayBinding)) {
if (!expectedType.isProperType(true))
return null;
if (this.environment.convertToRawType(method.returnType.erasure(), false).isCompatibleWith(expectedType))
return method;
}
ProblemMethodBinding problemMethod = new ProblemMethodBinding(method, method.selector, method.parameters, ProblemReasons.InvocationTypeInferenceFailure);
problemMethod.returnType = expectedType != null ? expectedType : method.returnType;
problemMethod.inferenceContext = this;
return problemMethod;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer("Inference Context");
switch (this.stepCompleted) {
case NOT_INFERRED: buf.append(" (initial)");break;
case APPLICABILITY_INFERRED: buf.append(" (applicability inferred)");break;
case TYPE_INFERRED: buf.append(" (type inferred)");break;
case TYPE_INFERRED_FINAL: buf.append(" (type inferred final)");break;
}
switch (this.inferenceKind) {
case CHECK_STRICT: buf.append(" (strict)");break;
case CHECK_LOOSE: buf.append(" (loose)");break;
case CHECK_VARARG: buf.append(" (vararg)");break;
}
if (this.currentBounds != null && isResolved(this.currentBounds))
buf.append(" (resolved)");
buf.append('\n');
if (this.inferenceVariables != null) {
buf.append("Inference Variables:\n");
for (int i = 0; i < this.inferenceVariables.length; i++) {
buf.append('\t').append(this.inferenceVariables[i].sourceName).append("\t:\t");
if (this.currentBounds != null && this.currentBounds.isInstantiated(this.inferenceVariables[i]))
buf.append(this.currentBounds.getInstantiation(this.inferenceVariables[i], this.environment).readableName());
else
buf.append("NOT INSTANTIATED");
buf.append('\n');
}
}
if (this.initialConstraints != null) {
buf.append("Initial Constraints:\n");
for (int i = 0; i < this.initialConstraints.length; i++)
if (this.initialConstraints[i] != null)
buf.append('\t').append(this.initialConstraints[i].toString()).append('\n');
}
if (this.currentBounds != null)
buf.append(this.currentBounds.toString());
return buf.toString();
}
public static ParameterizedTypeBinding parameterizedWithWildcard(TypeBinding type) {
if (type == null || type.kind() != Binding.PARAMETERIZED_TYPE)
return null;
ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) type;
TypeBinding[] arguments = parameterizedType.arguments;
if (arguments != null) {
for (int i = 0; i < arguments.length; i++)
if (arguments[i].isWildcard())
return parameterizedType;
}
return null;
}
public TypeBinding[] getFunctionInterfaceArgumentSolutions(TypeBinding[] a) {
int m = a.length;
TypeBinding[] aprime = new TypeBinding[m];
for (int i = 0; i < this.inferenceVariables.length; i++) {
InferenceVariable alphai = this.inferenceVariables[i];
TypeBinding t = this.currentBounds.getInstantiation(alphai, this.environment);
if (t != null)
aprime[i] = t;
else
aprime[i] = a[i];
}
return aprime;
}
public void recordUncheckedConversion(ConstraintTypeFormula constraint) {
if (this.constraintsWithUncheckedConversion == null)
this.constraintsWithUncheckedConversion = new ArrayList<ConstraintFormula>();
this.constraintsWithUncheckedConversion.add(constraint);
this.usesUncheckedConversion = true;
}
void reportUncheckedConversions(BoundSet solution) {
if (this.constraintsWithUncheckedConversion != null) {
int len = this.constraintsWithUncheckedConversion.size();
Substitution substitution = getResultSubstitution(solution);
for (int i = 0; i < len; i++) {
ConstraintTypeFormula constraint = (ConstraintTypeFormula) this.constraintsWithUncheckedConversion.get(i);
TypeBinding expectedType = constraint.right;
TypeBinding providedType = constraint.left;
if (!expectedType.isProperType(true)) {
expectedType = Scope.substitute(substitution, expectedType);
}
if (!providedType.isProperType(true)) {
providedType = Scope.substitute(substitution, providedType);
}
}
}
}
public boolean usesUncheckedConversion() {
return this.constraintsWithUncheckedConversion != null;
}
public static void missingImplementation(String msg) {
throw new UnsupportedOperationException(msg);
}
public void forwardResults(BoundSet result, Invocation invocation, ParameterizedMethodBinding pmb, TypeBinding targetType) {
if (targetType != null)
invocation.registerResult(targetType, pmb);
Expression[] arguments = invocation.arguments();
for (int i = 0, length = arguments == null ? 0 : arguments.length; i < length; i++) {
Expression [] expressions = arguments[i].getPolyExpressions();
for (int j = 0, jLength = expressions.length; j < jLength; j++) {
Expression expression = expressions[j];
if (!(expression instanceof Invocation))
continue;
Invocation polyInvocation = (Invocation) expression;
MethodBinding binding = polyInvocation.binding();
if (binding == null || !binding.isValidBinding())
continue;
ParameterizedMethodBinding methodSubstitute = null;
if (binding instanceof ParameterizedGenericMethodBinding) {
MethodBinding shallowOriginal = binding.shallowOriginal();
TypeBinding[] solutions = getSolutions(shallowOriginal.typeVariables(), polyInvocation, result);
if (solutions == null)
continue;
methodSubstitute = this.environment.createParameterizedGenericMethod(shallowOriginal, solutions);
} else {
if (!binding.isConstructor() || !(binding instanceof ParameterizedMethodBinding))
continue;
MethodBinding shallowOriginal = binding.shallowOriginal();
ReferenceBinding genericType = shallowOriginal.declaringClass;
TypeBinding[] solutions = getSolutions(genericType.typeVariables(), polyInvocation, result);
if (solutions == null)
continue;
ParameterizedTypeBinding parameterizedType = this.environment.createParameterizedType(genericType, solutions, binding.declaringClass.enclosingType());
for (MethodBinding parameterizedMethod : parameterizedType.methods()) {
if (parameterizedMethod.original() == shallowOriginal) {
methodSubstitute = (ParameterizedMethodBinding) parameterizedMethod;
break;
}
}
}
if (methodSubstitute == null || !methodSubstitute.isValidBinding())
continue;
boolean variableArity = pmb.isVarargs();
final TypeBinding[] parameters = pmb.parameters;
if (variableArity && parameters.length == arguments.length && i == length - 1) {
TypeBinding returnType = methodSubstitute.returnType.capture(this.scope, expression.sourceStart, expression.sourceEnd);
if (returnType.isCompatibleWith(parameters[parameters.length - 1], this.scope)) {
variableArity = false;
}
}
TypeBinding parameterType = InferenceContext18.getParameter(parameters, i, variableArity);
forwardResults(result, polyInvocation, methodSubstitute, parameterType);
}
}
}
public void cleanUp() {
this.b2 = null;
this.currentBounds = null;
}
}