mirror of
https://github.com/apple/pkl.git
synced 2026-07-09 22:52:44 +02:00
Fix for-generator variable resolution in nested eager scope (#1706)
Fixes an issue around resolving for-generator variables in some cases
This commit is contained in:
@@ -724,7 +724,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
// elem
|
// elem
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
return p.levelsUp() == 0
|
return p.levelsUp() == 0 && !p.needsFrameSkip()
|
||||||
? ReadExactFrameSlotNodeGen.create(sourceSection, p.slot())
|
? ReadExactFrameSlotNodeGen.create(sourceSection, p.slot())
|
||||||
: ReadFrameSlotNodeGen.create(
|
: ReadFrameSlotNodeGen.create(
|
||||||
sourceSection, p.slot(), new GetEnclosingFrameNode(p.levelsUp()));
|
sourceSection, p.slot(), new GetEnclosingFrameNode(p.levelsUp()));
|
||||||
|
|||||||
@@ -509,6 +509,7 @@ public final class SymbolTable {
|
|||||||
private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) {
|
private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) {
|
||||||
var levelsUp = 0;
|
var levelsUp = 0;
|
||||||
var shouldSkip = false;
|
var shouldSkip = false;
|
||||||
|
var skippedObjectScope = false;
|
||||||
for (var scope = this; scope != null; scope = scope.getParent()) {
|
for (var scope = this; scope != null; scope = scope.getParent()) {
|
||||||
// for headers resolve variables one scope up
|
// for headers resolve variables one scope up
|
||||||
if (scope instanceof EagerGeneratorScope) {
|
if (scope instanceof EagerGeneratorScope) {
|
||||||
@@ -524,11 +525,24 @@ public final class SymbolTable {
|
|||||||
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
|
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
|
||||||
levelsUp++;
|
levelsUp++;
|
||||||
}
|
}
|
||||||
|
// An EagerGeneratorScope (for `when` predicates) skipped an ObjectScope.
|
||||||
|
// The ObjectScope may become a parse-time-invisible amend function at runtime,
|
||||||
|
// so for-gen variables at levelsUp == 0 need ReadFrameSlotNode
|
||||||
|
// (which calls skipInvisibleScopes) rather than ReadExactFrameSlotNode.
|
||||||
|
if (scope instanceof ObjectScope) {
|
||||||
|
skippedObjectScope = true;
|
||||||
|
}
|
||||||
shouldSkip = false;
|
shouldSkip = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var result = fun.apply(lex, levelsUp);
|
var result = fun.apply(lex, levelsUp);
|
||||||
if (result != null) return result;
|
if (result != null) {
|
||||||
|
if (result instanceof ForGeneratorVariableOrLetBinding p && skippedObjectScope) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (R) new ForGeneratorVariableOrLetBinding(p.slot(), p.levelsUp(), true);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
if (scope instanceof MethodScope
|
if (scope instanceof MethodScope
|
||||||
|| scope instanceof ForGeneratorScope
|
|| scope instanceof ForGeneratorScope
|
||||||
|| scope instanceof LetExpressionScope) {
|
|| scope instanceof LetExpressionScope) {
|
||||||
@@ -798,7 +812,7 @@ public final class SymbolTable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (name.equals(binding.name())) {
|
if (name.equals(binding.name())) {
|
||||||
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp);
|
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp, false);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -910,10 +924,10 @@ public final class SymbolTable {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
||||||
if (keyBinding != null && keyBinding.name().equals(name)) {
|
if (keyBinding != null && keyBinding.name().equals(name)) {
|
||||||
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp);
|
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp, false);
|
||||||
}
|
}
|
||||||
if (valueBinding != null && valueBinding.name().equals(name)) {
|
if (valueBinding != null && valueBinding.name().equals(name)) {
|
||||||
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp);
|
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp, false);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ public sealed interface VariableResolution {
|
|||||||
// method, lambda, object body param
|
// method, lambda, object body param
|
||||||
record Parameter(int slot, int levelsUp) implements VariableResolution {}
|
record Parameter(int slot, int levelsUp) implements VariableResolution {}
|
||||||
|
|
||||||
record ForGeneratorVariableOrLetBinding(int slot, int levelsUp) implements VariableResolution {}
|
record ForGeneratorVariableOrLetBinding(int slot, int levelsUp, boolean needsFrameSkip)
|
||||||
|
implements VariableResolution {}
|
||||||
|
|
||||||
// Implicit base module lookup
|
// Implicit base module lookup
|
||||||
record ImplicitBaseProperty() implements VariableResolution {}
|
record ImplicitBaseProperty() implements VariableResolution {}
|
||||||
|
|||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
class Foo {
|
||||||
|
prop: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
local foos: Mapping<String, Mixin<Foo>> = new Mapping<String, Mixin<Foo>> {
|
||||||
|
for (key, value in Map("foo", Map("key", "value"))) {
|
||||||
|
[key] = new {
|
||||||
|
when (value.keys.contains("key")) {
|
||||||
|
prop = value["key"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = foos["foo"].apply(new Foo {})
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
res {
|
||||||
|
prop = "value"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user