Skip to content

Commit

Permalink
[23] VerifyError when instantiating a local class inside a flexible
Browse files Browse the repository at this point in the history
constructor

+ more locations to treat early-constr-ctx like isConstructorCall
+ includes tests adapted from test for lambda-as-ctor-arg

Fixes eclipse-jdt#3115
  • Loading branch information
stephan-herrmann committed Jan 22, 2025
1 parent 825b15c commit f7e7c25
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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 };
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<SourceTypeBinding,SyntheticArgumentBinding> 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<SourceTypeBinding,SyntheticArgumentBinding> stbToSynthetic = ((LambdaExpression) this.referenceContext).mapSyntheticEnclosingTypes;
if (stbToSynthetic != null)
sa = stbToSynthetic.get(targetEnclosingType);
}
}
return sa != null ? new Object[] {sa} : null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
"""
},
"");
}
}

0 comments on commit f7e7c25

Please sign in to comment.