From d8f71eb7089fb90ea5663f50eb2b3e8b4470ca9d Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Wed, 8 Mar 2023 22:59:04 +0100 Subject: [PATCH] Remove left overs from implementing version 0.7.0 of the JSR 305 spec Fixes #818 Code changes: - impl of JLS 18.5.3 refreshed from Java 19 spec - small code unification towards PTB.getNonWildcardParameterization() Test adjustments: - fewer captures created cause different capture-names - wildcards eliminated from some error messages --- .../compiler/ast/LambdaExpression.java | 11 +-- .../lookup/ConstraintExpressionFormula.java | 2 +- .../compiler/lookup/InferenceContext18.java | 74 ++++++++++--------- .../lookup/ParameterizedTypeBinding.java | 18 ++++- .../GenericsRegressionTest_1_8.java | 2 +- .../NegativeLambdaExpressionsTest.java | 6 +- 6 files changed, 59 insertions(+), 54 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java index c9c3dd879ac..fcc1528fb01 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java @@ -533,7 +533,7 @@ private ReferenceBinding findGroundTargetType(BlockScope blockScope, TypeBinding freshInferenceContext.cleanUp(); } } else { - return findGroundTargetTypeForElidedLambda(blockScope, withWildCards); + return withWildCards.getNonWildcardParameterization(blockScope); } } if (targetType instanceof ReferenceBinding) @@ -542,15 +542,6 @@ private ReferenceBinding findGroundTargetType(BlockScope blockScope, TypeBinding return null; } - public ReferenceBinding findGroundTargetTypeForElidedLambda(BlockScope blockScope, ParameterizedTypeBinding withWildCards) { - // non-wildcard parameterization (9.8) of the target type - TypeBinding[] types = withWildCards.getNonWildcardParameterization(blockScope); - if (types == null) - return null; - ReferenceBinding genericType = withWildCards.genericType(); - return blockScope.environment().createParameterizedType(genericType, types, withWildCards.enclosingType()); - } - @Override public boolean argumentsTypeElided() { return (this.arguments.length > 0 && this.arguments[0].hasElidedType()) || this.argumentsTypeVar; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java index dd3d9c79bdb..db086b496d9 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java @@ -234,7 +234,7 @@ public static ReferenceBinding findGroundTargetType(InferenceContext18 inference LambdaExpression lambda, ParameterizedTypeBinding targetTypeWithWildCards) { if (lambda.argumentsTypeElided()) { - return lambda.findGroundTargetTypeForElidedLambda(scope, targetTypeWithWildCards); + return targetTypeWithWildCards.getNonWildcardParameterization(scope); } else { SuspendedInferenceRecord previous = inferenceContext.enterLambda(lambda); try { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index 790b6b94f71..6197892b311 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java @@ -761,21 +761,39 @@ public ReferenceBinding inferFunctionalInterfaceParameterization(LambdaExpressio { TypeBinding[] q = createBoundsForFunctionalInterfaceParameterizationInference(targetTypeWithWildCards); if (q == null || q.length != lambda.arguments().length) { - // fail TODO: can this still happen here? + return null; } else { if (reduceWithEqualityConstraints(lambda.argumentTypes(), q)) { ReferenceBinding genericType = targetTypeWithWildCards.genericType(); TypeBinding[] a = targetTypeWithWildCards.arguments; // a is not-null by construction of parameterizedWithWildcard() TypeBinding[] aprime = getFunctionInterfaceArgumentSolutions(a); - // If F is a well-formed type, ... - ParameterizedTypeBinding ptb = blockScope.environment().createParameterizedType(genericType, aprime, targetTypeWithWildCards.enclosingType()); - TypeVariableBinding[] vars = ptb.genericType().typeVariables(); - ParameterizedTypeBinding captured = ptb.capture(blockScope, lambda.sourceStart, lambda.sourceEnd); + // If F is not a well-formed type, ... + ParameterizedTypeBinding f_aprime = blockScope.environment().createParameterizedType(genericType, aprime, targetTypeWithWildCards.enclosingType()); + TypeVariableBinding[] vars = f_aprime.genericType().typeVariables(); + boolean hasWildcard = false; for (int i = 0; i < vars.length; i++) { - if (vars[i].boundCheck(captured, aprime[i], blockScope, lambda) == BoundCheckStatus.MISMATCH) - return null; + if (vars[i].boundCheck(f_aprime, aprime[i], blockScope, lambda) == BoundCheckStatus.MISMATCH) + return null; // ... no valid parameterization exists + hasWildcard |= aprime[i].kind() == Binding.WILDCARD_TYPE; + } + /* as per spec we should do the following: + * + * // or if F is not a subtype of F + * if (!f_aprime.isSubtypeOf(targetTypeWithWildCards, false)) + * return null; // ... no valid parameterization exists + * + * but that would surface as + * "The target type of this expression is not a well formed parameterized type due to bound(s) mismatch" + * whereas the ill-formed type only emerged during inference. + * So let final checks detect the incompatibility for a better error message. + */ + // ... the inferred parameterization is either F, if all the type arguments are types, + // or the non-wildcard parameterization (§9.9) of F, if one or more type arguments are still wildcards. + if (hasWildcard) { + return f_aprime.getNonWildcardParameterization(blockScope); + } else { + return f_aprime; } - return ptb; } } return targetTypeWithWildCards; @@ -792,38 +810,17 @@ TypeBinding[] createBoundsForFunctionalInterfaceParameterizationInference(Parame TypeBinding[] a = functionalInterface.arguments; if (a == null) return null; - InferenceVariable[] alpha = addInitialTypeVariableSubstitutions(a); + addInitialTypeVariableSubstitutions(a); - createAdditionalBounds(a, alpha); TypeBinding falpha = substitute(functionalInterface); return falpha.getSingleAbstractMethod(this.scope, true).parameters; } - private void createAdditionalBounds(TypeBinding[] a, InferenceVariable[] alpha) { - for (int i = 0; i < a.length; i++) { - TypeBound bound; - if (a[i].kind() == Binding.WILDCARD_TYPE) { - WildcardBinding wildcard = (WildcardBinding) a[i]; - switch(wildcard.boundKind) { - case Wildcard.EXTENDS : - bound = new TypeBound(alpha[i], wildcard.allBounds(), ReductionResult.SUBTYPE); - break; - case Wildcard.SUPER : - bound = new TypeBound(alpha[i], wildcard.bound, ReductionResult.SUPERTYPE); - break; - case Wildcard.UNBOUND : - bound = new TypeBound(alpha[i], this.object, ReductionResult.SUBTYPE); - break; - default: - continue; // cannot - } - } else { - bound = new TypeBound(alpha[i], a[i], ReductionResult.SAME); - } - this.currentBounds.addBound(bound, this.environment); - } - } - + /** + * from 18.5.3: + * Otherwise, a set of constraint formulas is formed with, for all i (1 ≤ i ≤ n), ‹Pi = Qi›. + * This constraint formula set is reduced to form the bound set B. + */ public boolean reduceWithEqualityConstraints(TypeBinding[] p, TypeBinding[] q) { if (p != null) { for (int i = 0; i < p.length; i++) { @@ -1823,6 +1820,13 @@ public static ParameterizedTypeBinding parameterizedWithWildcard(TypeBinding typ return null; } + /** + * From 18.5.3: + *
    + *
  • If B contains an instantiation (§18.1.3) for αi, T, then A'i = T. + *
  • Otherwise, A'i = Ai. + *
+ */ public TypeBinding[] getFunctionInterfaceArgumentSolutions(TypeBinding[] a) { int m = a.length; TypeBinding[] aprime = new TypeBinding[m]; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java index 6c9df545e14..df833128834 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java @@ -1692,7 +1692,7 @@ public RecordComponentBinding[] unResolvedComponents() { @Override protected MethodBinding[] getInterfaceAbstractContracts(Scope scope, boolean replaceWildcards, boolean filterDefaultMethods) throws InvalidInputException { if (replaceWildcards) { - TypeBinding[] types = getNonWildcardParameterization(scope); + TypeBinding[] types = getNonWildcardParameters(scope); if (types == null) return new MethodBinding[] { new ProblemMethodBinding(TypeConstants.ANONYMOUS_METHOD, null, ProblemReasons.NotAWellFormedParameterizedType) }; for (int i = 0; i < types.length; i++) { @@ -1732,7 +1732,7 @@ public MethodBinding getSingleAbstractMethod(final Scope scope, boolean replaceW ParameterizedTypeBinding declaringType = null; TypeBinding [] types = this.arguments; if (replaceWildcards) { - types = getNonWildcardParameterization(scope); + types = getNonWildcardParameters(scope); if (types == null) return this.singleAbstractMethod[index] = new ProblemMethodBinding(TypeConstants.ANONYMOUS_METHOD, null, ProblemReasons.NotAWellFormedParameterizedType); } else if (types == null) { @@ -1764,8 +1764,8 @@ public MethodBinding getSingleAbstractMethod(final Scope scope, boolean replaceW return this.singleAbstractMethod[index]; } - // from JLS 9.8 - public TypeBinding[] getNonWildcardParameterization(Scope scope) { + // from JLS 9.9 + public TypeBinding[] getNonWildcardParameters(Scope scope) { // precondition: isValidBinding() TypeBinding[] typeArguments = this.arguments; // A1 ... An if (typeArguments == null) @@ -1833,6 +1833,16 @@ public TypeBinding[] getNonWildcardParameterization(Scope scope) { } return types; } + + public ReferenceBinding getNonWildcardParameterization(BlockScope blockScope) { + // non-wildcard parameterization (9.9) of the target type + TypeBinding[] types = getNonWildcardParameters(blockScope); + if (types == null) + return null; + ReferenceBinding genericType = genericType(); + return blockScope.environment().createParameterizedType(genericType, types, enclosingType()); + } + @Override public long updateTagBits() { if (this.arguments != null) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java index dd9732a72c5..8e963202fec 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java @@ -2953,7 +2953,7 @@ public void testBug430296() { "2. ERROR in AnnotationCollector.java (at line 9)\n" + " return persons.collect(Collectors.toMap((Person p) -> p.getLastName(),\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from Function to Function\n" + + "Type mismatch: cannot convert from Function to Function\n" + "----------\n" + "3. ERROR in AnnotationCollector.java (at line 10)\n" + " Function::identity,\n" + diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java index 79befc0b879..a4cdf0fc934 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java @@ -8601,12 +8601,12 @@ public void test428177() { "4. ERROR in X.java (at line 36)\n" + " if(\"1\" == \"\") { return stream.collect(Collectors.toList()).stream(); // ERROR\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from Stream to Stream\n" + + "Type mismatch: cannot convert from Stream to Stream\n" + "----------\n" + "5. ERROR in X.java (at line 38)\n" + " return stream.collect(Collectors.toList()); // NO ERROR\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from List to Stream\n" + + "Type mismatch: cannot convert from List to Stream\n" + "----------\n"); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428795, - [1.8]Internal compiler error: java.lang.NullPointerException at org.eclipse.jdt.internal.compiler.ast.MessageSend.analyseCode @@ -9884,7 +9884,7 @@ public void testGroundTargetTypeWithWithWildcards() { "1. ERROR in X.java (at line 10)\n" + " return m((X x1, X x2) -> { return new Y(); });\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from I to I\n" + + "Type mismatch: cannot convert from I to I\n" + "----------\n"); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=474522, [1.8][compiler] ecj doesn't handle captured final fields correctly in lambdas