Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 65fc171f5db8c92938f543c22b2c46fac573e0e3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
/*******************************************************************************
 * Copyright (c) 2013, 2017 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
 *     Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
 *     						Bug 473178
 *******************************************************************************/
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.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ExpressionContext;
import org.eclipse.jdt.internal.compiler.ast.Invocation;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18.SuspendedInferenceRecord;

/**
 * Implementation of 18.1.2 in JLS8, case:
 * <ul>
 * <li>Expression -> T</li>
 * </ul>
 */
class ConstraintExpressionFormula extends ConstraintFormula {
	Expression left;

	// this flag contributes to the workaround controlled by InferenceContext18.ARGUMENT_CONSTRAINTS_ARE_SOFT:
	boolean isSoft;

	ConstraintExpressionFormula(Expression expression, TypeBinding type, int relation) {
		this.left = expression;
		this.right = type;
		this.relation = relation;
	}
	
	ConstraintExpressionFormula(Expression expression, TypeBinding type, int relation, boolean isSoft) {
		this(expression, type, relation);
		this.isSoft = isSoft;
	}

	public Object reduce(InferenceContext18 inferenceContext) throws InferenceFailureException {
		
		if (this.relation == POTENTIALLY_COMPATIBLE) {
			/* 15.12.2.1: ... The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface 
			   target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after 
			   overload resolution. These rules allow the form of the lambda expression to still be taken into account, discarding obviously incorrect target types that might 
			   otherwise cause ambiguity errors.
			*/
			
			return  this.left.isPotentiallyCompatibleWith(this.right, inferenceContext.scope) ? TRUE: FALSE;
		}
	
		// JLS 18.2.1
		if (this.right.isProperType(true)) {
			return this.left.isCompatibleWith(this.right, inferenceContext.scope) || this.left.isBoxingCompatibleWith(this.right, inferenceContext.scope) ? TRUE : FALSE;
		}
		if (!canBePolyExpression(this.left)) {
			TypeBinding exprType = this.left.resolvedType;
			if (exprType == null || !exprType.isValidBinding()) {
				if (this.left instanceof MessageSend && ((MessageSend)this.left).actualReceiverType instanceof InferenceVariable)
					return null; // nothing valuable to infer from this
				return FALSE;
			}
			return ConstraintTypeFormula.create(exprType, this.right, COMPATIBLE, this.isSoft);
		} else {
			// shapes of poly expressions (18.2.1)
			// - parenthesized expression : these are transparent in our AST
			if (this.left instanceof Invocation) {
				Invocation invocation = (Invocation) this.left;
				MethodBinding previousMethod = invocation.binding();
				if (previousMethod == null)  	// can happen, e.g., if inside a copied lambda with ignored errors
					return null; 				// -> proceed with no new constraints
				MethodBinding method = previousMethod;
				// ignore previous (inner) inference result and do a fresh start:
				// avoid original(), since we only want to discard one level of instantiation 
				// (method type variables - not class type variables)!
				method = previousMethod.shallowOriginal();
				SuspendedInferenceRecord prevInvocation = inferenceContext.enterPolyInvocation(invocation, invocation.arguments());

				// Invocation Applicability Inference: 18.5.1 & Invocation Type Inference: 18.5.2
				try {
					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;
					if (previousMethod instanceof ParameterizedGenericMethodBinding) {
						// find the previous inner inference context to see what inference kind this invocation needs:
						InferenceContext18 innerCtx = invocation.getInferenceContext((ParameterizedGenericMethodBinding) previousMethod);
						if (innerCtx == null) { 
							/* No inference context -> the method was likely manufactured by Scope.findExactMethod -> assume it wasn't really poly after all.
							   -> proceed as for non-poly expressions.
							*/
							TypeBinding exprType = this.left.resolvedType;
							if (exprType == null || !exprType.isValidBinding())
								return FALSE;
							return ConstraintTypeFormula.create(exprType, this.right, COMPATIBLE, this.isSoft);
						}
						if (innerCtx.stepCompleted >= InferenceContext18.APPLICABILITY_INFERRED) {
							inferenceContext.integrateInnerInferenceB2(innerCtx);
						} else {
							return FALSE; // should not reach here.
						}
						// b2 has been lifted, inferring poly invocation type amounts to lifting b3.
					} else {
						inferenceContext.inferenceKind = inferenceContext.getInferenceKind(previousMethod, argumentTypes);
						boolean isDiamond = method.isConstructor() && this.left.isPolyExpression(method);
						inferInvocationApplicability(inferenceContext, method, argumentTypes, isDiamond, inferenceContext.inferenceKind);
						// b2 has been lifted, inferring poly invocation type amounts to lifting b3.
					}
					if (!inferenceContext.computeB3(invocation, this.right, method))
						return FALSE;
					return null; // already incorporated
				} finally {
					inferenceContext.resumeSuspendedInference(prevInvocation);
				}
			} else if (this.left instanceof ConditionalExpression) {
				ConditionalExpression conditional = (ConditionalExpression) this.left;
				return new ConstraintFormula[] {
					new ConstraintExpressionFormula(conditional.valueIfTrue, this.right, this.relation, this.isSoft),
					new ConstraintExpressionFormula(conditional.valueIfFalse, this.right, this.relation, this.isSoft)
				};
			} else if (this.left instanceof LambdaExpression) {
				LambdaExpression lambda = (LambdaExpression) this.left;
				BlockScope scope = lambda.enclosingScope;
				if (!this.right.isFunctionalInterface(scope))
					return FALSE;
				
				ReferenceBinding t = (ReferenceBinding) this.right;
				ParameterizedTypeBinding withWildCards = InferenceContext18.parameterizedWithWildcard(t);
				if (withWildCards != null) {
					t = findGroundTargetType(inferenceContext, scope, lambda, withWildCards);
				}
				if (t == null)
					return FALSE;
				MethodBinding functionType = t.getSingleAbstractMethod(scope, true);
				if (functionType == null)
					return FALSE;
				TypeBinding[] parameters = functionType.parameters;
				if (parameters.length != lambda.arguments().length)
					return FALSE;
				if (lambda.argumentsTypeElided())
					for (int i = 0; i < parameters.length; i++)
						if (!parameters[i].isProperType(true))
							return FALSE;
				lambda = lambda.resolveExpressionExpecting(t, inferenceContext.scope, inferenceContext);
				if (lambda == null)
					return FALSE; // not strictly unreduceable, but proceeding with TRUE would likely produce secondary errors
				if (functionType.returnType == TypeBinding.VOID) {
					if (!lambda.isVoidCompatible())
						return FALSE;
				} else {
					if (!lambda.isValueCompatible())
						return FALSE;
				}
				List<ConstraintFormula> result = new ArrayList<>();
				if (!lambda.argumentsTypeElided()) {
					Argument[] arguments = lambda.arguments();
					for (int i = 0; i < parameters.length; i++)
						result.add(ConstraintTypeFormula.create(parameters[i], arguments[i].type.resolvedType, SAME));
					// in addition, ⟨T' <: T⟩:
					if (lambda.resolvedType != null)
						result.add(ConstraintTypeFormula.create(lambda.resolvedType, this.right, SUBTYPE));
				}
				if (functionType.returnType != TypeBinding.VOID) {
					TypeBinding r = functionType.returnType;
					Expression[] exprs = lambda.resultExpressions();
					for (int i = 0, length = exprs == null ? 0 : exprs.length; i < length; i++) {
						Expression expr = exprs[i];
						if (r.isProperType(true) && expr.resolvedType != null) {
							TypeBinding exprType = expr.resolvedType;
							// "not compatible in an assignment context with R"?
							if (!(expr.isConstantValueOfTypeAssignableToType(exprType, r)
									|| exprType.isCompatibleWith(r) || expr.isBoxingCompatible(exprType, r, expr, scope)))
								return FALSE;
						} else {
							result.add(new ConstraintExpressionFormula(expr, r, COMPATIBLE, this.isSoft));
						}
					}
				}
				if (result.size() == 0)
					return TRUE;
				return result.toArray(new ConstraintFormula[result.size()]);
			} else if (this.left instanceof ReferenceExpression) {
				return reduceReferenceExpressionCompatibility((ReferenceExpression) this.left, inferenceContext);
			}
		}
		return FALSE;
	}

	public static ReferenceBinding findGroundTargetType(InferenceContext18 inferenceContext, BlockScope scope,
													LambdaExpression lambda, ParameterizedTypeBinding targetTypeWithWildCards)
	{
		if (lambda.argumentsTypeElided()) {
			return lambda.findGroundTargetTypeForElidedLambda(scope, targetTypeWithWildCards);
		} else {
			SuspendedInferenceRecord previous = inferenceContext.enterLambda(lambda);
			try {
				return inferenceContext.inferFunctionalInterfaceParameterization(lambda, scope, targetTypeWithWildCards);
			} finally {
				inferenceContext.resumeSuspendedInference(previous);
			}
		}
	}

	private boolean canBePolyExpression(Expression expr) {
		// when inferring compatibility against a right type, the check isPolyExpression 
		// must assume that expr occurs in s.t. like an assignment context:
		ExpressionContext previousExpressionContext = expr.getExpressionContext();
		if (previousExpressionContext == ExpressionContext.VANILLA_CONTEXT)
			this.left.setExpressionContext(ExpressionContext.ASSIGNMENT_CONTEXT);
		try {
			return expr.isPolyExpression();
		} finally {
			expr.setExpressionContext(previousExpressionContext);
		}
	}

	private Object reduceReferenceExpressionCompatibility(ReferenceExpression reference, InferenceContext18 inferenceContext) {
		TypeBinding t = this.right;
		if (t.isProperType(true))
			throw new IllegalStateException("Should not reach here with T being a proper type"); //$NON-NLS-1$
		if (!t.isFunctionalInterface(inferenceContext.scope))
			return FALSE;
		MethodBinding functionType = t.getSingleAbstractMethod(inferenceContext.scope, true);
		if (functionType == null)
			return FALSE;
		// potentially-applicable method for the method reference when targeting T (15.13.1),
		reference = reference.resolveExpressionExpecting(t, inferenceContext.scope, inferenceContext);
		MethodBinding potentiallyApplicable = reference != null ? reference.binding : null;
		if (potentiallyApplicable == null)
			return FALSE;
		if (reference.isExactMethodReference()) {
			List<ConstraintFormula> newConstraints = new ArrayList<>();
			TypeBinding[] p = functionType.parameters;
			int n = p.length;
			TypeBinding[] pPrime = potentiallyApplicable.parameters;
			int k = pPrime.length;
			int offset = 0;
			if (n == k+1) {
				newConstraints.add(ConstraintTypeFormula.create(p[0], reference.lhs.resolvedType, COMPATIBLE));
				offset = 1;
			}
			for (int i = offset; i < n; i++)
				newConstraints.add(ConstraintTypeFormula.create(p[i], pPrime[i-offset], COMPATIBLE));
			TypeBinding r = functionType.returnType;
			if (r != TypeBinding.VOID) {
				TypeBinding rAppl = potentiallyApplicable.isConstructor() && !reference.isArrayConstructorReference() ? potentiallyApplicable.declaringClass : potentiallyApplicable.returnType;
				if (rAppl == TypeBinding.VOID)
					return FALSE;
				TypeBinding rPrime = rAppl.capture(inferenceContext.scope, reference.sourceStart, reference.sourceEnd);
				newConstraints.add(ConstraintTypeFormula.create(rPrime, r, COMPATIBLE));
			}
			return newConstraints.toArray(new ConstraintFormula[newConstraints.size()]);
		} else { // inexact
			int n = functionType.parameters.length;
			for (int i = 0; i < n; i++)
				if (!functionType.parameters[i].isProperType(true))
					return FALSE;
			// Otherwise, a search for a compile-time declaration is performed, as defined in 15.13.1....
			// Note: we currently don't distinguish search for a potentially-applicable method from searching the compiler-time declaration,
			// hence reusing the method binding from above
			MethodBinding compileTimeDecl = potentiallyApplicable;
			if (!compileTimeDecl.isValidBinding())
				return FALSE;
			TypeBinding r = functionType.isConstructor() ? functionType.declaringClass : functionType.returnType;
			if (r.id == TypeIds.T_void)
				return TRUE;
			// ignore parameterization of resolve result and do a fresh start:
			MethodBinding original = compileTimeDecl.shallowOriginal();
			if (needsInference(reference, original)) {
				TypeBinding[] argumentTypes;
				if (t.isParameterizedType()) {
					MethodBinding capturedFunctionType = ((ParameterizedTypeBinding)t).getSingleAbstractMethod(inferenceContext.scope, true, reference.sourceStart, reference.sourceEnd);
					argumentTypes = capturedFunctionType.parameters;
				} else {
					argumentTypes = functionType.parameters;
				}
				SuspendedInferenceRecord prevInvocation = inferenceContext.enterPolyInvocation(reference, reference.createPseudoExpressions(argumentTypes));

				// Invocation Applicability Inference: 18.5.1 & Invocation Type Inference: 18.5.2
				try {
					InferenceContext18 innerContext = reference.getInferenceContext((ParameterizedMethodBinding) compileTimeDecl);
					int innerInferenceKind = determineInferenceKind(compileTimeDecl, argumentTypes, innerContext);
					inferInvocationApplicability(inferenceContext, original, argumentTypes, original.isConstructor()/*mimic a diamond?*/, innerInferenceKind);
					if (!inferenceContext.computeB3(reference, r, original))
						return FALSE;
					return null; // already incorporated
				} catch (InferenceFailureException e) {
					return FALSE;
				} finally {
					inferenceContext.resumeSuspendedInference(prevInvocation);
				}
			}
			TypeBinding rPrime = compileTimeDecl.isConstructor() ? compileTimeDecl.declaringClass : compileTimeDecl.returnType.capture(inferenceContext.scope, reference.sourceStart(), reference.sourceEnd());
			if (rPrime.id == TypeIds.T_void)
				return FALSE;
			return ConstraintTypeFormula.create(rPrime, r, COMPATIBLE, this.isSoft);
		}
	}

	private boolean needsInference(ReferenceExpression reference, MethodBinding original) {
		if (reference.typeArguments != null)
			return false;
		TypeBinding compileTimeReturn;
		if (original.isConstructor()) {
			// not checking r.mentionsAny for constructors, because A::new resolves to the raw type
			// whereas in fact the type of all expressions of this shape depend on their type variable (if any)
			if (original.declaringClass.typeVariables() != Binding.NO_TYPE_VARIABLES
					&& reference.receiverType.isRawType())
				return true; // diamond
			compileTimeReturn = original.declaringClass;
		} else {
			compileTimeReturn =  original.returnType;
		}
		return (original.typeVariables() != Binding.NO_TYPE_VARIABLES 
				&& compileTimeReturn.mentionsAny(original.typeVariables(), -1));
	}

	private int determineInferenceKind(MethodBinding original, TypeBinding[] argumentTypes, InferenceContext18 innerContext) {
		if (innerContext != null)
			return innerContext.inferenceKind;
		if (original.isVarargs()) {
			int expectedLen = original.parameters.length;
			int providedLen = argumentTypes.length;
			if (expectedLen < providedLen) {
				return InferenceContext18.CHECK_VARARG;
			} else if (expectedLen == providedLen) {
				TypeBinding providedLast = argumentTypes[expectedLen-1];
				TypeBinding expectedLast = original.parameters[expectedLen-1];
				if (!providedLast.isCompatibleWith(expectedLast)) {
					if (expectedLast.isArrayType()) {
						expectedLast = expectedLast.leafComponentType();
						if (providedLast.isCompatibleWith(expectedLast))
							return InferenceContext18.CHECK_VARARG;
					}
				}
			}
		}
		return InferenceContext18.CHECK_STRICT;
	}

	static void inferInvocationApplicability(InferenceContext18 inferenceContext, MethodBinding method, TypeBinding[] arguments, boolean isDiamond, int checkType)
	{
		// 18.5.1
		TypeVariableBinding[] typeVariables = method.getAllTypeVariables(isDiamond);
		InferenceVariable[] inferenceVariables = inferenceContext.createInitialBoundSet(typeVariables); // creates initial bound set B

		// check if varargs need special treatment:
		int paramLength = method.parameters.length;
		TypeBinding varArgsType = null;
		if (method.isVarargs()) {
			int varArgPos = paramLength-1;
			varArgsType = method.parameters[varArgPos];
		}
		inferenceContext.createInitialConstraintsForParameters(method.parameters, checkType==InferenceContext18.CHECK_VARARG, varArgsType, method);
		inferenceContext.addThrowsContraints(typeVariables, inferenceVariables, method.thrownExceptions);
	}

	/** Perform steps from JLS 18.5.2. needed for computing the bound set B3. */
	static boolean inferPolyInvocationType(InferenceContext18 inferenceContext, InvocationSite invocationSite, TypeBinding targetType, MethodBinding method) 
				throws InferenceFailureException 
	{
		TypeBinding[] typeArguments = invocationSite.genericTypeArguments();
		if (typeArguments == null) {
			// invocation type inference (18.5.2):
			TypeBinding returnType = method.isConstructor() ? method.declaringClass : method.returnType;
			if (returnType == TypeBinding.VOID)
				throw new InferenceFailureException("expression has no value"); //$NON-NLS-1$

			if (inferenceContext.usesUncheckedConversion) {
				TypeBinding erasure = getRealErasure(returnType, inferenceContext.environment);
				ConstraintTypeFormula newConstraint = ConstraintTypeFormula.create(erasure, targetType, COMPATIBLE);
				return inferenceContext.reduceAndIncorporate(newConstraint);
			}
			TypeBinding rTheta = inferenceContext.substitute(returnType);
			ParameterizedTypeBinding parameterizedType = InferenceContext18.parameterizedWithWildcard(rTheta);
			if (parameterizedType != null && parameterizedType.arguments != null) {
				TypeBinding[] arguments = parameterizedType.arguments;
				InferenceVariable[] betas = inferenceContext.addTypeVariableSubstitutions(arguments);
				ParameterizedTypeBinding gbeta = inferenceContext.environment.createParameterizedType(
						parameterizedType.genericType(), betas, parameterizedType.enclosingType(), parameterizedType.getTypeAnnotations());
				inferenceContext.currentBounds.captures.put(gbeta, parameterizedType); // established: both types have nonnull arguments
				if (InferenceContext18.SHOULD_WORKAROUND_BUG_JDK_8054721) {
					for (int i = 0, length = arguments.length; i < length; i++) {
						if (arguments[i].isWildcard()) {
							WildcardBinding wc = (WildcardBinding) arguments[i];
							switch (wc.boundKind) {
								case Wildcard.EXTENDS:
									inferenceContext.currentBounds.addBound(new TypeBound(betas[i], wc.bound(), SUBTYPE), inferenceContext.environment);
									break;
								case Wildcard.SUPER:
									inferenceContext.currentBounds.addBound(new TypeBound(betas[i], wc.bound(), SUPERTYPE), inferenceContext.environment);
									break;
							}
						}
					}
				}
				ConstraintTypeFormula newConstraint = ConstraintTypeFormula.create(gbeta, targetType, COMPATIBLE);
				return inferenceContext.reduceAndIncorporate(newConstraint);
			}
			if (rTheta.leafComponentType() instanceof InferenceVariable) { // https://bugs.openjdk.java.net/browse/JDK-8062082
				InferenceVariable alpha = (InferenceVariable) rTheta.leafComponentType();
				TypeBinding targetLeafType = targetType.leafComponentType();
				boolean toResolve = false;
				if (inferenceContext.currentBounds.condition18_5_2_bullet_3_3_1(alpha, targetLeafType)) {
					toResolve = true;
				} else if (inferenceContext.currentBounds.condition18_5_2_bullet_3_3_2(alpha, targetLeafType, inferenceContext)) {
					toResolve = true;
				} else if (targetLeafType.isPrimitiveType()) {
					TypeBinding wrapper = inferenceContext.currentBounds.findWrapperTypeBound(alpha);
					if (wrapper != null)
						toResolve = true;
				}
				if (toResolve) {
					BoundSet solution = inferenceContext.solve(new InferenceVariable[]{alpha});
					if (solution == null)
						return false;
					TypeBinding u = solution.getInstantiation(alpha, null).capture(inferenceContext.scope, invocationSite.sourceStart(), invocationSite.sourceEnd());
					if (rTheta.dimensions() != 0) {
						u = inferenceContext.environment.createArrayType(u, rTheta.dimensions());
					}
					ConstraintTypeFormula newConstraint = ConstraintTypeFormula.create(u, targetType, COMPATIBLE);
					return inferenceContext.reduceAndIncorporate(newConstraint);
				}
			}
			ConstraintTypeFormula newConstraint = ConstraintTypeFormula.create(rTheta, targetType, COMPATIBLE);
			if (!inferenceContext.reduceAndIncorporate(newConstraint))
				return false;
		}
		return true;
	}

	private static TypeBinding getRealErasure(TypeBinding type, LookupEnvironment environment) {
		TypeBinding erasure = type.erasure();
		// could still be / contain a generic type that needs to be converted to raw:
		TypeBinding erasedLeaf = erasure.leafComponentType();
		if (erasedLeaf.isGenericType())
			erasedLeaf = environment.convertToRawType(erasedLeaf, false);
		if (erasure.isArrayType())
			return environment.createArrayType(erasedLeaf, erasure.dimensions());
		return erasedLeaf;
	}

	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;
				ReferenceBinding targetType = (ReferenceBinding) this.right;
				ParameterizedTypeBinding withWildCards = InferenceContext18.parameterizedWithWildcard(targetType);
				if (withWildCards != null) {
					targetType = ConstraintExpressionFormula.findGroundTargetType(context, lambda.enclosingScope, lambda, withWildCards);
				}
				if (targetType == null) {
					return EMPTY_VARIABLE_LIST;
				}
				MethodBinding sam = targetType.getSingleAbstractMethod(context.scope, true);
				final Set<InferenceVariable> variables = new HashSet<>();
				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)
					final TypeBinding r = sam.returnType;
					LambdaExpression resolved = lambda.resolveExpressionExpecting(this.right, context.scope, context);
					Expression[] resultExpressions = resolved != null ? resolved.resultExpressions() : null;
					for (int i = 0, length = resultExpressions == null ? 0 : resultExpressions.length; i < length; i++) {
						variables.addAll(new ConstraintExpressionFormula(resultExpressions[i], r, COMPATIBLE).inputVariables(context));
					}
				}
				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) && !this.left.isExactMethodReference()) {
				MethodBinding sam = this.right.getSingleAbstractMethod(context.scope, true);
				final Set<InferenceVariable> variables = new HashSet<>();
				int len = sam.parameters.length;
				for (int i = 0; i < len; i++) {
					sam.parameters[i].collectInferenceVariables(variables);
				}
				return variables;
			}			
		} else if (this.left instanceof ConditionalExpression && this.left.isPolyExpression()) {
			ConditionalExpression expr = (ConditionalExpression) this.left;
			Set<InferenceVariable> variables = new HashSet<>();
			variables.addAll(new ConstraintExpressionFormula(expr.valueIfTrue, this.right, COMPATIBLE).inputVariables(context));
			variables.addAll(new ConstraintExpressionFormula(expr.valueIfFalse, this.right, COMPATIBLE).inputVariables(context));
			return variables;
		}
		return EMPTY_VARIABLE_LIST;
	}

	// debugging:
	public String toString() {
		StringBuffer buf = new StringBuffer().append(LEFT_ANGLE_BRACKET);
		this.left.printExpression(4, buf);
		buf.append(relationToString(this.relation));
		appendTypeName(buf, this.right);
		buf.append(RIGHT_ANGLE_BRACKET);
		return buf.toString();
	}
}

Back to the top