From 08bfcd03661da7fe272f749d634fe7dfbecd23ff Mon Sep 17 00:00:00 2001 From: Jen Basch Date: Sun, 28 Jun 2026 17:41:50 -0700 Subject: [PATCH 1/3] Correctly invalidate noop typechecks during alias instantiation, fix always-lazy alias type checking --- .../java/org/pkl/core/ast/type/TypeNode.java | 81 ++++++++++++++++--- .../input/types/typeAlias4.pkl | 17 ++++ .../output/types/typeAlias4.pcf | 6 ++ 3 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias4.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias4.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 4b614d3d8..a91b2fb24 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -66,6 +66,10 @@ public interface ClassTypeNode { VmClass getVmClass(); } + private interface StatefulTypeNode { + void invalidate(); + } + protected TypeNode(SourceSection sourceSection) { super(sourceSection); } @@ -847,9 +851,9 @@ protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer co } } - public static class UnionTypeNode extends WriteFrameSlotTypeNode { + public static class UnionTypeNode extends WriteFrameSlotTypeNode implements StatefulTypeNode { @Children final TypeNode[] elementTypeNodes; - private final boolean skipElementTypeChecks; + private boolean skipElementTypeChecks; private final int defaultIndex; public UnionTypeNode( @@ -864,6 +868,15 @@ public UnionTypeNode( this.skipElementTypeChecks = skipElementTypeChecks; } + @Override + public void invalidate() { + var skipElementTypeChecks = true; + for (var i = 0; i < elementTypeNodes.length; i++) { + skipElementTypeChecks &= elementTypeNodes[i].isNoopTypeCheck(); + } + this.skipElementTypeChecks = skipElementTypeChecks; + } + @Override public VmTyped getMirror() { return MirrorFactories.unionTypeFactory.create(this); @@ -1162,7 +1175,6 @@ public static final class CollectionTypeNode extends ObjectSlotTypeNode { @Child private TypeNode elementTypeNode; public CollectionTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) { - super(sourceSection); this.elementTypeNode = elementTypeNode; } @@ -1270,9 +1282,9 @@ protected boolean isParametric() { } } - public static final class ListTypeNode extends ObjectSlotTypeNode { + public static final class ListTypeNode extends ObjectSlotTypeNode implements StatefulTypeNode { @Child private TypeNode elementTypeNode; - private final boolean skipElementTypeChecks; + private boolean skipElementTypeChecks; public ListTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) { super(sourceSection); @@ -1280,6 +1292,11 @@ public ListTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) { skipElementTypeChecks = elementTypeNode.isNoopTypeCheck(); } + @Override + public void invalidate() { + skipElementTypeChecks = elementTypeNode.isNoopTypeCheck(); + } + @Override protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) { if (visitTypeArguments) { @@ -1369,9 +1386,9 @@ protected boolean isParametric() { } } - public abstract static class SetTypeNode extends ObjectSlotTypeNode { + public abstract static class SetTypeNode extends ObjectSlotTypeNode implements StatefulTypeNode { @Child private TypeNode elementTypeNode; - private final boolean skipElementTypeChecks; + private boolean skipElementTypeChecks; protected SetTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) { super(sourceSection); @@ -1379,6 +1396,11 @@ protected SetTypeNode(SourceSection sourceSection, TypeNode elementTypeNode) { skipElementTypeChecks = elementTypeNode.isNoopTypeCheck(); } + @Override + public void invalidate() { + skipElementTypeChecks = elementTypeNode.isNoopTypeCheck(); + } + @Override public final Object createDefaultValue( VirtualFrame frame, @@ -1448,19 +1470,23 @@ protected boolean isParametric() { } } - public static final class MapTypeNode extends ObjectSlotTypeNode { + public static final class MapTypeNode extends ObjectSlotTypeNode implements StatefulTypeNode { @Child private TypeNode keyTypeNode; @Child private TypeNode valueTypeNode; - private final boolean skipEntryTypeChecks; + private boolean skipEntryTypeChecks; public MapTypeNode(SourceSection sourceSection, TypeNode keyTypeNode, TypeNode valueTypeNode) { - super(sourceSection); this.keyTypeNode = keyTypeNode; this.valueTypeNode = valueTypeNode; skipEntryTypeChecks = keyTypeNode.isNoopTypeCheck() && valueTypeNode.isNoopTypeCheck(); } + @Override + public void invalidate() { + skipEntryTypeChecks = keyTypeNode.isNoopTypeCheck() && valueTypeNode.isNoopTypeCheck(); + } + @Override protected Object executeLazily(VirtualFrame frame, Object value) { if (value instanceof VmMap vmMap) { @@ -1706,14 +1732,15 @@ protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer co } } - public abstract static class ListingOrMappingTypeNode extends ObjectSlotTypeNode { + public abstract static class ListingOrMappingTypeNode extends ObjectSlotTypeNode + implements StatefulTypeNode { private final VmLanguage language; @Child protected @Nullable TypeNode keyTypeNode; @Child protected TypeNode valueTypeNode; @Child @Nullable protected ListingOrMappingTypeCastNode valueTypeCastNode; - private final boolean skipKeyTypeChecks; - private final boolean skipValueTypeChecks; + private boolean skipKeyTypeChecks; + private boolean skipValueTypeChecks; protected ListingOrMappingTypeNode( SourceSection sourceSection, @@ -1730,6 +1757,12 @@ protected ListingOrMappingTypeNode( skipValueTypeChecks = valueTypeNode.isNoopTypeCheck(); } + @Override + public void invalidate() { + skipKeyTypeChecks = keyTypeNode == null || keyTypeNode.isNoopTypeCheck(); + skipValueTypeChecks = valueTypeNode.isNoopTypeCheck(); + } + private boolean isListing() { return keyTypeNode == null; } @@ -2762,6 +2795,13 @@ public TypeAliasTypeNode( this.typeAlias = typeAlias; this.typeArgumentNodes = typeArgumentNodes; aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes); + aliasedTypeNode.accept( + node -> { + if (node instanceof StatefulTypeNode typeNode) { + typeNode.invalidate(); + } + return true; + }); checkReferentConstraints(typeAlias); } @@ -2839,6 +2879,21 @@ protected Object executeLazily(VirtualFrame frame, Object value) { } } + @Override + public Object executeEagerly(VirtualFrame frame, Object value) { + var prevOwner = VmUtils.getOwner(frame); + var prevReceiver = VmUtils.getReceiver(frame); + setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); + setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); + + try { + return aliasedTypeNode.executeEagerly(frame, value); + } finally { + setOwner(frame, prevOwner); + setReceiver(frame, prevReceiver); + } + } + /** See docstring on {@link TypeAliasTypeNode#executeLazily}. */ @Override public Object executeAndSet(VirtualFrame frame, Object value) { diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias4.pkl new file mode 100644 index 000000000..cd88f87f7 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias4.pkl @@ -0,0 +1,17 @@ +typealias MyList = List +res1 = List(new Dynamic {}) is MyList + +typealias MySet = Set +res2 = Set(new Dynamic {}) is MySet + +typealias MyMap = Map +res3 = Map("foo", new Dynamic {}) is MyMap + +typealias MyListing = Listing +res4 = new Listing { new Dynamic {} } is MyListing + +typealias MyMapping = Mapping +res5 = new Mapping { ["foo"] = new Dynamic {} } is MyMapping + +typealias MyUnion = A | B +res6 = new Dynamic {} is MyUnion diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias4.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias4.pcf new file mode 100644 index 000000000..7236e46df --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias4.pcf @@ -0,0 +1,6 @@ +res1 = false +res2 = false +res3 = false +res4 = false +res5 = false +res6 = false From d357c3e00aa203671acd934ff07ec3ff2564c0d9 Mon Sep 17 00:00:00 2001 From: Jen Basch Date: Sun, 28 Jun 2026 15:59:05 -0700 Subject: [PATCH 2/3] Correctly handle `module` types as typealias parameters --- .../java/org/pkl/core/ast/type/TypeNode.java | 50 +++++++++++++++++-- .../org/pkl/core/runtime/VmLocalContext.java | 13 +++++ .../input-helper/types/moduleUnion1.pkl | 5 ++ .../input-helper/types/moduleUnion2.pkl | 3 ++ .../input/types/currentModuleType4.pkl | 12 +++++ .../output/types/currentModuleType4.err | 15 ++++++ 6 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion1.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index a91b2fb24..cd759e360 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -24,6 +24,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlotKind; +import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.LoopNode; @@ -469,9 +470,20 @@ protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer co } } + private abstract static class SelfTypeNode extends ObjectSlotTypeNode { + public SelfTypeNode(SourceSection sourceSection) { + super(sourceSection); + } + + protected final VirtualFrame getEffectiveFrame(VirtualFrame frame) { + var localContext = VmLanguage.get(this).localContext.get(); + var realFrame = localContext.getRealTypeAliasFrame(); + return realFrame != null ? realFrame : frame; + } + } + /** The `module` type for an open module. */ - public static final class NonFinalModuleTypeNode extends ObjectSlotTypeNode - implements ClassTypeNode { + public static final class NonFinalModuleTypeNode extends SelfTypeNode implements ClassTypeNode { private final VmClass moduleClass; // only used by getVmClass() @Child private ExpressionNode getModuleNode; @@ -483,7 +495,8 @@ public NonFinalModuleTypeNode(SourceSection sourceSection, VmClass moduleClass) @Override protected Object executeLazily(VirtualFrame frame, Object value) { - var moduleClass = ((VmTyped) getModuleNode.executeGeneric(frame)).getVmClass(); + var moduleClass = + ((VmTyped) getModuleNode.executeGeneric(getEffectiveFrame(frame))).getVmClass(); if (value instanceof VmTyped typed) { var valueClass = typed.getVmClass(); @@ -527,7 +540,8 @@ protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer co VmLanguage language, SourceSection headerSection, String qualifiedName) { - var moduleClass = ((VmTyped) getModuleNode.executeGeneric(frame)).getVmClass(); + var moduleClass = + ((VmTyped) getModuleNode.executeGeneric(getEffectiveFrame(frame))).getVmClass(); return TypeNode.createDefaultValue(moduleClass); } } @@ -2897,16 +2911,44 @@ public Object executeEagerly(VirtualFrame frame, Object value) { /** See docstring on {@link TypeAliasTypeNode#executeLazily}. */ @Override public Object executeAndSet(VirtualFrame frame, Object value) { + var localContext = VmLanguage.get(this).localContext.get(); var prevOwner = VmUtils.getOwner(frame); var prevReceiver = VmUtils.getReceiver(frame); + var prevRealFrame = localContext.getRealTypeAliasFrame(); setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); + localContext.setRealTypeAliasFrame(new FakeFrame(prevReceiver, prevOwner)); try { return aliasedTypeNode.executeAndSet(frame, value); } finally { setOwner(frame, prevOwner); setReceiver(frame, prevReceiver); + localContext.setRealTypeAliasFrame(prevRealFrame); + } + } + + private static final class FakeFrame implements VirtualFrame, MaterializedFrame { + + private final Object[] args; + + public FakeFrame(Object receiver, Object owner) { + this.args = new Object[] {receiver, owner}; + } + + @Override + public FrameDescriptor getFrameDescriptor() { + throw PklBugException.unreachableCode(); + } + + @Override + public Object[] getArguments() { + return args; + } + + @Override + public MaterializedFrame materialize() { + return this; } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java index f3f799ce8..04c204454 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java @@ -15,6 +15,9 @@ */ package org.pkl.core.runtime; +import com.oracle.truffle.api.frame.VirtualFrame; +import org.jspecify.annotations.Nullable; + /** A per-context thread-local value that can be used to influence execution. */ public class VmLocalContext { private boolean shouldEagerTypecheck = false; @@ -30,6 +33,8 @@ public class VmLocalContext { private boolean instrumentationEverUsed = false; + private @Nullable VirtualFrame realTypeAliasFrame = null; + public VmLocalContext() {} public void shouldEagerTypecheck(boolean shouldEagerTypecheck) { @@ -64,4 +69,12 @@ public boolean hasActiveTracker() { public boolean isInstrumentationEverUsed() { return instrumentationEverUsed; } + + public @Nullable VirtualFrame getRealTypeAliasFrame() { + return realTypeAliasFrame; + } + + public void setRealTypeAliasFrame(@Nullable VirtualFrame realTypeAliasFrame) { + this.realTypeAliasFrame = realTypeAliasFrame; + } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion1.pkl new file mode 100644 index 000000000..a3eb34de4 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion1.pkl @@ -0,0 +1,5 @@ +open module moduleUnion1 + +import "moduleUnion2.pkl" + +typealias ModuleUnion = moduleUnion2.ModuleUnion diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion2.pkl new file mode 100644 index 000000000..5c9171afd --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/moduleUnion2.pkl @@ -0,0 +1,3 @@ +open module moduleUnion2 + +typealias ModuleUnion = T | U | module diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl new file mode 100644 index 000000000..abb5c1864 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl @@ -0,0 +1,12 @@ +open module currentModuleType4 + +import ".../input-helper/types/moduleUnion1.pkl" +import ".../input-helper/types/moduleUnion2.pkl" + +// test that `module` type resolves to the module where it's used + +typealias ModuleUnion = moduleUnion1.ModuleUnion + +res1 = (module as ModuleUnion).res2 +res2 = moduleUnion1 as ModuleUnion +res3 = moduleUnion2 as ModuleUnion diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err new file mode 100644 index 000000000..5c139be47 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err @@ -0,0 +1,15 @@ +–– Pkl Error –– +Expected value of type `T | U | module`, but got type `currentModuleType4`. +Value: new ModuleClass { res1 = ?; res2 = ?; res3 = ? } + +x | typealias ModuleUnion = T | U | module + ^^^^^^^^^^^^^^ +at currentModuleType4#res1 (file:///$snippetsDir/input-helper/types/moduleUnion2.pkl) + +xxx | renderer.renderDocument(value) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +at pkl.base#Module.output.text (pkl:base) + +xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8") + ^^^^ +at pkl.base#Module.output.bytes (pkl:base) From 517b3938c469a4af919de17942cca76b7c55be58 Mon Sep 17 00:00:00 2001 From: Jen Basch Date: Mon, 29 Jun 2026 20:10:16 -0700 Subject: [PATCH 3/3] yucky --- .../java/org/pkl/core/ast/type/TypeNode.java | 39 ++++++++++++++++--- .../org/pkl/core/runtime/VmLocalContext.java | 17 +++++--- .../org/pkl/core/runtime/VmTypeAlias.java | 17 ++++++-- .../input/types/currentModuleType4.pkl | 3 +- .../output/types/currentModuleType4.err | 15 ------- .../output/types/currentModuleType4.pcf | 4 ++ 6 files changed, 63 insertions(+), 32 deletions(-) delete mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index cd759e360..b34c13a07 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -470,15 +470,37 @@ protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer co } } - private abstract static class SelfTypeNode extends ObjectSlotTypeNode { + public abstract static class SelfTypeNode extends ObjectSlotTypeNode { + private @Nullable VmTypeAlias originalAnchor; + public SelfTypeNode(SourceSection sourceSection) { super(sourceSection); } + public void setOriginalAnchor(VmTypeAlias originalAnchor) { + if (this.originalAnchor == null) { + this.originalAnchor = originalAnchor; + } + } + protected final VirtualFrame getEffectiveFrame(VirtualFrame frame) { + if (originalAnchor == null) return frame; + + var levelsUp = -1; + for (var node = getParent(); node != null; node = node.getParent()) { + if (node instanceof TypeAliasTypeNode typeAliasTypeNode) { + levelsUp++; + if (typeAliasTypeNode.getTypeAlias() == originalAnchor) { + break; + } + } + } + if (levelsUp == -1) { + return frame; + } + var localContext = VmLanguage.get(this).localContext.get(); - var realFrame = localContext.getRealTypeAliasFrame(); - return realFrame != null ? realFrame : frame; + return localContext.getRealTypeAliasFrame(levelsUp); } } @@ -2880,31 +2902,37 @@ public VmList getTypeArgumentMirrors() { * where the typealias was declared, so that we preserve its original scope. */ protected Object executeLazily(VirtualFrame frame, Object value) { + var localContext = VmLanguage.get(this).localContext.get(); var prevOwner = VmUtils.getOwner(frame); var prevReceiver = VmUtils.getReceiver(frame); setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); + localContext.pushRealTypeAliasFrame(new FakeFrame(prevReceiver, prevOwner)); try { return aliasedTypeNode.executeLazily(frame, value); } finally { setOwner(frame, prevOwner); setReceiver(frame, prevReceiver); + localContext.popRealTypeAliasFrame(); } } @Override public Object executeEagerly(VirtualFrame frame, Object value) { + var localContext = VmLanguage.get(this).localContext.get(); var prevOwner = VmUtils.getOwner(frame); var prevReceiver = VmUtils.getReceiver(frame); setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); + localContext.pushRealTypeAliasFrame(new FakeFrame(prevReceiver, prevOwner)); try { return aliasedTypeNode.executeEagerly(frame, value); } finally { setOwner(frame, prevOwner); setReceiver(frame, prevReceiver); + localContext.popRealTypeAliasFrame(); } } @@ -2914,17 +2942,16 @@ public Object executeAndSet(VirtualFrame frame, Object value) { var localContext = VmLanguage.get(this).localContext.get(); var prevOwner = VmUtils.getOwner(frame); var prevReceiver = VmUtils.getReceiver(frame); - var prevRealFrame = localContext.getRealTypeAliasFrame(); setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); - localContext.setRealTypeAliasFrame(new FakeFrame(prevReceiver, prevOwner)); + localContext.pushRealTypeAliasFrame(new FakeFrame(prevReceiver, prevOwner)); try { return aliasedTypeNode.executeAndSet(frame, value); } finally { setOwner(frame, prevOwner); setReceiver(frame, prevReceiver); - localContext.setRealTypeAliasFrame(prevRealFrame); + localContext.popRealTypeAliasFrame(); } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java index 04c204454..ae494151d 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmLocalContext.java @@ -16,7 +16,8 @@ package org.pkl.core.runtime; import com.oracle.truffle.api.frame.VirtualFrame; -import org.jspecify.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; /** A per-context thread-local value that can be used to influence execution. */ public class VmLocalContext { @@ -33,7 +34,7 @@ public class VmLocalContext { private boolean instrumentationEverUsed = false; - private @Nullable VirtualFrame realTypeAliasFrame = null; + private List realTypeAliasFrames = new ArrayList<>(); public VmLocalContext() {} @@ -70,11 +71,15 @@ public boolean isInstrumentationEverUsed() { return instrumentationEverUsed; } - public @Nullable VirtualFrame getRealTypeAliasFrame() { - return realTypeAliasFrame; + public VirtualFrame getRealTypeAliasFrame(int index) { + return realTypeAliasFrames.get(realTypeAliasFrames.size() - 1 - index); } - public void setRealTypeAliasFrame(@Nullable VirtualFrame realTypeAliasFrame) { - this.realTypeAliasFrame = realTypeAliasFrame; + public void pushRealTypeAliasFrame(VirtualFrame frame) { + realTypeAliasFrames.add(frame); + } + + public void popRealTypeAliasFrame() { + realTypeAliasFrames.remove(realTypeAliasFrames.size() - 1); } } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java index 208b042bd..0c7683443 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmTypeAlias.java @@ -32,6 +32,7 @@ import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode; +import org.pkl.core.ast.type.TypeNode.SelfTypeNode; import org.pkl.core.ast.type.TypeNode.TypeVariableNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; @@ -192,10 +193,18 @@ public TypeNode instantiate(TypeNode[] typeArgumentNodes) { int index = typeVarNode.getTypeParameterIndex(); // should not need to clone type argument node because it is not used by its original // root node - node.replace( - typeArgumentNodes.length == 0 - ? new UnknownTypeNode(sourceSection) - : typeArgumentNodes[index]); + if (typeArgumentNodes.length == 0) { + node.replace(new UnknownTypeNode(sourceSection)); + } else { + node.replace(typeArgumentNodes[index]) + .accept( + n -> { + if (n instanceof SelfTypeNode selfTypeNode) { + selfTypeNode.setOriginalAnchor(this); + } + return true; + }); + } } return true; }); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl index abb5c1864..af6d65a1c 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/currentModuleType4.pkl @@ -7,6 +7,7 @@ import ".../input-helper/types/moduleUnion2.pkl" typealias ModuleUnion = moduleUnion1.ModuleUnion -res1 = (module as ModuleUnion).res2 +x = 1 +res1 = (module as ModuleUnion).x res2 = moduleUnion1 as ModuleUnion res3 = moduleUnion2 as ModuleUnion diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err deleted file mode 100644 index 5c139be47..000000000 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.err +++ /dev/null @@ -1,15 +0,0 @@ -–– Pkl Error –– -Expected value of type `T | U | module`, but got type `currentModuleType4`. -Value: new ModuleClass { res1 = ?; res2 = ?; res3 = ? } - -x | typealias ModuleUnion = T | U | module - ^^^^^^^^^^^^^^ -at currentModuleType4#res1 (file:///$snippetsDir/input-helper/types/moduleUnion2.pkl) - -xxx | renderer.renderDocument(value) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -at pkl.base#Module.output.text (pkl:base) - -xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8") - ^^^^ -at pkl.base#Module.output.bytes (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.pcf new file mode 100644 index 000000000..b7948f2c2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/currentModuleType4.pcf @@ -0,0 +1,4 @@ +x = 1 +res1 = 1 +res2 {} +res3 {}