diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ExecuteCustomThisWithRootNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ExecuteCustomThisWithRootNode.java index 184d8aed4..15a0f6f18 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ExecuteCustomThisWithRootNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ExecuteCustomThisWithRootNode.java @@ -18,6 +18,7 @@ package org.pkl.core.ast.expression.primary; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.SimpleRootNode; @@ -43,6 +44,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode { // shouldn't be marked `@Child` because this node is actually the child of the SimpleRootNode // created in the constructor. private final ExpressionNode expressionNode; + private final FrameDescriptor frameDescriptor; + private final String qualifiedName; + private final int[] forGeneratorSlots; + private final int[] parameterSlots; public ExecuteCustomThisWithRootNode( SourceSection sourceSection, @@ -53,6 +58,10 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode { int[] parameterSlots) { super(sourceSection); this.expressionNode = expressionNode; + this.frameDescriptor = frameDescriptor; + this.qualifiedName = qualifiedName; + this.forGeneratorSlots = forGeneratorSlots; + this.parameterSlots = parameterSlots; frameDescriptor.findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID); var rootNode = new SimpleRootNode( @@ -68,6 +77,18 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode { return expressionNode; } + @Override + public Node deepCopy() { + // The expression belongs to a separate root and is not copied as a child of this node. + return new ExecuteCustomThisWithRootNode( + sourceSection, + (ExpressionNode) expressionNode.deepCopy(), + frameDescriptor, + qualifiedName, + forGeneratorSlots, + parameterSlots); + } + @Override public Object executeGeneric(VirtualFrame frame) { var customThis = customThisNode.executeGeneric(frame); 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 605087360..6bcdf429c 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 @@ -19,6 +19,7 @@ import com.google.errorprone.annotations.concurrent.GuardedBy; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.api.source.SourceSection; import java.net.URI; import java.util.ArrayList; @@ -30,6 +31,7 @@ import org.pkl.core.PObject; import org.pkl.core.TypeAlias; import org.pkl.core.TypeParameter; import org.pkl.core.ast.VmModifier; +import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode; import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode; import org.pkl.core.ast.type.TypeNode.TypeVariableNode; @@ -200,25 +202,31 @@ public final class VmTypeAlias extends VmValue { var clone = (TypeNode) typeNode.deepCopy(); clone.accept( - node -> { - if (node instanceof TypeVariableNode typeVarNode) { - var index = typeVarNode.getTypeParameterIndex(); - node.replace( - typeArgumentNodes.length == 0 - ? new UnknownTypeNode(sourceSection) - : deepCopy(typeArgumentNodes[index])); - } else if (node instanceof UnresolvedTypeNode.TypeVariable unresolvedTypeVar) { - // Type variables inside constraint expressions (e.g. `every((it) -> it is T)`) - // are still unresolved at instantiation time. Replace them with a resolved - // unresolved type node that returns the concrete type argument. - var index = unresolvedTypeVar.getTypeParameterIndex(); - node.replace( - typeArgumentNodes.length == 0 - ? new UnresolvedTypeNode.Unknown(sourceSection) - : new UnresolvedTypeNode.Resolved( - sourceSection, deepCopy(typeArgumentNodes[index]))); + new NodeVisitor() { + @Override + public boolean visit(com.oracle.truffle.api.nodes.Node node) { + if (node instanceof TypeVariableNode typeVarNode) { + var index = typeVarNode.getTypeParameterIndex(); + node.replace( + typeArgumentNodes.length == 0 + ? new UnknownTypeNode(sourceSection) + : deepCopy(typeArgumentNodes[index])); + } else if (node instanceof UnresolvedTypeNode.TypeVariable unresolvedTypeVar) { + // Type variables inside constraint expressions (e.g. `every((it) -> it is T)`) + // are still unresolved at instantiation time. Replace them with a resolved + // unresolved type node that returns the concrete type argument. + var index = unresolvedTypeVar.getTypeParameterIndex(); + node.replace( + typeArgumentNodes.length == 0 + ? new UnresolvedTypeNode.Unknown(sourceSection) + : new UnresolvedTypeNode.Resolved( + sourceSection, deepCopy(typeArgumentNodes[index]))); + } else if (node instanceof ExecuteCustomThisWithRootNode customThisNode) { + // A constraint with frame slots runs in a separate root, outside this tree. + customThisNode.getExpressionNode().accept(this); + } + return true; } - return true; }); return clone; diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint11.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint11.pkl new file mode 100644 index 000000000..b18c6afb8 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint11.pkl @@ -0,0 +1,14 @@ +import "pkl:test" + +class Foo { bar: Int } +const function isOk(f: Foo) = f.bar > 0 + +typealias WithInferredNew = Int(isOk(new { bar = 1 }) && this is X) +typealias WithLet = Int((let (n = 1) n == 1) && this is X) + +wrongType = 3 is WithInferredNew +matchingType = 3 is WithInferredNew +wrongTypeAgain = 3 is WithInferredNew +letWrongType = 3 is WithLet +letMatchingType = 3 is WithLet +castWrongType = test.catch(() -> 3 as WithInferredNew) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint11.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint11.pcf new file mode 100644 index 000000000..a470b7b2f --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint11.pcf @@ -0,0 +1,6 @@ +wrongType = false +matchingType = true +wrongTypeAgain = false +letWrongType = false +letMatchingType = true +castWrongType = "Type constraint `isOk(new { bar = 1 }) && this is X` violated. Value: 3"