From f7e7c255c99b34f66bc4b427eef593f7d0697bee Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Wed, 22 Jan 2025 21:56:49 +0100 Subject: [PATCH] [23] VerifyError when instantiating a local class inside a flexible constructor + more locations to treat early-constr-ctx like isConstructorCall + includes tests adapted from test for lambda-as-ctor-arg Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3115 --- .../internal/compiler/lookup/BlockScope.java | 13 +- .../internal/compiler/lookup/MethodScope.java | 27 +++- .../jdt/internal/compiler/lookup/Scope.java | 10 +- .../regression/SuperAfterStatementsTest.java | 146 ++++++++++++++++++ 4 files changed, 183 insertions(+), 13 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java index 63d7841b6aa..80802938ef3 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2024 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for @@ -132,7 +136,8 @@ public final void addLocalType(TypeDeclaration localType) { while (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) { LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext; if (!lambda.scope.isStatic && !lambda.scope.isConstructorCall) { - lambda.shouldCaptureInstance = true; + if (!isInsideEarlyConstructionContext(null, true)) + lambda.shouldCaptureInstance = true; } methodScope = methodScope.enclosingMethodScope(); } @@ -926,7 +931,9 @@ public Object[] getEmulationPath(ReferenceBinding targetEnclosingType, boolean o FieldBinding syntheticField = sourceType.getSyntheticField(targetEnclosingType, onlyExactMatch); Object[] synEAoL = currentMethodScope.getSyntheticEnclosingArgumentOfLambda(targetEnclosingType); if (syntheticField != null) { - if (currentMethodScope.isConstructorCall){ + boolean inEarlyConstructionContext = JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(compilerOptions()) + && currentMethodScope.isInsideEarlyConstructionContext(sourceType, false); + if (currentMethodScope.isConstructorCall || inEarlyConstructionContext){ return synEAoL != null ? synEAoL : BlockScope.NoEnclosingInstanceInConstructorCall; } return new Object[] { syntheticField }; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java index c49d92e345d..f9b233b9c5f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java @@ -1,5 +1,5 @@ /******************************************************************************* - * * Copyright (c) 2000, 2024 IBM Corporation and others. + * * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -8,6 +8,10 @@ * * SPDX-License-Identifier: EPL-2.0 * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for * bug 349326 - [1.7] new warning for missing try-with-resources @@ -22,6 +26,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; +import static org.eclipse.jdt.internal.compiler.impl.JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES; + import java.util.Map; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; @@ -502,8 +508,8 @@ public FieldBinding findField(TypeBinding receiverType, char[] fieldName, Invoca // hence here we only handle single name references: if (invocationSite instanceof SingleNameReference nameRef && (nameRef.bits & ASTNode.IsStrictlyAssigned) != 0 - && JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.matchesCompliance(compilerOptions())) { - problemReporter().validateJavaFeatureSupport(JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES, invocationSite.sourceStart(), invocationSite.sourceEnd()); + && FLEXIBLE_CONSTRUCTOR_BODIES.matchesCompliance(compilerOptions())) { + problemReporter().validateJavaFeatureSupport(FLEXIBLE_CONSTRUCTOR_BODIES, invocationSite.sourceStart(), invocationSite.sourceEnd()); return field; } } else { @@ -535,10 +541,17 @@ public FieldBinding findField(TypeBinding receiverType, char[] fieldName, Invoca protected Object[] getSyntheticEnclosingArgumentOfLambda(ReferenceBinding targetEnclosingType) { SyntheticArgumentBinding sa = null; - if (this.isConstructorCall && this.referenceContext instanceof LambdaExpression) { - Map stbToSynthetic = ((LambdaExpression) this.referenceContext).mapSyntheticEnclosingTypes; - if (stbToSynthetic != null) - sa = stbToSynthetic.get(targetEnclosingType); + if (this.referenceContext instanceof LambdaExpression) { + boolean isEarlyContext = this.isConstructorCall; + if (FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(compilerOptions())) { + // if the immediately enclosing class isn't fully cooked, then ALL access has to go through synth arguments + isEarlyContext |= classScope().insideEarlyConstructionContext; + } + if (isEarlyContext) { + Map stbToSynthetic = ((LambdaExpression) this.referenceContext).mapSyntheticEnclosingTypes; + if (stbToSynthetic != null) + sa = stbToSynthetic.get(targetEnclosingType); + } } return sa != null ? new Object[] {sa} : null; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java index a212439cfb0..e61418caa2c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2024 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -5664,12 +5664,16 @@ public void tagAsAccessingEnclosingInstanceStateOf(ReferenceBinding enclosingTyp methodScope = methodScope.enclosingMethodScope(); } } + boolean isFlexibleConstructorsEnabled = methodScope != null + && JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(methodScope.compilerOptions()); MethodBinding enclosingMethod = enclosingType != null ? enclosingType.enclosingMethod() : null; while (methodScope != null) { while (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) { LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext; SourceTypeBinding lambdaEnclosingType = methodScope.classScope().referenceContext.binding; - if (methodScope.isConstructorCall) { + boolean insideEarlyConstructionContext = isFlexibleConstructorsEnabled && + methodScope.isInsideEarlyConstructionContext(lambdaEnclosingType, false); + if (methodScope.isConstructorCall || insideEarlyConstructionContext) { ReferenceBinding tmp = lambdaEnclosingType; while ((tmp = tmp.enclosingType()) != null) { if (!TypeBinding.equalsEquals(enclosingType, tmp)) continue; @@ -5678,7 +5682,7 @@ public void tagAsAccessingEnclosingInstanceStateOf(ReferenceBinding enclosingTyp break; } } - if (!typeVariableAccess && !lambda.scope.isStatic && !lambda.hasOuterClassMemberReference) + if (!typeVariableAccess && !lambda.scope.isStatic && !lambda.hasOuterClassMemberReference && !insideEarlyConstructionContext) lambda.shouldCaptureInstance = true; // lambda can still be static, only when `this' is touched (implicitly or otherwise) it cannot be. methodScope = methodScope.enclosingMethodScope(); } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java index 89238d4c8dd..3da72fedfe4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java @@ -2558,4 +2558,150 @@ public static void main(String... args) { runner.expectedOutputString = "-1"; runner.runConformTest(); } + + public void testGH3115() { + Runner runner = new Runner(); + runner.testFiles = new String[] { + "X.java", + """ + public class X { + public static void main(String argv[]) { + X test = new X(); + } + X() { + class InnerLocal {} + java.util.function.IntSupplier foo = () -> { + new InnerLocal() {}; // This is trouble + return 0; + }; + super(); + } + } + """ + }; + runner.runConformTest(); + } + + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=406614, [1.8][compiler] Missing and incorrect errors for lambda in explicit constructor call. + // Variant for early construction context + public void test406614() { + this.runNegativeTest( + new String[] { + "X.java", + """ + interface I { + int doit(); + } + @SuppressWarnings("preview") + public class X { + int f; + X() { + } + X(byte b) { + I i = () -> this.f; + this(); + } + X(short s) { + I i = () -> this.g(); + this(); + } + X (int x) { + I i = () -> f; + this(); + } + X (long x) { + I i = () -> g(); + this(); + } + int g() { + return 0; + } + class Member { + Member() {} + Member(byte b) { + I i = () -> X.this.f; + this(); + } + Member(short s) { + I i = () -> X.this.g(); + this(); + } + Member(int x) { + I i = () -> f; + this(); + } + Member(long x) { + I i = () -> g(); + this(); + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " I i = () -> this.f;\n" + + " ^^^^^^\n" + + "Cannot read field f in an early construction context\n" + + "----------\n" + + "2. ERROR in X.java (at line 14)\n" + + " I i = () -> this.g();\n" + + " ^^^^\n" + + "Cannot use \'this\' in an early construction context\n" + + "----------\n" + + "3. ERROR in X.java (at line 18)\n" + + " I i = () -> f;\n" + + " ^\n" + + "Cannot read field f in an early construction context\n" + + "----------\n" + + "4. ERROR in X.java (at line 22)\n" + + " I i = () -> g();\n" + + " ^^^\n" + + "Cannot invoke method g() in an early construction context\n" + + "----------\n"); + } + + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=406614, [1.8][compiler] Missing and incorrect errors for lambda in explicit constructor call. + // Variant for early construction context - this time a lambda in early construction of a member accesses the outer this - OK. + public void test406614_member() { + this.runConformTest( + new String[] { + "X.java", + """ + interface I { + int doit(); + } + @SuppressWarnings("preview") + public class X { + int f; + int g() { + return 0; + } + class Member { + Member() {} + Member(byte b) { + I i = () -> X.this.f; + this(); + } + Member(short s) { + I i = () -> X.this.g(); + this(); + } + Member(int x) { + I i = () -> f; + this(); + } + Member(long x) { + I i = () -> g(); + this(); + } + } + public static void main(String... args) { + new X().new Member((byte)13); + } + } + """ + }, + ""); + } }