/** * Given some type expectation, and type variable bounds, perform some inference. Returns true if * still had unresolved type variable at the end of the operation */ private ParameterizedGenericMethodBinding inferFromExpectedType( Scope scope, InferenceContext inferenceContext) { TypeVariableBinding[] originalVariables = this.originalMethod.typeVariables; // immediate parent (could be a parameterized method) int varLength = originalVariables.length; // infer from expected return type if (inferenceContext.expectedType != null) { this.returnType.collectSubstitutes( scope, inferenceContext.expectedType, inferenceContext, TypeConstants.CONSTRAINT_SUPER); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } // infer from bounds of type parameters for (int i = 0; i < varLength; i++) { TypeVariableBinding originalVariable = originalVariables[i]; TypeBinding argument = this.typeArguments[i]; boolean argAlreadyInferred = argument != originalVariable; if (originalVariable.firstBound == originalVariable.superclass) { TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superclass); argument.collectSubstitutes( scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution // JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference // e.g. given: <E extends Object, S extends Collection<E>> S test1(S param) // invocation: test1(new Vector<String>()) will infer: S=Vector<String> // and with code below: E=String if (argAlreadyInferred) { substitutedBound.collectSubstitutes( scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } } for (int j = 0, max = originalVariable.superInterfaces.length; j < max; j++) { TypeBinding substitutedBound = Scope.substitute(this, originalVariable.superInterfaces[j]); argument.collectSubstitutes( scope, substitutedBound, inferenceContext, TypeConstants.CONSTRAINT_SUPER); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution // JLS 15.12.2.8 claims reverse inference shouldn't occur, however it improves inference if (argAlreadyInferred) { substitutedBound.collectSubstitutes( scope, argument, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } } } if (!resolveSubstituteConstraints( scope, originalVariables, inferenceContext, true /*consider Ti<:Uk*/)) return null; // incompatible // this.typeArguments = substitutes; - no op since side effects got performed during // #resolveSubstituteConstraints for (int i = 0; i < varLength; i++) { TypeBinding substitute = inferenceContext.substitutes[i]; if (substitute != null) { this.typeArguments[i] = inferenceContext.substitutes[i]; } else { // remaining unresolved variable are considered to be Object (or their bound actually) this.typeArguments[i] = originalVariables[i].upperBound(); } } // may still need an extra substitution at the end (see // https://bugs.eclipse.org/bugs/show_bug.cgi?id=121369) // to properly substitute a remaining unresolved variable which also appear in a formal bound this.typeArguments = Scope.substitute(this, this.typeArguments); // adjust method types to reflect latest inference TypeBinding oldReturnType = this.returnType; this.returnType = Scope.substitute(this, this.returnType); this.inferredReturnType = inferenceContext.hasExplicitExpectedType && this.returnType != oldReturnType; this.parameters = Scope.substitute(this, this.parameters); this.thrownExceptions = Scope.substitute(this, this.thrownExceptions); // error case where exception type variable would have been substituted by a non-reference type // (207573) if (this.thrownExceptions == null) this.thrownExceptions = Binding.NO_EXCEPTIONS; checkMissingType: { if ((this.tagBits & TagBits.HasMissingType) != 0) break checkMissingType; if ((this.returnType.tagBits & TagBits.HasMissingType) != 0) { this.tagBits |= TagBits.HasMissingType; break checkMissingType; } for (int i = 0, max = this.parameters.length; i < max; i++) { if ((this.parameters[i].tagBits & TagBits.HasMissingType) != 0) { this.tagBits |= TagBits.HasMissingType; break checkMissingType; } } for (int i = 0, max = this.thrownExceptions.length; i < max; i++) { if ((this.thrownExceptions[i].tagBits & TagBits.HasMissingType) != 0) { this.tagBits |= TagBits.HasMissingType; break checkMissingType; } } } return this; }
/** Collect argument type mapping, handling varargs */ private static ParameterizedGenericMethodBinding inferFromArgumentTypes( Scope scope, MethodBinding originalMethod, TypeBinding[] arguments, TypeBinding[] parameters, InferenceContext inferenceContext) { if (originalMethod.isVarargs()) { int paramLength = parameters.length; int minArgLength = paramLength - 1; int argLength = arguments.length; // process mandatory arguments for (int i = 0; i < minArgLength; i++) { parameters[i].collectSubstitutes( scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } // process optional arguments if (minArgLength < argLength) { TypeBinding varargType = parameters[minArgLength]; // last arg type - as is ? TypeBinding lastArgument = arguments[minArgLength]; checkVarargDimension: { if (paramLength == argLength) { if (lastArgument == TypeBinding.NULL) break checkVarargDimension; switch (lastArgument.dimensions()) { case 0: break; // will remove one dim case 1: if (!lastArgument.leafComponentType().isBaseType()) break checkVarargDimension; break; // will remove one dim default: break checkVarargDimension; } } // eliminate one array dimension varargType = ((ArrayBinding) varargType).elementsType(); } for (int i = minArgLength; i < argLength; i++) { varargType.collectSubstitutes( scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } } } else { int paramLength = parameters.length; for (int i = 0; i < paramLength; i++) { parameters[i].collectSubstitutes( scope, arguments[i], inferenceContext, TypeConstants.CONSTRAINT_EXTENDS); if (inferenceContext.status == InferenceContext.FAILED) return null; // impossible substitution } } TypeVariableBinding[] originalVariables = originalMethod.typeVariables; if (!resolveSubstituteConstraints( scope, originalVariables, inferenceContext, false /*ignore Ti<:Uk*/)) return null; // impossible substitution // apply inferred variable substitutions - replacing unresolved variable with original ones in // param method TypeBinding[] inferredSustitutes = inferenceContext.substitutes; TypeBinding[] actualSubstitutes = inferredSustitutes; for (int i = 0, varLength = originalVariables.length; i < varLength; i++) { if (inferredSustitutes[i] == null) { if (actualSubstitutes == inferredSustitutes) { System.arraycopy( inferredSustitutes, 0, actualSubstitutes = new TypeBinding[varLength], 0, i); // clone to replace null with original variable in param method } actualSubstitutes[i] = originalVariables[i]; } else if (actualSubstitutes != inferredSustitutes) { actualSubstitutes[i] = inferredSustitutes[i]; } } ParameterizedGenericMethodBinding paramMethod = scope.environment().createParameterizedGenericMethod(originalMethod, actualSubstitutes); return paramMethod; }