diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java index 8546dbdc..48bdd2a7 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java @@ -61,6 +61,7 @@ public final class AmendFunctionNode extends PklNode { } else { parameterSlots = new int[0]; } + var hasForGenVars = false; for (var i = 0; i < hostFrameDesecriptor.getNumberOfSlots(); i++) { var slotInfo = hostFrameDesecriptor.getSlotInfo(i); // Copy for-generator variables from the outer frame descriptor into inner lambda. @@ -74,10 +75,22 @@ public final class AmendFunctionNode extends PklNode { // frame (e.g. with `new Mixin { ... }` syntax), so it injects for-generator vars into the // wrong frame. // - // As a remedy, we simply copy outer for-generator variables into this frame. + // As a remedy, we simply copy outer variables into this frame if there are any for generator + // variables. + // + // We need to preserve the frame slot index, so we insert dummy identifiers + // for other slots that aren't for generator variables. if (slotInfo != null && slotInfo.equals(SymbolTable.FOR_GENERATOR_VARIABLE)) { + if (!hasForGenVars) { + hasForGenVars = true; + for (var j = 0; j < i; j++) { + builder.addSlot(FrameSlotKind.Illegal, Identifier.DUMMY, null); + } + } builder.addSlot( hostFrameDesecriptor.getSlotKind(i), hostFrameDesecriptor.getSlotName(i), null); + } else if (hasForGenVars) { + builder.addSlot(FrameSlotKind.Illegal, Identifier.DUMMY, null); } } var objectToAmendSlot = builder.addSlot(FrameSlotKind.Object, new Object(), null); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java b/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java index 9644e87e..dac80cc4 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java @@ -152,6 +152,9 @@ public final class Identifier implements Comparable { // common in lambdas etc public static final Identifier IT = get("it"); + // dummy, unrepresentable identifier + public static final Identifier DUMMY = get("`#_"); + private final String name; private Identifier(String name) { diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInMixins.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInMixins.pkl index 5cd49c6f..c3c8d6c3 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInMixins.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInMixins.pkl @@ -38,3 +38,13 @@ function mapEnvLiteral(_env: Dynamic) = (it) -> (it) { } } } + +function addElements(keys: List): Mixin> = new { + for (key in keys) { + [key] = key + } +} + +res = new Mapping { + ["base"] = "alreadyThere" +} |> addElements(List("newElement")) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInMixins.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInMixins.pcf index 5f4b13ae..14079f19 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInMixins.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInMixins.pcf @@ -36,3 +36,7 @@ foo { } } } +res { + ["base"] = "alreadyThere" + ["newElement"] = "newElement" +}