Skip to content

Commit

Permalink
Looks into indirect types during compilation (#2543)
Browse files Browse the repository at this point in the history
+ tolerate missing types during BTB.resolveTypesFor(MethodBinding)
+ new parameterCompatibilityLevel NEED_MISSING_TYPE
+ when NEED_MISSING_TYPE is encountered during resolution
  answer ProblemMethodBinding(ProblemReason.MissingTypeInSignature)
+ for varargs invocation try to discern if a missing type is relevant
+ detect when method return type can be ignored
+ detect missing types during type inference
  - don't let constraint with missing type fail type inference
  - prefer that candidate during overload resolution
+ let more locations propagate HasMissingType
+ improve collectMissingTypes() & protect against infinite recursion

Tests:
+ adjust expected secondary errors (neither is better than the other)
+ Java50Tests pointed to incomplete impl. at compliance below 1.8
  => restrict new policy to 1.8+
+ DependencyTests.testMissingClassFile()
  + bump test to 1.8 and let it include positive & negative cases
+ MultiProjectTests.test461074_error() split to 1.5 and 1.8 variants:
  + no change at 1.5
  + no longer an error at 1.8+
+ improved errors in GenericsRegressionTest_1_8.testBug525580*() et al
+ fix JCL_LIB -> JCL18_LIB in ImportRewrite18Test
+ JavaSearchBugsTest2:
   - fix tests by adding required import
   - create one test variant with missing import and POTENTIAL_MATCH

fixes #543
  • Loading branch information
stephan-herrmann authored Jul 11, 2024
1 parent bc7e525 commit f6fe3c8
Show file tree
Hide file tree
Showing 33 changed files with 1,850 additions and 144 deletions.
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
@@ -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 @@ -63,13 +63,10 @@ public CaptureBinding(WildcardBinding wildcard, ReferenceBinding sourceType, int
this.environment.typeSystem.cacheDerivedType(this, unannotated, this);
// propagate from wildcard to capture - use super version, because our own method propagates type annotations in the opposite direction:
super.setTypeAnnotations(wildcard.getTypeAnnotations(), wildcard.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled);
if (wildcard.hasNullTypeAnnotations())
this.tagBits |= TagBits.HasNullTypeAnnotation;
this.tagBits |= wildcard.tagBits & (TagBits.HasNullTypeAnnotation|TagBits.HasMissingType);
} else {
computeId(this.environment);
if(wildcard.hasNullTypeAnnotations()) {
this.tagBits |= (wildcard.tagBits & TagBits.AnnotationNullMASK) | TagBits.HasNullTypeAnnotation;
}
this.tagBits |= wildcard.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation|TagBits.HasMissingType);
}
}

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 @@ -144,8 +144,7 @@ public TypeBinding substitute(TypeVariableBinding typeVariable) {
typeVariable.superclass = superclass;
typeVariable.superInterfaces = superInterfaces;
typeVariable.firstBound = superclass != null ? superclass : superInterfaces[0];
if (typeVariable.firstBound.hasNullTypeAnnotations())
typeVariable.tagBits |= TagBits.HasNullTypeAnnotation;
typeVariable.tagBits |= typeVariable.firstBound.tagBits & (TagBits.HasNullTypeAnnotation|TagBits.HasMissingType);
}
return typeVariable;
}
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));
}
this.typeVariables = substitutedVariables;

Expand Down Expand Up @@ -209,7 +209,7 @@ public ParameterizedMethodBinding(final ReferenceBinding declaringClass, MethodB
this,
originalVariable.rank,
environment);
substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation));
substitutedVariables[i].tagBits |= (originalVariable.tagBits & (TagBits.AnnotationNullMASK|TagBits.HasNullTypeAnnotation|TagBits.HasMissingType));
}
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;
}
Loading

0 comments on commit f6fe3c8

Please sign in to comment.