From a55a3d7c3376ad89e9a5e4b215b5c8a21a629d14 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Thu, 30 Jul 2026 10:22:45 -0700 Subject: [PATCH] Improve evaluation of typealiases (#1796) This simplifies the resolution of names in typealias constraints. This removes the existing logic around swapping out the frame's owner/receiver, and instead favors resolving the variable at parse time. * During variable resolution, create read variable nodes that read off of a receiver node * Introduce `GetTypeAliasModuleNode` that provides the typealias's enclosing module * Inject the enclosing module during typealias instantiation --- .../org/pkl/core/ast/builder/AstBuilder.java | 63 ++++++++++++++++--- .../org/pkl/core/ast/builder/SymbolTable.java | 23 ++++--- .../AbstractInvokeLexicalMethodNode.java | 45 +++++++++++++ ...ode.java => AbstractInvokeMethodNode.java} | 30 ++++----- .../AbstractInvokeQualifiedMethodNode.java | 43 +++++++++++++ ...ava => AbstractReadLocalPropertyNode.java} | 47 +++----------- ...java => InvokeLexicalClassMethodNode.java} | 6 +- ...ava => InvokeLexicalObjectMethodNode.java} | 7 ++- .../InvokeQualifiedClassMethodNode.java | 50 +++++++++++++++ .../InvokeQualifiedObjectMethodNode.java | 51 +++++++++++++++ .../ReadAmbiguousLocalityPropertyNode.java | 2 +- .../member/ReadLexicalLocalPropertyNode.java | 55 ++++++++++++++++ .../ReadQualifiedLocalPropertyNode.java | 53 ++++++++++++++++ .../primary/GetTypeAliasModuleNode.java | 52 +++++++++++++++ .../pkl/core/ast/member/TypeAliasNode.java | 11 +++- .../java/org/pkl/core/ast/type/TypeNode.java | 61 +----------------- .../input-helper/types/idType.pkl | 1 + .../input-helper/types/typeAliasAmendBase.pkl | 1 + .../types/typeAliasAmendModule.pkl | 9 +++ .../types/typeAliasClosedModule.pkl | 3 + .../types/typeAliasLocalMethod.pkl | 3 + .../input-helper/types/typeAliasModule.pkl | 1 + .../types/typeAliasOpenModule.pkl | 5 ++ .../input-helper/types/typealias1.pkl | 3 + .../input-helper/types/typealias2.pkl | 5 ++ .../input/types/typeAlias5.pkl | 8 +++ .../input/types/typeAlias6.pkl | 3 + .../input/types/typeAliasConstraint4.pkl | 3 + .../input/types/typeAliasConstraint5.pkl | 5 ++ .../input/types/typeAliasConstraint6.pkl | 5 ++ .../input/types/typeAliasConstraint7.pkl | 5 ++ .../input/types/typeAliasConstraint8.pkl | 5 ++ .../output/types/typeAlias5.pcf | 1 + .../output/types/typeAlias6.err | 15 +++++ .../output/types/typeAliasConstraint4.err | 23 +++++++ .../output/types/typeAliasConstraint5.err | 23 +++++++ .../output/types/typeAliasConstraint6.err | 23 +++++++ .../output/types/typeAliasConstraint7.err | 23 +++++++ .../output/types/typeAliasConstraint8.pcf | 1 + 39 files changed, 634 insertions(+), 139 deletions(-) create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalMethodNode.java rename pkl-core/src/main/java/org/pkl/core/ast/expression/member/{AbstractInvokeMethodLexicalNode.java => AbstractInvokeMethodNode.java} (79%) create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeQualifiedMethodNode.java rename pkl-core/src/main/java/org/pkl/core/ast/expression/member/{ReadLocalPropertyNode.java => AbstractReadLocalPropertyNode.java} (56%) rename pkl-core/src/main/java/org/pkl/core/ast/expression/member/{InvokeClassMethodNode.java => InvokeLexicalClassMethodNode.java} (90%) rename pkl-core/src/main/java/org/pkl/core/ast/expression/member/{InvokeObjectMethodNode.java => InvokeLexicalObjectMethodNode.java} (88%) create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedClassMethodNode.java create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedObjectMethodNode.java create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLexicalLocalPropertyNode.java create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadQualifiedLocalPropertyNode.java create mode 100644 pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetTypeAliasModuleNode.java create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/idType.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendBase.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendModule.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasClosedModule.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasLocalMethod.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasModule.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasOpenModule.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias1.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias5.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias6.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint4.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint5.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint6.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint7.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint8.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias5.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias6.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint4.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint5.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint6.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint7.err create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint8.pcf diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java index e6294a8e3..22836aaa9 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java @@ -112,14 +112,17 @@ import org.pkl.core.ast.expression.literal.TrueLiteralNode; import org.pkl.core.ast.expression.member.InferParentWithinMethodNode; import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNode; import org.pkl.core.ast.expression.member.InferParentWithinPropertyNodeGen; -import org.pkl.core.ast.expression.member.InvokeClassMethodNode; +import org.pkl.core.ast.expression.member.InvokeLexicalClassMethodNode; +import org.pkl.core.ast.expression.member.InvokeLexicalObjectMethodNode; import org.pkl.core.ast.expression.member.InvokeMethodDirectNode; import org.pkl.core.ast.expression.member.InvokeMethodVirtualNodeGen; -import org.pkl.core.ast.expression.member.InvokeObjectMethodNode; +import org.pkl.core.ast.expression.member.InvokeQualifiedClassMethodNode; +import org.pkl.core.ast.expression.member.InvokeQualifiedObjectMethodNode; import org.pkl.core.ast.expression.member.InvokeSuperMethodNodeGen; import org.pkl.core.ast.expression.member.ReadAmbiguousLocalityPropertyNode; -import org.pkl.core.ast.expression.member.ReadLocalPropertyNode; +import org.pkl.core.ast.expression.member.ReadLexicalLocalPropertyNode; import org.pkl.core.ast.expression.member.ReadPropertyNodeGen; +import org.pkl.core.ast.expression.member.ReadQualifiedLocalPropertyNode; import org.pkl.core.ast.expression.member.ReadSuperEntryNode; import org.pkl.core.ast.expression.member.ReadSuperPropertyNode; import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode; @@ -128,6 +131,7 @@ import org.pkl.core.ast.expression.primary.GetMemberKeyNode; import org.pkl.core.ast.expression.primary.GetModuleNode; import org.pkl.core.ast.expression.primary.GetOwnerNode; import org.pkl.core.ast.expression.primary.GetReceiverNode; +import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode; import org.pkl.core.ast.expression.primary.OuterNode; import org.pkl.core.ast.expression.primary.ThisNode; import org.pkl.core.ast.expression.ternary.IfElseNode; @@ -526,7 +530,7 @@ public class AstBuilder extends AbstractAstBuilder { } @Override - public GetModuleNode visitModuleExpr(ModuleExpr expr) { + public ExpressionNode visitModuleExpr(ModuleExpr expr) { var currentScope = symbolTable.getCurrentScope(); // cannot use unqualified `module` in a const context if (currentScope.getConstLevel().isConst() && !(expr.parent() instanceof QualifiedAccessExpr)) { @@ -549,7 +553,9 @@ public class AstBuilder extends AbstractAstBuilder { .withSourceSection(createSourceSection(expr)) .build(); } - return new GetModuleNode(createSourceSection(expr)); + return symbolTable.isInTypeAliasScope + ? new GetTypeAliasModuleNode(createSourceSection(expr)) + : new GetModuleNode(createSourceSection(expr)); } @Override @@ -687,12 +693,30 @@ public class AstBuilder extends AbstractAstBuilder { case MODULE -> p.isModuleScope(); case ALL -> p.levelsUp() > constDepth; }; + // Assumption: typealiases can only be declared on the module. + // If we ever allow typealiases in classes, this code needs to change. + if (symbolTable.isInTypeAliasScope && p.isModuleScope()) { + var getModuleNode = new GetTypeAliasModuleNode(sourceSection); + if (p.isLocal()) { + return new ReadQualifiedLocalPropertyNode( + sourceSection, + org.pkl.core.runtime.Identifier.localProperty(name), + needsConst, + getModuleNode); + } + return ReadPropertyNodeGen.create( + sourceSection, + org.pkl.core.runtime.Identifier.get(name), + MemberLookupMode.IMPLICIT_LEXICAL, + needsConst, + getModuleNode); + } if (p.isAmbiguousLocality()) { return new ReadAmbiguousLocalityPropertyNode( sourceSection, org.pkl.core.runtime.Identifier.get(name), p.levelsUp(), needsConst); } if (p.isLocal()) { - return new ReadLocalPropertyNode( + return new ReadLexicalLocalPropertyNode( sourceSection, org.pkl.core.runtime.Identifier.localProperty(name), p.levelsUp(), @@ -769,11 +793,34 @@ public class AstBuilder extends AbstractAstBuilder { case MODULE -> method.isModuleScope(); case ALL -> method.levelsUp() > constDepth; }; + // Assumption: typealiases can only be declared on the module. + // If we ever allow typealiases in classes, this code needs to change. + if (symbolTable.isInTypeAliasScope && method.isModuleScope()) { + var getModuleNode = new GetTypeAliasModuleNode(sourceSection); + if (method.isObjectMethod()) { + return new InvokeQualifiedObjectMethodNode( + sourceSection, identifier, args, needsConst, getModuleNode); + } + if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) { + return new InvokeQualifiedClassMethodNode( + sourceSection, identifier, args, needsConst, getModuleNode); + } + return InvokeMethodVirtualNodeGen.create( + sourceSection, + identifier, + args, + MemberLookupMode.IMPLICIT_LEXICAL, + needsConst, + getModuleNode, + GetClassNodeGen.create(null)); + } if (method.isObjectMethod()) { - return new InvokeObjectMethodNode(sourceSection, identifier, levelsUp, args, needsConst); + return new InvokeLexicalObjectMethodNode( + sourceSection, identifier, levelsUp, args, needsConst); } if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) { - return new InvokeClassMethodNode(sourceSection, identifier, levelsUp, args, needsConst); + return new InvokeLexicalClassMethodNode( + sourceSection, identifier, levelsUp, args, needsConst); } return InvokeMethodVirtualNodeGen.create( sourceSection, diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java index fe39738fe..c502469d1 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java @@ -47,6 +47,8 @@ import org.pkl.parser.Lexer; public final class SymbolTable { private Scope currentScope; + // consider having each scope keep track of this individually rather than set on SymbolTable. + public boolean isInTypeAliasScope; public SymbolTable(ModuleInfo moduleInfo, boolean isBaseModule) { currentScope = new ModuleScope(moduleInfo, isBaseModule); @@ -85,14 +87,19 @@ public final class SymbolTable { Identifier name, List typeParameters, Function nodeFactory) { - return doEnter( - new TypeAliasScope( - currentScope, - name, - toQualifiedName(name), - new FrameDescriptorBuilder(), - typeParameters), - nodeFactory); + try { + this.isInTypeAliasScope = true; + return doEnter( + new TypeAliasScope( + currentScope, + name, + toQualifiedName(name), + new FrameDescriptorBuilder(), + typeParameters), + nodeFactory); + } finally { + this.isInTypeAliasScope = false; + } } public T enterMethod( diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalMethodNode.java new file mode 100644 index 000000000..bb019be8e --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalMethodNode.java @@ -0,0 +1,45 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmUtils; + +public abstract sealed class AbstractInvokeLexicalMethodNode extends AbstractInvokeMethodNode + permits InvokeLexicalClassMethodNode, InvokeLexicalObjectMethodNode { + private final int levelsUp; + + public AbstractInvokeLexicalMethodNode( + SourceSection sourceSection, + Identifier methodName, + int levelsUp, + ExpressionNode[] argumentNodes, + boolean needsConst) { + super(sourceSection, methodName, argumentNodes, needsConst); + this.levelsUp = levelsUp; + } + + @Override + public final Object executeGeneric(VirtualFrame frame) { + var capturedFrame = VmUtils.getFrame(frame, levelsUp); + var owner = VmUtils.getOwner(capturedFrame); + var receiver = VmUtils.getReceiver(capturedFrame); + return invoke(frame, owner, receiver); + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodLexicalNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java similarity index 79% rename from pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodLexicalNode.java rename to pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java index 5eaabbae4..a30432eda 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodLexicalNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java @@ -26,40 +26,40 @@ import org.jspecify.annotations.Nullable; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.VmObjectLike; -import org.pkl.core.runtime.VmUtils; -public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionNode - permits InvokeObjectMethodNode, InvokeClassMethodNode { +/** + * A non-virtual (statically dispatched) method call. + * + *

Subclasses differ only in how they obtain the {@code owner}/{@code receiver} that the method + * is invoked on: either by walking the frame chain ({@link InvokeLexicalClassMethodNode}, {@link + * InvokeLexicalObjectMethodNode}), or off of an explicit receiver expression ({@link + * InvokeQualifiedClassMethodNode}, {@link InvokeQualifiedObjectMethodNode}). + */ +public abstract sealed class AbstractInvokeMethodNode extends ExpressionNode + permits AbstractInvokeQualifiedMethodNode, AbstractInvokeLexicalMethodNode { protected final Identifier methodName; - protected final int levelsUp; private final boolean needsConst; @Children private ExpressionNode[] argumentNodes; @Child private @Nullable DirectCallNode callNode; @CompilationFinal protected boolean isConstChecked; - protected AbstractInvokeMethodLexicalNode( + protected AbstractInvokeMethodNode( SourceSection sourceSection, Identifier methodName, - int levelsUp, ExpressionNode[] argumentNodes, boolean needsConst) { super(sourceSection); this.methodName = methodName; - this.levelsUp = levelsUp; this.argumentNodes = argumentNodes; this.needsConst = needsConst; this.isConstChecked = false; } - @Override @ExplodeLoop - public final Object executeGeneric(VirtualFrame frame) { - var args = new Object[2 + argumentNodes.length]; - var capturedFrame = VmUtils.getFrame(frame, levelsUp); - var owner = VmUtils.getOwner(capturedFrame); - var receiver = VmUtils.getReceiver(capturedFrame); + protected final Object invoke(VirtualFrame frame, VmObjectLike owner, Object receiver) { checkConst(owner); + var args = new Object[2 + argumentNodes.length]; args[0] = receiver; args[1] = owner; for (var i = 0; i < argumentNodes.length; i++) { @@ -81,10 +81,6 @@ public abstract sealed class AbstractInvokeMethodLexicalNode extends ExpressionN protected abstract void doCheckConst(VmObjectLike owner); - protected final VmObjectLike getOwner(VirtualFrame frame) { - return VmUtils.getOwner(frame, levelsUp); - } - protected DirectCallNode getCallNode(VmObjectLike owner) { if (callNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeQualifiedMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeQualifiedMethodNode.java new file mode 100644 index 000000000..4fb3694e4 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeQualifiedMethodNode.java @@ -0,0 +1,43 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmTyped; + +public abstract sealed class AbstractInvokeQualifiedMethodNode extends AbstractInvokeMethodNode + permits InvokeQualifiedClassMethodNode, InvokeQualifiedObjectMethodNode { + @Child private ExpressionNode getReceiverNode; + + protected AbstractInvokeQualifiedMethodNode( + SourceSection sourceSection, + Identifier methodName, + ExpressionNode[] argumentNodes, + boolean needsConst, + ExpressionNode getReceiverNode) { + super(sourceSection, methodName, argumentNodes, needsConst); + this.getReceiverNode = getReceiverNode; + } + + @Override + public final Object executeGeneric(VirtualFrame frame) { + var receiver = (VmTyped) getReceiverNode.executeGeneric(frame); + return invoke(frame, receiver, receiver); + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLocalPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractReadLocalPropertyNode.java similarity index 56% rename from pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLocalPropertyNode.java rename to pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractReadLocalPropertyNode.java index 9cfa4e1cd..b25eb9bb9 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLocalPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractReadLocalPropertyNode.java @@ -1,5 +1,5 @@ /* - * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,69 +15,38 @@ */ package org.pkl.core.ast.expression.member; -import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; -import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.source.SourceSection; import org.jspecify.annotations.Nullable; -import org.pkl.core.PklBugException; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.VmObjectLike; -import org.pkl.core.runtime.VmUtils; -/** Reads a local non-constant property that is known to exist in the lexical scope of this node. */ -public final class ReadLocalPropertyNode extends ExpressionNode { +public abstract class AbstractReadLocalPropertyNode extends ExpressionNode { + private final Identifier name; - private final int levelsUp; private final boolean needsConst; @Child private @Nullable DirectCallNode callNode; @CompilationFinal @Nullable private ObjectMember property; - public ReadLocalPropertyNode( - SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { - + public AbstractReadLocalPropertyNode( + SourceSection sourceSection, Identifier name, boolean needsConst) { super(sourceSection); - CompilerAsserts.neverPartOfCompilation(); - this.name = name; - this.levelsUp = levelsUp; this.needsConst = needsConst; } - @Override - @ExplodeLoop - public Object executeGeneric(VirtualFrame frame) { - var owner = VmUtils.getOwner(frame, levelsUp); - var property = getProperty(owner); - var constantValue = property.getConstantValue(); - if (constantValue != null) { - return constantValue; - } - - var receiver = (VmObjectLike) VmUtils.getReceiver(frame, levelsUp); - var result = receiver.getCachedValue(property); - - if (result == null) { - result = getCallNode(property).call(receiver, owner, property.getName()); - receiver.setCachedValue(property, result); - } - - return result; - } - - private ObjectMember getProperty(VmObjectLike owner) { + protected ObjectMember getProperty(VmObjectLike owner) { if (property == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); property = owner.getMember(name); if (property == null) { // should never happen CompilerDirectives.transferToInterpreter(); - throw new PklBugException("Couldn't find local variable `" + name + "`."); + throw exceptionBuilder().bug("Couldn't find local variable `" + name + "`.").build(); } if (needsConst && !property.isConst()) { throw exceptionBuilder().evalError("propertyMustBeConst", name.toString()).build(); @@ -86,7 +55,7 @@ public final class ReadLocalPropertyNode extends ExpressionNode { return property; } - public DirectCallNode getCallNode(ObjectMember property) { + protected DirectCallNode getCallNode(ObjectMember property) { if (callNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); callNode = DirectCallNode.create(property.getCallTarget()); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeClassMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java similarity index 90% rename from pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeClassMethodNode.java rename to pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java index d18831702..32e0480c6 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeClassMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java @@ -24,11 +24,9 @@ import org.pkl.core.runtime.VmObjectLike; /** * A non-virtual call of closed methods (methods whose enclosing class/module is not open nor * abstract, and is lexically scoped). - * - *

For local methods, use {@link InvokeObjectMethodNode}. */ -public final class InvokeClassMethodNode extends AbstractInvokeMethodLexicalNode { - public InvokeClassMethodNode( +public final class InvokeLexicalClassMethodNode extends AbstractInvokeLexicalMethodNode { + public InvokeLexicalClassMethodNode( SourceSection sourceSection, Identifier methodName, int levelsUp, diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeObjectMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java similarity index 88% rename from pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeObjectMethodNode.java rename to pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java index 8537881d6..5040d8c0b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeObjectMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java @@ -22,9 +22,9 @@ import org.pkl.core.ast.VmModifier; import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.VmObjectLike; -/** A non-virtual call of a local method. */ -public final class InvokeObjectMethodNode extends AbstractInvokeMethodLexicalNode { - public InvokeObjectMethodNode( +/** A non-virtual call of an object method that is lexically scoped. */ +public final class InvokeLexicalObjectMethodNode extends AbstractInvokeLexicalMethodNode { + public InvokeLexicalObjectMethodNode( SourceSection sourceSection, Identifier methodName, int levelsUp, @@ -33,6 +33,7 @@ public final class InvokeObjectMethodNode extends AbstractInvokeMethodLexicalNod super(sourceSection, methodName, levelsUp, argumentNodes, needsConst); } + @Override protected void doCheckConst(VmObjectLike owner) { var member = owner.getMember(methodName); assert member != null; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedClassMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedClassMethodNode.java new file mode 100644 index 000000000..e739072e7 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedClassMethodNode.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmObjectLike; + +/** A non-virtual call of a closed class method, invoked off of an explicit receiver. */ +public final class InvokeQualifiedClassMethodNode extends AbstractInvokeQualifiedMethodNode { + public InvokeQualifiedClassMethodNode( + SourceSection sourceSection, + Identifier methodName, + ExpressionNode[] argumentNodes, + boolean needsConst, + ExpressionNode getReceiverNode) { + super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode); + } + + @Override + protected void doCheckConst(VmObjectLike owner) { + var method = owner.getVmClass().getDeclaredMethod(methodName); + assert method != null; + if (!method.isConst()) { + throw exceptionBuilder().evalError("methodMustBeConst", methodName).build(); + } + } + + @Override + protected CallTarget getCallTarget(VmObjectLike owner) { + var method = owner.getVmClass().getDeclaredMethod(methodName); + assert method != null; + return method.getCallTarget(getSourceSection()); + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedObjectMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedObjectMethodNode.java new file mode 100644 index 000000000..e31849907 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeQualifiedObjectMethodNode.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.ast.VmModifier; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmObjectLike; + +/** A non-virtual call of an object method, invoked off of an explicit receiver. */ +public final class InvokeQualifiedObjectMethodNode extends AbstractInvokeQualifiedMethodNode { + public InvokeQualifiedObjectMethodNode( + SourceSection sourceSection, + Identifier methodName, + ExpressionNode[] argumentNodes, + boolean needsConst, + ExpressionNode getReceiverNode) { + super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode); + } + + @Override + protected void doCheckConst(VmObjectLike owner) { + var member = owner.getMember(methodName); + assert member != null; + if (!VmModifier.isConst(member.getModifiers())) { + throw exceptionBuilder().evalError("methodMustBeConst", methodName).build(); + } + } + + @Override + protected CallTarget getCallTarget(VmObjectLike owner) { + var method = owner.getMember(methodName); + assert method != null && method.isLocal(); + return (CallTarget) method.getCallTarget().call(owner, owner); + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadAmbiguousLocalityPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadAmbiguousLocalityPropertyNode.java index e51d82cdc..2408ffc7c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadAmbiguousLocalityPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadAmbiguousLocalityPropertyNode.java @@ -67,7 +67,7 @@ public final class ReadAmbiguousLocalityPropertyNode extends ExpressionNode { CompilerDirectives.transferToInterpreterAndInvalidate(); readLocalPropertyNode = insert( - new ReadLocalPropertyNode( + new ReadLexicalLocalPropertyNode( sourceSection, name.toLocalProperty(), levelsUp, needsConst)); } return readLocalPropertyNode; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLexicalLocalPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLexicalLocalPropertyNode.java new file mode 100644 index 000000000..be815cdb4 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadLexicalLocalPropertyNode.java @@ -0,0 +1,55 @@ +/* + * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmObjectLike; +import org.pkl.core.runtime.VmUtils; + +/** Reads a local non-constant property that is known to exist in the lexical scope of this node. */ +public final class ReadLexicalLocalPropertyNode extends AbstractReadLocalPropertyNode { + private final int levelsUp; + + public ReadLexicalLocalPropertyNode( + SourceSection sourceSection, Identifier name, int levelsUp, boolean needsConst) { + super(sourceSection, name, needsConst); + CompilerAsserts.neverPartOfCompilation(); + this.levelsUp = levelsUp; + } + + @Override + @ExplodeLoop + public Object executeGeneric(VirtualFrame frame) { + var owner = VmUtils.getOwner(frame, levelsUp); + var property = getProperty(owner); + var constantValue = property.getConstantValue(); + if (constantValue != null) { + return constantValue; + } + + var receiver = (VmObjectLike) VmUtils.getReceiver(frame, levelsUp); + var result = receiver.getCachedValue(property); + if (result == null) { + result = getCallNode(property).call(receiver, owner, property.getName()); + receiver.setCachedValue(property, result); + } + return result; + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadQualifiedLocalPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadQualifiedLocalPropertyNode.java new file mode 100644 index 000000000..96e054b4a --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadQualifiedLocalPropertyNode.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.member; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.runtime.Identifier; +import org.pkl.core.runtime.VmTyped; + +/** Reads a local property off of the receiver node. */ +public final class ReadQualifiedLocalPropertyNode extends AbstractReadLocalPropertyNode { + @Child private ExpressionNode getReceiverNode; + + public ReadQualifiedLocalPropertyNode( + SourceSection sourceSection, + Identifier identifier, + boolean needsConst, + ExpressionNode getReceiverNode) { + super(sourceSection, identifier, needsConst); + this.getReceiverNode = getReceiverNode; + } + + @Override + public Object executeGeneric(VirtualFrame frame) { + var receiver = (VmTyped) getReceiverNode.executeGeneric(frame); + var property = getProperty(receiver); + var constantValue = property.getConstantValue(); + if (constantValue != null) { + return constantValue; + } + + var result = receiver.getCachedValue(property); + if (result == null) { + result = getCallNode(property).call(receiver, receiver, property.getName()); + receiver.setCachedValue(property, result); + } + return result; + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetTypeAliasModuleNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetTypeAliasModuleNode.java new file mode 100644 index 000000000..eb674bf82 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/GetTypeAliasModuleNode.java @@ -0,0 +1,52 @@ +/* + * Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pkl.core.ast.expression.primary; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; +import org.jspecify.annotations.Nullable; +import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.runtime.VmTyped; + +public final class GetTypeAliasModuleNode extends ExpressionNode { + + @CompilationFinal private @Nullable VmTyped module; + + public GetTypeAliasModuleNode(SourceSection sourceSection) { + super(sourceSection); + } + + @Override + public boolean isInstrumentable() { + return false; + } + + public void lateInitModule(VmTyped module) { + // must only set this once; the first typealias initialization wins. + // guards against nested typealiases; e.g. `typealias A = module; typealias B = A` + if (this.module != null) return; + CompilerDirectives.transferToInterpreterAndInvalidate(); + this.module = module; + } + + @Override + public Object executeGeneric(VirtualFrame frame) { + assert module != null; + return module; + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/TypeAliasNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/TypeAliasNode.java index 22be156e6..54518c5d7 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/TypeAliasNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/TypeAliasNode.java @@ -24,6 +24,7 @@ import java.util.List; import org.jspecify.annotations.Nullable; import org.pkl.core.TypeParameter; import org.pkl.core.ast.ExpressionNode; +import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode; import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.runtime.VmTypeAlias; import org.pkl.core.runtime.VmTyped; @@ -86,7 +87,15 @@ public final class TypeAliasNode extends ExpressionNode { frame.materialize()); VmUtils.evaluateAnnotations(frame, annotationNodes, annotations); - cachedTypeAlias.initTypeCheckNode(typeAnnotationNode.execute(frame)); + var bodyTypeNode = typeAnnotationNode.execute(frame); + bodyTypeNode.accept( + node -> { + if (node instanceof GetTypeAliasModuleNode getTypeAliasModuleNode) { + getTypeAliasModuleNode.lateInitModule(module); + } + return true; + }); + cachedTypeAlias.initTypeCheckNode(bodyTypeNode); return cachedTypeAlias; } 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 925dc3029..933188582 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 @@ -21,7 +21,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -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.VirtualFrame; @@ -2735,56 +2734,18 @@ public abstract class TypeNode extends PklNode { return getMirrors(typeArgumentNodes); } - /** - * A typealias body is effectively inlined into the type node, and not executed in its own - * frame. - * - *

Before executing the typealias body, use the owner and receiver of the original frame - * where the typealias was declared, so that we preserve its original scope. - */ protected Object executeLazily(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.executeLazily(frame, value); - } finally { - setOwner(frame, prevOwner); - setReceiver(frame, prevReceiver); - } + return aliasedTypeNode.executeLazily(frame, 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); - } + return aliasedTypeNode.executeEagerly(frame, value); } - /** See docstring on {@link TypeAliasTypeNode#executeLazily}. */ @Override public Object executeAndSet(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.executeAndSet(frame, value); - } finally { - setOwner(frame, prevOwner); - setReceiver(frame, prevReceiver); - } + return aliasedTypeNode.executeAndSet(frame, value); } @TruffleBoundary @@ -2859,22 +2820,6 @@ public abstract class TypeNode extends PklNode { protected boolean isParametric() { return typeArgumentNodes.length > 0; } - - // Note that mutating a frame's receiver and owner argument is very risky - // because any VmObject instantiated within the same root node execution - // holds a reference to (not immutable snapshot of) the frame - // via VmObjectLike.enclosingFrame. - // *Maybe* this works out for TypeAliasTypeNode because an object instantiated - // within a type constraint doesn't escape the constraint expression. - // If mutating receiver and owner can't be avoided, it would be safer - // to have VmObjectLike store them directly instead of storing enclosingFrame. - private static void setReceiver(Frame frame, Object receiver) { - frame.getArguments()[0] = receiver; - } - - private static void setOwner(Frame frame, VmObjectLike owner) { - frame.getArguments()[1] = owner; - } } public static final class ConstrainedTypeNode extends TypeNode { diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/idType.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/idType.pkl new file mode 100644 index 000000000..299e6faae --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/idType.pkl @@ -0,0 +1 @@ +typealias Id = T diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendBase.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendBase.pkl new file mode 100644 index 000000000..a81c57329 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendBase.pkl @@ -0,0 +1 @@ +name: String = "base" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendModule.pkl new file mode 100644 index 000000000..8eba2ba8c --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasAmendModule.pkl @@ -0,0 +1,9 @@ +amends "typeAliasAmendBase.pkl" + +local const function isValid(s: String): Boolean = s.startsWith("Bob") + +local typealias ValidString4 = String(isValid(this)) + +local myProp: ValidString4 = "Bob Marley" + +name = myProp diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasClosedModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasClosedModule.pkl new file mode 100644 index 000000000..309419350 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasClosedModule.pkl @@ -0,0 +1,3 @@ +const function isValid(s: String): Boolean = s.startsWith("Bob") + +typealias ValidString2 = String(isValid(this)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasLocalMethod.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasLocalMethod.pkl new file mode 100644 index 000000000..171dd2ae7 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasLocalMethod.pkl @@ -0,0 +1,3 @@ +local const function isValid(s: String): Boolean = s.startsWith("Bob") + +typealias ValidString3 = String(isValid(this)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasModule.pkl new file mode 100644 index 000000000..7a2b4e609 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasModule.pkl @@ -0,0 +1 @@ +typealias MyModule = module diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasOpenModule.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasOpenModule.pkl new file mode 100644 index 000000000..4906a6b04 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typeAliasOpenModule.pkl @@ -0,0 +1,5 @@ +open module typeAliasOpenModule + +const function isValid(s: String): Boolean = s.startsWith("Bob") + +typealias ValidString = String(isValid(this)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias1.pkl new file mode 100644 index 000000000..bfe5e280c --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias1.pkl @@ -0,0 +1,3 @@ +const name = "Bob" + +typealias Inner = String(startsWith(name)) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias2.pkl new file mode 100644 index 000000000..87cb316c2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input-helper/types/typealias2.pkl @@ -0,0 +1,5 @@ +import "typealias1.pkl" + +const name = "Alice" + +typealias Outer = typealias1.Inner diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias5.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias5.pkl new file mode 100644 index 000000000..722e33143 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias5.pkl @@ -0,0 +1,8 @@ +import "../../input-helper/types/idType.pkl" + +hidden modules: Listing> = new { + new module {} + new module {} +} + +res = modules.length diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias6.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias6.pkl new file mode 100644 index 000000000..a83f7bc54 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAlias6.pkl @@ -0,0 +1,3 @@ +import "../../input-helper/types/typeAliasModule.pkl" + +res: typeAliasModule = module diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint4.pkl new file mode 100644 index 000000000..3004c3cbb --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint4.pkl @@ -0,0 +1,3 @@ +import "../../input-helper/types/typealias2.pkl" + +myProp: typealias2.Outer = "Alice Jenkins" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint5.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint5.pkl new file mode 100644 index 000000000..c41564854 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint5.pkl @@ -0,0 +1,5 @@ +import "../../input-helper/types/typeAliasOpenModule.pkl" + +const function isValid(s: String): Boolean = s.startsWith("Alice") + +myProp: typeAliasOpenModule.ValidString = "Alice Jenkins" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint6.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint6.pkl new file mode 100644 index 000000000..30f5af295 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint6.pkl @@ -0,0 +1,5 @@ +import "../../input-helper/types/typeAliasClosedModule.pkl" + +const function isValid(s: String): Boolean = s.startsWith("Alice") + +myProp: typeAliasClosedModule.ValidString2 = "Alice Jenkins" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint7.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint7.pkl new file mode 100644 index 000000000..b40cc8414 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint7.pkl @@ -0,0 +1,5 @@ +import "../../input-helper/types/typeAliasLocalMethod.pkl" + +local const function isValid(s: String): Boolean = s.startsWith("Alice") + +myProp: typeAliasLocalMethod.ValidString3 = "Alice Jenkins" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint8.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint8.pkl new file mode 100644 index 000000000..9ba0e8c2b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/types/typeAliasConstraint8.pkl @@ -0,0 +1,5 @@ +import "../../input-helper/types/typeAliasAmendModule.pkl" + +local const function isValid(s: String): Boolean = s.startsWith("Alice") + +res = typeAliasAmendModule.name diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias5.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias5.pcf new file mode 100644 index 000000000..ca7185cbe --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias5.pcf @@ -0,0 +1 @@ +res = 2 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias6.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias6.err new file mode 100644 index 000000000..7ae49d5a9 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAlias6.err @@ -0,0 +1,15 @@ +–– Pkl Error –– +Expected value of type `typeAliasModule`, but got type `typeAlias6`. +Value: new ModuleClass { res = ? } + +x | res: typeAliasModule = module + ^^^^^^^^^^^^^^^ +at typeAlias6#res (file:///$snippetsDir/input/types/typeAlias6.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/typeAliasConstraint4.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint4.err new file mode 100644 index 000000000..47166852d --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint4.err @@ -0,0 +1,23 @@ +–– Pkl Error –– +Type constraint `startsWith(name)` violated. +Value: "Alice Jenkins" + + startsWith(name) + │ │ + false "Bob" + +x | typealias Inner = String(startsWith(name)) + ^^^^^^^^^^^^^^^^ +at typeAliasConstraint4#myProp (file:///$snippetsDir/input-helper/types/typealias1.pkl) + +x | myProp: typealias2.Outer = "Alice Jenkins" + ^^^^^^^^^^^^^^^ +at typeAliasConstraint4#myProp (file:///$snippetsDir/input/types/typeAliasConstraint4.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/typeAliasConstraint5.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint5.err new file mode 100644 index 000000000..8afb84cd8 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint5.err @@ -0,0 +1,23 @@ +–– Pkl Error –– +Type constraint `isValid(this)` violated. +Value: "Alice Jenkins" + + isValid(this) + │ │ + false "Alice Jenkins" + +x | typealias ValidString = String(isValid(this)) + ^^^^^^^^^^^^^ +at typeAliasConstraint5#myProp (file:///$snippetsDir/input-helper/types/typeAliasOpenModule.pkl) + +x | myProp: typeAliasOpenModule.ValidString = "Alice Jenkins" + ^^^^^^^^^^^^^^^ +at typeAliasConstraint5#myProp (file:///$snippetsDir/input/types/typeAliasConstraint5.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/typeAliasConstraint6.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint6.err new file mode 100644 index 000000000..3363e5bf9 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint6.err @@ -0,0 +1,23 @@ +–– Pkl Error –– +Type constraint `isValid(this)` violated. +Value: "Alice Jenkins" + + isValid(this) + │ │ + false "Alice Jenkins" + +x | typealias ValidString2 = String(isValid(this)) + ^^^^^^^^^^^^^ +at typeAliasConstraint6#myProp (file:///$snippetsDir/input-helper/types/typeAliasClosedModule.pkl) + +x | myProp: typeAliasClosedModule.ValidString2 = "Alice Jenkins" + ^^^^^^^^^^^^^^^ +at typeAliasConstraint6#myProp (file:///$snippetsDir/input/types/typeAliasConstraint6.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/typeAliasConstraint7.err b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint7.err new file mode 100644 index 000000000..c562686ac --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint7.err @@ -0,0 +1,23 @@ +–– Pkl Error –– +Type constraint `isValid(this)` violated. +Value: "Alice Jenkins" + + isValid(this) + │ │ + false "Alice Jenkins" + +x | typealias ValidString3 = String(isValid(this)) + ^^^^^^^^^^^^^ +at typeAliasConstraint7#myProp (file:///$snippetsDir/input-helper/types/typeAliasLocalMethod.pkl) + +x | myProp: typeAliasLocalMethod.ValidString3 = "Alice Jenkins" + ^^^^^^^^^^^^^^^ +at typeAliasConstraint7#myProp (file:///$snippetsDir/input/types/typeAliasConstraint7.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/typeAliasConstraint8.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint8.pcf new file mode 100644 index 000000000..44438e09d --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/types/typeAliasConstraint8.pcf @@ -0,0 +1 @@ +res = "Bob Marley"