From d5a7dc021e1ea0c71afd52509238926c1d614851 Mon Sep 17 00:00:00 2001 From: Daniel Chao Date: Tue, 22 Sep 2026 12:12:57 -0700 Subject: [PATCH] Switch inferred `new {}` expressions in method args to using frame slots (#1857) This changes the logic for inferring `new {}` expressions in method arguments to using regular frame slots, instead of auxiliary slots. Overall, this is a performance improvement: no looking up auxiliary slot numbers, which makes this code much more PE friendly. * At parse time, get or create a frame slot that holds the method * Introduce `ArgInfo` record (preserve primitive int) over using `Pair` * Remove unneeded `isImplicitNewExpr` method; this is not needed because we can just keep track of the method slot on the frame descriptor builder. --- .../org/pkl/core/ast/builder/AstBuilder.java | 166 ++++++++---------- .../org/pkl/core/ast/builder/SymbolTable.java | 14 +- .../AbstractInvokeLexicalMethodNode.java | 4 +- ...actInvokeLexicalOrQualifiedMethodNode.java | 4 +- .../member/AbstractInvokeMethodNode.java | 28 +-- .../AbstractInvokeQualifiedMethodNode.java | 4 +- .../InferParentWithinMethodArgumentNode.java | 19 +- .../member/InvokeLexicalClassMethodNode.java | 4 +- .../member/InvokeLexicalObjectMethodNode.java | 4 +- .../member/InvokeMethodDirectNode.java | 4 +- .../member/InvokeMethodVirtualNode.java | 17 +- .../InvokeQualifiedClassMethodNode.java | 10 +- .../InvokeQualifiedObjectMethodNode.java | 10 +- .../member/InvokeSuperMethodNode.java | 4 +- .../pkl/core/ast/internal/ToStringNode.java | 2 +- .../java/org/pkl/core/ast/type/TypeNode.java | 8 +- .../core/runtime/FrameDescriptorBuilder.java | 33 +++- .../java/org/pkl/core/runtime/VmUtils.java | 8 - .../LanguageSnippetTests/input/basic/let2.pkl | 27 ++- .../input/basic/letInTypeConstraint1.pkl | 11 ++ .../input/basic/letInTypeConstraint2.pkl | 8 + .../input/basic/letInTypeConstraint3.pkl | 33 ++++ .../input/basic/letInTypeConstraint4.pkl | 12 ++ .../input/methods/inferParameterType2.pkl | 15 ++ .../input/methods/inferParameterType3.pkl | 10 ++ .../input/methods/inferParameterType4.pkl | 20 +++ .../input/methods/inferParameterType5.pkl | 17 ++ .../input/methods/inferParameterType6.pkl | 29 +++ .../input/methods/inferParameterType7.pkl | 19 ++ .../output/basic/let2.pcf | 14 +- .../output/basic/letInTypeConstraint1.pcf | 10 ++ .../output/basic/letInTypeConstraint2.pcf | 4 + .../output/basic/letInTypeConstraint3.pcf | 18 ++ .../output/basic/letInTypeConstraint4.pcf | 3 + .../output/methods/inferParameterType2.pcf | 5 + .../output/methods/inferParameterType3.pcf | 1 + .../output/methods/inferParameterType4.pcf | 2 + .../output/methods/inferParameterType5.pcf | 10 ++ .../output/methods/inferParameterType6.pcf | 3 + .../output/methods/inferParameterType7.pcf | 10 ++ 40 files changed, 422 insertions(+), 202 deletions(-) create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint1.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint3.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint4.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType2.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType3.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType4.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType5.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType6.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType7.pkl create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint1.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint2.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint3.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint4.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType2.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType3.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType4.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType5.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType6.pcf create mode 100644 pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType7.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 329bb0b37..338b5de02 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 @@ -528,14 +528,21 @@ public class AstBuilder extends AbstractAstBuilder { var exprs = type.getExprs(); var constraints = new TypeConstraintNode[exprs.size()]; for (var i = 0; i < constraints.length; i++) { - var currentFrameDescriptorSize = scope.frameDescriptorBuilder.getSize(); + var initialState = scope.frameDescriptorBuilder.state(); var expr = visitExpr(exprs.get(i)); - var writesFrameSlotVars = - scope.frameDescriptorBuilder.getSize() > currentFrameDescriptorSize; - // if a constraint expression writes to frame slots (only known case: is a `let` expr), - // create a new root node and execute the constraint within this root node - // e.g. String(let (x = this) x.length > 5) - if (writesFrameSlotVars) { + var needsFrameSlots = scope.frameDescriptorBuilder.state() > initialState; + // If a constraint expression writes to frame slots, create a new root node and execute + // the constraint within this root node. + // + // This can happen if the constraint contains a let expression, or if it contains a + // method call argument with an inferred `new {}`, e.g. + // + // * String(let (x = this) x.length > 5) + // * String(isFoo(new {})) + // + // We only need to do this if we are in a typealias because they get inlined into their + // usage site (they don't influence the frame descriptor of where they are inlined). + if (needsFrameSlots && symbolTable.isInTypeAliasScope) { expr = getExprWithinCustomThis(scope, expr); } constraints[i] = TypeConstraintNodeGen.create(expr.getSourceSection(), expr); @@ -894,55 +901,45 @@ public class AstBuilder extends AbstractAstBuilder { return new InvokeQualifiedObjectMethodNode( sourceSection, identifier, - argInfo.getFirst(), + argInfo.arguments, needsConst, getModuleNode, - argInfo.getSecond()); + argInfo.methodSlot); } if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) { return new InvokeQualifiedClassMethodNode( sourceSection, identifier, - argInfo.getFirst(), + argInfo.arguments, needsConst, getModuleNode, - argInfo.getSecond()); + argInfo.methodSlot); } return InvokeMethodVirtualNodeGen.create( sourceSection, identifier, - argInfo.getFirst(), + argInfo.arguments, MemberLookupMode.IMPLICIT_LEXICAL, needsConst, - argInfo.getSecond(), + argInfo.methodSlot, getModuleNode, GetClassNodeGen.create(null)); } if (method.isObjectMethod()) { return new InvokeLexicalObjectMethodNode( - sourceSection, - identifier, - levelsUp, - argInfo.getFirst(), - needsConst, - argInfo.getSecond()); + sourceSection, identifier, levelsUp, argInfo.arguments, needsConst, argInfo.methodSlot); } if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) { return new InvokeLexicalClassMethodNode( - sourceSection, - identifier, - levelsUp, - argInfo.getFirst(), - needsConst, - argInfo.getSecond()); + sourceSection, identifier, levelsUp, argInfo.arguments, needsConst, argInfo.methodSlot); } return InvokeMethodVirtualNodeGen.create( sourceSection, identifier, - argInfo.getFirst(), + argInfo.arguments, MemberLookupMode.IMPLICIT_LEXICAL, needsConst, - argInfo.getSecond(), + argInfo.methodSlot, levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp), GetClassNodeGen.create(null)); } else if (resolution instanceof ImplicitBaseMethod) { @@ -969,20 +966,22 @@ public class AstBuilder extends AbstractAstBuilder { createSourceSection(expr), method, new ConstantValueNode(baseModule), - argInfo.getFirst(), - argInfo.getSecond()); + argInfo.arguments, + argInfo.methodSlot); } } else if (resolution instanceof ImplicitThisMethod) { var isCustomThis = scope.isCustomThisScope(); var needsConst = constLevel == ConstLevel.ALL && constDepth == -1 && !isCustomThis; var argInfo = visitArgumentList(argList); + var arguments = argInfo.arguments; + var methodSlot = argInfo.methodSlot; return InvokeMethodVirtualNodeGen.create( sourceSection, org.pkl.core.runtime.Identifier.get(name), - argInfo.getFirst(), + arguments, MemberLookupMode.IMPLICIT_THIS, needsConst, - argInfo.getSecond(), + methodSlot, VmUtils.createThisNode(VmUtils.unavailableSourceSection(), isCustomThis), GetClassNodeGen.create(null)); } else { @@ -1081,7 +1080,6 @@ public class AstBuilder extends AbstractAstBuilder { var parent = expr.parent(); var scope = symbolTable.getCurrentScope(); - // keep in sync with isImplicitNewExpr while (parent instanceof IfExpr ifExpr && (ifExpr.getThen() == child || ifExpr.getEls() == child) || parent instanceof TraceExpr @@ -1122,8 +1120,10 @@ public class AstBuilder extends AbstractAstBuilder { // - generic methods: pkl.base#Pair(), etc. // these will throw cannotInferParent at runtime var argIndex = argumentList.getArguments().indexOf(child); + var methodSlot = symbolTable.getCurrentScope().frameDescriptorBuilder.getOrAddMethodSlot(); inferredParentNode = - InferParentWithinMethodArgumentNodeGen.create(sourceSection, language, argIndex); + InferParentWithinMethodArgumentNodeGen.create( + sourceSection, language, argIndex, methodSlot); } else { throw exceptionBuilder() .evalError("cannotInferParent") @@ -1160,7 +1160,7 @@ public class AstBuilder extends AbstractAstBuilder { var argInfo = visitArgumentList(argCtx); return InvokeSuperMethodNodeGen.create( - sourceSection, memberName, argInfo.getFirst(), needsConst, argInfo.getSecond()); + sourceSection, memberName, argInfo.arguments, needsConst, argInfo.methodSlot); } // superproperty call @@ -1340,13 +1340,14 @@ public class AstBuilder extends AbstractAstBuilder { descriptorBuilder, scope -> { var exprNode = visitExpr(expr.getExpr()); + var parameterTypeNodes = doVisitParameterTypes(params); var functionNode = new UnresolvedFunctionNode( language, scope.buildFrameDescriptor(), new Lambda(sourceSection, scope.getQualifiedName()), paramCount, - doVisitParameterTypes(params), + parameterTypeNodes, null, exprNode); @@ -1479,18 +1480,7 @@ public class AstBuilder extends AbstractAstBuilder { public GeneratorMemberNode visitMemberPredicate(MemberPredicate ctx) { var keyNode = symbolTable.enterEagerGenerator( - (scp) -> - symbolTable.enterCustomThisScope( - scope -> { - var currentFrameDescriptorSize = scope.frameDescriptorBuilder.getSize(); - var expr = visitExpr(ctx.getPred()); - var writesFrameSlotVars = - scope.frameDescriptorBuilder.getSize() > currentFrameDescriptorSize; - if (writesFrameSlotVars) { - return getExprWithinCustomThis(scope, expr); - } - return expr; - })); + (scp) -> symbolTable.enterCustomThisScope(scope -> visitExpr(ctx.getPred()))); var member = doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.getExpr(), ctx.getBodyList()); var isFrameStored = @@ -2179,6 +2169,8 @@ public class AstBuilder extends AbstractAstBuilder { .build(); } } + var parameterTypes = doVisitParameterTypes(paramListCtx); + var typeAnnotationNode = visitTypeAnnotation(entry.getTypeAnnotation()); return new UnresolvedMethodNode( language, @@ -2192,8 +2184,8 @@ public class AstBuilder extends AbstractAstBuilder { scope.getQualifiedName(), paramCount, typeParameters, - doVisitParameterTypes(paramListCtx), - visitTypeAnnotation(entry.getTypeAnnotation()), + parameterTypes, + typeAnnotationNode, isMethodReturnTypeChecked, bodyNode); }); @@ -2329,32 +2321,21 @@ public class AstBuilder extends AbstractAstBuilder { } @Override - public Pair visitArgumentList(ArgumentList argumentList) { + public ArgInfo visitArgumentList(ArgumentList argumentList) { var args = argumentList.getArguments(); - var res = new ExpressionNode[args.size()]; - var argsRequireInference = false; - for (int i = 0; i < res.length; i++) { + var arguments = new ExpressionNode[args.size()]; + var frameDescriptorBuilder = symbolTable.getCurrentScope().frameDescriptorBuilder; + var initialState = frameDescriptorBuilder.state(); + for (var i = 0; i < arguments.length; i++) { var expr = args.get(i); - res[i] = visitExpr(expr); - argsRequireInference = argsRequireInference || isImplicitNewExpr(expr); + arguments[i] = visitExpr(expr); } - return Pair.of(res, argsRequireInference); - } - - private static boolean isImplicitNewExpr(Expr expr) { - // keep in sync with doVisitNewExprWithInferredParent - if (expr instanceof NewExpr newExpr && newExpr.getType() == null) { - return true; - } else if (expr instanceof IfExpr ifExpr) { - return isImplicitNewExpr(ifExpr.getThen()) || isImplicitNewExpr(ifExpr.getEls()); - } else if (expr instanceof TraceExpr traceExpr) { - return isImplicitNewExpr(traceExpr.getExpr()); - } else if (expr instanceof ParenthesizedExpr parenthesizedExpr) { - return isImplicitNewExpr(parenthesizedExpr.getExpr()); - } else if (expr instanceof LetExpr letExpr) { - return isImplicitNewExpr(letExpr.getExpr()); - } - return false; + var argsRequiresInference = frameDescriptorBuilder.state() > initialState; + var methodSlot = + argsRequiresInference + ? symbolTable.getCurrentScope().frameDescriptorBuilder.getMethodSlot() + : -1; + return new ArgInfo(arguments, methodSlot); } @Override @@ -2439,9 +2420,12 @@ public class AstBuilder extends AbstractAstBuilder { parametersDescriptorAndBindings == null ? new FrameSlotVariable[0] : parametersDescriptorAndBindings.second; + var parametersFrameDescriptorBuilder = + parametersDescriptorAndBindings == null ? null : parametersDescriptorAndBindings.first; return symbolTable.enterObjectScope( bindings, + parametersFrameDescriptorBuilder, (scope) -> { addObjectNamesToScope(scope, body); var objectMembers = body.getMembers(); @@ -2451,11 +2435,13 @@ public class AstBuilder extends AbstractAstBuilder { } var sourceSection = createSourceSection(body.parent()); + // must visit parameter types before building the parameters frame descriptor, since + // constraints (e.g. containing `let` expressions) may add further slots to it + var parameterTypes = doVisitParameterTypes(body); var parametersDescriptor = parametersDescriptorAndBindings == null ? null : parametersDescriptorAndBindings.first.build(); - var parameterTypes = doVisitParameterTypes(body); var members = EconomicMaps.create(); var elements = new ArrayList(); @@ -2496,7 +2482,8 @@ public class AstBuilder extends AbstractAstBuilder { || memberCtx instanceof ObjectSpread; // bail out and create GeneratorObjectLiteralNode instead // (but can't we easily reuse members/elements/keyNodes/values?) - return doVisitGeneratorObjectBody(body, parentNode); + return doVisitGeneratorObjectBody( + body, parentNode, parametersDescriptor, parameterTypes); } var currentScope = symbolTable.getCurrentScope(); @@ -2677,6 +2664,7 @@ public class AstBuilder extends AbstractAstBuilder { .build(); } } + var typeAnnotation = visitTypeAnnotation(typeAnn); ExpressionNode bodyNode; if (body != null && !body.isEmpty()) { // foo { ... } @@ -2713,7 +2701,7 @@ public class AstBuilder extends AbstractAstBuilder { scope.buildFrameDescriptor(), modifiers, bodyNode, - visitTypeAnnotation(typeAnn)) + typeAnnotation) : VmUtils.createObjectProperty( language, sourceSection, @@ -2836,6 +2824,8 @@ public class AstBuilder extends AbstractAstBuilder { scope.getName(), scope.getQualifiedName()); var body = visitExpr(expr); + var typeNode = visitTypeAnnotation(typeAnnotation); + var parameterTypeNodes = doVisitParameterTypes(paramList); var node = new ObjectMethodNode( language, @@ -2843,8 +2833,8 @@ public class AstBuilder extends AbstractAstBuilder { member, body, paramList.getParameters().size(), - doVisitParameterTypes(paramList), - visitTypeAnnotation(typeAnnotation)); + parameterTypeNodes, + typeNode); member.initMemberNode(node); return member; @@ -2852,10 +2842,10 @@ public class AstBuilder extends AbstractAstBuilder { } private GeneratorObjectLiteralNode doVisitGeneratorObjectBody( - ObjectBody body, ExpressionNode parentNode) { - var parametersDescriptorBuilderAndFrameSlotVariables = - createFrameDescriptorBuilderAndSlotVariables(body); - var parameterTypes = doVisitParameterTypes(body); + ObjectBody body, + ExpressionNode parentNode, + @Nullable FrameDescriptor parametersDescriptor, + UnresolvedTypeNode[] parameterTypes) { var memberNodes = doVisitGeneratorMemberNodes(body.getMembers()); var currentScope = symbolTable.getCurrentScope(); //noinspection ConstantConditions @@ -2864,9 +2854,7 @@ public class AstBuilder extends AbstractAstBuilder { language, currentScope.getQualifiedName(), currentScope.isCustomThisScope(), - parametersDescriptorBuilderAndFrameSlotVariables == null - ? null - : parametersDescriptorBuilderAndFrameSlotVariables.first.build(), + parametersDescriptor, parameterTypes, memberNodes, parentNode); @@ -2944,10 +2932,10 @@ public class AstBuilder extends AbstractAstBuilder { InvokeMethodVirtualNodeGen.create( sourceSection, functionName, - argInfo.getFirst(), + argInfo.arguments, MemberLookupMode.EXPLICIT_RECEIVER, needsConst, - argInfo.getSecond(), + argInfo.methodSlot, PropagateNullReceiverNodeGen.create(unavailableSourceSection(), receiver), GetClassNodeGen.create(null))); } @@ -2956,10 +2944,10 @@ public class AstBuilder extends AbstractAstBuilder { return InvokeMethodVirtualNodeGen.create( sourceSection, functionName, - argInfo.getFirst(), + argInfo.arguments, MemberLookupMode.EXPLICIT_RECEIVER, needsConst, - argInfo.getSecond(), + argInfo.methodSlot, receiver, GetClassNodeGen.create(null)); } @@ -3340,4 +3328,6 @@ public class AstBuilder extends AbstractAstBuilder { } return false; } + + public record ArgInfo(ExpressionNode[] arguments, int methodSlot) {} } 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 b73976ab2..7f1e23f29 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 @@ -211,9 +211,14 @@ public final class SymbolTable { } public T enterObjectScope( - FrameSlotVariable[] bindings, Function nodeFactory) { - return doEnter( - new ObjectScope(currentScope, bindings, currentScope.frameDescriptorBuilder), nodeFactory); + FrameSlotVariable[] bindings, + @Nullable FrameDescriptorBuilder parametersFrameDescriptorBuilder, + Function nodeFactory) { + var frameDescriptorBuilder = + parametersFrameDescriptorBuilder != null + ? parametersFrameDescriptorBuilder + : currentScope.frameDescriptorBuilder; + return doEnter(new ObjectScope(currentScope, bindings, frameDescriptorBuilder), nodeFactory); } private T doEnter(S scope, Function nodeFactory) { @@ -861,6 +866,9 @@ public final class SymbolTable { // A generator scope that is resolved eagerly and one level above public static final class EagerGeneratorScope extends Scope { private static FrameDescriptorBuilder getFrameDescriptorBuilder(Scope parent) { + if (parent instanceof ObjectScope objectScope && objectScope.hasParams()) { + return parent.frameDescriptorBuilder; + } var grandParent = parent.parent; assert grandParent != null; return grandParent.frameDescriptorBuilder; 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 index cbb0b9669..49fd9b312 100644 --- 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 @@ -32,8 +32,8 @@ public abstract sealed class AbstractInvokeLexicalMethodNode int levelsUp, ExpressionNode[] argumentNodes, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, methodName, argumentNodes, needsConst, argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, argumentNodes, needsConst, methodSlot); this.levelsUp = levelsUp; } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalOrQualifiedMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalOrQualifiedMethodNode.java index 39b8dae22..ca040da9e 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalOrQualifiedMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeLexicalOrQualifiedMethodNode.java @@ -48,8 +48,8 @@ public abstract sealed class AbstractInvokeLexicalOrQualifiedMethodNode Identifier methodName, ExpressionNode[] argumentNodes, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, argumentNodes, argsRequireInference); + int methodSlot) { + super(sourceSection, argumentNodes, methodSlot); this.methodName = methodName; this.needsConst = needsConst; this.isConstChecked = false; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java index aa6634fa1..a8bf0a82f 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/AbstractInvokeMethodNode.java @@ -15,44 +15,32 @@ */ package org.pkl.core.ast.expression.member; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.source.SourceSection; import org.jspecify.annotations.Nullable; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.member.Method; -import org.pkl.core.runtime.VmUtils; public abstract class AbstractInvokeMethodNode extends ExpressionNode { @Children protected final ExpressionNode[] argumentNodes; - protected final boolean argsRequireInference; + protected final int methodSlot; public AbstractInvokeMethodNode( - SourceSection sourceSection, ExpressionNode[] argumentNodes, boolean argsRequireInference) { + SourceSection sourceSection, ExpressionNode[] argumentNodes, int methodSlot) { super(sourceSection); this.argumentNodes = argumentNodes; - this.argsRequireInference = argsRequireInference; - } - - @TruffleBoundary - private int getMethodSlot(FrameDescriptor frameDescriptor) { - // can't store the slot id as this node may be called from different root nodes - // (see constraints14 snippet) - return frameDescriptor.findOrAddAuxiliarySlot(VmUtils.METHOD_FRAME_SLOT_ID); + this.methodSlot = methodSlot; } @ExplodeLoop protected Object[] evalArgs( VirtualFrame frame, @Nullable Method method, Object owner, @Nullable Object receiver) { - int methodSlot = -1; Object prevMethod = null; - if (argsRequireInference) { - methodSlot = getMethodSlot(frame.getFrameDescriptor()); - prevMethod = frame.getAuxiliarySlot(methodSlot); - frame.setAuxiliarySlot(methodSlot, method); + if (methodSlot > -1) { + prevMethod = frame.getObject(methodSlot); + frame.setObject(methodSlot, method); } var args = new Object[2 + argumentNodes.length]; @@ -64,8 +52,8 @@ public abstract class AbstractInvokeMethodNode extends ExpressionNode { args[2 + i] = argumentNodes[i].executeGeneric(frame); } } finally { - if (argsRequireInference) { - frame.setAuxiliarySlot(methodSlot, prevMethod); + if (methodSlot > -1) { + frame.setObject(methodSlot, prevMethod); } } 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 index a6d0271e2..9fd619d50 100644 --- 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 @@ -32,8 +32,8 @@ public abstract sealed class AbstractInvokeQualifiedMethodNode ExpressionNode[] argumentNodes, boolean needsConst, ExpressionNode getReceiverNode, - boolean argsRequireInference) { - super(sourceSection, methodName, argumentNodes, needsConst, argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, argumentNodes, needsConst, methodSlot); this.getReceiverNode = getReceiverNode; } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodArgumentNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodArgumentNode.java index 9d2989aa8..cf540768f 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodArgumentNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InferParentWithinMethodArgumentNode.java @@ -16,41 +16,30 @@ package org.pkl.core.ast.expression.member; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.jspecify.annotations.Nullable; import org.pkl.core.ast.member.Method; import org.pkl.core.ast.type.TypeNode; import org.pkl.core.runtime.VmLanguage; -import org.pkl.core.runtime.VmUtils; public abstract class InferParentWithinMethodArgumentNode extends AbstractInferParentFromMethodNode { private final int argIndex; + private final int methodSlot; public InferParentWithinMethodArgumentNode( - SourceSection sourceSection, VmLanguage language, int argIndex) { + SourceSection sourceSection, VmLanguage language, int argIndex, int methodSlot) { super(sourceSection, language); this.argIndex = argIndex; - } - - @TruffleBoundary - private int getMethodSlot(FrameDescriptor frameDescriptor) { - var methodSlot = frameDescriptor.getAuxiliarySlots().get(VmUtils.METHOD_FRAME_SLOT_ID); - if (methodSlot == null) { - // used in intrinsic constructor e.g. pkl.base#List() - throw exceptionBuilder().evalError("cannotInferParent").build(); - } - return methodSlot; + this.methodSlot = methodSlot; } @Override protected Method getMethod(VirtualFrame frame) { - var method = (Method) frame.getAuxiliarySlot(getMethodSlot(frame.getFrameDescriptor())); + var method = (Method) frame.getObject(methodSlot); if (method == null) { // used in FunctionN.apply() CompilerDirectives.transferToInterpreter(); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java index 6987a41cf..c25be41e8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalClassMethodNode.java @@ -32,8 +32,8 @@ public final class InvokeLexicalClassMethodNode extends AbstractInvokeLexicalMet int levelsUp, ExpressionNode[] argumentNodes, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, methodSlot); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java index 689fa092d..924cdd84e 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeLexicalObjectMethodNode.java @@ -31,8 +31,8 @@ public final class InvokeLexicalObjectMethodNode extends AbstractInvokeLexicalMe int levelsUp, ExpressionNode[] argumentNodes, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, methodSlot); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java index 2431856b6..78283cb89 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java @@ -35,8 +35,8 @@ public final class InvokeMethodDirectNode extends AbstractInvokeMethodNode { ClassMethod method, ExpressionNode receiverNode, ExpressionNode[] argumentNodes, - boolean argsRequireInference) { - super(sourceSection, argumentNodes, argsRequireInference); + int methodSlot) { + super(sourceSection, argumentNodes, methodSlot); this.method = method; this.owner = method.getOwner(); this.receiverNode = receiverNode; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java index 008355683..1b61b58e4 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java @@ -53,8 +53,8 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode { ExpressionNode[] argumentNodes, MemberLookupMode lookupMode, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, argumentNodes, argsRequireInference); + int methodSlot) { + super(sourceSection, argumentNodes, methodSlot); this.methodName = methodName; this.lookupMode = lookupMode; this.needsConst = needsConst; @@ -65,8 +65,8 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode { Identifier methodName, ExpressionNode[] argumentNodes, MemberLookupMode lookupMode, - boolean argsRequireInference) { - this(sourceSection, methodName, argumentNodes, lookupMode, false, argsRequireInference); + int methodSlot) { + this(sourceSection, methodName, argumentNodes, lookupMode, false, methodSlot); } /** @@ -146,14 +146,7 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode { @Override public WrapperNode createWrapper(ProbeNode probe) { return new InvokeMethodVirtualNodeWrapper( - sourceSection, - methodName, - argumentNodes, - lookupMode, - needsConst, - argsRequireInference, - this, - probe); + sourceSection, methodName, argumentNodes, lookupMode, needsConst, methodSlot, this, probe); } private void checkConst(ClassMethod method) { 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 index 9e8b6347d..9dd8b4159 100644 --- 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 @@ -29,14 +29,8 @@ public final class InvokeQualifiedClassMethodNode extends AbstractInvokeQualifie ExpressionNode[] argumentNodes, boolean needsConst, ExpressionNode getReceiverNode, - boolean argsRequireInference) { - super( - sourceSection, - methodName, - argumentNodes, - needsConst, - getReceiverNode, - argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode, methodSlot); } @Override 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 index 23fb43e42..9aa288ab9 100644 --- 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 @@ -31,14 +31,8 @@ public final class InvokeQualifiedObjectMethodNode extends AbstractInvokeQualifi ExpressionNode[] argumentNodes, boolean needsConst, ExpressionNode getReceiverNode, - boolean argsRequireInference) { - super( - sourceSection, - methodName, - argumentNodes, - needsConst, - getReceiverNode, - argsRequireInference); + int methodSlot) { + super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode, methodSlot); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java index 8a0d6c272..ee7075cbe 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java @@ -36,8 +36,8 @@ public abstract class InvokeSuperMethodNode extends AbstractInvokeMethodNode { Identifier methodName, ExpressionNode[] argumentNodes, boolean needsConst, - boolean argsRequireInference) { - super(sourceSection, argumentNodes, argsRequireInference); + int methodSlot) { + super(sourceSection, argumentNodes, methodSlot); this.needsConst = needsConst; assert !methodName.isLocalMethod(); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java index 7af13a07f..da83d23c9 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java @@ -86,7 +86,7 @@ public abstract class ToStringNode extends UnaryExpressionNode { Identifier.TO_STRING, new ExpressionNode[] {}, MemberLookupMode.EXPLICIT_RECEIVER, - false, + -1, null, null); } 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 ad1ab0963..e6664957b 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 @@ -1401,7 +1401,7 @@ public abstract class TypeNode extends PklNode { vmListing, EconomicMaps.emptyMap(), vmListing.getLength(), - getValueTypeCastNode(), + getValueTypeCastNode(frame.getFrameDescriptor()), VmUtils.getReceiver(frame), VmUtils.getOwner(frame)); } @@ -1459,7 +1459,7 @@ public abstract class TypeNode extends PklNode { vmMapping.getEnclosingFrame(), vmMapping, EconomicMaps.emptyMap(), - getValueTypeCastNode(), + getValueTypeCastNode(frame.getFrameDescriptor()), VmUtils.getReceiver(frame), VmUtils.getOwner(frame)); } @@ -1518,12 +1518,12 @@ public abstract class TypeNode extends PklNode { return valueTypeNode; } - protected ListingOrMappingTypeCastNode getValueTypeCastNode() { + protected ListingOrMappingTypeCastNode getValueTypeCastNode(FrameDescriptor descriptor) { if (valueTypeCastNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); valueTypeCastNode = new ListingOrMappingTypeCastNode( - language, new FrameDescriptor(), valueTypeNode, getRootNode().getName()); + language, descriptor, valueTypeNode, getRootNode().getName()); } return valueTypeCastNode; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/FrameDescriptorBuilder.java b/pkl-core/src/main/java/org/pkl/core/runtime/FrameDescriptorBuilder.java index d781fab22..a94673b24 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/FrameDescriptorBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/FrameDescriptorBuilder.java @@ -20,7 +20,10 @@ import com.oracle.truffle.api.frame.FrameSlotKind; import java.util.Arrays; import org.jspecify.annotations.Nullable; -/** A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also gives us the current size. */ +/** + * A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also helps track size, identifiers, + * and other metadata. + */ public class FrameDescriptorBuilder { private @Nullable Identifier[] names; @@ -29,6 +32,8 @@ public class FrameDescriptorBuilder { private final FrameDescriptor.Builder underlying; private static final int DEFAULT_CAPACITY = 8; + private int methodSlot = -1; + private int state = 0; public FrameDescriptorBuilder() { this(DEFAULT_CAPACITY); @@ -50,10 +55,36 @@ public class FrameDescriptorBuilder { ensureCapacity(1); names[size] = name; size++; + state++; var slot = underlying.addSlot(kind, name, info); return new FrameSlotVariable(name.toString(), slot); } + /** + * A monotonically increasing number representing the "virtual" state of the frame descriptor; a + * higher number means that something tried to modify the descriptor (but did not necessarily + * change the actual descriptor). + */ + public int state() { + return state; + } + + public int getOrAddMethodSlot() { + state++; + if (methodSlot != -1) { + return methodSlot; + } + ensureCapacity(1); + names[size] = null; + size++; + methodSlot = underlying.addSlot(FrameSlotKind.Object, null, null); + return methodSlot; + } + + public int getMethodSlot() { + return methodSlot; + } + public FrameDescriptor build() { return underlying.build(); } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java index e7610cdd7..0e79cafc6 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java @@ -84,14 +84,6 @@ public final class VmUtils { } }; - public static final Object METHOD_FRAME_SLOT_ID = - new Object() { - @Override - public String toString() { - return "method"; - } - }; - private static final Engine PKL_ENGINE = Engine.newBuilder("pkl").option("engine.WarnInterpreterOnly", "false").build(); diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/let2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/let2.pkl index 902fb0267..dfd9e4380 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/let2.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/let2.pkl @@ -1,30 +1,27 @@ -// let expr inside custom this scopes -typealias Foo = String(let (x = this) this == x) - -typealias Foo2 = String(let (x = "foo") this.startsWith(x), let (y = "o") this.endsWith(y)) - -typealias Foo3 = String(let (x = "foo") this.startsWith(x))(let (y = "o") this.endsWith(y)) - -res1: Foo = "foo" - -res2: Foo2 = "foo" - -res3: Foo3 = "foo" - hidden bar { new { name = "fooey" } + new { + name = "quxxey" + } } -res4 = (bar) { +res1 = (bar) { [[let (x = "foo") this.name.startsWith(x)]] { name = "bob" } + [[let (y = "qux") this.name.startsWith(y)]] { + name = "bob" + } } -res5 = (bar) { +res2 = (bar) { [[let (x = this) x.name.startsWith("foo")]] { name = "bob" } } + +function isPositive(num: Int(let (q = 0) this > q)) = true + +res3: Int(isPositive(this)) = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint1.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint1.pkl new file mode 100644 index 000000000..26aa765c0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint1.pkl @@ -0,0 +1,11 @@ +foo { + new { + prop = 1 + } +} + +res = (foo) { + [[this.prop is Int(let (q = 0) this > q)]] { + prop = 2 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint2.pkl new file mode 100644 index 000000000..2f7e3477c --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint2.pkl @@ -0,0 +1,8 @@ +res1 { + local x: Int(let (q = 0) this > q) = 5 + z = x +} + +res2 = f() + +function f(): Int(let (q = 0) this > q) = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint3.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint3.pkl new file mode 100644 index 000000000..dcf0a627b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint3.pkl @@ -0,0 +1,33 @@ +local f = (a, b) -> new Dynamic { + x = a + y = b +} + +res = + (f) { p, q: Int(let (z = 0) this > z) -> + x = p + y = q + }.apply("hi", 9) + +local g = (a, b: Int(let (z = 0) this > z)) -> new Dynamic { + x = a + y = b +} + +res2 = g.apply("hi", 9) + +res3 = new Dynamic { + local function h(a, b: Int(let (z = 0) this > z)) = new Dynamic { + x = a + y = b + } + `out` = h("hi", 9) +} + +res4 = + (f) { p, q: Int(let (z = 0) this > z) -> + when (true) { + x = p + y = q + } + }.apply("hi", 9) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint4.pkl new file mode 100644 index 000000000..be9b2ba44 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/letInTypeConstraint4.pkl @@ -0,0 +1,12 @@ +// let exprs inside typealiases turn into their own root node +typealias Foo = String(let (x = this) this == x) + +typealias Foo2 = String(let (x = "foo") this.startsWith(x), let (y = "o") this.endsWith(y)) + +typealias Foo3 = String(let (x = "foo") this.startsWith(x))(let (y = "o") this.endsWith(y)) + +res1: Foo = "foo" + +res2: Foo2 = "foo" + +res3: Foo3 = "foo" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType2.pkl new file mode 100644 index 000000000..54c6544ae --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType2.pkl @@ -0,0 +1,15 @@ +class Foo { + bar: Int +} + +const function chk2(i: Int): Boolean = i > 0 + +const function mk(f: Foo): Listing = new { + f.bar +} + +class C { + xs: Listing = mk(new { bar = 2 }) +} + +c: C = new {} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType3.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType3.pkl new file mode 100644 index 000000000..8c2e96bea --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType3.pkl @@ -0,0 +1,10 @@ +class Foo { + bar: Int +} + +const function isOk(f: Foo): Boolean = f.bar > 0 +const function isPos(i: Int): Boolean = i > 0 + +typealias T = Int(isOk(new { bar = 1 }), isPos(this), isOk(new { bar = 2 })) + +x: T = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType4.pkl new file mode 100644 index 000000000..45dbb36b3 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType4.pkl @@ -0,0 +1,20 @@ +class Foo { + bar: Int +} +const function isOk(f: Foo): Boolean = f.bar > 0 +const function and(a: Boolean, b: Boolean) = a && b + +function m(p: Int(isOk(new { bar = 1 }))): Int = p + +function n( + // outer method call does not have implicit new, but inner method call does + p: Int( + and( + isOk(new { bar = 1 }), + true, + ), + ), +): Int = p + +res1 = m(3) +res2 = n(3) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType5.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType5.pkl new file mode 100644 index 000000000..365a14d4b --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType5.pkl @@ -0,0 +1,17 @@ +class Foo { + bar: Int +} +const function isOk(f: Foo): Boolean = f.bar > 0 +const function isPos(num: Int) = num > 0 + +foo { + new { + prop = 1 + } +} + +res = (foo) { + [[this.prop is Int(isOk(new { bar = 1 }), isPos(this), isOk(new { bar = 2 }))]] { + prop = 2 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType6.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType6.pkl new file mode 100644 index 000000000..3a656faf0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType6.pkl @@ -0,0 +1,29 @@ +class Foo { + bar: Int +} +const function isOk(f: Foo): Boolean = f.bar > 0 +const function and(a: Boolean, b: Boolean) = a && b + +typealias Okay = Int(isOk(new { bar = 1 })) + +typealias Okay2 = + Int( + // no inferred new arg + and( + // nested call has inferred new arg + isOk(new { bar = 1 }), + true, + ), + ) + +typealias Okay3 = + Int( + // no inferred new arg + isOk(new Foo { bar = 1 }), + // inferred new arg + isOk(new { bar = 1 }), + ) + +res1: Okay = 3 +res2: Okay2 = 3 +res3: Okay3 = 3 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType7.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType7.pkl new file mode 100644 index 000000000..4f52b4656 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/methods/inferParameterType7.pkl @@ -0,0 +1,19 @@ +class Foo { + bar: Int +} +const function isOk2(f: Foo, n: Int): Boolean = f.bar > 0 && n > 0 +local f = (a, b) -> new Dynamic { y = a; w = b } + +res = + (f) { p, q -> + when (isOk2(new { bar = 1 }, p)) { + z = p + } + }.apply(1, 2) + +res2 = + (f) { + when (isOk2(new { bar = 1 }, 2)) { + z = 2 + } + }.apply(1, 2) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/let2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/let2.pcf index b13435df8..535cc74ca 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/let2.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/let2.pcf @@ -1,13 +1,17 @@ -res1 = "foo" -res2 = "foo" -res3 = "foo" -res4 { +res1 { + new { + name = "bob" + } new { name = "bob" } } -res5 { +res2 { new { name = "bob" } + new { + name = "quxxey" + } } +res3 = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint1.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint1.pcf new file mode 100644 index 000000000..5369d826a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint1.pcf @@ -0,0 +1,10 @@ +foo { + new { + prop = 1 + } +} +res { + new { + prop = 2 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint2.pcf new file mode 100644 index 000000000..3087de264 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint2.pcf @@ -0,0 +1,4 @@ +res1 { + z = 5 +} +res2 = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint3.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint3.pcf new file mode 100644 index 000000000..416c48074 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint3.pcf @@ -0,0 +1,18 @@ +res { + x = "hi" + y = 9 +} +res2 { + x = "hi" + y = 9 +} +res3 { + `out` { + x = "hi" + y = 9 + } +} +res4 { + x = "hi" + y = 9 +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint4.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint4.pcf new file mode 100644 index 000000000..6a2c5ed98 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/letInTypeConstraint4.pcf @@ -0,0 +1,3 @@ +res1 = "foo" +res2 = "foo" +res3 = "foo" diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType2.pcf new file mode 100644 index 000000000..02e0682e4 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType2.pcf @@ -0,0 +1,5 @@ +c { + xs { + 2 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType3.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType3.pcf new file mode 100644 index 000000000..fed79d05f --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType3.pcf @@ -0,0 +1 @@ +x = 5 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType4.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType4.pcf new file mode 100644 index 000000000..359544936 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType4.pcf @@ -0,0 +1,2 @@ +res1 = 3 +res2 = 3 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType5.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType5.pcf new file mode 100644 index 000000000..5369d826a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType5.pcf @@ -0,0 +1,10 @@ +foo { + new { + prop = 1 + } +} +res { + new { + prop = 2 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType6.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType6.pcf new file mode 100644 index 000000000..7c8617906 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType6.pcf @@ -0,0 +1,3 @@ +res1 = 3 +res2 = 3 +res3 = 3 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType7.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType7.pcf new file mode 100644 index 000000000..8ad7912e0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/methods/inferParameterType7.pcf @@ -0,0 +1,10 @@ +res { + y = 1 + w = 2 + z = 1 +} +res2 { + y = 1 + w = 2 + z = 2 +}