Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Looks into indirect types during compilation #2543

Merged
merged 16 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -4485,7 +4485,7 @@ public int generateMethodInfoAttributes(MethodBinding methodBinding) {
}
}
if ((methodBinding.tagBits & TagBits.HasMissingType) != 0) {
this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes);
this.missingTypes = methodBinding.collectMissingTypes(this.missingTypes, true);
}
return attributesNumber;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2021 IBM Corporation and others.
* Copyright (c) 2000, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -505,7 +505,7 @@ public TypeBinding resolveType(BlockScope scope) {
scope.problemReporter().invalidConstructor(this, this.binding);
return this.resolvedType;
}
if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
if ((this.binding.tagBits & TagBits.HasMissingType) != 0 && isMissingTypeRelevant()) {
scope.problemReporter().missingTypeInConstructor(this, this.binding);
}
if (isMethodUseDeprecated(this.binding, scope, true, this)) {
Expand Down Expand Up @@ -539,6 +539,20 @@ public TypeBinding resolveType(BlockScope scope) {
return this.resolvedType;
}

protected boolean isMissingTypeRelevant() {
if (this.binding != null && this.binding.isVarargs()) {
if (this.arguments.length < this.binding.parameters.length) {
// are all but the irrelevant varargs type present?
for (int i = 0; i < this.arguments.length; i++) {
if ((this.binding.parameters[i].tagBits & TagBits.HasMissingType) != 0)
return true; // this one *is* relevant - actually this case is already detected during findConstructorBinding()
}
return false;
}
}
return true;
}

protected void checkPreConstructorContext(BlockScope scope) {
if (this.inPreConstructorContext && this.type != null &&
this.type.resolvedType instanceof ReferenceBinding currentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ public TypeBinding resolveType(BlockScope scope) {
this.binding = scope.environment().updatePolymorphicMethodReturnType((PolymorphicMethodBinding) this.binding, TypeBinding.VOID);
}
}
if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
if ((this.binding.tagBits & TagBits.HasMissingType) != 0 && isMissingTypeRelevant()) {
scope.problemReporter().missingTypeInMethod(this, this.binding);
}
if (!this.binding.isStatic()) {
Expand Down Expand Up @@ -1112,6 +1112,25 @@ protected boolean isUnnecessaryReceiverCast(BlockScope scope, TypeBinding uncast
|| MethodVerifier.doesMethodOverride(otherMethod, this.binding, scope.environment());
}

protected boolean isMissingTypeRelevant() {
if ((this.bits & ASTNode.InsideExpressionStatement) != 0) {
if (this.binding.collectMissingTypes(null, false) == null)
return false; // only irrelevant return type is missing
}
if ((this.binding.returnType.tagBits & TagBits.HasMissingType) == 0
&& this.binding.isVarargs()) {
if (this.arguments.length < this.binding.parameters.length) {
// are all but the irrelevant varargs type present?
for (int i = 0; i < this.arguments.length; i++) {
if ((this.binding.parameters[i].tagBits & TagBits.HasMissingType) != 0)
return true; // this one *is* relevant - actually this case is already detected during findMethodBinding()
}
return false;
}
}
return true;
}

protected TypeBinding handleNullnessCodePatterns(BlockScope scope, TypeBinding returnType) {
// j.u.s.Stream.filter() may modify nullness of stream elements:
if (this.binding.isWellknownMethod(TypeConstants.JAVA_UTIL_STREAM__STREAM, TypeConstants.FILTER)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ private TypeBinding resolveTypeForQualifiedAllocationExpression(BlockScope scope
scope.problemReporter().invalidConstructor(this, constructorBinding);
return this.resolvedType;
}
if ((constructorBinding.tagBits & TagBits.HasMissingType) != 0) {
if ((constructorBinding.tagBits & TagBits.HasMissingType) != 0 && isMissingTypeRelevant()) {
scope.problemReporter().missingTypeInConstructor(this, constructorBinding);
}
if (this.enclosingInstance != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1951,33 +1951,39 @@ MethodBinding resolveTypesFor(MethodBinding method) {

if ((method.modifiers & ExtraCompilerModifiers.AccUnresolved) == 0)
return method;
boolean tolerateSave = this.environment.mayTolerateMissingType;
this.environment.mayTolerateMissingType |= this.environment.globalOptions.complianceLevel >= ClassFileConstants.JDK1_8; // tolerance only implemented for 1.8+
try {

if (!method.isConstructor()) {
TypeBinding resolvedType = resolveType(method.returnType, this.environment, true /* raw conversion */);
method.returnType = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
if (!method.isConstructor()) {
TypeBinding resolvedType = resolveType(method.returnType, this.environment, true /* raw conversion */);
method.returnType = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
}
}
}
for (int i = method.parameters.length; --i >= 0;) {
TypeBinding resolvedType = resolveType(method.parameters[i], this.environment, true /* raw conversion */);
method.parameters[i] = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
for (int i = method.parameters.length; --i >= 0;) {
TypeBinding resolvedType = resolveType(method.parameters[i], this.environment, true /* raw conversion */);
method.parameters[i] = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
}
}
}
for (int i = method.thrownExceptions.length; --i >= 0;) {
ReferenceBinding resolvedType = (ReferenceBinding) resolveType(method.thrownExceptions[i], this.environment, true /* raw conversion */);
method.thrownExceptions[i] = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
for (int i = method.thrownExceptions.length; --i >= 0;) {
ReferenceBinding resolvedType = (ReferenceBinding) resolveType(method.thrownExceptions[i], this.environment, true /* raw conversion */);
method.thrownExceptions[i] = resolvedType;
if ((resolvedType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
}
}
for (int i = method.typeVariables.length; --i >= 0;) {
method.typeVariables[i].resolve();
}
method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
return method;
} finally {
this.environment.mayTolerateMissingType = tolerateSave;
}
for (int i = method.typeVariables.length; --i >= 0;) {
method.typeVariables[i].resolve();
}
method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
return method;
}
@Override
AnnotationBinding[] retrieveAnnotations(Binding binding) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public ConstraintExceptionFormula(FunctionalExpression left, TypeBinding type) {

@Override
public Object reduce(InferenceContext18 inferenceContext) {
if ((this.right.tagBits & TagBits.HasMissingType) != 0) {
inferenceContext.hasIgnoredMissingType = true;
return TRUE;
}
// JLS 18.2.5
Scope scope = inferenceContext.scope;
if (!this.right.isFunctionalInterface(scope))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class ConstraintExpressionFormula extends ConstraintFormula {

@Override
public Object reduce(InferenceContext18 inferenceContext) throws InferenceFailureException {
if ((this.right.tagBits & TagBits.HasMissingType) != 0) {
inferenceContext.hasIgnoredMissingType = true;
return TRUE;
}

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ private ConstraintTypeFormula(TypeBinding exprType, TypeBinding right, int relat
// return: ReductionResult or ConstraintFormula[]
@Override
public Object reduce(InferenceContext18 inferenceContext) {
if ((this.left.tagBits & TagBits.HasMissingType) != 0 || (this.right.tagBits & TagBits.HasMissingType) != 0) {
inferenceContext.hasIgnoredMissingType = true;
return TRUE;
}
switch (this.relation) {
case COMPATIBLE:
// 18.2.2:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ public class InferenceContext18 {
// the following two flags control to what degree we continue with incomplete information:
private boolean isInexactVarargsInference = false;
boolean prematureOverloadResolution = false;
// during reduction we ignore missing types but record that fact here:
boolean hasIgnoredMissingType;

public static boolean isSameSite(InvocationSite site1, InvocationSite site2) {
if (site1 == site2)
Expand Down Expand Up @@ -754,6 +756,7 @@ private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormul
protected int getInferenceKind(MethodBinding nonGenericMethod, TypeBinding[] argumentTypes) {
switch (this.scope.parameterCompatibilityLevel(nonGenericMethod, argumentTypes)) {
case Scope.AUTOBOX_COMPATIBLE:
case Scope.COMPATIBLE_IGNORING_MISSING_TYPE: // if in doubt the method with missing types should be accepted to signal its relevance for resolution
return CHECK_LOOSE;
case Scope.VARARGS_COMPATIBLE:
return CHECK_VARARG;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,11 @@ public final boolean canBeSeenBy(TypeBinding receiverType, InvocationSite invoca
return false;
}

public List<TypeBinding> collectMissingTypes(List<TypeBinding> missingTypes) {
public List<TypeBinding> collectMissingTypes(List<TypeBinding> missingTypes, boolean considerReturnType) {
if ((this.tagBits & TagBits.HasMissingType) != 0) {
missingTypes = this.returnType.collectMissingTypes(missingTypes);
if (considerReturnType) {
missingTypes = this.returnType.collectMissingTypes(missingTypes);
}
for (TypeBinding parameter : this.parameters) {
missingTypes = parameter.collectMissingTypes(missingTypes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod
ParameterizedGenericMethodBinding substitute = inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
if (substitute != null && substitute.returnType.isCompatibleWith(expectedType)) {
// Do not use the new solution if it results in incompatibilities in parameter types
if ((scope.parameterCompatibilityLevel(substitute, arguments, false)) > Scope.NOT_COMPATIBLE) {
if ((scope.parameterCompatibilityLevel(substitute, arguments, false)) > Scope.NOT_COMPATIBLE) { // don't worry about COMPATIBLE_IGNORING_MISSING_TYPE in 1.7 context
methodSubstitute = substitute;
} else {
inferenceContext = oldContext;
Expand Down Expand Up @@ -307,6 +307,9 @@ public static MethodBinding computeCompatibleMethod18(MethodBinding originalMeth
if (invocationSite instanceof Invocation && allArgumentsAreProper && (expectedType == null || expectedType.isProperType(true)))
infCtx18.forwardResults(result, (Invocation) invocationSite, methodSubstitute, expectedType);
try {
if (infCtx18.hasIgnoredMissingType) {
return new ProblemMethodBinding(originalMethod, originalMethod.selector, parameters, ProblemReasons.MissingTypeInSignature);
}
if (hasReturnProblem) { // illegally working from the provisional result?
MethodBinding problemMethod = infCtx18.getReturnProblemMethodIfNeeded(expectedType, methodSubstitute);
if (problemMethod instanceof ProblemMethodBinding) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public ParameterizedMethodBinding(final ParameterizedTypeBinding parameterizedDe
for (int i = 0; i < length; i++) { // copy original type variable to relocate
TypeVariableBinding originalVariable = originalVariables[i];
substitutedVariables[i] = new TypeVariableBinding(originalVariable.sourceName, this, originalVariable.rank, parameterizedDeclaringClass.environment);
substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation));
substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation|TagBits.HasMissingType));
}
stephan-herrmann marked this conversation as resolved.
Show resolved Hide resolved
this.typeVariables = substitutedVariables;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ public interface ProblemReasons {
final int InterfaceMethodInvocationNotBelow18 = 29;
final int NotAccessible = 30; // JLS 6.6.1 - module aspects
final int ErrorAlreadyReported = 31;
final int MissingTypeInSignature = 32;
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public char[] readableName() {
public final static int COMPATIBLE = 0;
public final static int AUTOBOX_COMPATIBLE = 1;
public final static int VARARGS_COMPATIBLE = 2;
public final static int COMPATIBLE_IGNORING_MISSING_TYPE = -2;

/* Type Compatibilities */
public static final int EQUAL_OR_MORE_SPECIFIC = -1;
Expand Down Expand Up @@ -871,13 +872,16 @@ protected final MethodBinding computeCompatibleMethod(MethodBinding method, Type
}


if ((parameterCompatibilityLevel(method, arguments, tiebreakingVarargsMethods)) > NOT_COMPATIBLE) {
int level = parameterCompatibilityLevel(method, arguments, tiebreakingVarargsMethods);
if (level > NOT_COMPATIBLE) {
if (method.hasPolymorphicSignature(this)) {
// generate polymorphic method and set polymorphic tagbits as well
method.tagBits |= TagBits.AnnotationPolymorphicSignature;
return this.environment().createPolymorphicMethod(method, arguments, this);
}
return method;
} else if (level == COMPATIBLE_IGNORING_MISSING_TYPE) {
return new ProblemMethodBinding(method, method.selector, method.parameters, ProblemReasons.MissingTypeInSignature);
}
// if method is generic and type arguments have been supplied, only then answer a problem
// of ParameterizedMethodTypeMismatch, else a non-generic method was invoked using type arguments
Expand Down Expand Up @@ -978,7 +982,7 @@ protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean c
} else {
typeVariable.setSuperInterfaces(new ReferenceBinding[] {superRefType});
}
typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences;
typeVariable.tagBits |= superType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType);
typeVariable.setFirstBound(superRefType); // first bound used to compute erasure
}
}
Expand All @@ -993,7 +997,7 @@ protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean c
typeVariable.tagBits |= TagBits.HierarchyHasProblems;
continue nextBound;
} else {
typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences;
typeVariable.tagBits |= superType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType);
boolean didAlreadyComplain = !typeRef.resolvedType.isValidBinding();
if (isFirstBoundTypeVariable && j == 0) {
problemReporter().noAdditionalBoundAfterTypeVariable(typeRef);
Expand Down Expand Up @@ -1764,6 +1768,8 @@ public MethodBinding findMethod0(ReferenceBinding receiverType, char[] selector,
if (candidatesCount == 0)
candidates = new MethodBinding[foundSize];
candidates[candidatesCount++] = compatibleMethod;
} else if (compatibleMethod.problemId() == ProblemReasons.MissingTypeInSignature) {
return compatibleMethod; // use this method for error message to give a hint about the missing type
} else if (problemMethod == null) {
problemMethod = compatibleMethod;
}
Expand Down Expand Up @@ -2495,6 +2501,8 @@ public MethodBinding getConstructor0(ReferenceBinding receiverType, TypeBinding[
if (compatibleMethod != null) {
if (compatibleMethod.isValidBinding())
compatible[compatibleIndex++] = compatibleMethod;
else if (compatibleMethod.problemId() == ProblemReasons.MissingTypeInSignature)
return compatibleMethod; // use this method for error message to give a hint about the missing type
else if (problemMethod == null)
problemMethod = compatibleMethod;
}
Expand Down Expand Up @@ -4648,6 +4656,10 @@ protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible,
int compatibleCount = 0;
for (int i = 0; i < visibleSize; i++)
if ((compatibilityLevels[i] = parameterCompatibilityLevel(visible[i], argumentTypes, invocationSite)) != NOT_COMPATIBLE) {
if (compatibilityLevels[i] == COMPATIBLE_IGNORING_MISSING_TYPE) {
// cannot conclusively select any candidate, use the method with missing types in the error message
return new ProblemMethodBinding(visible[i], visible[i].selector, visible[i].parameters, ProblemReasons.Ambiguous);
}
if (i != compatibleCount) {
visible[compatibleCount] = visible[i];
compatibilityLevels[compatibleCount] = compatibilityLevels[i];
Expand Down Expand Up @@ -5144,13 +5156,17 @@ public int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] argum
TypeBinding param = ((ArrayBinding) parameters[lastIndex]).elementsType();
for (int i = lastIndex; i < argLength; i++) {
TypeBinding arg = (tiebreakingVarargsMethods && (i == (argLength - 1))) ? ((ArrayBinding)arguments[i]).elementsType() : arguments[i];
if (TypeBinding.notEquals(param, arg) && parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods, method) == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
if (TypeBinding.notEquals(param, arg)) {
level = parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods, method);
if (level == NOT_COMPATIBLE)
stephan-herrmann marked this conversation as resolved.
Show resolved Hide resolved
return NOT_COMPATIBLE;
}
stephan-herrmann marked this conversation as resolved.
Show resolved Hide resolved
}
} else if (lastIndex != argLength) { // can call foo(int i, X ... x) with foo(1) but NOT foo();
return NOT_COMPATIBLE;
}
level = VARARGS_COMPATIBLE; // varargs support needed
if (level != COMPATIBLE_IGNORING_MISSING_TYPE) // preserve any COMPATIBLE_IGNORING_MISSING_TYPE
level = VARARGS_COMPATIBLE; // varargs support needed
}
} else if (paramLength != argLength) {
return NOT_COMPATIBLE;
Expand All @@ -5161,9 +5177,9 @@ public int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] argum
TypeBinding arg = (tiebreakingVarargsMethods && (i == (argLength - 1))) ? ((ArrayBinding)arguments[i]).elementsType() : arguments[i];
if (TypeBinding.notEquals(arg,param)) {
int newLevel = parameterCompatibilityLevel(arg, param, env, tiebreakingVarargsMethods, method);
if (newLevel == NOT_COMPATIBLE)
return NOT_COMPATIBLE;
if (newLevel > level)
if (newLevel < COMPATIBLE)
return newLevel;
if (newLevel > level && level != COMPATIBLE_IGNORING_MISSING_TYPE) // preserve any COMPATIBLE_IGNORING_MISSING_TYPE
level = newLevel;
}
}
Expand Down Expand Up @@ -5193,6 +5209,8 @@ private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, Look
// only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5
if (arg == null || param == null)
return NOT_COMPATIBLE;
if ((param.tagBits & TagBits.HasMissingType) != 0)
return COMPATIBLE_IGNORING_MISSING_TYPE;
if (arg instanceof PolyTypeBinding && !((PolyTypeBinding) arg).expression.isPertinentToApplicability(param, method)) {
if (arg.isPotentiallyCompatibleWith(param, this))
return COMPATIBLE;
Expand Down
Loading
Loading