Skip to content

Commit

Permalink
Lambda passed to explicit constructor invocation may raise Internal
Browse files Browse the repository at this point in the history
compiler error

improve outer access emulation:
+ match a synthetic lambda arg to synth. arg of enclosing constructor
+ consider lambdaScope before assuming implicit this is available

Fixes eclipse-jdt#3655
Fixes eclipse-jdt#3653
  • Loading branch information
stephan-herrmann committed Feb 2, 2025
1 parent 8ae4c8c commit 66617c5
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,7 @@ public SyntheticArgumentBinding addSyntheticArgument(ReferenceBinding enclosingT
this.outerLocalVariables[newSlot] = syntheticLocal = new SyntheticArgumentBinding(enclosingType);
syntheticLocal.resolvedPosition = this.outerLocalVariablesSlotSize; // may need adjusting later if we need to generate an instance method for the lambda.
syntheticLocal.declaringScope = this.scope;
syntheticLocal.actualOuterLocalVariable = getActualOuterLocalFromEnclosingScope(enclosingType);
int parameterCount = this.binding.parameters.length;
TypeBinding [] newParameters = new TypeBinding[parameterCount + 1];
newParameters[newSlot] = enclosingType;
Expand All @@ -1366,6 +1367,18 @@ public SyntheticArgumentBinding addSyntheticArgument(ReferenceBinding enclosingT
}
return syntheticLocal;
}

private SyntheticArgumentBinding getActualOuterLocalFromEnclosingScope(ReferenceBinding enclosingType) {
// check if access to enclosingType actually relates to a synthetic argument of an outer constructor:
MethodScope currentMethodScope = this.scope.enclosingMethodScope();
if (currentMethodScope != null && currentMethodScope.isInsideInitializerOrConstructor()) {
// use synthetic constructor arguments if possible
if (currentMethodScope.enclosingSourceType() instanceof NestedTypeBinding nested)
return nested.getSyntheticArgument(enclosingType, true, currentMethodScope.isConstructorCall);
}
return null;
}

public SyntheticArgumentBinding getSyntheticArgument(LocalVariableBinding actualOuterLocalVariable) {
for (int i = 0, length = this.outerLocalVariables == null ? 0 : this.outerLocalVariables.length; i < length; i++)
if (this.outerLocalVariables[i].actualOuterLocalVariable == actualOuterLocalVariable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,19 +828,21 @@ public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariabl
MethodScope currentMethodScope = methodScope();
SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType();

// identity check
BlockScope variableScope = outerLocalVariable.declaringScope;
if (variableScope == null /*val$this$0*/ || currentMethodScope == variableScope.methodScope()) {
return new VariableBinding[] { outerLocalVariable };
// implicit this is good enough
}
if (currentMethodScope.isLambdaScope()) {
LambdaExpression lambda = (LambdaExpression) currentMethodScope.referenceContext;
SyntheticArgumentBinding syntheticArgument;
if ((syntheticArgument = lambda.getSyntheticArgument(outerLocalVariable)) != null) {
return new VariableBinding[] { syntheticArgument };
}
}

// identity check
BlockScope variableScope = outerLocalVariable.declaringScope;
if (variableScope == null /*val$this$0*/ || currentMethodScope == variableScope.methodScope()) {
return new VariableBinding[] { outerLocalVariable };
// implicit this is good enough
}

// use synthetic constructor arguments if possible
if (currentMethodScope.isInsideInitializerOrConstructor()
&& (sourceType.isNestedType())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2929,4 +2929,66 @@ public static void main(String[] args) {
"""},
"");
}
public void testGH3655() {
runConformTest(new String[] {
"Test1.java",
"""
public class Test1 {
class Inner {
Inner() {
this(() -> new Object() { { Test1.this.print(); } });
}
Inner(Runnable r) {
r.run();
}
}
void print() {
System.out.print(3);
}
public static void main(String... args) {
new Test1().new Inner();
}
}
"""
},
"3");
}
public void testGH3653() {
runConformTest(new String[] {
"Outer.java",
"""
class Outer { // bug
interface A { }
class Inner {
Inner() {
this(() -> {
class Local {
void g() {
m();
}
}
new Object() {
void k() { new Local().g(); }
}.k();
});
}
Inner(Runnable tr) {
tr.run();
}
}
void m() {
System.out.println("Hello");
}
public static void main(String[] args) {
new Outer().new Inner();
}
}
"""
},
"Hello");
}
}

0 comments on commit 66617c5

Please sign in to comment.