Add inference for implicitly typed new in method arguments (#1768)

This commit is contained in:
Jen Basch
2026-09-02 23:55:24 +00:00
committed by GitHub
parent 92b26aca17
commit 7f5ae2a712
34 changed files with 678 additions and 323 deletions
@@ -109,8 +109,9 @@ import org.pkl.core.ast.expression.literal.MapLiteralNode;
import org.pkl.core.ast.expression.literal.PropertiesLiteralNodeGen; import org.pkl.core.ast.expression.literal.PropertiesLiteralNodeGen;
import org.pkl.core.ast.expression.literal.SetLiteralNode; import org.pkl.core.ast.expression.literal.SetLiteralNode;
import org.pkl.core.ast.expression.literal.TrueLiteralNode; import org.pkl.core.ast.expression.literal.TrueLiteralNode;
import org.pkl.core.ast.expression.member.InferParentWithinMethodNode; import org.pkl.core.ast.expression.member.InferParentWithinMethodArgumentNodeGen;
import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNode; import org.pkl.core.ast.expression.member.InferParentWithinMethodNodeGen;
import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNodeGen;
import org.pkl.core.ast.expression.member.InferParentWithinPropertyNodeGen; import org.pkl.core.ast.expression.member.InferParentWithinPropertyNodeGen;
import org.pkl.core.ast.expression.member.InvokeLexicalClassMethodNode; import org.pkl.core.ast.expression.member.InvokeLexicalClassMethodNode;
import org.pkl.core.ast.expression.member.InvokeLexicalObjectMethodNode; import org.pkl.core.ast.expression.member.InvokeLexicalObjectMethodNode;
@@ -168,7 +169,7 @@ import org.pkl.core.ast.member.UnresolvedFunctionNode;
import org.pkl.core.ast.member.UnresolvedMethodNode; import org.pkl.core.ast.member.UnresolvedMethodNode;
import org.pkl.core.ast.member.UnresolvedPropertyNode; import org.pkl.core.ast.member.UnresolvedPropertyNode;
import org.pkl.core.ast.member.UntypedObjectMemberNode; import org.pkl.core.ast.member.UntypedObjectMemberNode;
import org.pkl.core.ast.type.GetParentForTypeNode; import org.pkl.core.ast.type.GetParentForTypeNodeGen;
import org.pkl.core.ast.type.ResolveDeclaredTypeNode; import org.pkl.core.ast.type.ResolveDeclaredTypeNode;
import org.pkl.core.ast.type.ResolveQualifiedDeclaredTypeNode; import org.pkl.core.ast.type.ResolveQualifiedDeclaredTypeNode;
import org.pkl.core.ast.type.ResolveSimpleDeclaredTypeNode; import org.pkl.core.ast.type.ResolveSimpleDeclaredTypeNode;
@@ -1034,8 +1035,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var expr = var expr =
doVisitObjectBody( doVisitObjectBody(
newExpr.getBody(), newExpr.getBody(),
new GetParentForTypeNode( GetParentForTypeNodeGen.create(
createSourceSection(newExpr), createSourceSection(newExpr),
language,
parentType, parentType,
symbolTable.getCurrentScope().getQualifiedName())); symbolTable.getCurrentScope().getQualifiedName()));
if (type instanceof DeclaredType declaredType && declaredType.getArgs() != null) { if (type instanceof DeclaredType declaredType && declaredType.getArgs() != null) {
@@ -1079,9 +1081,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
org.pkl.core.runtime.Identifier scopeName = scope.getName(); org.pkl.core.runtime.Identifier scopeName = scope.getName();
inferredParentNode = inferredParentNode =
isObjectMethod isObjectMethod
? new InferParentWithinObjectMethodNode( ? InferParentWithinObjectMethodNodeGen.create(
createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode()) createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode())
: new InferParentWithinMethodNode( : InferParentWithinMethodNodeGen.create(
createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode()); createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode());
} else if (parent instanceof LetExpr letExpr && letExpr.getBindingExpr() == child) { } else if (parent instanceof LetExpr letExpr && letExpr.getBindingExpr() == child) {
// TODO correctly infer parent, e.g. `let (x: Person = new {}) ...` // TODO correctly infer parent, e.g. `let (x: Person = new {}) ...`
@@ -1089,6 +1091,16 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
.evalError("cannotInferParent") .evalError("cannotInferParent")
.withSourceSection(createSourceSection(expr.newSpan())) .withSourceSection(createSourceSection(expr.newSpan()))
.build(); .build();
} else if (parent instanceof ArgumentList argumentList) {
// cases we can't cover currently
// - FunctionN.apply: the parameter type nodes are not stored
// - pkl.base intrinsic constructors: List(), Set(), Map(), Bytes()
// - generic methods: pkl.base#Pair(), etc.
// these will throw cannotInferParent at runtime
var sourceSection = createSourceSection(expr.newSpan());
var argIndex = argumentList.getArguments().indexOf(child);
inferredParentNode =
InferParentWithinMethodArgumentNodeGen.create(sourceSection, language, argIndex);
} else { } else {
throw exceptionBuilder() throw exceptionBuilder()
.evalError("cannotInferParent") .evalError("cannotInferParent")
@@ -40,6 +40,7 @@ import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.ModuleInfo; import org.pkl.core.runtime.ModuleInfo;
import org.pkl.core.runtime.VmDataSize; import org.pkl.core.runtime.VmDataSize;
import org.pkl.core.runtime.VmDuration; import org.pkl.core.runtime.VmDuration;
import org.pkl.core.runtime.VmUtils;
import org.pkl.core.util.ArrayUtils; import org.pkl.core.util.ArrayUtils;
import org.pkl.core.util.LateInit; import org.pkl.core.util.LateInit;
import org.pkl.parser.Lexer; import org.pkl.parser.Lexer;
@@ -1066,16 +1067,9 @@ public final class SymbolTable {
* A scope where {@code this} has a special meaning (type constraint, object member predicate). * A scope where {@code this} has a special meaning (type constraint, object member predicate).
* *
* <p>Technically, a scope where {@code this} isn't {@code frame.getArguments()[0]}, but the value * <p>Technically, a scope where {@code this} isn't {@code frame.getArguments()[0]}, but the value
* at an auxiliary slot identified by {@link CustomThisScope#FRAME_SLOT_ID}. * at an auxiliary slot identified by {@link VmUtils#CUSTOM_THIS_FRAME_SLOT_ID}.
*/ */
public static final class CustomThisScope extends Scope { public static final class CustomThisScope extends Scope {
public static final Object FRAME_SLOT_ID =
new Object() {
@Override
public String toString() {
return "customThisSlot";
}
};
public CustomThisScope(Scope parent, FrameDescriptorBuilder frameDescriptorBuilder) { public CustomThisScope(Scope parent, FrameDescriptorBuilder frameDescriptorBuilder) {
super( super(
@@ -1,5 +1,5 @@
/* /*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. * Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.EconomicMaps;
@@ -132,7 +131,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot // deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
customThisSlot = customThisSlot =
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID); frame.getFrameDescriptor().findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
} }
} }
} }
@@ -0,0 +1,41 @@
/*
* 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.dsl.Idempotent;
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.member.ObjectMethodNode;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.VmLanguage;
public abstract class AbstractInferParentFromMethodNode extends AbstractInferParentNode {
public AbstractInferParentFromMethodNode(SourceSection sourceSection, VmLanguage language) {
super(sourceSection, language);
}
protected abstract Method getMethod(VirtualFrame frame);
protected abstract @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method);
@Idempotent
protected boolean isFinalType(Method method, @Nullable TypeNode typeNode) {
return method instanceof ObjectMethodNode || (typeNode != null && typeNode.isFinalType());
}
}
@@ -0,0 +1,69 @@
/*
* 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.CompilerDirectives;
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.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmUtils;
public abstract class AbstractInferParentNode extends ExpressionNode {
protected final VmLanguage language;
public AbstractInferParentNode(SourceSection sourceSection, VmLanguage language) {
super(sourceSection);
this.language = language;
}
protected final Object getDefaultValue(
VirtualFrame frame,
@Nullable TypeNode typeNode,
SourceSection headerSection,
String qualifiedName) {
if (typeNode == null || typeNode instanceof UnknownTypeNode) {
return VmDynamic.empty();
}
var defaultValue = typeNode.createDefaultValue(frame, language, headerSection, qualifiedName);
if (defaultValue != null) {
return defaultValue;
}
CompilerDirectives.transferToInterpreter();
if (typeNode instanceof TypeVariableNode) {
throw exceptionBuilder().evalError("cannotInferParent").build();
}
// try to produce a more specific error message than "cannotInstantiateType"
var clazz = typeNode.getVmClass();
if (clazz != null) {
VmUtils.checkIsInstantiable(clazz, typeNode);
}
throw exceptionBuilder()
.evalError("cannotInstantiateType", typeNode.getSourceSection().getCharacters())
.build();
}
}
@@ -21,7 +21,8 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmUtils; import org.pkl.core.runtime.VmUtils;
public abstract sealed class AbstractInvokeLexicalMethodNode extends AbstractInvokeMethodNode public abstract sealed class AbstractInvokeLexicalMethodNode
extends AbstractInvokeLexicalOrQualifiedMethodNode
permits InvokeLexicalClassMethodNode, InvokeLexicalObjectMethodNode { permits InvokeLexicalClassMethodNode, InvokeLexicalObjectMethodNode {
private final int levelsUp; private final int levelsUp;
@@ -0,0 +1,86 @@
/*
* 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.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.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.Identifier;
import org.pkl.core.runtime.VmObjectLike;
/**
* A non-virtual (statically dispatched) method call.
*
* <p>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 AbstractInvokeLexicalOrQualifiedMethodNode
extends AbstractInvokeMethodNode
permits AbstractInvokeQualifiedMethodNode, AbstractInvokeLexicalMethodNode {
protected final Identifier methodName;
private final boolean needsConst;
@Child private @Nullable DirectCallNode callNode;
@CompilationFinal protected boolean isConstChecked;
protected AbstractInvokeLexicalOrQualifiedMethodNode(
SourceSection sourceSection,
Identifier methodName,
ExpressionNode[] argumentNodes,
boolean needsConst) {
super(sourceSection, argumentNodes);
this.methodName = methodName;
this.needsConst = needsConst;
this.isConstChecked = false;
}
protected final Object invoke(VirtualFrame frame, VmObjectLike owner, Object receiver) {
checkConst(owner);
var method = getMethod(owner);
var args = evalArgs(frame, method, owner, receiver);
return getCallNode(method, owner).call(args);
}
private void checkConst(VmObjectLike owner) {
if (!needsConst || isConstChecked) {
return;
}
CompilerDirectives.transferToInterpreterAndInvalidate();
doCheckConst(owner);
isConstChecked = true;
}
protected abstract Method getMethod(VmObjectLike owner);
protected abstract void doCheckConst(VmObjectLike owner);
protected DirectCallNode getCallNode(Method method, VmObjectLike owner) {
if (callNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callNode = DirectCallNode.create(method.getCallTarget(getSourceSection(), owner));
insert(callNode);
}
assert callNode != null;
return callNode;
}
}
@@ -15,79 +15,52 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame; 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.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.ast.member.Method;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmUtils;
/** public abstract class AbstractInvokeMethodNode extends ExpressionNode {
* A non-virtual (statically dispatched) method call.
*
* <p>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; @Children protected final ExpressionNode[] argumentNodes;
private final boolean needsConst;
@Children private ExpressionNode[] argumentNodes;
@Child private @Nullable DirectCallNode callNode;
@CompilationFinal protected boolean isConstChecked;
protected AbstractInvokeMethodNode( public AbstractInvokeMethodNode(SourceSection sourceSection, ExpressionNode[] argumentNodes) {
SourceSection sourceSection,
Identifier methodName,
ExpressionNode[] argumentNodes,
boolean needsConst) {
super(sourceSection); super(sourceSection);
this.methodName = methodName;
this.argumentNodes = argumentNodes; this.argumentNodes = argumentNodes;
this.needsConst = needsConst; }
this.isConstChecked = false;
@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);
} }
@ExplodeLoop @ExplodeLoop
protected final Object invoke(VirtualFrame frame, VmObjectLike owner, Object receiver) { protected Object[] evalArgs(
checkConst(owner); VirtualFrame frame, @Nullable Method method, Object owner, @Nullable Object receiver) {
// TODO: optimize this away when the call does not contain any implicit new args
var methodSlot = getMethodSlot(frame.getFrameDescriptor());
var prevMethod = frame.getAuxiliarySlot(methodSlot);
frame.setAuxiliarySlot(methodSlot, method);
var args = new Object[2 + argumentNodes.length]; var args = new Object[2 + argumentNodes.length];
args[0] = receiver; args[0] = receiver;
args[1] = owner; args[1] = owner;
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame); try {
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
} finally {
frame.setAuxiliarySlot(methodSlot, prevMethod);
} }
return getCallNode(owner).call(args);
}
private void checkConst(VmObjectLike owner) { return args;
if (!needsConst || isConstChecked) {
return;
}
CompilerDirectives.transferToInterpreterAndInvalidate();
doCheckConst(owner);
isConstChecked = true;
}
protected abstract CallTarget getCallTarget(VmObjectLike owner);
protected abstract void doCheckConst(VmObjectLike owner);
protected DirectCallNode getCallNode(VmObjectLike owner) {
if (callNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callNode = DirectCallNode.create(getCallTarget(owner));
insert(callNode);
}
assert callNode != null;
return callNode;
} }
} }
@@ -21,7 +21,8 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmTyped; import org.pkl.core.runtime.VmTyped;
public abstract sealed class AbstractInvokeQualifiedMethodNode extends AbstractInvokeMethodNode public abstract sealed class AbstractInvokeQualifiedMethodNode
extends AbstractInvokeLexicalOrQualifiedMethodNode
permits InvokeQualifiedClassMethodNode, InvokeQualifiedObjectMethodNode { permits InvokeQualifiedClassMethodNode, InvokeQualifiedObjectMethodNode {
@Child private ExpressionNode getReceiverNode; @Child private ExpressionNode getReceiverNode;
@@ -0,0 +1,88 @@
/*
* 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.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;
public InferParentWithinMethodArgumentNode(
SourceSection sourceSection, VmLanguage language, int argIndex) {
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;
}
@Override
protected Method getMethod(VirtualFrame frame) {
var method = (Method) frame.getAuxiliarySlot(getMethodSlot(frame.getFrameDescriptor()));
if (method == null) {
// used in FunctionN.apply()
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().evalError("cannotInferParent").build();
}
return method;
}
@Override
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
return method.getParameterTypeNode(frame, argIndex);
}
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
@Specialization(
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
protected final Object evalCached(
@SuppressWarnings("unused") VirtualFrame frame,
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
@Cached(
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
Object defaultValue) {
return defaultValue;
}
@Specialization(replaces = "evalCached")
protected final Object eval(VirtualFrame frame) {
var method = getMethod(frame);
var typeNode = getTypeNode(frame, method);
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
}
}
@@ -15,66 +15,65 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.member.Method;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
/** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */ /** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */
public final class InferParentWithinMethodNode extends ExpressionNode { public abstract class InferParentWithinMethodNode extends AbstractInferParentFromMethodNode {
private final VmLanguage language;
private final Identifier methodName; private final Identifier methodName;
@Child private @Nullable ExpressionNode ownerNode; @Child private ExpressionNode ownerNode;
@CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinMethodNode( protected InferParentWithinMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
VmLanguage language, VmLanguage language,
Identifier methodName, Identifier methodName,
ExpressionNode ownerNode) { ExpressionNode ownerNode) {
super(sourceSection); super(sourceSection, language);
this.language = language;
this.methodName = methodName; this.methodName = methodName;
this.ownerNode = ownerNode; this.ownerNode = ownerNode;
} }
@Override @Override
public Object executeGeneric(VirtualFrame frame) { protected Method getMethod(VirtualFrame frame) {
if (inferredParent != null) return inferredParent;
// remaining code only runs first time this node is executed
// (assuming evaluation isn't continued despite errors)
CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
assert owner.isPrototype(); assert owner.isPrototype();
var method = owner.getVmClass().getDeclaredMethod(methodName); var method = owner.getVmClass().getDeclaredMethod(methodName);
assert method != null; assert method != null;
return method;
}
var returnTypeNode = method.getReturnTypeNode(); @Override
if (returnTypeNode == null || returnTypeNode instanceof UnknownTypeNode) { protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
inferredParent = VmDynamic.empty(); return method.getReturnTypeNode(frame);
ownerNode = null; }
return inferredParent;
}
var returnTypeDefaultValue = // keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
returnTypeNode.createDefaultValue(
frame, language, method.getHeaderSection(), method.getQualifiedName());
if (returnTypeDefaultValue != null) {
inferredParent = returnTypeDefaultValue;
ownerNode = null;
return inferredParent;
}
throw exceptionBuilder().evalError("cannotInferParent").build(); @Specialization(
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
protected final Object evalCached(
@SuppressWarnings("unused") VirtualFrame frame,
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
@Cached(
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
Object defaultValue) {
return defaultValue;
}
@Specialization(replaces = "evalCached")
protected final Object eval(VirtualFrame frame) {
var method = getMethod(frame);
var typeNode = getTypeNode(frame, method);
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
} }
} }
@@ -15,34 +15,31 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.Method;
import org.pkl.core.ast.member.ObjectMethodNode; import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmDynamic;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
/** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */ /** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */
public final class InferParentWithinObjectMethodNode extends ExpressionNode { public abstract class InferParentWithinObjectMethodNode extends AbstractInferParentFromMethodNode {
private final VmLanguage language;
private final Identifier localMethodName; private final Identifier localMethodName;
@Child private @Nullable ExpressionNode ownerNode; @Child private ExpressionNode ownerNode;
@CompilationFinal private @Nullable Object inferredParent;
public InferParentWithinObjectMethodNode( protected InferParentWithinObjectMethodNode(
SourceSection sourceSection, SourceSection sourceSection,
VmLanguage language, VmLanguage language,
Identifier localMethodName, Identifier localMethodName,
ExpressionNode ownerNode) { ExpressionNode ownerNode) {
super(sourceSection); super(sourceSection, language);
this.language = language;
this.localMethodName = localMethodName; this.localMethodName = localMethodName;
this.ownerNode = ownerNode; this.ownerNode = ownerNode;
@@ -50,15 +47,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
} }
@Override @Override
public Object executeGeneric(VirtualFrame frame) { protected Method getMethod(VirtualFrame frame) {
if (inferredParent != null) return inferredParent;
// remaining code only runs first time this node is executed
// (assuming evaluation isn't continued despite errors)
CompilerDirectives.transferToInterpreter();
assert ownerNode != null;
var owner = (VmObjectLike) ownerNode.executeGeneric(frame); var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
var member = owner.getMember(localMethodName); var member = owner.getMember(localMethodName);
@@ -66,23 +55,32 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
var methodNode = (ObjectMethodNode) member.getMemberNode(); var methodNode = (ObjectMethodNode) member.getMemberNode();
assert methodNode != null; assert methodNode != null;
return methodNode;
}
var returnTypeNode = methodNode.getReturnTypeNode(); @Override
if (returnTypeNode == null || returnTypeNode instanceof UnknownTypeNode) { protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
inferredParent = VmDynamic.empty(); return method.getReturnTypeNode(frame);
ownerNode = null; }
return inferredParent;
}
Object defaultReturnTypeValue = // keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
returnTypeNode.createDefaultValue(
frame, language, member.getHeaderSection(), member.getQualifiedName());
if (defaultReturnTypeValue != null) {
inferredParent = defaultReturnTypeValue;
ownerNode = null;
return inferredParent;
}
throw exceptionBuilder().evalError("cannotInferParent").build(); @Specialization(
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
protected final Object evalCached(
@SuppressWarnings("unused") VirtualFrame frame,
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
@Cached(
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
Object defaultValue) {
return defaultValue;
}
@Specialization(replaces = "evalCached")
protected final Object eval(VirtualFrame frame) {
var method = getMethod(frame);
var typeNode = getTypeNode(frame, method);
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
} }
} }
@@ -15,9 +15,9 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.Method;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -45,9 +45,9 @@ public final class InvokeLexicalClassMethodNode extends AbstractInvokeLexicalMet
} }
@Override @Override
protected CallTarget getCallTarget(VmObjectLike owner) { protected Method getMethod(VmObjectLike owner) {
var method = owner.getVmClass().getDeclaredMethod(methodName); var method = owner.getVmClass().getDeclaredMethod(methodName);
assert method != null; assert method != null;
return method.getCallTarget(getSourceSection()); return method;
} }
} }
@@ -15,10 +15,11 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.member.Method;
import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -43,9 +44,11 @@ public final class InvokeLexicalObjectMethodNode extends AbstractInvokeLexicalMe
} }
@Override @Override
protected CallTarget getCallTarget(VmObjectLike owner) { protected Method getMethod(VmObjectLike owner) {
var method = owner.getMember(methodName); var member = owner.getMember(methodName);
assert method != null && method.isLocal(); assert member != null && member.isLocal();
return (CallTarget) method.getCallTarget().call(owner, owner); var method = (ObjectMethodNode) member.getMemberNode();
assert method != null;
return method;
} }
} }
@@ -17,17 +17,16 @@ package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ClassMethod; import org.pkl.core.ast.member.ClassMethod;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
/** A non-virtual ("direct") method call. Used only for methods on {@code pkl:base}. */ /** A non-virtual ("direct") method call. Used only for methods on {@code pkl:base}. */
public final class InvokeMethodDirectNode extends ExpressionNode { public final class InvokeMethodDirectNode extends AbstractInvokeMethodNode {
private final ClassMethod method;
private final VmObjectLike owner; private final VmObjectLike owner;
@Child private ExpressionNode receiverNode; @Child private ExpressionNode receiverNode;
@Children private final ExpressionNode[] argumentNodes;
@Child private DirectCallNode callNode; @Child private DirectCallNode callNode;
@@ -37,24 +36,17 @@ public final class InvokeMethodDirectNode extends ExpressionNode {
ExpressionNode receiverNode, ExpressionNode receiverNode,
ExpressionNode[] argumentNodes) { ExpressionNode[] argumentNodes) {
super(sourceSection); super(sourceSection, argumentNodes);
this.method = method;
this.owner = method.getOwner(); this.owner = method.getOwner();
this.receiverNode = receiverNode; this.receiverNode = receiverNode;
this.argumentNodes = argumentNodes;
callNode = DirectCallNode.create(method.getCallTarget(sourceSection)); callNode = DirectCallNode.create(method.getCallTarget(sourceSection));
} }
@Override @Override
@ExplodeLoop
public Object executeGeneric(VirtualFrame frame) { public Object executeGeneric(VirtualFrame frame) {
var args = new Object[2 + argumentNodes.length]; var args = evalArgs(frame, method, owner, receiverNode.executeGeneric(frame));
args[0] = receiverNode.executeGeneric(frame);
args[1] = owner;
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
return callNode.call(args); return callNode.call(args);
} }
} }
@@ -26,7 +26,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.GenerateWrapper;
import com.oracle.truffle.api.instrumentation.ProbeNode; import com.oracle.truffle.api.instrumentation.ProbeNode;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
@@ -43,9 +42,8 @@ import org.pkl.core.runtime.VmFunction;
@NodeChild(value = "receiverNode", type = ExpressionNode.class) @NodeChild(value = "receiverNode", type = ExpressionNode.class)
@NodeChild(value = "receiverClassNode", type = GetClassNode.class, executeWith = "receiverNode") @NodeChild(value = "receiverClassNode", type = GetClassNode.class, executeWith = "receiverNode")
@GenerateWrapper @GenerateWrapper
public abstract class InvokeMethodVirtualNode extends ExpressionNode { public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode {
protected final Identifier methodName; protected final Identifier methodName;
@Children private final ExpressionNode[] argumentNodes;
private final MemberLookupMode lookupMode; private final MemberLookupMode lookupMode;
private final boolean needsConst; private final boolean needsConst;
@@ -56,9 +54,8 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
MemberLookupMode lookupMode, MemberLookupMode lookupMode,
boolean needsConst) { boolean needsConst) {
super(sourceSection); super(sourceSection, argumentNodes);
this.methodName = methodName; this.methodName = methodName;
this.argumentNodes = argumentNodes;
this.lookupMode = lookupMode; this.lookupMode = lookupMode;
this.needsConst = needsConst; this.needsConst = needsConst;
} }
@@ -78,7 +75,6 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
public abstract Object executeWith(VirtualFrame frame, Object value, VmClass clazz); public abstract Object executeWith(VirtualFrame frame, Object value, VmClass clazz);
/** Intrinsifies `FunctionN.apply()` calls. */ /** Intrinsifies `FunctionN.apply()` calls. */
@ExplodeLoop
@Specialization(guards = {"methodName == APPLY", "receiver.getCallTarget() == cachedCallTarget"}) @Specialization(guards = {"methodName == APPLY", "receiver.getCallTarget() == cachedCallTarget"})
protected Object evalFunctionCached( protected Object evalFunctionCached(
VirtualFrame frame, VirtualFrame frame,
@@ -87,37 +83,21 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
@Cached("receiver.getCallTarget()") @SuppressWarnings("unused") @Cached("receiver.getCallTarget()") @SuppressWarnings("unused")
RootCallTarget cachedCallTarget, RootCallTarget cachedCallTarget,
@Cached("create(cachedCallTarget)") DirectCallNode callNode) { @Cached("create(cachedCallTarget)") DirectCallNode callNode) {
var args = evalArgs(frame, null, receiver, receiver.getThisValue());
var args = new Object[2 + argumentNodes.length];
args[0] = receiver.getThisValue();
args[1] = receiver;
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
return callNode.call(args); return callNode.call(args);
} }
/** Intrinsifies `FunctionN.apply()` calls. */ /** Intrinsifies `FunctionN.apply()` calls. */
@ExplodeLoop
@Specialization(guards = "methodName == APPLY", replaces = "evalFunctionCached") @Specialization(guards = "methodName == APPLY", replaces = "evalFunctionCached")
protected Object evalFunction( protected Object evalFunction(
VirtualFrame frame, VirtualFrame frame,
VmFunction receiver, VmFunction receiver,
@SuppressWarnings("unused") VmClass receiverClass, @SuppressWarnings("unused") VmClass receiverClass,
@Exclusive @Cached("create()") IndirectCallNode callNode) { @Exclusive @Cached("create()") IndirectCallNode callNode) {
var args = evalArgs(frame, null, receiver, receiver.getThisValue());
var args = new Object[2 + argumentNodes.length];
args[0] = receiver.getThisValue();
args[1] = receiver;
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
return callNode.call(receiver.getCallTarget(), args); return callNode.call(receiver.getCallTarget(), args);
} }
@ExplodeLoop
@Specialization(guards = "receiverClass == cachedReceiverClass") @Specialization(guards = "receiverClass == cachedReceiverClass")
protected Object evalCached( protected Object evalCached(
VirtualFrame frame, VirtualFrame frame,
@@ -126,32 +106,18 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
@Cached("receiverClass") @SuppressWarnings("unused") VmClass cachedReceiverClass, @Cached("receiverClass") @SuppressWarnings("unused") VmClass cachedReceiverClass,
@Cached("resolveMethod(receiverClass)") ClassMethod method, @Cached("resolveMethod(receiverClass)") ClassMethod method,
@Cached("create(method.getCallTarget(sourceSection))") DirectCallNode callNode) { @Cached("create(method.getCallTarget(sourceSection))") DirectCallNode callNode) {
var args = evalArgs(frame, method, method.getOwner(), receiver);
var args = new Object[2 + argumentNodes.length];
args[0] = receiver;
args[1] = method.getOwner();
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
return callNode.call(args); return callNode.call(args);
} }
@ExplodeLoop
@Specialization(replaces = "evalCached") @Specialization(replaces = "evalCached")
protected Object eval( protected Object eval(
VirtualFrame frame, VirtualFrame frame,
Object receiver, Object receiver,
VmClass receiverClass, VmClass receiverClass,
@Exclusive @Cached("create()") IndirectCallNode callNode) { @Exclusive @Cached("create()") IndirectCallNode callNode) {
var method = resolveMethod(receiverClass); var method = resolveMethod(receiverClass);
var args = new Object[2 + argumentNodes.length]; var args = evalArgs(frame, method, method.getOwner(), receiver);
args[0] = receiver;
args[1] = method.getOwner();
for (var i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
// Deprecation should not report here (getCallTarget(sourceSection)), as this happens for each // Deprecation should not report here (getCallTarget(sourceSection)), as this happens for each
// and every call. // and every call.
@@ -15,9 +15,9 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.Method;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -42,9 +42,9 @@ public final class InvokeQualifiedClassMethodNode extends AbstractInvokeQualifie
} }
@Override @Override
protected CallTarget getCallTarget(VmObjectLike owner) { protected Method getMethod(VmObjectLike owner) {
var method = owner.getVmClass().getDeclaredMethod(methodName); var method = owner.getVmClass().getDeclaredMethod(methodName);
assert method != null; assert method != null;
return method.getCallTarget(getSourceSection()); return method;
} }
} }
@@ -15,10 +15,11 @@
*/ */
package org.pkl.core.ast.expression.member; package org.pkl.core.ast.expression.member;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.member.Method;
import org.pkl.core.ast.member.ObjectMethodNode;
import org.pkl.core.runtime.Identifier; import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmObjectLike; import org.pkl.core.runtime.VmObjectLike;
@@ -43,9 +44,11 @@ public final class InvokeQualifiedObjectMethodNode extends AbstractInvokeQualifi
} }
@Override @Override
protected CallTarget getCallTarget(VmObjectLike owner) { protected Method getMethod(VmObjectLike owner) {
var method = owner.getMember(methodName); var member = owner.getMember(methodName);
assert method != null && method.isLocal(); assert member != null && member.isLocal();
return (CallTarget) method.getCallTarget().call(owner, owner); var method = (ObjectMethodNode) member.getMemberNode();
assert method != null;
return method;
} }
} }
@@ -20,7 +20,6 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.member.ClassMethod; import org.pkl.core.ast.member.ClassMethod;
@@ -28,9 +27,8 @@ import org.pkl.core.runtime.Identifier;
import org.pkl.core.runtime.VmFunction; import org.pkl.core.runtime.VmFunction;
import org.pkl.core.runtime.VmUtils; import org.pkl.core.runtime.VmUtils;
public abstract class InvokeSuperMethodNode extends ExpressionNode { public abstract class InvokeSuperMethodNode extends AbstractInvokeMethodNode {
private final Identifier methodName; private final Identifier methodName;
@Children private final ExpressionNode[] argumentNodes;
private final boolean needsConst; private final boolean needsConst;
protected InvokeSuperMethodNode( protected InvokeSuperMethodNode(
@@ -39,29 +37,21 @@ public abstract class InvokeSuperMethodNode extends ExpressionNode {
ExpressionNode[] argumentNodes, ExpressionNode[] argumentNodes,
boolean needsConst) { boolean needsConst) {
super(sourceSection); super(sourceSection, argumentNodes);
this.needsConst = needsConst; this.needsConst = needsConst;
assert !methodName.isLocalMethod(); assert !methodName.isLocalMethod();
this.methodName = methodName; this.methodName = methodName;
this.argumentNodes = argumentNodes;
} }
@ExplodeLoop
@Specialization @Specialization
protected Object eval( protected Object eval(
VirtualFrame frame, VirtualFrame frame,
@Cached(value = "findSupermethod(frame)", neverDefault = true) ClassMethod supermethod, @Cached(value = "findSupermethod(frame)", neverDefault = true) ClassMethod supermethod,
@Cached("create(supermethod.getCallTarget(sourceSection))") DirectCallNode callNode) { @Cached("create(supermethod.getCallTarget(sourceSection))") DirectCallNode callNode) {
var args =
var args = new Object[2 + argumentNodes.length]; evalArgs(frame, supermethod, supermethod.getOwner(), VmUtils.getReceiverOrNull(frame));
args[0] = VmUtils.getReceiverOrNull(frame);
args[1] = supermethod.getOwner();
for (int i = 0; i < argumentNodes.length; i++) {
args[2 + i] = argumentNodes[i].executeGeneric(frame);
}
return callNode.call(args); return callNode.call(args);
} }
@@ -21,7 +21,6 @@ import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.SimpleRootNode; import org.pkl.core.ast.SimpleRootNode;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmUtils; import org.pkl.core.runtime.VmUtils;
@@ -54,7 +53,7 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
int[] parameterSlots) { int[] parameterSlots) {
super(sourceSection); super(sourceSection);
this.expressionNode = expressionNode; this.expressionNode = expressionNode;
frameDescriptor.findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID); frameDescriptor.findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
var rootNode = var rootNode =
new SimpleRootNode( new SimpleRootNode(
VmLanguage.get(this), VmLanguage.get(this),
@@ -18,6 +18,7 @@ package org.pkl.core.ast.member;
import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
@@ -28,7 +29,7 @@ import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit; import org.pkl.core.util.LateInit;
public final class ClassMethod extends ClassMember { public final class ClassMethod extends ClassMember implements Method {
private final List<TypeParameter> typeParameters; private final List<TypeParameter> typeParameters;
// null = not deprecated, "" = no/empty message in the @Deprecated body // null = not deprecated, "" = no/empty message in the @Deprecated body
@@ -91,6 +92,11 @@ public final class ClassMethod extends ClassMember {
return functionNode.getCallTarget(); return functionNode.getCallTarget();
} }
@Override
public CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner) {
return getCallTarget(callSite);
}
public int getParameterCount() { public int getParameterCount() {
return functionNode.getParameterCount(); return functionNode.getParameterCount();
} }
@@ -99,6 +105,11 @@ public final class ClassMethod extends ClassMember {
return functionNode.getReturnTypeNode(); return functionNode.getReturnTypeNode();
} }
@Override
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
return functionNode.getReturnTypeNode();
}
@Override @Override
public String getCallSignature() { public String getCallSignature() {
return functionNode.getCallSignature(); return functionNode.getCallSignature();
@@ -131,4 +142,8 @@ public final class ClassMethod extends ClassMember {
public PClass.Method export(PClass owner) { public PClass.Method export(PClass owner) {
return functionNode.export(owner, docComment, annotations, modifiers, typeParameters); return functionNode.export(owner, docComment, annotations, modifiers, typeParameters);
} }
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
return functionNode.getParameterTypeNode(idx);
}
} }
@@ -81,6 +81,11 @@ public final class FunctionNode extends RegularMemberNode {
return paramCount; return paramCount;
} }
public @Nullable TypeNode getParameterTypeNode(int idx) {
if (idx >= paramCount) return null;
return parameterTypeNodes[idx];
}
public @Nullable TypeNode getReturnTypeNode() { public @Nullable TypeNode getReturnTypeNode() {
return returnTypeNode; return returnTypeNode;
} }
@@ -176,7 +181,7 @@ public final class FunctionNode extends RegularMemberNode {
assert argCount != paramCount; assert argCount != paramCount;
return exceptionBuilder() return exceptionBuilder()
.evalError("wrongFunctionArgumentCount", paramCount, argCount) .evalError("wrongFunctionArgumentCount", paramCount, argCount, paramCount == 1 ? "" : "s")
.withSourceSection(member.getHeaderSection()) .withSourceSection(member.getHeaderSection())
.build(); .build();
} }
@@ -0,0 +1,35 @@
/*
* 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.member;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.runtime.VmObjectLike;
public interface Method {
CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner);
@Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx);
@Nullable TypeNode getReturnTypeNode(VirtualFrame frame);
SourceSection getHeaderSection();
String getQualifiedName();
}
@@ -20,13 +20,14 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode; import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
public final class ObjectMethodNode extends RegularMemberNode { public final class ObjectMethodNode extends RegularMemberNode implements Method {
private final VmLanguage language; private final VmLanguage language;
private final int parameterCount; private final int parameterCount;
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes; @Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
@@ -57,29 +58,50 @@ public final class ObjectMethodNode extends RegularMemberNode {
return functionNode.getReturnTypeNode(); return functionNode.getReturnTypeNode();
} }
@Override
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
return getFunctionNode(frame, true).getReturnTypeNode();
}
@Override
public CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner) {
return (CallTarget) getCallTarget().call(owner, owner);
}
@Override
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
return getFunctionNode(frame, true).getParameterTypeNode(idx);
}
@Override @Override
protected CallTarget executeImpl(VirtualFrame frame) { protected CallTarget executeImpl(VirtualFrame frame) {
if (functionNode == null) { return getFunctionNode(frame, false).getCallTarget();
CompilerDirectives.transferToInterpreter(); }
var parameterTypeNodes = private FunctionNode getFunctionNode(VirtualFrame frame, boolean isPreInit) {
VmUtils.resolveParameterTypes(frame, getFrameDescriptor(), unresolvedParameterTypeNodes); if (functionNode != null) return functionNode;
CompilerDirectives.transferToInterpreterAndInvalidate();
var returnTypeNode = if (isPreInit) {
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null; // TODO: with VmType/CreateDefaultValueNode this may be removable
adoptChildren();
functionNode =
new FunctionNode(
language,
getFrameDescriptor(),
member,
parameterCount,
parameterTypeNodes,
returnTypeNode,
true,
bodyNode);
} }
var parameterTypeNodes =
VmUtils.resolveParameterTypes(frame, getFrameDescriptor(), unresolvedParameterTypeNodes);
return functionNode.getCallTarget(); var returnTypeNode =
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null;
functionNode =
new FunctionNode(
language,
getFrameDescriptor(),
member,
parameterCount,
parameterTypeNodes,
returnTypeNode,
true,
bodyNode);
return functionNode;
} }
} }
@@ -65,15 +65,15 @@ public final class PropertyTypeNode extends PklRootNode {
} }
public @Nullable Object getDefaultValue(VirtualFrame frame) { public @Nullable Object getDefaultValue(VirtualFrame frame) {
if (!defaultValueInitialized) { if (defaultValueInitialized) return defaultValue;
defaultValue =
typeNode.createDefaultValue( defaultValue =
frame, VmLanguage.get(this), getSourceSection(), qualifiedPropertyName); typeNode.createDefaultValue(
// can't cache default value for `module` type in a non-final module because it's a self-type frame, VmLanguage.get(this), getSourceSection(), qualifiedPropertyName);
// (the default value changes when inherited). // can't cache default value for `module` type in a non-final module because it's a self-type
if (typeNode.isFinalType()) { // (the default value changes when inherited).
defaultValueInitialized = true; if (typeNode.isFinalType()) {
} defaultValueInitialized = true;
} }
return defaultValue; return defaultValue;
} }
@@ -16,31 +16,34 @@
package org.pkl.core.ast.type; package org.pkl.core.ast.type;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.expression.member.AbstractInferParentNode;
import org.pkl.core.runtime.*; import org.pkl.core.runtime.*;
import org.pkl.core.util.LateInit;
/** Resolves `<type>` to the type's default value in `new <type> { ... }`. */ /** Resolves `<type>` to the type's default value in `new <type> { ... }`. */
public final class GetParentForTypeNode extends ExpressionNode { public abstract class GetParentForTypeNode extends AbstractInferParentNode {
@Child private UnresolvedTypeNode unresolvedTypeNode; @Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
@Child private @Nullable TypeNode typeNode; @Child private @Nullable TypeNode typeNode;
private final String qualifiedName; protected final String qualifiedName;
@CompilationFinal @LateInit Object defaultValue; protected GetParentForTypeNode(
SourceSection sourceSection,
public GetParentForTypeNode( VmLanguage language,
SourceSection sourceSection, UnresolvedTypeNode unresolvedTypeNode, String qualifiedName) { UnresolvedTypeNode unresolvedTypeNode,
super(sourceSection); String qualifiedName) {
super(sourceSection, language);
this.unresolvedTypeNode = unresolvedTypeNode; this.unresolvedTypeNode = unresolvedTypeNode;
this.qualifiedName = qualifiedName; this.qualifiedName = qualifiedName;
} }
private TypeNode getTypeNode(VirtualFrame frame) { protected TypeNode getTypeNode(VirtualFrame frame) {
if (typeNode == null) { if (typeNode == null) {
assert unresolvedTypeNode != null;
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
typeNode = unresolvedTypeNode.execute(frame); typeNode = unresolvedTypeNode.execute(frame);
adoptChildren(); adoptChildren();
@@ -48,33 +51,19 @@ public final class GetParentForTypeNode extends ExpressionNode {
return typeNode; return typeNode;
} }
@Override @Specialization(guards = {"typeNode.isFinalType()"})
public Object executeGeneric(VirtualFrame frame) { protected final Object evalCached(
//noinspection ConstantValue @SuppressWarnings("unused") VirtualFrame frame,
if (defaultValue != null) return defaultValue; @Bind("getTypeNode(frame)") @SuppressWarnings("unused") TypeNode typeNode,
CompilerDirectives.transferToInterpreterAndInvalidate(); @Cached(
value = "getDefaultValue(frame, typeNode, sourceSection, qualifiedName)",
neverDefault = true)
Object defaultValue) {
return defaultValue;
}
var typeNode = getTypeNode(frame); @Specialization(replaces = "evalCached")
var defaultValue = protected final Object eval(VirtualFrame frame, @Bind("getTypeNode(frame)") TypeNode typeNode) {
typeNode.createDefaultValue(frame, VmLanguage.get(this), sourceSection, qualifiedName); return getDefaultValue(frame, typeNode, sourceSection, qualifiedName);
// can't cache default value for `module`/`this` types in a non-final modules/classes because
// they're self types (the default value changes when inherited).
if (typeNode.isFinalType() && defaultValue != null) {
unresolvedTypeNode = null;
this.defaultValue = defaultValue;
}
if (defaultValue != null) {
return defaultValue;
}
// try to produce a more specific error message than "cannotInstantiateType"
var clazz = typeNode.getVmClass();
if (clazz != null) VmUtils.checkIsInstantiable(clazz, typeNode);
throw exceptionBuilder()
.evalError("cannotInstantiateType", typeNode.getSourceSection().getCharacters())
.build();
} }
} }
@@ -57,7 +57,7 @@ public final class IdentityMixinNode extends PklRootNode {
if (arguments.length != 3) { if (arguments.length != 3) {
CompilerDirectives.transferToInterpreter(); CompilerDirectives.transferToInterpreter();
throw exceptionBuilder() throw exceptionBuilder()
.evalError("wrongFunctionArgumentCount", 1, arguments.length - 2) .evalError("wrongFunctionArgumentCount", 1, arguments.length - 2, "")
.withSourceSection(sourceSection) .withSourceSection(sourceSection)
.build(); .build();
} }
@@ -44,7 +44,6 @@ import org.pkl.core.PklBugException;
import org.pkl.core.StackFrame; import org.pkl.core.StackFrame;
import org.pkl.core.TypeParameter; import org.pkl.core.TypeParameter;
import org.pkl.core.ast.*; import org.pkl.core.ast.*;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
import org.pkl.core.ast.expression.primary.GetModuleNode; import org.pkl.core.ast.expression.primary.GetModuleNode;
import org.pkl.core.ast.expression.primary.GetReceiverClassNode; import org.pkl.core.ast.expression.primary.GetReceiverClassNode;
import org.pkl.core.ast.expression.primary.GetReceiverNode; import org.pkl.core.ast.expression.primary.GetReceiverNode;
@@ -2906,25 +2905,21 @@ public abstract class TypeNode extends PklNode {
return this; return this;
} }
@TruffleBoundary
private int getCustomThisSlot(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.CUSTOM_THIS_FRAME_SLOT_ID);
}
@ExplodeLoop @ExplodeLoop
protected Object executeLazily(VirtualFrame frame, Object value) { protected Object executeLazily(VirtualFrame frame, Object value) {
int customThisSlot;
var numberOfAuxiliarySlots = frame.getFrameDescriptor().getNumberOfAuxiliarySlots();
if (numberOfAuxiliarySlots == 0) {
CompilerDirectives.transferToInterpreterAndInvalidate();
customThisSlot =
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
} else {
// assertion: we only use auxiliary slots for custom `this`.
assert numberOfAuxiliarySlots == 1;
customThisSlot = 0;
}
var ret = childNode.executeLazily(frame, value); var ret = childNode.executeLazily(frame, value);
var localContext = language.localContext.get(); var localContext = language.localContext.get();
var prevShouldTypeCheck = localContext.shouldEagerTypecheck(); var prevShouldTypeCheck = localContext.shouldEagerTypecheck();
localContext.shouldEagerTypecheck(true); localContext.shouldEagerTypecheck(true);
frame.setAuxiliarySlot(customThisSlot, value); frame.setAuxiliarySlot(getCustomThisSlot(frame.getFrameDescriptor()), value);
try { try {
for (var node : constraintNodes) { for (var node : constraintNodes) {
node.execute(frame); node.execute(frame);
@@ -52,7 +52,6 @@ import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.SimpleRootNode; import org.pkl.core.ast.SimpleRootNode;
import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.builder.AstBuilder; import org.pkl.core.ast.builder.AstBuilder;
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
import org.pkl.core.ast.expression.primary.CustomThisNode; import org.pkl.core.ast.expression.primary.CustomThisNode;
import org.pkl.core.ast.expression.primary.ThisNode; import org.pkl.core.ast.expression.primary.ThisNode;
import org.pkl.core.ast.member.*; import org.pkl.core.ast.member.*;
@@ -77,6 +76,22 @@ public final class VmUtils {
public static final URI REPL_TEXT_URI = URI.create(REPL_TEXT); public static final URI REPL_TEXT_URI = URI.create(REPL_TEXT);
public static final Object CUSTOM_THIS_FRAME_SLOT_ID =
new Object() {
@Override
public String toString() {
return "customThisSlot";
}
};
public static final Object METHOD_FRAME_SLOT_ID =
new Object() {
@Override
public String toString() {
return "method";
}
};
private static final Engine PKL_ENGINE = private static final Engine PKL_ENGINE =
Engine.newBuilder("pkl").option("engine.WarnInterpreterOnly", "false").build(); Engine.newBuilder("pkl").option("engine.WarnInterpreterOnly", "false").build();
@@ -1066,7 +1081,7 @@ public final class VmUtils {
} }
public static int findCustomThisSlot(VirtualFrame frame) { public static int findCustomThisSlot(VirtualFrame frame) {
var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CustomThisScope.FRAME_SLOT_ID); var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CUSTOM_THIS_FRAME_SLOT_ID);
assert result != null; assert result != null;
return result; return result;
} }
@@ -310,7 +310,7 @@ cannotDefineExternalMember=\
External members can only be defined by standard library modules. External members can only be defined by standard library modules.
wrongFunctionArgumentCount=\ wrongFunctionArgumentCount=\
Expected {0} function arguments but got {1}. Expected {0} function argument{2} but got {1}.
noOuterScope=\ noOuterScope=\
Top-level scope does not have an outer scope. Top-level scope does not have an outer scope.
@@ -0,0 +1,50 @@
open module inferParameterType
import "pkl:test"
hidden x: Int
open class Foo extends module {
function bar(baz: Foo): Int = super.bar(baz) * this.x
function superBar(baz: Foo): Int = super.bar(new { x = 5 + baz.x }) * this.x
function thisBar(baz: Foo): Int = this.x + baz.x
}
class Qux extends Foo {
function bar(baz: Foo): Int = thisBar(new { x = baz.x })
}
function bar(baz: Foo): Int = baz.x
const function outerMethod(a: Int, b: Qux) = a + b.x * 2
local quux = (a: Foo) -> a.x
qualified = this.bar(new { x = 1 })
unqualifiedLexical = bar(new { x = 1 })
unqualifiedThis = new Qux { x = 7 }.bar(new { x = 1 })
`super` = new Foo { x = 2 }.superBar(new Foo { x = 3 })
nestedMethodCalls = outerMethod(bar(new { x = 8 }), new { x = 9 })
objectMethod {
local function corge(prop: Foo) = prop
call = corge(new { x = 0 }).x
tooManyParams = test.catch(() -> corge(new {}, new {}))
}
intrinsicConstructor = test.catch(() -> List(new {}))
genericMethod = test.catch(() -> Pair(new {}, new {}))
fnApply = test.catch(() -> quux.apply(new {}))
tooManyParams = test.catch(() -> bar(new {}, new {}))
class Bar1 { x: Int = 1 }
class Bar2 { x: Int = 2 }
class C1 { function f(p: Bar1) = p.x }
class C2 { function f(p: Bar2) = p.x * 10 }
local function call(a) = a.f(new {}) // poly call
polyCall1 = call(new C1 {})
polyCall2 = call(new C2 {})
@@ -139,18 +139,18 @@ one {
res31 { res31 {
name = "Other" name = "Other"
} }
res32 = "Expected 1 function arguments but got 2." res32 = "Expected 1 function argument but got 2."
res33 { res33 {
name = "Override" name = "Override"
} }
res34 = "Expected 1 function arguments but got 2." res34 = "Expected 1 function argument but got 2."
res35 { res35 {
name = "Other" name = "Other"
} }
res36 = "Expected 1 function arguments but got 2." res36 = "Expected 1 function argument but got 2."
res37 = "Expected value of type `new#Person`, but got type `Int`. Value: 1" res37 = "Expected value of type `new#Person`, but got type `Int`. Value: 1"
res38 { res38 {
name = "Override" name = "Override"
} }
res39 = "Expected 1 function arguments but got 2." res39 = "Expected 1 function argument but got 2."
res40 = "Expected value of type `new#Person`, but got type `Int`. Value: 1" res40 = "Expected value of type `new#Person`, but got type `Int`. Value: 1"
@@ -63,20 +63,20 @@ res8 {
res9 { res9 {
name = "Other" name = "Other"
} }
res9b = "Expected 1 function arguments but got 2." res9b = "Expected 1 function argument but got 2."
res9c { res9c {
name = "Override" name = "Override"
} }
res9d = "Expected 1 function arguments but got 2." res9d = "Expected 1 function argument but got 2."
res10 { res10 {
name = "Other" name = "Other"
} }
res10b = "Expected 1 function arguments but got 2." res10b = "Expected 1 function argument but got 2."
res10c = "Expected value of type `newType#Person`, but got type `Int`. Value: 1" res10c = "Expected value of type `newType#Person`, but got type `Int`. Value: 1"
res10d { res10d {
name = "Override" name = "Override"
} }
res10e = "Expected 1 function arguments but got 2." res10e = "Expected 1 function argument but got 2."
res10f = "Expected value of type `newType#Person`, but got type `Int`. Value: 1" res10f = "Expected value of type `newType#Person`, but got type `Int`. Value: 1"
res11 { res11 {
name = "Pigeon" name = "Pigeon"
@@ -0,0 +1,15 @@
qualified = 1
unqualifiedLexical = 1
unqualifiedThis = 8
`super` = 16
nestedMethodCalls = 26
objectMethod {
call = 0
tooManyParams = "Expected 1 function argument but got 2."
}
intrinsicConstructor = "Cannot tell which parent to amend."
genericMethod = "Cannot tell which parent to amend."
fnApply = "Cannot tell which parent to amend."
tooManyParams = "Expected 1 function argument but got 2."
polyCall1 = 1
polyCall2 = 20