From 97c73e7c28a51802caaeefe6e75fb9b2bd7fe050 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Mon, 19 Oct 2026 12:23:38 -0700 Subject: [PATCH] Fix qualified type resolution (#1915) Fixes an issue where `GetModuleOwnerNode` resolves to levelsUp == 0, but its enclosing owner is a parse-time invisible scope. --- .../expression/primary/GetEnclosingOwnerNode.java | 2 -- .../ast/expression/primary/GetModuleOwnerNode.java | 10 ++++++++-- .../input/types/qualifiedTypeInAmendedFunction.pkl | 12 ++++++++++++ .../output/types/qualifiedTypeInAmendedFunction.pcf | 3 +++ 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/qualifiedTypeInAmendedFunction.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/qualifiedTypeInAmendedFunction.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetEnclosingOwnerNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetEnclosingOwnerNode.java index 738cdb3f9..065737527 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetEnclosingOwnerNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetEnclosingOwnerNode.java @@ -24,8 +24,6 @@ public final class GetEnclosingOwnerNode extends ExpressionNode { public GetEnclosingOwnerNode(int levelsUp) { this.levelsUp = levelsUp; - - assert levelsUp > 0 : "shouldn't be using GetEnclosingOwnerNode for levelsUp == 0"; } public Object executeGeneric(VirtualFrame frame) { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetModuleOwnerNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetModuleOwnerNode.java index fbaadd271..826d9255b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetModuleOwnerNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetModuleOwnerNode.java @@ -32,15 +32,21 @@ public final class GetModuleOwnerNode extends ExpressionNode { CompilerDirectives.transferToInterpreter(); var levelsUp = -1; + var skippedInvisibleScopes = false; for (var current = VmUtils.getOwner(frame); current != null; current = current.getEnclosingOwner()) { - if (!current.isParseTimeInvisibleScope()) { + if (current.isParseTimeInvisibleScope()) { + skippedInvisibleScopes = true; + } else { levelsUp += 1; } } - return replace(levelsUp == 0 ? new GetOwnerNode() : new GetEnclosingOwnerNode(levelsUp)) + return replace( + levelsUp == 0 && !skippedInvisibleScopes + ? new GetOwnerNode() + : new GetEnclosingOwnerNode(levelsUp)) .executeGeneric(frame); } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/qualifiedTypeInAmendedFunction.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/qualifiedTypeInAmendedFunction.pkl new file mode 100644 index 000000000..419cb23bd --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/qualifiedTypeInAmendedFunction.pkl @@ -0,0 +1,12 @@ +import "pkl:semver" + +local foo = () -> new Dynamic {} + +// The `when` predicate is evaluated with the amended function (an invisible scope) as its owner. +local bar = (foo) { + when (new semver.Version {} is semver.Version) { + x = 1 + } +} + +res = bar.apply() diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/qualifiedTypeInAmendedFunction.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/qualifiedTypeInAmendedFunction.pcf new file mode 100644 index 000000000..ba4e1ac61 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/qualifiedTypeInAmendedFunction.pcf @@ -0,0 +1,3 @@ +res { + x = 1 +}