1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.lookup;
13 import java.util.HashMap;
15 import org.eclipse.jdt.internal.compiler.ast.MessageSend;
16 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
19 * Binding denoting a generic method after type parameter substitutions got performed.
20 * On parameterized type bindings, all methods got substituted, regardless whether
21 * their signature did involve generics or not, so as to get the proper declaringClass for
24 public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution {
26 public TypeBinding[] typeArguments;
27 private LookupEnvironment environment;
28 public boolean inferredReturnType;
29 public boolean wasInferred; // only set to true for instances resulting from method invocation inferrence
30 public boolean isRaw; // set to true for method behaving as raw for substitution purpose
31 public MethodBinding tiebreakMethod;
34 * Create method of parameterized type, substituting original parameters with type arguments.
36 public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments, LookupEnvironment environment) {
38 this.environment = environment;
39 this.modifiers = originalMethod.modifiers;
40 this.selector = originalMethod.selector;
41 this.declaringClass = originalMethod.declaringClass;
42 this.typeVariables = NoTypeVariables;
43 this.typeArguments = typeArguments;
45 this.originalMethod = originalMethod;
46 this.parameters = Scope.substitute(this, originalMethod.parameters);
47 this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions);
48 this.returnType = this.substitute(originalMethod.returnType);
49 this.wasInferred = true;// resulting from method invocation inferrence
53 * Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method variables)
54 * Only invoked for non-static generic methods of raw type
56 public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType, LookupEnvironment environment) {
58 TypeVariableBinding[] originalVariables = originalMethod.typeVariables;
59 int length = originalVariables.length;
60 TypeBinding[] rawArguments = new TypeBinding[length];
61 for (int i = 0; i < length; i++) {
62 rawArguments[i] = originalVariables[i].erasure();
65 this.environment = environment;
66 this.modifiers = originalMethod.modifiers;
67 this.selector = originalMethod.selector;
68 this.declaringClass = rawType == null ? originalMethod.declaringClass : rawType;
69 this.typeVariables = NoTypeVariables;
70 this.typeArguments = rawArguments;
71 this.originalMethod = originalMethod;
72 boolean ignoreRawTypeSubstitution = rawType == null || originalMethod.isStatic();
73 this.parameters = Scope.substitute(this, ignoreRawTypeSubstitution
74 ? originalMethod.parameters // no substitution if original was static
75 : Scope.substitute(rawType, originalMethod.parameters));
76 this.thrownExceptions = Scope.substitute(this, ignoreRawTypeSubstitution
77 ? originalMethod.thrownExceptions // no substitution if original was static
78 : Scope.substitute(rawType, originalMethod.thrownExceptions));
79 this.returnType = this.substitute(ignoreRawTypeSubstitution
80 ? originalMethod.returnType // no substitution if original was static
81 : rawType.substitute(originalMethod.returnType));
82 this.wasInferred = false; // not resulting from method invocation inferrence
86 * Perform inference of generic method type parameters and/or expected type
88 public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) {
90 ParameterizedGenericMethodBinding methodSubstitute;
91 TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
92 TypeBinding[] substitutes = invocationSite.genericTypeArguments();
94 if (substitutes != null) {
95 if (substitutes.length != typeVariables.length) {
96 // incompatible due to wrong arity
97 return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, TypeParameterArityMismatch);
99 methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment());
101 // perform type inference based on argument types and expected type
103 // collect substitutes by pattern matching parameters and arguments
104 TypeBinding[] parameters = originalMethod.parameters;
105 int varLength = typeVariables.length;
106 HashMap collectedSubstitutes = new HashMap(varLength);
107 for (int i = 0; i < varLength; i++)
108 collectedSubstitutes.put(typeVariables[i], new TypeBinding[1]);
110 // collect argument type mapping, handling varargs
111 if (originalMethod.isVarargs()) {
112 int paramLength = parameters.length;
113 int minArgLength = paramLength - 1;
114 int argLength = arguments.length;
115 // process mandatory arguments
116 for (int i = 0; i < minArgLength; i++)
117 parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes);
118 // process optional arguments
119 if (minArgLength < argLength) {
120 TypeBinding varargType = parameters[minArgLength]; // last arg type - as is ?
121 if (paramLength != argLength // argument is passed as is ?
122 || (arguments[minArgLength] != NullBinding
123 && (arguments[minArgLength].dimensions() != varargType.dimensions()))) {
124 varargType = ((ArrayBinding)varargType).elementsType(); // eliminate one array dimension
126 for (int i = minArgLength; i < argLength; i++)
127 varargType.collectSubstitutes(arguments[i], collectedSubstitutes);
130 int paramLength = parameters.length;
131 for (int i = 0; i < paramLength; i++)
132 parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes);
134 boolean needReturnTypeInference = false;
135 if (collectedSubstitutes.isEmpty()) {
136 // raw generic method inferred
137 methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, (RawTypeBinding)null, scope.environment());
139 substitutes = new TypeBinding[varLength];
140 for (int i = 0; i < varLength; i++) {
141 TypeBinding[] variableSubstitutes = (TypeBinding[]) collectedSubstitutes.get(typeVariables[i]);
142 TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes);
143 if (mostSpecificSubstitute == null)
144 return null; // incompatible
145 if (mostSpecificSubstitute == VoidBinding) {
146 needReturnTypeInference = true;
147 mostSpecificSubstitute = typeVariables[i];
149 substitutes[i] = mostSpecificSubstitute;
151 // apply inferred variable substitutions
152 methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment());
155 if (needReturnTypeInference && invocationSite instanceof MessageSend) {
156 MessageSend message = (MessageSend) invocationSite;
157 TypeBinding expectedType = message.expectedType;
158 if (expectedType == null) {
159 // 15.12.2.8 - if no expected type, then assume Object
160 // actually it rather seems to handle the returned variable case by expecting its erasure instead
161 if (methodSubstitute.returnType.isTypeVariable()) {
162 expectedType = methodSubstitute.returnType.erasure();
164 expectedType =scope.getJavaLangObject();
167 methodSubstitute.inferFromExpectedType(expectedType, scope);
171 if (!methodSubstitute.isRaw) {
172 for (int i = 0, length = typeVariables.length; i < length; i++) {
173 TypeVariableBinding typeVariable = typeVariables[i];
174 TypeBinding substitute = substitutes[i];
175 if (!typeVariable.boundCheck(methodSubstitute, substitute))
176 // incompatible due to bound check
177 return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, new TypeBinding[]{substitutes[i], typeVariables[i] }, ParameterBoundMismatch);
181 return methodSubstitute;
185 * parameterizedDeclaringUniqueKey dot selector originalMethodGenericSignature percent typeArguments
186 * p.X<U> { <T> void bar(T t, U u) { new X<String>().bar(this, "") } } --> Lp/X<Ljava/lang/String;>;.bar<T:Ljava/lang/Object;>(TT;TU;)V%<Lp/X;>
188 public char[] computeUniqueKey() {
190 return super.computeUniqueKey();
191 StringBuffer buffer = new StringBuffer();
192 buffer.append(super.computeUniqueKey());
195 int length = this.typeArguments.length;
196 for (int i = 0; i < length; i++) {
197 TypeBinding typeArgument = this.typeArguments[i];
198 buffer.append(typeArgument.computeUniqueKey());
201 int resultLength = buffer.length();
202 char[] result = new char[resultLength];
203 buffer.getChars(0, resultLength, result, 0);
209 * Returns true if some parameters got substituted.
210 * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
212 public boolean hasSubstitutedParameters() {
213 // generic parameterized method can represent either an invocation or a raw generic method
214 if (this.wasInferred)
215 return this.originalMethod.hasSubstitutedParameters();
216 return super.hasSubstitutedParameters();
219 * Returns true if the return type got substituted.
220 * NOTE: generic method invocation delegates to its declaring method (could be a parameterized one)
222 public boolean hasSubstitutedReturnType() {
223 if (this.wasInferred)
224 return this.originalMethod.hasSubstitutedReturnType();
225 return super.hasSubstitutedReturnType();
228 public void inferFromExpectedType(TypeBinding expectedType, Scope scope) {
229 if (this.returnType == expectedType)
231 if ((this.returnType.tagBits & TagBits.HasTypeVariable) == 0)
233 Map substitutes = new HashMap(1);
234 int length = this.typeArguments.length;
235 TypeVariableBinding[] originalVariables = this.original().typeVariables;
236 boolean hasUnboundParameters = false;
237 for (int i = 0; i < length; i++) {
238 if (this.typeArguments[i] == originalVariables[i]) {
239 hasUnboundParameters = true;
240 substitutes.put(originalVariables[i], new TypeBinding[1]);
242 substitutes.put(originalVariables[i], new TypeBinding[] { this.typeArguments[i] });
245 if (!hasUnboundParameters)
247 returnType.collectSubstitutes(expectedType, substitutes);
248 if (substitutes.isEmpty()) {
249 // raw generic method inferred
251 for (int i = 0; i < length; i++) {
252 this.typeArguments[i] = originalVariables[i].erasure();
255 for (int i = 0; i < length; i++) {
256 TypeBinding[] variableSubstitutes = (TypeBinding[]) substitutes.get(originalVariables[i]);
257 TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes);
258 if (mostSpecificSubstitute == null) {
259 return; // TODO (philippe) should report no way to infer type
261 if (mostSpecificSubstitute == VoidBinding) {
262 // 15.12.2.8 - any remaining variable is assumed to be its erasure
263 mostSpecificSubstitute = originalVariables[i].erasure();
265 this.typeArguments[i] = mostSpecificSubstitute;
268 TypeBinding oldReturnType = this.returnType;
269 this.returnType = this.substitute(this.returnType);
270 this.inferredReturnType = this.returnType != oldReturnType;
271 this.parameters = Scope.substitute(this, this.parameters);
272 this.thrownExceptions = Scope.substitute(this, this.thrownExceptions);
276 * Returns a type, where original type was substituted using the receiver
277 * parameterized method.
279 public TypeBinding substitute(TypeBinding originalType) {
281 switch (originalType.kind()) {
283 case Binding.TYPE_PARAMETER:
284 TypeVariableBinding originalVariable = (TypeVariableBinding) originalType;
285 TypeVariableBinding[] variables = this.originalMethod.typeVariables;
286 int length = variables.length;
287 // check this variable can be substituted given parameterized type
288 if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) {
289 return this.typeArguments[originalVariable.rank];
291 if (this.declaringClass instanceof Substitution) {
292 return ((Substitution)this.declaringClass).substitute(originalType);
296 case Binding.PARAMETERIZED_TYPE:
297 ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType;
298 ReferenceBinding originalEnclosing = originalType.enclosingType();
299 ReferenceBinding substitutedEnclosing = originalEnclosing;
300 if (originalEnclosing != null) {
301 substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
304 return this.environment.createRawType(originalParameterizedType.type, substitutedEnclosing);
306 TypeBinding[] originalArguments = originalParameterizedType.arguments;
307 TypeBinding[] substitutedArguments = originalArguments;
308 if (originalArguments != null) {
309 substitutedArguments = Scope.substitute(this, originalArguments);
311 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
312 identicalVariables: { // if substituted with original variables, then answer the generic type itself
313 if (substitutedEnclosing != originalEnclosing) break identicalVariables;
314 TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables();
315 length = originalVariables.length;
316 for (int i = 0; i < length; i++) {
317 if (substitutedArguments[i] != originalVariables[i]) break identicalVariables;
319 return originalParameterizedType.type;
321 return this.environment.createParameterizedType(
322 originalParameterizedType.type, substitutedArguments, substitutedEnclosing);
326 case Binding.ARRAY_TYPE:
327 TypeBinding originalLeafComponentType = originalType.leafComponentType();
328 TypeBinding substitute = substitute(originalLeafComponentType); // substitute could itself be array type
329 if (substitute != originalLeafComponentType) {
330 return this.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions());
334 case Binding.WILDCARD_TYPE:
335 WildcardBinding wildcard = (WildcardBinding) originalType;
336 if (wildcard.kind != Wildcard.UNBOUND) {
337 TypeBinding originalBound = wildcard.bound;
338 TypeBinding substitutedBound = substitute(originalBound);
339 if (substitutedBound != originalBound) {
340 return this.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, wildcard.kind);
346 case Binding.GENERIC_TYPE:
347 // treat as if parameterized with its type variables
348 ReferenceBinding originalGenericType = (ReferenceBinding) originalType;
349 originalEnclosing = originalType.enclosingType();
350 substitutedEnclosing = originalEnclosing;
351 if (originalEnclosing != null) {
352 substitutedEnclosing = (ReferenceBinding) this.substitute(originalEnclosing);
355 return this.environment.createRawType(originalGenericType, substitutedEnclosing);
357 TypeVariableBinding[] originalVariables = originalGenericType.typeVariables();
358 length = originalVariables.length;
359 System.arraycopy(originalVariables, 0, originalArguments = new TypeBinding[length], 0, length);
360 substitutedArguments = Scope.substitute(this, originalArguments);
361 if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
362 return this.environment.createParameterizedType(
363 originalGenericType, substitutedArguments, substitutedEnclosing);
370 * Returns the method to use during tiebreak (usually the method itself).
371 * For generic method invocations, tiebreak needs to use generic method with erasure substitutes.
373 public MethodBinding tiebreakMethod() {
374 if (this.tiebreakMethod == null) {
375 this.tiebreakMethod = new ParameterizedGenericMethodBinding(this.originalMethod, (RawTypeBinding)null, this.environment);
377 return this.tiebreakMethod;