/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.Wildcard; /** * Binding denoting a generic method after type parameter substitutions got performed. * On parameterized type bindings, all methods got substituted, regardless whether * their signature did involve generics or not, so as to get the proper declaringClass for * these methods. */ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBinding implements Substitution { public TypeBinding[] typeArguments; private LookupEnvironment environment; public boolean inferredReturnType; /** * Create method of parameterized type, substituting original parameters with type arguments. */ public ParameterizedGenericMethodBinding(MethodBinding originalMethod, TypeBinding[] typeArguments, LookupEnvironment environment) { this.environment = environment; this.modifiers = originalMethod.modifiers; this.selector = originalMethod.selector; this.declaringClass = originalMethod.declaringClass; this.typeVariables = NoTypeVariables; this.typeArguments = typeArguments; this.originalMethod = originalMethod; this.parameters = Scope.substitute(this, originalMethod.parameters); this.thrownExceptions = Scope.substitute(this, originalMethod.thrownExceptions); this.returnType = this.substitute(originalMethod.returnType); } /** * Create raw generic method for raw type (double substitution from type vars with raw type arguments, and erasure of method variables) */ public ParameterizedGenericMethodBinding(MethodBinding originalMethod, RawTypeBinding rawType, LookupEnvironment environment) { TypeVariableBinding[] originalVariables = originalMethod.typeVariables; int length = originalVariables.length; TypeBinding[] rawArguments = new TypeBinding[length]; for (int i = 0; i < length; i++) { rawArguments[i] = originalVariables[i].erasure(); } this.environment = environment; this.modifiers = originalMethod.modifiers; this.selector = originalMethod.selector; this.declaringClass = rawType; this.typeVariables = NoTypeVariables; this.typeArguments = rawArguments; this.originalMethod = originalMethod; boolean isStatic = originalMethod.isStatic(); this.parameters = Scope.substitute(this, isStatic ? originalMethod.parameters : Scope.substitute(rawType, originalMethod.parameters)); this.thrownExceptions = Scope.substitute(this, isStatic ? originalMethod.thrownExceptions : Scope.substitute(rawType, originalMethod.thrownExceptions)); this.returnType = this.substitute(isStatic ? originalMethod.returnType : rawType.substitute(originalMethod.returnType)); } /** * Perform inference of generic method type parameters and/or expected type */ public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) { ParameterizedGenericMethodBinding methodSubstitute; TypeVariableBinding[] typeVariables = originalMethod.typeVariables; TypeBinding[] substitutes = invocationSite.genericTypeArguments(); if (substitutes != null) { if (substitutes.length != typeVariables.length) { // incompatible due to wrong arity return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, TypeParameterArityMismatch); } methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment()); } else { // perform type inference based on argument types and expected type // collect substitutes by pattern matching parameters and arguments int argLength = arguments.length; TypeBinding[] parameters = originalMethod.parameters; int varLength = typeVariables.length; HashMap collectedSubstitutes = new HashMap(varLength); for (int i = 0; i < varLength; i++) collectedSubstitutes.put(typeVariables[i], new TypeBinding[1]); for (int i = 0; i < argLength; i++) parameters[i].collectSubstitutes(arguments[i], collectedSubstitutes); substitutes = new TypeBinding[varLength]; boolean needReturnTypeInference = false; for (int i = 0; i < varLength; i++) { TypeBinding[] variableSubstitutes = (TypeBinding[]) collectedSubstitutes.get(typeVariables[i]); TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes); //TypeBinding mostSpecificSubstitute = scope.mostSpecificCommonType(variableSubstitutes); if (mostSpecificSubstitute == null) return null; // incompatible if (mostSpecificSubstitute == VoidBinding) { needReturnTypeInference = true; mostSpecificSubstitute = typeVariables[i]; } substitutes[i] = mostSpecificSubstitute; } // apply inferred variable substitutions methodSubstitute = new ParameterizedGenericMethodBinding(originalMethod, substitutes, scope.environment()); if (needReturnTypeInference && invocationSite instanceof MessageSend) { MessageSend message = (MessageSend) invocationSite; TypeBinding expectedType = message.expectedType; if (expectedType != null) methodSubstitute.inferFromExpectedType(message.expectedType, scope); } } // check bounds for (int i = 0, length = typeVariables.length; i < length; i++) { TypeVariableBinding typeVariable = typeVariables[i]; if (!typeVariable.boundCheck(methodSubstitute, substitutes[i])) // incompatible due to bound check return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, new TypeBinding[]{substitutes[i], typeVariables[i] }, ParameterBoundMismatch); } return methodSubstitute; } public void inferFromExpectedType(TypeBinding expectedType, Scope scope) { if (this.returnType == expectedType) return; if ((this.returnType.tagBits & TagBits.HasTypeVariable) == 0) return; Map substitutes = new HashMap(1); int length = this.typeArguments.length; TypeVariableBinding[] originalVariables = this.original().typeVariables; boolean hasUnboundParameters = false; for (int i = 0; i < length; i++) { if (this.typeArguments[i] == originalVariables[i]) { hasUnboundParameters = true; substitutes.put(originalVariables[i], new TypeBinding[1]); } else { substitutes.put(originalVariables[i], new TypeBinding[] { this.typeArguments[i] }); } } if (!hasUnboundParameters) return; returnType.collectSubstitutes(expectedType, substitutes); for (int i = 0; i < length; i++) { TypeBinding[] variableSubstitutes = (TypeBinding[]) substitutes.get(originalVariables[i]); TypeBinding mostSpecificSubstitute = scope.lowerUpperBound(variableSubstitutes); //TypeBinding mostSpecificSubstitute = scope.mostSpecificCommonType(variableSubstitutes); if (mostSpecificSubstitute == null) { return; // TODO (philippe) should report no way to infer type } if (mostSpecificSubstitute != VoidBinding) this.typeArguments[i] = mostSpecificSubstitute; } TypeBinding oldReturnType = this.returnType; this.returnType = this.substitute(this.returnType); this.inferredReturnType = this.returnType != oldReturnType; this.parameters = Scope.substitute(this, this.parameters); this.thrownExceptions = Scope.substitute(this, this.thrownExceptions); } /** * Returns a type, where original type was substituted using the receiver * parameterized method. */ public TypeBinding substitute(TypeBinding originalType) { if ((originalType.tagBits & TagBits.HasTypeVariable) != 0) { if (originalType.isTypeVariable()) { TypeVariableBinding originalVariable = (TypeVariableBinding) originalType; TypeVariableBinding[] variables = this.originalMethod.typeVariables; int length = variables.length; // check this variable can be substituted given parameterized type if (originalVariable.rank < length && variables[originalVariable.rank] == originalVariable) { return this.typeArguments[originalVariable.rank]; } } else if (originalType.isParameterizedType()) { ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType; TypeBinding[] originalArguments = originalParameterizedType.arguments; TypeBinding[] substitutedArguments = Scope.substitute(this, originalArguments); if (substitutedArguments != originalArguments) { identicalVariables: { // if substituted with original variables, then answer the generic type itself TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables(); for (int i = 0, length = originalVariables.length; i < length; i++) { if (substitutedArguments[i] != originalVariables[i]) break identicalVariables; } return originalParameterizedType.type; } return this.environment.createParameterizedType( originalParameterizedType.type, substitutedArguments, originalParameterizedType.enclosingType()); } } else if (originalType.isArrayType()) { TypeBinding originalLeafComponentType = originalType.leafComponentType(); TypeBinding substitute = substitute(originalLeafComponentType); // substitute could itself be array type if (substitute != originalLeafComponentType) { return this.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions()); } } else if (originalType.isWildcard()) { WildcardBinding wildcard = (WildcardBinding) originalType; if (wildcard.kind != Wildcard.UNBOUND) { TypeBinding originalBound = wildcard.bound; TypeBinding substitutedBound = substitute(originalBound); if (substitutedBound != originalBound) { return this.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, wildcard.kind); } } } } else if (originalType.isGenericType()) { // treat as if parameterized with its type variables ReferenceBinding originalGenericType = (ReferenceBinding) originalType; TypeVariableBinding[] originalVariables = originalGenericType.typeVariables(); int length = originalVariables.length; TypeBinding[] originalArguments; System.arraycopy(originalVariables, 0, originalArguments = new TypeBinding[length], 0, length); TypeBinding[] substitutedArguments = Scope.substitute(this, originalArguments); if (substitutedArguments != originalArguments) { return this.environment.createParameterizedType( originalGenericType, substitutedArguments, null); } } return originalType; } }