From 9d385f2194e149899d2a228f912d835e3cae50ef Mon Sep 17 00:00:00 2001 From: Akshat Anand Date: Sat, 10 Jan 2026 09:29:20 +0530 Subject: [PATCH] Fix super method call inside let expression (#1383) Fixes #1309 The issue was that super calls were blocked inside let expressions because: 1. The compiler's isClassMemberScope() check didn't skip over lambda scopes created by let expressions 2. The runtime's findSupermethod() didn't traverse past VmFunction owners to find the actual class prototype Changes: - SymbolTable.java: Updated isClassMemberScope() to skip lambda scopes before checking if the parent is a class or module scope - InvokeSuperMethodNode.java: Updated findSupermethod() to skip VmFunction owners when looking for the class prototype Added regression tests covering: - Super method calls inside let expressions - Super property access inside let expressions - Nested let expressions with super calls --------- Co-authored-by: Jen Basch --- .../org/pkl/core/ast/builder/SymbolTable.java | 4 +- .../member/InvokeSuperMethodNode.java | 7 ++- .../input/classes/supercallsInLet.pkl | 44 +++++++++++++++++++ .../output/classes/supercallsInLet.pcf | 3 ++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/classes/supercallsInLet.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/classes/supercallsInLet.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java index 20c856fd..4f2eb64f 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -327,6 +327,8 @@ public final class SymbolTable { } public final boolean isClassMemberScope() { + var effectiveScope = skipLambdaScopes(); + var parent = effectiveScope.parent; if (parent == null) return false; return parent.isClassScope() diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java index 720835cc..fffc4ed5 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.member.ClassMethod; import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmFunction; import org.pkl.core.runtime.VmUtils; public abstract class InvokeSuperMethodNode extends ExpressionNode { @@ -66,6 +67,10 @@ public abstract class InvokeSuperMethodNode extends ExpressionNode { protected ClassMethod findSupermethod(VirtualFrame frame) { var owner = VmUtils.getOwner(frame); + while (owner instanceof VmFunction) { + owner = owner.getEnclosingOwner(); + } + assert owner != null : "VmFunction always has a parent"; assert owner.isPrototype(); var superclass = owner.getVmClass().getSuperclass(); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/classes/supercallsInLet.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/classes/supercallsInLet.pkl new file mode 100644 index 00000000..397ef406 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/classes/supercallsInLet.pkl @@ -0,0 +1,44 @@ +open class A { + function foo() = "a" +} + +class B extends A { + function foo() = + let (bar = "b") + super.foo() + bar +} + +local b = new B {} + +res1 = b.foo() + +// Also test with property access +open class C { + value = "c" +} + +class D extends C { + result = + let (x = "d") + super.value + x +} + +local d = new D {} + +res2 = d.result + +// Test with nested let expressions +open class E { + function getValue() = "e" +} + +class F extends E { + function getValue() = + let (x = "f") + let (y = " and " + x) + super.getValue() + y +} + +local f = new F {} + +res3 = f.getValue() diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/classes/supercallsInLet.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/classes/supercallsInLet.pcf new file mode 100644 index 00000000..f9a9460f --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/classes/supercallsInLet.pcf @@ -0,0 +1,3 @@ +res1 = "ab" +res2 = "cd" +res3 = "e and f"