Add user generic methods (#1849)
This commit is contained in:
1 parent
939e844c03
commit
21d278eba5
160 files changed
+2597
-880
No files matched your search
@@ -2100,6 +2100,8 @@ The xref:blog:ROOT:class-as-a-function.adoc[Class-as-a-function] pattern may be
|
||||
TIP: In most cases, methods without parameters should not be defined.
|
||||
Instead, use <<fixed-properties,`fixed` properties>> on the module or class.
|
||||
|
||||
Methods may also be <<generic-methods,generic>>.
|
||||
|
||||
[[modules]]
|
||||
== Modules
|
||||
|
||||
@@ -3282,7 +3284,8 @@ This section discusses language features that are generally more relevant to tem
|
||||
<<projects,Projects>> +
|
||||
<<external-readers,External Readers>> +
|
||||
<<mirroring_packages,Mirroring packages>> +
|
||||
<<references,References>>
|
||||
<<references,References>> +
|
||||
<<generic-methods,Generic Methods>>
|
||||
|
||||
[[meaning-of-new]]
|
||||
=== Meaning of `new`
|
||||
@@ -5994,32 +5997,13 @@ There are some restrictions on properties that may be referenced; attempting to
|
||||
* Properties originally defined in `external` classes (this only includes `Module.output`)
|
||||
|
||||
pkldoc:Reference[pkl:ref] instances are created using the constructor method pkldoc:#Reference()[pkl:ref].
|
||||
This method accepts three parameters:
|
||||
This method accepts two type arguments and two parameters:
|
||||
|
||||
* `domain` - An instance of a `ref.Domain` subclass; its type becomes the `D` type argument of the resulting `Reference` instance.
|
||||
* `class` - A `Class` instance that will be the referent type of the returned `Reference`; it is the `T` type argument of the resulting instance.
|
||||
* `D` - The type of the reference's domain. Must be a subclass of `ref.Domain`.
|
||||
* `Referent` - The type the reference refers to.
|
||||
* `domain` - An instance of `D` that becomes the domain resulting `Reference` instance.
|
||||
* `data` - A domain-specific value used to identify the root or context of the reference.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
It is only possible to construct new references with a referent type that is a simple (non-generic) class type.
|
||||
To obtain a reference to values of other types (generic, typealias, nullable, union, etc.), libraries must wrap those types in a class:
|
||||
|
||||
[source%parsed,pkl]
|
||||
----
|
||||
import "pkl:ref"
|
||||
|
||||
class TypeHolder {
|
||||
$: *Listing<String>? | Mapping<UInt, String> // <1>
|
||||
}
|
||||
|
||||
local r: ref.Reference<MyDomain, Listing<String>? | Mapping<UInt, String>> =
|
||||
ref.Reference(myDomain, TypeHolder, null).$ // <2>
|
||||
----
|
||||
<1> Define a "holder" class with a property of the desired type.
|
||||
<2> Access the `$` property of the reference to get a reference to the desired non-class type.
|
||||
====
|
||||
|
||||
Here's a realistic example of how references might be used:
|
||||
|
||||
[source,pkl]
|
||||
@@ -6027,7 +6011,7 @@ Here's a realistic example of how references might be used:
|
||||
----
|
||||
import "pkl:ref"
|
||||
|
||||
class Domain extends ref.Domain { // <1>
|
||||
class Domain extends ref.AbstractDomain { // <1>
|
||||
function renderReference(reference: ref.Reference<Domain, Any>): String =
|
||||
if (reference.getData() is Task)
|
||||
let (path = reference.getPath().map((access) -> access.property ?? access.key.toString()))
|
||||
@@ -6038,18 +6022,20 @@ class Domain extends ref.Domain { // <1>
|
||||
|
||||
typealias Ref<T> = ref.Reference<Domain, T> // <2>
|
||||
|
||||
abstract class Task { // <3>
|
||||
const function Ref<T>(data: Any): Ref<T> = ref.Reference::<Domain, T>(new Domain {}, data) // <3>
|
||||
|
||||
abstract class Task { // <4>
|
||||
name: String
|
||||
}
|
||||
|
||||
class TaskA extends Task {
|
||||
input1: String | Ref<String> // <4>
|
||||
input1: String | Ref<String> // <5>
|
||||
input2: Int | Ref<Int>
|
||||
|
||||
hidden fixed outputs: Ref<TaskAOutputs> = ref.Reference(new Domain {}, TaskAOutputs, this) // <5>
|
||||
hidden fixed outputs: Ref<TaskAOutputs> = Ref::<TaskAOutputs>(this) // <6>
|
||||
}
|
||||
|
||||
abstract class TaskAOutputs { // <6>
|
||||
abstract class TaskAOutputs { // <7>
|
||||
foo: String
|
||||
bar: Int
|
||||
}
|
||||
@@ -6059,24 +6045,25 @@ class TaskB extends Task {
|
||||
inputY: Int | Ref<Int>
|
||||
}
|
||||
|
||||
tasks: Listing<Task>(isDistinctBy((it) -> it.name)) // <7>
|
||||
tasks: Listing<Task>(isDistinctBy((it) -> it.name)) // <8>
|
||||
|
||||
output {
|
||||
renderer {
|
||||
converters {
|
||||
[ref.Reference] = (it) -> it.toString() // <8>
|
||||
[ref.Reference] = (it) -> it.toString() // <9>
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Define a `ref.Domain` subclass that determines how `Reference` instances are rendered as strings.
|
||||
<2> For convenience, create a type alias for references of this domain.
|
||||
<3> Create a new base class that can be referenced. `Task` instances will be identified by their `name` value.
|
||||
<4> In a specific `Task` subclass, input values accept both literal values and references to the same type.
|
||||
<5> `Task` subclasses that provide output values define an `outputs` property where the referent type is `TaskAOutputs`.
|
||||
<6> The `TaskAOutputs` class defines the possible runtime outputs of `TaskA` tasks. It is `abstract` and never instantiated, only used for producing references.
|
||||
<7> Users provide `Task` instances with unique `name` values.
|
||||
<8> Delegate to `Domain` for rendering `Reference` values.
|
||||
<3> For convenience, wrap the `Reference` constructor to pass appropriate the domain type and value.
|
||||
<4> Create a new base class that can be referenced. `Task` instances will be identified by their `name` value.
|
||||
<5> In a specific `Task` subclass, input values accept both literal values and references to the same type.
|
||||
<6> `Task` subclasses that provide output values define an `outputs` property where the referent type is `TaskAOutputs`.
|
||||
<7> The `TaskAOutputs` class defines the possible runtime outputs of `TaskA` tasks. It is `abstract` and never instantiated, only used for producing references.
|
||||
<8> Users provide `Task` instances with unique `name` values.
|
||||
<9> Delegate to `Domain` for rendering `Reference` values.
|
||||
|
||||
[source,pkl]
|
||||
.myWorkflow.pkl
|
||||
@@ -6119,3 +6106,37 @@ tasks {
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[generic-methods]]
|
||||
=== Generic Methods
|
||||
|
||||
Methods may be declared as "generic", with types that are themselves parameters.
|
||||
Method type parameters may be used within the method anywhere else a type may be used: in a parameter's type, in the return type, or within the method body (e.g. <<defining-objects,object instantiation>>, <<type-tests,type tests>>, <<type-casts,type casts>>).
|
||||
|
||||
When calling a generic method, a caller may manually specify type arguments by writing `methodName::<TypeArg1, TypeArg2>(arg1, arg2)`.
|
||||
When no type arguments are provided, type checks of parameter types behave like <<unknown-type,`unknown`>> and always succeed.
|
||||
|
||||
[source,pkl]
|
||||
----
|
||||
class Pigeon {
|
||||
name: String
|
||||
}
|
||||
|
||||
class Hawk {
|
||||
name: String
|
||||
}
|
||||
|
||||
function makeFlock<Bird>(names: Listing<String>): Listing<Bird> = new {
|
||||
for (n in names) {
|
||||
new { name = n }
|
||||
}
|
||||
}
|
||||
|
||||
pigeonFlock = makeFlock::<Pigeon>(new { "Alice"; "Bill"; "Ruth" }) // <1>
|
||||
hawkFlock = makeFlock::<Hawk>(new { "Florence"; "Holly"; "Jim" }) // <2>
|
||||
----
|
||||
<1> Result: a `Listing` where every element is a `Pigeon`.
|
||||
<2> Result: a `Listing` where every element is a `Hawk`.
|
||||
|
||||
NOTE: The `::<>` syntax is colloquially known as the "turbofish", after https://doc.rust-lang.org/reference/glossary.html#turbofish[the same syntax in Rust].
|
||||
Pkl requires this syntax for the same reason as Rust: to avoid parser ambiguity.
|
||||
@@ -62,7 +62,7 @@ Library authors describe types as `ref.Reference<D, T>`, where `D` is the _domai
|
||||
----
|
||||
import "pkl:ref"
|
||||
|
||||
class MyDomain extends ref.Domain { // <1>
|
||||
class MyDomain extends ref.AbstractDomain { // <1>
|
||||
function renderReference(ref: ref.Reference<MyDomain, Any>): String =
|
||||
"${ " + ref.getData() + "." + ref.getPath().join(".") + " }"
|
||||
}
|
||||
|
||||
@@ -29,8 +29,18 @@ import org.pkl.core.runtime.*;
|
||||
@NodeInfo(language = "Pkl")
|
||||
@TypeSystemReference(VmTypes.class)
|
||||
public abstract class PklRootNode extends RootNode {
|
||||
private final boolean propagateTypeMismatchExceptions;
|
||||
|
||||
protected PklRootNode(@Nullable VmLanguage language, FrameDescriptor descriptor) {
|
||||
this(language, descriptor, false);
|
||||
}
|
||||
|
||||
protected PklRootNode(
|
||||
@Nullable VmLanguage language,
|
||||
FrameDescriptor descriptor,
|
||||
boolean propagateTypeMismatchExceptions) {
|
||||
super(language, descriptor);
|
||||
this.propagateTypeMismatchExceptions = propagateTypeMismatchExceptions;
|
||||
}
|
||||
|
||||
public abstract SourceSection getSourceSection();
|
||||
@@ -46,7 +56,7 @@ public abstract class PklRootNode extends RootNode {
|
||||
return executeImpl(frame);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
throw propagateTypeMismatchExceptions ? e : e.toVmException();
|
||||
} catch (VmException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,8 +31,7 @@ public final class SimpleRootNode extends PklRootNode {
|
||||
SourceSection sourceSection,
|
||||
String qualifiedName,
|
||||
ExpressionNode bodyNode) {
|
||||
|
||||
super(language, descriptor);
|
||||
super(language, descriptor, false);
|
||||
this.sourceSection = sourceSection;
|
||||
this.qualifiedName = qualifiedName;
|
||||
this.bodyNode = bodyNode;
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.PClassInfo;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.SecurityManagerException;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.TypeParameter.Variance;
|
||||
import org.pkl.core.ast.ByteConstantValueNode;
|
||||
import org.pkl.core.ast.ConstantNode;
|
||||
@@ -189,6 +188,7 @@ import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||
import org.pkl.core.runtime.FrameSlotVariable;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.ModuleResolver;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.runtime.VmBytes;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmContext;
|
||||
@@ -285,6 +285,7 @@ import org.pkl.parser.syntax.Type.UnionType;
|
||||
import org.pkl.parser.syntax.Type.UnknownType;
|
||||
import org.pkl.parser.syntax.TypeAlias;
|
||||
import org.pkl.parser.syntax.TypeAnnotation;
|
||||
import org.pkl.parser.syntax.TypeArgumentList;
|
||||
import org.pkl.parser.syntax.TypeParameterList;
|
||||
|
||||
public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
@@ -487,9 +488,13 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
if (args == null) {
|
||||
if (identifier.getIdentifiers().size() == 1) {
|
||||
var text = identifier.getIdentifiers().get(0).getValue();
|
||||
var typeParameter = symbolTable.findTypeParameter(text);
|
||||
if (typeParameter != null) {
|
||||
return new UnresolvedTypeNode.TypeVariable(createSourceSection(type), typeParameter);
|
||||
var typeParameterResolution = symbolTable.getCurrentScope().resolveTypeParameter(text);
|
||||
if (typeParameterResolution != null) {
|
||||
return new UnresolvedTypeNode.TypeVariable(
|
||||
createSourceSection(type),
|
||||
typeParameterResolution.typeParameter(),
|
||||
typeParameterResolution.ownerType(),
|
||||
typeParameterResolution.levelsUp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,8 +877,20 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
}
|
||||
|
||||
private UnresolvedTypeNode @Nullable [] doVisitMethodTypeArguments(
|
||||
@Nullable TypeArgumentList typeArgumentList) {
|
||||
if (typeArgumentList == null) return null;
|
||||
var types = typeArgumentList.getTypes();
|
||||
var res = new UnresolvedTypeNode[types.size()];
|
||||
for (var i = 0; i < res.length; i++) {
|
||||
res[i] = visitType(types.get(i));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private ExpressionNode resolvedMethodCall(UnqualifiedAccessExpr expr, ArgumentList argList) {
|
||||
var name = expr.getIdentifier().getValue();
|
||||
var typeArgs = doVisitMethodTypeArguments(expr.getTypeArgumentList());
|
||||
var scope = symbolTable.getCurrentScope();
|
||||
var sourceSection = createSourceSection(expr);
|
||||
var constLevel = scope.getConstLevel();
|
||||
@@ -897,45 +914,67 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return new InvokeQualifiedObjectMethodNode(
|
||||
sourceSection,
|
||||
identifier,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
needsConst,
|
||||
getModuleNode,
|
||||
argInfo.methodSlot);
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName());
|
||||
}
|
||||
if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) {
|
||||
return new InvokeQualifiedClassMethodNode(
|
||||
sourceSection,
|
||||
identifier,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
needsConst,
|
||||
getModuleNode,
|
||||
argInfo.methodSlot);
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName());
|
||||
}
|
||||
return InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
identifier,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
MemberLookupMode.IMPLICIT_LEXICAL,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName(),
|
||||
getModuleNode,
|
||||
GetClassNodeGen.create(null));
|
||||
}
|
||||
if (method.isObjectMethod()) {
|
||||
return new InvokeLexicalObjectMethodNode(
|
||||
sourceSection, identifier, levelsUp, argInfo.arguments, needsConst, argInfo.methodSlot);
|
||||
sourceSection,
|
||||
identifier,
|
||||
levelsUp,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName());
|
||||
}
|
||||
if (method.isOnClosedClass() || method.isLocal() || method.isExternal()) {
|
||||
return new InvokeLexicalClassMethodNode(
|
||||
sourceSection, identifier, levelsUp, argInfo.arguments, needsConst, argInfo.methodSlot);
|
||||
sourceSection,
|
||||
identifier,
|
||||
levelsUp,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName());
|
||||
}
|
||||
return InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
identifier,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
MemberLookupMode.IMPLICIT_LEXICAL,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName(),
|
||||
levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp),
|
||||
GetClassNodeGen.create(null));
|
||||
} else if (resolution instanceof ImplicitBaseMethod) {
|
||||
@@ -962,8 +1001,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
createSourceSection(expr),
|
||||
method,
|
||||
new ConstantValueNode(baseModule),
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
argInfo.methodSlot);
|
||||
argInfo.methodSlot,
|
||||
scope.getQualifiedName());
|
||||
}
|
||||
} else if (resolution instanceof ImplicitThisMethod) {
|
||||
var isCustomThis = scope.isCustomThisScope();
|
||||
@@ -974,10 +1015,12 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
org.pkl.core.runtime.Identifier.get(name),
|
||||
typeArgs,
|
||||
arguments,
|
||||
MemberLookupMode.IMPLICIT_THIS,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
scope.getQualifiedName(),
|
||||
VmUtils.createThisNode(VmUtils.unavailableSourceSection(), isCustomThis),
|
||||
GetClassNodeGen.create(null));
|
||||
} else {
|
||||
@@ -1154,9 +1197,16 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
.build();
|
||||
}
|
||||
|
||||
var typeArgs = doVisitMethodTypeArguments(expr.getTypeArgumentList());
|
||||
var argInfo = visitArgumentList(argCtx);
|
||||
return InvokeSuperMethodNodeGen.create(
|
||||
sourceSection, memberName, argInfo.arguments, needsConst, argInfo.methodSlot);
|
||||
sourceSection,
|
||||
memberName,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
currentScope.getQualifiedName());
|
||||
}
|
||||
|
||||
// superproperty call
|
||||
@@ -1318,10 +1368,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
public ExpressionNode visitFunctionLiteralExpr(FunctionLiteralExpr expr) {
|
||||
var sourceSection = createSourceSection(expr);
|
||||
var params = expr.getParameterList();
|
||||
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(params);
|
||||
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(params, null);
|
||||
var paramCount = params.getParameters().size();
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||
var bindings = descriptorBuilderAndBindings.second;
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.builder;
|
||||
var bindings = descriptorBuilderAndBindings.bindings;
|
||||
if (paramCount > 5) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("tooManyFunctionParameters")
|
||||
@@ -1688,7 +1738,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
modifiers,
|
||||
PClassInfo.forModuleClass(
|
||||
moduleInfo.getModuleName(), moduleInfo.getModuleKey().getUri()),
|
||||
List.of(),
|
||||
new TypeParameter[0],
|
||||
moduleInfo,
|
||||
supertypeNode,
|
||||
moduleProperties,
|
||||
@@ -1855,10 +1905,18 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
@Override
|
||||
public ObjectMember visitClass(Class clazz) {
|
||||
|
||||
var sourceSection = createSourceSection(clazz);
|
||||
var headerSection = createSourceSection(clazz.getHeaderSpan());
|
||||
|
||||
var typeParameters = visitTypeParameterList(clazz.getTypeParameterList());
|
||||
var typeParameterList = clazz.getTypeParameterList();
|
||||
if (clazz.getTypeParameterList() != null && !isStdLibModule) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotDeclareTypeParameter")
|
||||
.withSourceSection(createSourceSection(typeParameterList.getParameters().get(0)))
|
||||
.build();
|
||||
}
|
||||
var typeParameters = visitTypeParameterList(typeParameterList, null);
|
||||
|
||||
var modifiers =
|
||||
doVisitModifiers(
|
||||
@@ -2108,8 +2166,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
public UnresolvedMethodNode visitClassMethod(ClassMethod entry) {
|
||||
var headerSection = createSourceSection(entry.getHeaderSpan());
|
||||
|
||||
var typeParameters = visitTypeParameterList(entry.getTypeParameterList());
|
||||
|
||||
var modifiers =
|
||||
doVisitModifiers(
|
||||
entry.getModifiers(), VmModifier.VALID_METHOD_MODIFIERS, "invalidMethodModifier");
|
||||
@@ -2119,11 +2175,15 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
var bodyContext = entry.getExpr();
|
||||
var paramListCtx = entry.getParameterList();
|
||||
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(paramListCtx);
|
||||
var descriptorBuilderAndBindings =
|
||||
createFrameDescriptorBuilderAndSlotVariables(paramListCtx, entry.getTypeParameterList());
|
||||
var paramCount = paramListCtx.getParameters().size();
|
||||
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||
var bindings = descriptorBuilderAndBindings.second;
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.builder;
|
||||
var bindings = descriptorBuilderAndBindings.bindings;
|
||||
var typeParameters =
|
||||
visitTypeParameterList(
|
||||
entry.getTypeParameterList(), descriptorBuilderAndBindings.typeBindings);
|
||||
|
||||
var annotations = doVisitAnnotations(entry.getAnnotations(), methodName);
|
||||
|
||||
@@ -2204,7 +2264,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var isLocal = VmModifier.isLocal(modifiers);
|
||||
var name = org.pkl.core.runtime.Identifier.property(typeAlias.getName().getValue(), isLocal);
|
||||
|
||||
var typeParameters = visitTypeParameterList(typeAlias.getTypeParameterList());
|
||||
var typeParameters = visitTypeParameterList(typeAlias.getTypeParameterList(), null);
|
||||
var objectMemberModifiers =
|
||||
isLocal ? VmModifier.LOCAL_TYPEALIAS_OBJECT_MEMBER : VmModifier.TYPEALIAS_OBJECT_MEMBER;
|
||||
|
||||
@@ -2274,41 +2334,43 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return (ExpressionNode) expr.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeParameter> visitTypeParameterList(@Nullable TypeParameterList ctx) {
|
||||
if (ctx == null) return List.of();
|
||||
|
||||
if (!(ctx.parent() instanceof TypeAlias) && !isStdLibModule) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotDeclareTypeParameter")
|
||||
.withSourceSection(createSourceSection(ctx.getParameters().get(0)))
|
||||
.build();
|
||||
}
|
||||
public TypeParameter[] visitTypeParameterList(
|
||||
@Nullable TypeParameterList ctx, FrameSlotVariable @Nullable [] typeBindings) {
|
||||
if (ctx == null) return new TypeParameter[0];
|
||||
|
||||
var params = ctx.getParameters();
|
||||
var size = params.size();
|
||||
var result = new ArrayList<TypeParameter>(size);
|
||||
var result = new TypeParameter[size];
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
var paramCtx = params.get(i);
|
||||
Variance variance;
|
||||
var nodeVariance = paramCtx.getVariance();
|
||||
if (nodeVariance == null) {
|
||||
variance = TypeParameter.Variance.INVARIANT;
|
||||
variance = org.pkl.core.TypeParameter.Variance.INVARIANT;
|
||||
} else {
|
||||
variance =
|
||||
switch (nodeVariance) {
|
||||
case IN -> TypeParameter.Variance.CONTRAVARIANT;
|
||||
case OUT -> TypeParameter.Variance.COVARIANT;
|
||||
case IN -> org.pkl.core.TypeParameter.Variance.CONTRAVARIANT;
|
||||
case OUT -> org.pkl.core.TypeParameter.Variance.COVARIANT;
|
||||
};
|
||||
}
|
||||
var parameterName = paramCtx.getIdentifier().getValue();
|
||||
if (result.stream().anyMatch(it -> it.getName().equals(parameterName))) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("duplicateTypeParameter", parameterName)
|
||||
.withSourceSection(createSourceSection(paramCtx))
|
||||
.build();
|
||||
for (var j = 0; j < i; j++) {
|
||||
if (result[j].name().equals(parameterName)) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("duplicateTypeParameter", parameterName)
|
||||
.withSourceSection(createSourceSection(paramCtx))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
result.add(new TypeParameter(variance, parameterName, i));
|
||||
result[i] =
|
||||
new TypeParameter(
|
||||
createSourceSection(paramCtx),
|
||||
typeBindings != null ? typeBindings[i].slot() : -1,
|
||||
i,
|
||||
parameterName,
|
||||
variance);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2417,9 +2479,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var bindings =
|
||||
parametersDescriptorAndBindings == null
|
||||
? new FrameSlotVariable[0]
|
||||
: parametersDescriptorAndBindings.second;
|
||||
: parametersDescriptorAndBindings.bindings;
|
||||
var parametersFrameDescriptorBuilder =
|
||||
parametersDescriptorAndBindings == null ? null : parametersDescriptorAndBindings.first;
|
||||
parametersDescriptorAndBindings == null ? null : parametersDescriptorAndBindings.builder;
|
||||
|
||||
return symbolTable.enterObjectScope(
|
||||
bindings,
|
||||
@@ -2437,9 +2499,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
// constraints (e.g. containing `let` expressions) may add further slots to it
|
||||
var parameterTypes = doVisitParameterTypes(body);
|
||||
var parametersDescriptor =
|
||||
parametersDescriptorAndBindings == null
|
||||
parametersFrameDescriptorBuilder == null
|
||||
? null
|
||||
: parametersDescriptorAndBindings.first.build();
|
||||
: parametersFrameDescriptorBuilder.build();
|
||||
|
||||
var members = EconomicMaps.<Object, ObjectMember>create();
|
||||
var elements = new ArrayList<ObjectMember>();
|
||||
@@ -2782,6 +2844,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
Expr expr,
|
||||
@Nullable TypeAnnotation typeAnnotation,
|
||||
boolean isModuleMethod) {
|
||||
|
||||
var modifiers =
|
||||
doVisitModifiers(
|
||||
modifierNodes, VmModifier.VALID_OBJECT_MEMBER_MODIFIERS, "invalidObjectMemberModifier");
|
||||
@@ -2795,25 +2858,21 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
var methodName = org.pkl.core.runtime.Identifier.method(identifier.getValue(), true);
|
||||
|
||||
var frameDescriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(paramList);
|
||||
var frameDescriptorBuilderAndBindings =
|
||||
createFrameDescriptorBuilderAndSlotVariables(paramList, typeParamList);
|
||||
|
||||
var frameDescriptorBuilder = frameDescriptorBuilderAndBindings.first;
|
||||
var bindings = frameDescriptorBuilderAndBindings.second;
|
||||
var frameDescriptorBuilder = frameDescriptorBuilderAndBindings.builder;
|
||||
var bindings = frameDescriptorBuilderAndBindings.bindings;
|
||||
var typeParameters =
|
||||
visitTypeParameterList(typeParamList, frameDescriptorBuilderAndBindings.typeBindings);
|
||||
|
||||
return symbolTable.enterMethod(
|
||||
methodName,
|
||||
getConstLevel(modifiers),
|
||||
bindings,
|
||||
frameDescriptorBuilder,
|
||||
List.of(),
|
||||
typeParameters,
|
||||
scope -> {
|
||||
if (typeParamList != null) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotDeclareTypeParameter")
|
||||
.withSourceSection(createSourceSection(typeParamList))
|
||||
.build();
|
||||
}
|
||||
|
||||
var member =
|
||||
new ObjectMember(
|
||||
createSourceSection(method),
|
||||
@@ -2831,6 +2890,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
member,
|
||||
body,
|
||||
paramList.getParameters().size(),
|
||||
typeParameters,
|
||||
parameterTypeNodes,
|
||||
typeNode);
|
||||
|
||||
@@ -2919,9 +2979,11 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
private ExpressionNode doVisitMethodAccessExpr(QualifiedAccessExpr expr, ArgumentList argList) {
|
||||
var sourceSection = createSourceSection(expr);
|
||||
var functionName = toIdentifier(expr.getIdentifier().getValue());
|
||||
var typeArgs = doVisitMethodTypeArguments(expr.getTypeArgumentList());
|
||||
var receiver = visitExpr(expr.getExpr());
|
||||
var needsConst = needsConst(receiver);
|
||||
var argInfo = visitArgumentList(argList);
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
|
||||
if (expr.isNullable()) {
|
||||
//noinspection ConstantConditions
|
||||
@@ -2930,10 +2992,12 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
functionName,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
currentScope.getQualifiedName(),
|
||||
PropagateNullReceiverNodeGen.create(unavailableSourceSection(), receiver),
|
||||
GetClassNodeGen.create(null)));
|
||||
}
|
||||
@@ -2942,10 +3006,12 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
functionName,
|
||||
typeArgs,
|
||||
argInfo.arguments,
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
needsConst,
|
||||
argInfo.methodSlot,
|
||||
currentScope.getQualifiedName(),
|
||||
receiver,
|
||||
GetClassNodeGen.create(null));
|
||||
}
|
||||
@@ -3140,18 +3206,44 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return slotVariables;
|
||||
}
|
||||
|
||||
private Pair<FrameDescriptorBuilder, FrameSlotVariable[]>
|
||||
createFrameDescriptorBuilderAndSlotVariables(ParameterList params) {
|
||||
var builder = new FrameDescriptorBuilder(params.getParameters().size());
|
||||
return Pair.of(builder, getSlotVariables(params.getParameters(), builder));
|
||||
private FrameSlotVariable[] getTypeSlotVariables(
|
||||
List<org.pkl.parser.syntax.TypeParameter> typeParams,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
var slotVariables = new FrameSlotVariable[typeParams.size()];
|
||||
for (var i = 0; i < typeParams.size(); i++) {
|
||||
var param = typeParams.get(i);
|
||||
slotVariables[i] =
|
||||
frameDescriptorBuilder.addSlot(
|
||||
FrameSlotKind.Illegal, toIdentifier(param.getIdentifier().getValue()), null);
|
||||
}
|
||||
return slotVariables;
|
||||
}
|
||||
|
||||
private @Nullable Pair<@Nullable FrameDescriptorBuilder, FrameSlotVariable[]>
|
||||
createFrameDescriptorBuilderAndSlotVariables(ObjectBody body) {
|
||||
private record FrameBindings(
|
||||
FrameDescriptorBuilder builder,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameSlotVariable[] typeBindings) {}
|
||||
|
||||
private FrameBindings createFrameDescriptorBuilderAndSlotVariables(
|
||||
ParameterList params, @Nullable TypeParameterList typeParamList) {
|
||||
List<org.pkl.parser.syntax.TypeParameter> typeParams =
|
||||
typeParamList != null ? typeParamList.getParameters() : List.of();
|
||||
var builder = new FrameDescriptorBuilder(params.getParameters().size() + typeParams.size());
|
||||
// NB: param slots are allocated before type param slots intentionally
|
||||
// despite type args coming before args in FunctionNode calls
|
||||
// this is because many places assume that param index == slot
|
||||
// if desired, we could change this in the future since it's just the internal frame layout
|
||||
var paramSlots = getSlotVariables(params.getParameters(), builder);
|
||||
var typeParamSlots = getTypeSlotVariables(typeParams, builder);
|
||||
return new FrameBindings(builder, paramSlots, typeParamSlots);
|
||||
}
|
||||
|
||||
private @Nullable FrameBindings createFrameDescriptorBuilderAndSlotVariables(ObjectBody body) {
|
||||
if (body.getParameters().isEmpty()) return null;
|
||||
|
||||
var builder = new FrameDescriptorBuilder(body.getParameters().size());
|
||||
return Pair.of(builder, getSlotVariables(body.getParameters(), builder));
|
||||
return new FrameBindings(
|
||||
builder, getSlotVariables(body.getParameters(), builder), new FrameSlotVariable[0]);
|
||||
}
|
||||
|
||||
private void checkNotInsideForGenerator(Node ctx, String errorMessageKey) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.ast.ConstantNode;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
@@ -38,6 +38,7 @@ import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||
import org.pkl.core.runtime.FrameSlotVariable;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
import org.pkl.core.runtime.VmDuration;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
@@ -59,19 +60,10 @@ public final class SymbolTable {
|
||||
return currentScope;
|
||||
}
|
||||
|
||||
public @Nullable TypeParameter findTypeParameter(String name) {
|
||||
TypeParameter result;
|
||||
for (var scope = currentScope; scope != null; scope = scope.getParent()) {
|
||||
result = scope.getTypeParameter(name);
|
||||
if (result != null) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObjectMember enterClass(
|
||||
Identifier name,
|
||||
int modifiers,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
Function<ClassScope, ObjectMember> nodeFactory) {
|
||||
return doEnter(
|
||||
new ClassScope(
|
||||
@@ -86,7 +78,7 @@ public final class SymbolTable {
|
||||
|
||||
public ObjectMember enterTypeAlias(
|
||||
Identifier name,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
Function<TypeAliasScope, ObjectMember> nodeFactory) {
|
||||
try {
|
||||
this.isInTypeAliasScope = true;
|
||||
@@ -108,7 +100,7 @@ public final class SymbolTable {
|
||||
ConstLevel constLevel,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
Function<MethodScope, T> nodeFactory) {
|
||||
return doEnter(
|
||||
new MethodScope(
|
||||
@@ -256,6 +248,7 @@ public final class SymbolTable {
|
||||
protected final Map<String, Member> properties = new HashMap<>();
|
||||
// The methods defined on this (lexical) scope
|
||||
protected final Map<String, Member> methods = new HashMap<>();
|
||||
protected final Set<String> knownTypeParameterNames;
|
||||
|
||||
static int[] getSlots(FrameSlotVariable[] bindings) {
|
||||
if (bindings.length == 0) {
|
||||
@@ -275,7 +268,8 @@ public final class SymbolTable {
|
||||
ConstLevel constLevel,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
int[] forGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
int[] parameterSlots,
|
||||
Set<String> knownTypeParameterNames) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.qualifiedName = qualifiedName;
|
||||
@@ -290,6 +284,7 @@ public final class SymbolTable {
|
||||
: constLevel;
|
||||
this.forGeneratorSlots = forGeneratorSlots;
|
||||
this.parameterSlots = parameterSlots;
|
||||
this.knownTypeParameterNames = knownTypeParameterNames;
|
||||
}
|
||||
|
||||
public final @Nullable Scope getParent() {
|
||||
@@ -509,7 +504,8 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public final VariableResolution resolveVariable(String name) {
|
||||
var resolved = resolveLexical((scope, levelUp) -> scope.doResolveProperty(name, levelUp));
|
||||
var resolved =
|
||||
resolveLexical((scope, levelUp) -> scope.doResolveProperty(name, levelUp), null);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
@@ -522,7 +518,7 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public final MethodResolution resolveMethod(String name) {
|
||||
var resolved = resolveLexical((scope, levelUp) -> scope.doResolveMethod(name, levelUp));
|
||||
var resolved = resolveLexical((scope, levelUp) -> scope.doResolveMethod(name, levelUp), null);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
@@ -534,13 +530,29 @@ public final class SymbolTable {
|
||||
return new ImplicitThisMethod();
|
||||
}
|
||||
|
||||
public @Nullable TypeParameterResolution resolveTypeParameter(String name) {
|
||||
if (!knownTypeParameterNames.contains(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return resolveLexical(
|
||||
(scope, levelUp) -> ((Scope) scope).doResolveTypeParameter(name, levelUp),
|
||||
(scope, levelUp) -> scope.doResolveTypeParameter(name, levelUp));
|
||||
}
|
||||
|
||||
protected @Nullable TypeParameterResolution doResolveTypeParameter(String name, int levelsUp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ResolutionFunction<T> {
|
||||
@Nullable T apply(LexicalScope scope, int levelUp);
|
||||
private interface ResolutionFunction<Scope, T> {
|
||||
@Nullable T apply(Scope scope, int levelUp);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private @Nullable <R> R resolveLexical(ResolutionFunction<R> fun) {
|
||||
private @Nullable <R> R resolveLexical(
|
||||
ResolutionFunction<LexicalScope, R> fun,
|
||||
@Nullable ResolutionFunction<TypeAliasScope, R> aliasFun) {
|
||||
var levelsUp = 0;
|
||||
var shouldSkip = false;
|
||||
var skippedObjectScope = false;
|
||||
@@ -554,7 +566,11 @@ public final class SymbolTable {
|
||||
if (scope instanceof AnnotationScope && scope.getParent() instanceof ClassScope) {
|
||||
levelsUp--;
|
||||
}
|
||||
if (scope instanceof LexicalScope lex) {
|
||||
if (aliasFun != null && scope instanceof TypeAliasScope typeAliasScope) {
|
||||
// when resolving type params, also visit TypeAliasScopes with aliasFun
|
||||
var result = aliasFun.apply(typeAliasScope, levelsUp);
|
||||
if (result != null) return result;
|
||||
} else if (scope instanceof LexicalScope lex) {
|
||||
if (shouldSkip && !(scope instanceof ForGeneratorScope)) {
|
||||
if (scope instanceof ObjectScope objectScope && objectScope.hasParams()) {
|
||||
// skipping the object scope should now see the object body params, because the params
|
||||
@@ -638,7 +654,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
EMPTY_INT_ARRAY);
|
||||
EMPTY_INT_ARRAY,
|
||||
parent.knownTypeParameterNames);
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
@@ -676,7 +693,7 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public abstract static class TypeParameterizableScope extends Scope {
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
|
||||
public TypeParameterizableScope(
|
||||
Scope parent,
|
||||
@@ -684,7 +701,7 @@ public final class SymbolTable {
|
||||
String qualifiedName,
|
||||
ConstLevel constLevel,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
int[] forGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
super(
|
||||
@@ -694,14 +711,27 @@ public final class SymbolTable {
|
||||
constLevel,
|
||||
frameDescriptorBuilder,
|
||||
forGeneratorSlots,
|
||||
parameterSlots);
|
||||
parameterSlots,
|
||||
extendTypeParams(parent.knownTypeParameterNames, typeParameters));
|
||||
this.typeParameters = typeParameters;
|
||||
}
|
||||
|
||||
private static Set<String> extendTypeParams(Set<String> base, TypeParameter[] typeParams) {
|
||||
var res = new HashSet<>(base);
|
||||
for (var typeParam : typeParams) {
|
||||
res.add(typeParam.name());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameters.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeParameter getTypeParameter(String name) {
|
||||
for (var param : typeParameters) {
|
||||
if (name.equals(param.getName())) return param;
|
||||
if (name.equals(param.name())) return param;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -721,7 +751,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
new FrameDescriptorBuilder(),
|
||||
EMPTY_INT_ARRAY,
|
||||
EMPTY_INT_ARRAY);
|
||||
EMPTY_INT_ARRAY,
|
||||
Set.of());
|
||||
this.isBaseModule = isBaseModule;
|
||||
this.moduleInfo = moduleInfo;
|
||||
this.isAmend = moduleInfo.isAmend();
|
||||
@@ -759,7 +790,7 @@ public final class SymbolTable {
|
||||
ConstLevel constLevel,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
TypeParameter[] typeParameters) {
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
@@ -781,6 +812,16 @@ public final class SymbolTable {
|
||||
public @Nullable MethodResolution doResolveMethod(String name, int levelsUp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable TypeParameterResolution doResolveTypeParameter(String name, int levelsUp) {
|
||||
var result = getTypeParameter(name);
|
||||
if (result == null) return null;
|
||||
if (result.variance() != org.pkl.core.TypeParameter.Variance.INVARIANT) {
|
||||
throw new PklBugException(
|
||||
"found type parameter variance modifier found on a method declaration");
|
||||
}
|
||||
return new TypeParameterResolution(result, TypeParameter.OwnerType.METHOD, levelsUp);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class LambdaScope extends Scope implements LexicalScope {
|
||||
@@ -798,7 +839,8 @@ public final class SymbolTable {
|
||||
parent.getConstLevel(),
|
||||
frameDescriptorBuilder,
|
||||
EMPTY_INT_ARRAY,
|
||||
getSlots(bindings));
|
||||
getSlots(bindings),
|
||||
parent.knownTypeParameterNames);
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
@@ -841,7 +883,8 @@ public final class SymbolTable {
|
||||
parent.getConstLevel(),
|
||||
parent.frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
getMyParameterSlots(parent, binding));
|
||||
getMyParameterSlots(parent, binding),
|
||||
parent.knownTypeParameterNames);
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@@ -893,7 +936,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
getFrameDescriptorBuilder(parent),
|
||||
getForGeneratorSlots(parent),
|
||||
getParameterSlots(parent));
|
||||
getParameterSlots(parent),
|
||||
parent.knownTypeParameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,7 +995,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
getMyForGeneratorSlots(parent, keyBinding, valueBinding),
|
||||
getMyParameterSlots(parent));
|
||||
getMyParameterSlots(parent),
|
||||
parent.knownTypeParameterNames);
|
||||
this.keyBinding = keyBinding;
|
||||
this.valueBinding = valueBinding;
|
||||
}
|
||||
@@ -996,7 +1041,8 @@ public final class SymbolTable {
|
||||
// object members inherit the for-generator slots of the parent for-generator, if it
|
||||
// exists.
|
||||
parent instanceof ForGeneratorScope ? parent.forGeneratorSlots : EMPTY_INT_ARRAY,
|
||||
parent.parameterSlots);
|
||||
parent.parameterSlots,
|
||||
parent.knownTypeParameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1010,7 +1056,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
parent instanceof ForGeneratorScope ? parent.forGeneratorSlots : EMPTY_INT_ARRAY,
|
||||
parent.parameterSlots);
|
||||
parent.parameterSlots,
|
||||
parent.knownTypeParameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,7 +1070,7 @@ public final class SymbolTable {
|
||||
String qualifiedName,
|
||||
int modifiers,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
TypeParameter[] typeParameters) {
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
@@ -1049,6 +1096,12 @@ public final class SymbolTable {
|
||||
if (member == null) return null;
|
||||
return new LexicalMethod(false, isClosed, false, member.modifiers, levelsUp);
|
||||
}
|
||||
|
||||
public @Nullable TypeParameterResolution doResolveTypeParameter(String name, int levelsUp) {
|
||||
var result = getTypeParameter(name);
|
||||
if (result == null) return null;
|
||||
return new TypeParameterResolution(result, TypeParameter.OwnerType.CLASS, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TypeAliasScope extends TypeParameterizableScope {
|
||||
@@ -1057,7 +1110,7 @@ public final class SymbolTable {
|
||||
Identifier name,
|
||||
String qualifiedName,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
TypeParameter[] typeParameters) {
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
@@ -1068,6 +1121,12 @@ public final class SymbolTable {
|
||||
parent.forGeneratorSlots,
|
||||
parent.parameterSlots);
|
||||
}
|
||||
|
||||
public @Nullable TypeParameterResolution doResolveTypeParameter(String name, int levelsUp) {
|
||||
var result = getTypeParameter(name);
|
||||
if (result == null) return null;
|
||||
return new TypeParameterResolution(result, TypeParameter.OwnerType.TYPEALIAS, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1086,7 +1145,8 @@ public final class SymbolTable {
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
parent.parameterSlots);
|
||||
parent.parameterSlots,
|
||||
parent.knownTypeParameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1100,7 +1160,8 @@ public final class SymbolTable {
|
||||
ConstLevel.MODULE,
|
||||
frameDescriptorBuilder,
|
||||
EMPTY_INT_ARRAY,
|
||||
EMPTY_INT_ARRAY);
|
||||
EMPTY_INT_ARRAY,
|
||||
parent.knownTypeParameterNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.builder;
|
||||
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
|
||||
public record TypeParameterResolution(
|
||||
TypeParameter typeParameter, TypeParameter.OwnerType ownerType, int levelsUp) {}
|
||||
@@ -31,6 +31,7 @@ import org.pkl.core.ast.frame.ReadExactFrameSlotNodeGen;
|
||||
import org.pkl.core.ast.member.FunctionNode;
|
||||
import org.pkl.core.ast.member.Lambda;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.TypeParameterNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
|
||||
public final class AmendFunctionNode extends PklNode {
|
||||
@@ -87,6 +88,7 @@ public final class AmendFunctionNode extends PklNode {
|
||||
parameterTypeNodes,
|
||||
null,
|
||||
true,
|
||||
new TypeParameterNode[0],
|
||||
new AmendFunctionBodyNode(
|
||||
sourceSection,
|
||||
hostNode.copy(
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ public abstract class AbstractInferParentFromMethodNode extends AbstractInferPar
|
||||
|
||||
protected abstract Method getMethod(VirtualFrame frame);
|
||||
|
||||
protected abstract @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method);
|
||||
protected abstract @Nullable TypeNode getTypeNode(Method method);
|
||||
|
||||
@Idempotent
|
||||
protected boolean isFinalType(Method method, @Nullable TypeNode typeNode) {
|
||||
|
||||
+14
-1
@@ -25,6 +25,7 @@ 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.VmTypeArgument;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class AbstractInferParentNode extends ExpressionNode {
|
||||
@@ -41,11 +42,23 @@ public abstract class AbstractInferParentNode extends ExpressionNode {
|
||||
@Nullable TypeNode typeNode,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
return getDefaultValue(frame, typeNode, headerSection, qualifiedName, false, null);
|
||||
}
|
||||
|
||||
protected Object getDefaultValue(
|
||||
VirtualFrame frame,
|
||||
@Nullable TypeNode typeNode,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
if (typeNode == null || typeNode instanceof UnknownTypeNode) {
|
||||
return VmDynamic.empty();
|
||||
}
|
||||
|
||||
var defaultValue = typeNode.createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
var defaultValue =
|
||||
typeNode.createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
+15
-5
@@ -17,23 +17,33 @@ package org.pkl.core.ast.expression.member;
|
||||
|
||||
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.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract sealed class AbstractInvokeLexicalMethodNode
|
||||
extends AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
permits InvokeLexicalClassMethodNode, InvokeLexicalObjectMethodNode {
|
||||
public abstract class AbstractInvokeLexicalMethodNode
|
||||
extends AbstractInvokeLexicalOrQualifiedMethodNode {
|
||||
private final int levelsUp;
|
||||
|
||||
public AbstractInvokeLexicalMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
int levelsUp,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, argumentNodes, needsConst, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
this.levelsUp = levelsUp;
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -23,6 +23,7 @@ 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.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -34,9 +35,7 @@ import org.pkl.core.runtime.VmObjectLike;
|
||||
* InvokeLexicalObjectMethodNode}), or off of an explicit receiver expression ({@link
|
||||
* InvokeQualifiedClassMethodNode}, {@link InvokeQualifiedObjectMethodNode}).
|
||||
*/
|
||||
public abstract sealed class AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
extends AbstractInvokeMethodNode
|
||||
permits AbstractInvokeQualifiedMethodNode, AbstractInvokeLexicalMethodNode {
|
||||
public abstract class AbstractInvokeLexicalOrQualifiedMethodNode extends AbstractInvokeMethodNode {
|
||||
|
||||
protected final Identifier methodName;
|
||||
private final boolean needsConst;
|
||||
@@ -46,10 +45,12 @@ public abstract sealed class AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
protected AbstractInvokeLexicalOrQualifiedMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, argumentNodes, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(sourceSection, unresolvedTypeArgumentNodes, argumentNodes, methodSlot, qualifiedName);
|
||||
this.methodName = methodName;
|
||||
this.needsConst = needsConst;
|
||||
this.isConstChecked = false;
|
||||
|
||||
+89
-6
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
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.TruffleSafepoint;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
@@ -22,36 +24,115 @@ 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.ast.type.TypeArgumentTypeNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
import org.pkl.core.runtime.VmTypeArgument;
|
||||
|
||||
public abstract class AbstractInvokeMethodNode extends ExpressionNode {
|
||||
|
||||
@Children protected final ExpressionNode[] argumentNodes;
|
||||
protected final int methodSlot;
|
||||
protected final String qualifiedName;
|
||||
|
||||
public AbstractInvokeMethodNode(
|
||||
SourceSection sourceSection, ExpressionNode[] argumentNodes, int methodSlot) {
|
||||
@Children protected UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes;
|
||||
|
||||
@CompilationFinal(dimensions = 1)
|
||||
protected TypeArgumentTypeNode @Nullable [] typeArgumentRootNodes;
|
||||
|
||||
@CompilationFinal boolean typeArgumentsNeedMaterializedFrame = false;
|
||||
|
||||
protected AbstractInvokeMethodNode(
|
||||
SourceSection sourceSection,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(sourceSection);
|
||||
this.unresolvedTypeArgumentNodes = unresolvedTypeArgumentNodes;
|
||||
this.argumentNodes = argumentNodes;
|
||||
this.methodSlot = methodSlot;
|
||||
this.qualifiedName = qualifiedName;
|
||||
}
|
||||
|
||||
protected TypeArgumentTypeNode @Nullable [] getTypeArgumentRootNodes(VirtualFrame frame) {
|
||||
if (typeArgumentRootNodes == null) {
|
||||
if (unresolvedTypeArgumentNodes != null) {
|
||||
var language = VmLanguage.get(this);
|
||||
var frameDescriptor = frame.getFrameDescriptor();
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
|
||||
var rootNodes = new TypeArgumentTypeNode[unresolvedTypeArgumentNodes.length];
|
||||
for (var i = 0; i < rootNodes.length; i++) {
|
||||
var typeNode = unresolvedTypeArgumentNodes[i].execute(frame);
|
||||
rootNodes[i] =
|
||||
new TypeArgumentTypeNode(
|
||||
language,
|
||||
frameDescriptor,
|
||||
sourceSection,
|
||||
qualifiedName + ".<typearg#" + (i + 1) + ">",
|
||||
typeNode);
|
||||
typeArgumentsNeedMaterializedFrame =
|
||||
typeArgumentsNeedMaterializedFrame || typeNode.getTypeArgumentRequiresFrame();
|
||||
}
|
||||
typeArgumentRootNodes = rootNodes;
|
||||
unresolvedTypeArgumentNodes = null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return typeArgumentRootNodes;
|
||||
}
|
||||
|
||||
protected VmTypeArgument @Nullable [] getTypeArguments(
|
||||
VirtualFrame frame, @Nullable Method method) {
|
||||
var rootNodes = getTypeArgumentRootNodes(frame);
|
||||
if (rootNodes == null) return null;
|
||||
|
||||
var typeParameterCount = method == null ? 0 : method.getTypeParameterCount();
|
||||
if (rootNodes.length != typeParameterCount) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
.evalError("wrongTypeArgumentCount", typeParameterCount, rootNodes.length)
|
||||
.build();
|
||||
}
|
||||
|
||||
var argFrame = typeArgumentsNeedMaterializedFrame ? frame.materialize() : null;
|
||||
var typeArgs = new VmTypeArgument[rootNodes.length];
|
||||
for (var i = 0; i < typeArgs.length; i++) {
|
||||
var rootNode = rootNodes[i];
|
||||
typeArgs[i] = new VmTypeArgument(rootNode, argFrame);
|
||||
}
|
||||
return typeArgs;
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
protected Object[] evalArgs(
|
||||
VirtualFrame frame, @Nullable Method method, Object owner, @Nullable Object receiver) {
|
||||
VirtualFrame frame, @Nullable Method method, VmObjectLike owner, @Nullable Object receiver) {
|
||||
Object prevMethod = null;
|
||||
var typeArgs = getTypeArguments(frame, method);
|
||||
if (methodSlot > -1) {
|
||||
if (method != null) {
|
||||
method.ensureInitialized(owner);
|
||||
}
|
||||
prevMethod = frame.getObject(methodSlot);
|
||||
frame.setObject(methodSlot, method);
|
||||
frame.setObject(methodSlot, new MethodCall(method, typeArgs));
|
||||
}
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
var typeParameterCount = method != null ? method.getTypeParameterCount() : 0;
|
||||
var args = new Object[2 + argumentNodes.length + typeParameterCount];
|
||||
args[0] = receiver;
|
||||
args[1] = owner;
|
||||
|
||||
if (typeArgs != null) {
|
||||
System.arraycopy(typeArgs, 0, args, 2, typeParameterCount);
|
||||
}
|
||||
|
||||
try {
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
TruffleSafepoint.poll(this);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + typeParameterCount + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
} finally {
|
||||
if (methodSlot > -1) {
|
||||
@@ -61,4 +142,6 @@ public abstract class AbstractInvokeMethodNode extends ExpressionNode {
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
public record MethodCall(@Nullable Method method, VmTypeArgument @Nullable [] typeArguments) {}
|
||||
}
|
||||
+13
-2
@@ -17,7 +17,9 @@ package org.pkl.core.ast.expression.member;
|
||||
|
||||
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.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
|
||||
@@ -29,11 +31,20 @@ public abstract sealed class AbstractInvokeQualifiedMethodNode
|
||||
protected AbstractInvokeQualifiedMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
ExpressionNode getReceiverNode,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, argumentNodes, needsConst, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
this.getReceiverNode = getReceiverNode;
|
||||
}
|
||||
|
||||
|
||||
+36
-11
@@ -21,9 +21,11 @@ import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.expression.member.AbstractInvokeMethodNode.MethodCall;
|
||||
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.VmTypeArgument;
|
||||
|
||||
public abstract class InferParentWithinMethodArgumentNode
|
||||
extends AbstractInferParentFromMethodNode {
|
||||
@@ -39,9 +41,14 @@ public abstract class InferParentWithinMethodArgumentNode
|
||||
|
||||
@Override
|
||||
protected Method getMethod(VirtualFrame frame) {
|
||||
var method = (Method) frame.getObject(methodSlot);
|
||||
var methodCall = (MethodCall) frame.getObject(methodSlot);
|
||||
if (methodCall == null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
|
||||
var method = methodCall.method();
|
||||
if (method == null) {
|
||||
// used in FunctionN.apply()
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
@@ -49,9 +56,18 @@ public abstract class InferParentWithinMethodArgumentNode
|
||||
return method;
|
||||
}
|
||||
|
||||
protected VmTypeArgument @Nullable [] getTypeArgumentOverrides(VirtualFrame frame) {
|
||||
var methodCall = (MethodCall) frame.getObject(methodSlot);
|
||||
if (methodCall == null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
return methodCall.typeArguments();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getParameterTypeNode(frame, argIndex);
|
||||
protected @Nullable TypeNode getTypeNode(Method method) {
|
||||
return method.getFunctionNode().getParameterTypeNode(argIndex);
|
||||
}
|
||||
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
@@ -61,9 +77,9 @@ public abstract class InferParentWithinMethodArgumentNode
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached("getTypeNode(cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName(), true, getTypeArgumentOverrides(frame))")
|
||||
Object defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
@@ -71,21 +87,30 @@ public abstract class InferParentWithinMethodArgumentNode
|
||||
@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());
|
||||
var typeNode = getTypeNode(method);
|
||||
return getDefaultValue(
|
||||
frame,
|
||||
typeNode,
|
||||
method.getHeaderSection(),
|
||||
method.getQualifiedName(),
|
||||
true,
|
||||
getTypeArgumentOverrides(frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getDefaultValue(
|
||||
protected final Object getDefaultValue(
|
||||
VirtualFrame frame,
|
||||
@Nullable TypeNode typeNode,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
if (typeNode != null && typeNode.isSelfType()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
|
||||
return super.getDefaultValue(frame, typeNode, headerSection, qualifiedName);
|
||||
return super.getDefaultValue(
|
||||
frame, typeNode, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -52,8 +52,8 @@ public abstract class InferParentWithinMethodNode extends AbstractInferParentFro
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getReturnTypeNode(frame);
|
||||
protected @Nullable TypeNode getTypeNode(Method method) {
|
||||
return method.getFunctionNode().getReturnTypeNode();
|
||||
}
|
||||
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
@@ -63,7 +63,7 @@ public abstract class InferParentWithinMethodNode extends AbstractInferParentFro
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached("getTypeNode(cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
Object defaultValue) {
|
||||
@@ -73,7 +73,7 @@ public abstract class InferParentWithinMethodNode extends AbstractInferParentFro
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame) {
|
||||
var method = getMethod(frame);
|
||||
var typeNode = getTypeNode(frame, method);
|
||||
var typeNode = getTypeNode(method);
|
||||
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -59,8 +59,8 @@ public abstract class InferParentWithinObjectMethodNode extends AbstractInferPar
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getReturnTypeNode(frame);
|
||||
protected @Nullable TypeNode getTypeNode(Method method) {
|
||||
return method.getFunctionNode().getReturnTypeNode();
|
||||
}
|
||||
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
@@ -70,7 +70,7 @@ public abstract class InferParentWithinObjectMethodNode extends AbstractInferPar
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached("getTypeNode(cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
Object defaultValue) {
|
||||
@@ -80,7 +80,7 @@ public abstract class InferParentWithinObjectMethodNode extends AbstractInferPar
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame) {
|
||||
var method = getMethod(frame);
|
||||
var typeNode = getTypeNode(frame, method);
|
||||
var typeNode = getTypeNode(method);
|
||||
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
|
||||
}
|
||||
}
|
||||
+14
-2
@@ -16,8 +16,10 @@
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
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.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -30,10 +32,20 @@ public final class InvokeLexicalClassMethodNode extends AbstractInvokeLexicalMet
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
int levelsUp,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
levelsUp,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-2
@@ -16,10 +16,12 @@
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -29,10 +31,20 @@ public final class InvokeLexicalObjectMethodNode extends AbstractInvokeLexicalMe
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
int levelsUp,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, levelsUp, argumentNodes, needsConst, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
levelsUp,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-2
@@ -18,8 +18,10 @@ package org.pkl.core.ast.expression.member;
|
||||
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.ClassMethod;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
/** A non-virtual ("direct") method call. Used only for methods on {@code pkl:base}. */
|
||||
@@ -34,9 +36,11 @@ public final class InvokeMethodDirectNode extends AbstractInvokeMethodNode {
|
||||
SourceSection sourceSection,
|
||||
ClassMethod method,
|
||||
ExpressionNode receiverNode,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
int methodSlot) {
|
||||
super(sourceSection, argumentNodes, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(sourceSection, unresolvedTypeArgumentNodes, argumentNodes, methodSlot, qualifiedName);
|
||||
this.method = method;
|
||||
this.owner = method.getOwner();
|
||||
this.receiverNode = receiverNode;
|
||||
|
||||
+28
-5
@@ -28,10 +28,12 @@ import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.MemberLookupMode;
|
||||
import org.pkl.core.ast.internal.GetClassNode;
|
||||
import org.pkl.core.ast.member.ClassMethod;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
@@ -50,11 +52,13 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode {
|
||||
protected InvokeMethodVirtualNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
MemberLookupMode lookupMode,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, argumentNodes, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(sourceSection, unresolvedTypeArgumentNodes, argumentNodes, methodSlot, qualifiedName);
|
||||
this.methodName = methodName;
|
||||
this.lookupMode = lookupMode;
|
||||
this.needsConst = needsConst;
|
||||
@@ -63,10 +67,20 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode {
|
||||
protected InvokeMethodVirtualNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
MemberLookupMode lookupMode,
|
||||
int methodSlot) {
|
||||
this(sourceSection, methodName, argumentNodes, lookupMode, false, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
this(
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
lookupMode,
|
||||
false,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +160,16 @@ public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode {
|
||||
@Override
|
||||
public WrapperNode createWrapper(ProbeNode probe) {
|
||||
return new InvokeMethodVirtualNodeWrapper(
|
||||
sourceSection, methodName, argumentNodes, lookupMode, needsConst, methodSlot, this, probe);
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
lookupMode,
|
||||
needsConst,
|
||||
methodSlot,
|
||||
qualifiedName,
|
||||
this,
|
||||
probe);
|
||||
}
|
||||
|
||||
private void checkConst(ClassMethod method) {
|
||||
|
||||
+14
-2
@@ -16,8 +16,10 @@
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
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.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -26,11 +28,21 @@ public final class InvokeQualifiedClassMethodNode extends AbstractInvokeQualifie
|
||||
public InvokeQualifiedClassMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
ExpressionNode getReceiverNode,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
getReceiverNode,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-2
@@ -16,10 +16,12 @@
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -28,11 +30,21 @@ public final class InvokeQualifiedObjectMethodNode extends AbstractInvokeQualifi
|
||||
public InvokeQualifiedObjectMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
ExpressionNode getReceiverNode,
|
||||
int methodSlot) {
|
||||
super(sourceSection, methodName, argumentNodes, needsConst, getReceiverNode, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(
|
||||
sourceSection,
|
||||
methodName,
|
||||
unresolvedTypeArgumentNodes,
|
||||
argumentNodes,
|
||||
needsConst,
|
||||
getReceiverNode,
|
||||
methodSlot,
|
||||
qualifiedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-2
@@ -21,8 +21,10 @@ import com.oracle.truffle.api.dsl.Specialization;
|
||||
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.ClassMethod;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
@@ -34,10 +36,12 @@ public abstract class InvokeSuperMethodNode extends AbstractInvokeMethodNode {
|
||||
protected InvokeSuperMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
UnresolvedTypeNode @Nullable [] unresolvedTypeArgumentNodes,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst,
|
||||
int methodSlot) {
|
||||
super(sourceSection, argumentNodes, methodSlot);
|
||||
int methodSlot,
|
||||
String qualifiedName) {
|
||||
super(sourceSection, unresolvedTypeArgumentNodes, argumentNodes, methodSlot, qualifiedName);
|
||||
this.needsConst = needsConst;
|
||||
|
||||
assert !methodName.isLocalMethod();
|
||||
|
||||
@@ -35,6 +35,12 @@ public final class GetModuleNode extends ExpressionNode {
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
var levelsUp = getLevelsUp(frame);
|
||||
return replace(levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp))
|
||||
.executeGeneric(frame);
|
||||
}
|
||||
|
||||
public static int getLevelsUp(VirtualFrame frame) {
|
||||
var levelsUp = -1;
|
||||
for (var current = VmUtils.getOwner(frame);
|
||||
current != null;
|
||||
@@ -43,8 +49,6 @@ public final class GetModuleNode extends ExpressionNode {
|
||||
levelsUp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return replace(levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp))
|
||||
.executeGeneric(frame);
|
||||
return levelsUp;
|
||||
}
|
||||
}
|
||||
@@ -90,9 +90,11 @@ public abstract class ToStringNode extends UnaryExpressionNode {
|
||||
return InvokeMethodVirtualNodeGen.create(
|
||||
sourceSection,
|
||||
Identifier.TO_STRING,
|
||||
null,
|
||||
new ExpressionNode[] {},
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
-1,
|
||||
"",
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -18,19 +18,17 @@ package org.pkl.core.ast.member;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.PClass;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
public final class ClassMethod extends ClassMember implements Method {
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
|
||||
// null = not deprecated, "" = no/empty message in the @Deprecated body
|
||||
private final @Nullable String deprecation;
|
||||
@@ -46,7 +44,7 @@ public final class ClassMethod extends ClassMember implements Method {
|
||||
SourceSection @Nullable [] docComment,
|
||||
List<VmTyped> annotations,
|
||||
VmTyped owner,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
@Nullable String deprecation) {
|
||||
|
||||
super(
|
||||
@@ -68,6 +66,14 @@ public final class ClassMethod extends ClassMember implements Method {
|
||||
this.functionNode = functionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionNode getFunctionNode(@Nullable SourceSection callSite) {
|
||||
if (callSite != null && deprecation != null) {
|
||||
reportDeprecation(callSite);
|
||||
}
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
public CallTarget getCallTarget() {
|
||||
return functionNode.getCallTarget();
|
||||
}
|
||||
@@ -101,13 +107,14 @@ public final class ClassMethod extends ClassMember implements Method {
|
||||
return functionNode.getParameterCount();
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getReturnTypeNode() {
|
||||
return functionNode.getReturnTypeNode();
|
||||
@Override
|
||||
public int getTypeParameterCount() {
|
||||
return functionNode.getTypeParameterCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
|
||||
return functionNode.getReturnTypeNode();
|
||||
public void ensureInitialized(VmObjectLike owner) {
|
||||
// do nothing; class methods are always initialized
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,10 +147,7 @@ public final class ClassMethod extends ClassMember implements Method {
|
||||
}
|
||||
|
||||
public PClass.Method export(PClass owner) {
|
||||
return functionNode.export(owner, docComment, annotations, modifiers, typeParameters);
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
|
||||
return functionNode.getParameterTypeNode(idx);
|
||||
return functionNode.export(
|
||||
owner, docComment, annotations, modifiers, TypeParameter.export(typeParameters));
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,14 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.graalvm.collections.EconomicMap;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.PClassInfo;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
@NodeInfo(shortName = "class")
|
||||
@@ -39,7 +38,7 @@ public final class ClassNode extends ExpressionNode {
|
||||
@Children private final ExpressionNode[] annotationNodes;
|
||||
private final int modifiers;
|
||||
private final PClassInfo<?> classInfo;
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
private final @Nullable ModuleInfo moduleInfo;
|
||||
// null iff this class is pkl.base#Any
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedSupertypeNode;
|
||||
@@ -57,7 +56,7 @@ public final class ClassNode extends ExpressionNode {
|
||||
ExpressionNode[] annotationNodes,
|
||||
int modifiers,
|
||||
PClassInfo<?> classInfo,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
@Nullable ModuleInfo moduleInfo,
|
||||
@Nullable UnresolvedTypeNode unresolvedSupertypeNode,
|
||||
EconomicMap<Object, ObjectMember> prototypeMembers,
|
||||
|
||||
@@ -26,10 +26,10 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.Member.SourceLocation;
|
||||
import org.pkl.core.PClass;
|
||||
import org.pkl.core.PType;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.TypeParameterNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.VmType.UnknownType;
|
||||
import org.pkl.core.util.CollectionUtils;
|
||||
@@ -37,21 +37,25 @@ import org.pkl.core.util.Pair;
|
||||
|
||||
public final class FunctionNode extends RegularMemberNode {
|
||||
// Every function (and property) call passes two implicit arguments at positions
|
||||
// frame.getArguments()[0] and [1]:
|
||||
// frame.getArguments()[0], [1]:
|
||||
// - the receiver (target) of the call, of type Object (see VmUtils.getReceiver())
|
||||
// - the owner (lexically enclosing object) of the function/property definition, of type VmTyped
|
||||
// (see VmUtils.getOwner())
|
||||
// For VmObject receivers, the owner is the same as or an ancestor of the receiver.
|
||||
// For other receivers, the owner is the prototype of the receiver's class.
|
||||
// The chain of enclosing owners forms a function/property's lexical scope.
|
||||
//
|
||||
// After the implicit args, the method's type args are passed, in order.
|
||||
// If the call does not contain type arguments, these args are null.
|
||||
//
|
||||
// Finally, the method's arguments are passed, in order.
|
||||
private static final int IMPLICIT_PARAM_COUNT = 2;
|
||||
|
||||
private final int paramCount;
|
||||
private final int typeParamCount;
|
||||
private final int totalParamCount;
|
||||
@Children private TypeParameterNode[] typeParameterNodes;
|
||||
|
||||
@Children private final TypeNode[] parameterTypeNodes;
|
||||
@Child private @Nullable TypeNode checkedReturnTypeNode;
|
||||
private @Nullable TypeNode returnTypeNode;
|
||||
private @Nullable TypeNode checkedReturnTypeNode;
|
||||
@Child private @Nullable TypeNode returnTypeNode;
|
||||
|
||||
@TruffleBoundary
|
||||
public FunctionNode(
|
||||
@@ -62,6 +66,7 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
TypeNode[] parameterTypeNodes,
|
||||
@Nullable TypeNode returnTypeNode,
|
||||
boolean isReturnTypeChecked,
|
||||
TypeParameterNode[] typeParameterNodes,
|
||||
ExpressionNode bodyNode) {
|
||||
|
||||
super(language, descriptor, member, bodyNode);
|
||||
@@ -74,14 +79,21 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
this.parameterTypeNodes = parameterTypeNodes;
|
||||
this.checkedReturnTypeNode = isReturnTypeChecked ? returnTypeNode : null;
|
||||
this.returnTypeNode = returnTypeNode;
|
||||
this.typeParameterNodes = typeParameterNodes;
|
||||
|
||||
totalParamCount = Math.addExact(IMPLICIT_PARAM_COUNT, paramCount);
|
||||
typeParamCount = typeParameterNodes.length;
|
||||
totalParamCount =
|
||||
Math.addExact(IMPLICIT_PARAM_COUNT, Math.addExact(paramCount, typeParamCount));
|
||||
}
|
||||
|
||||
public int getParameterCount() {
|
||||
return paramCount;
|
||||
}
|
||||
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameterNodes.length;
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getParameterTypeNode(int idx) {
|
||||
if (idx >= paramCount) return null;
|
||||
return parameterTypeNodes[idx];
|
||||
@@ -112,14 +124,20 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
protected Object executeImpl(VirtualFrame frame) {
|
||||
var totalArgCount = frame.getArguments().length;
|
||||
var args = frame.getArguments();
|
||||
var totalArgCount = args.length;
|
||||
if (totalArgCount != totalParamCount) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw wrongArgumentCount(totalArgCount - IMPLICIT_PARAM_COUNT);
|
||||
throw wrongArgumentCount(totalArgCount - typeParamCount - IMPLICIT_PARAM_COUNT);
|
||||
}
|
||||
|
||||
for (var i = 0; i < typeParamCount; i++) {
|
||||
var typeArg = args[IMPLICIT_PARAM_COUNT + i];
|
||||
typeParameterNodes[i].executeAndSet(frame, typeArg);
|
||||
}
|
||||
|
||||
for (var i = 0; i < parameterTypeNodes.length; i++) {
|
||||
var argument = frame.getArguments()[IMPLICIT_PARAM_COUNT + i];
|
||||
var argument = args[IMPLICIT_PARAM_COUNT + typeParamCount + i];
|
||||
parameterTypeNodes[i].executeAndSet(frame, argument);
|
||||
}
|
||||
|
||||
@@ -127,6 +145,19 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
|
||||
if (checkedReturnTypeNode != null) {
|
||||
return checkedReturnTypeNode.execute(frame, result);
|
||||
} else if (returnTypeNode != null && typeParamCount > 0 && args[IMPLICIT_PARAM_COUNT] != null) {
|
||||
// Return types are not typically checked for stdlib methods (isReturnTypeChecked == false)
|
||||
// but for generic stdlib methods (e.g. `List.map()`) when they are called with type args,
|
||||
// this behavior needs to be overridden so the type arg check on return type still runs.
|
||||
// otherwise, this code would succeed instead of failing:
|
||||
// ```
|
||||
// res = List("foo", "hello", "a").map::<String>((it) -> it.length)
|
||||
// ```
|
||||
// Assumption: this does not need to account for type args in enclosing frames because
|
||||
// no type parameters exist in the stdlib that would require this.
|
||||
// Assumption-breaking pattern example: snippet `callWithTypeArguments.pkl` property
|
||||
// `lexical.nestedInMethodParam`.
|
||||
return returnTypeNode.execute(frame, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -153,7 +184,7 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
SourceSection @Nullable [] docComment,
|
||||
List<VmTyped> annotations,
|
||||
int modifiers,
|
||||
List<TypeParameter> typeParameters) {
|
||||
List<org.pkl.core.TypeParameter> typeParameters) {
|
||||
|
||||
var parameters = CollectionUtils.<String, PType>newLinkedHashMap(paramCount);
|
||||
for (var i = 0; i < paramCount; i++) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.pkl.core.ast.member;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.PklRootNode;
|
||||
@@ -51,6 +52,7 @@ public final class ListingOrMappingTypeCastNode extends PklRootNode {
|
||||
|
||||
@Override
|
||||
protected Object executeImpl(VirtualFrame frame) {
|
||||
return typeNode.execute(frame, frame.getArguments()[2]);
|
||||
var enclosingFrame = (MaterializedFrame) frame.getArguments()[2];
|
||||
return typeNode.execute(enclosingFrame, frame.getArguments()[3]);
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,24 @@
|
||||
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);
|
||||
FunctionNode getFunctionNode(@Nullable SourceSection callSite);
|
||||
|
||||
@Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx);
|
||||
|
||||
@Nullable TypeNode getReturnTypeNode(VirtualFrame frame);
|
||||
default FunctionNode getFunctionNode() {
|
||||
return getFunctionNode(null);
|
||||
}
|
||||
|
||||
SourceSection getHeaderSection();
|
||||
|
||||
String getQualifiedName();
|
||||
|
||||
void ensureInitialized(VmObjectLike owner);
|
||||
|
||||
int getTypeParameterCount();
|
||||
|
||||
CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner);
|
||||
}
|
||||
@@ -18,18 +18,20 @@ package org.pkl.core.ast.member;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
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.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.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
|
||||
public final class ObjectMethodNode extends RegularMemberNode implements Method {
|
||||
private final VmLanguage language;
|
||||
private final int parameterCount;
|
||||
private final TypeParameter[] typeParameters;
|
||||
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
|
||||
|
||||
@@ -41,26 +43,38 @@ public final class ObjectMethodNode extends RegularMemberNode implements Method
|
||||
ObjectMember member,
|
||||
ExpressionNode bodyNode,
|
||||
int parameterCount,
|
||||
TypeParameter[] typeParameters,
|
||||
@Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes,
|
||||
@Nullable UnresolvedTypeNode unresolvedReturnTypeNode) {
|
||||
|
||||
super(language, descriptor, member, bodyNode);
|
||||
|
||||
this.language = language;
|
||||
this.parameterCount = parameterCount;
|
||||
this.typeParameters = typeParameters;
|
||||
this.unresolvedParameterTypeNodes = unresolvedParameterTypeNodes;
|
||||
this.unresolvedReturnTypeNode = unresolvedReturnTypeNode;
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getReturnTypeNode() {
|
||||
// this method is only called from child nodes
|
||||
assert functionNode != null;
|
||||
return functionNode.getReturnTypeNode();
|
||||
@Override
|
||||
public FunctionNode getFunctionNode(@Nullable SourceSection callSite) {
|
||||
assert isInitialized() : "getFunctionNode() called when not initialized";
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
|
||||
return getFunctionNode(frame, true).getReturnTypeNode();
|
||||
protected CallTarget executeImpl(VirtualFrame frame) {
|
||||
return getFunctionNode(frame).getCallTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureInitialized(VmObjectLike owner) {
|
||||
if (!isInitialized()) {
|
||||
getCallTarget().call(owner, owner);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return functionNode != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,28 +83,20 @@ public final class ObjectMethodNode extends RegularMemberNode implements Method
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
|
||||
return getFunctionNode(frame, true).getParameterTypeNode(idx);
|
||||
@TruffleBoundary
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameters.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget executeImpl(VirtualFrame frame) {
|
||||
return getFunctionNode(frame, false).getCallTarget();
|
||||
}
|
||||
|
||||
private FunctionNode getFunctionNode(VirtualFrame frame, boolean isPreInit) {
|
||||
public FunctionNode getFunctionNode(VirtualFrame frame) {
|
||||
if (functionNode != null) return functionNode;
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
|
||||
if (isPreInit) {
|
||||
// TODO: with VmType/CreateDefaultValueNode this may be removable
|
||||
adoptChildren();
|
||||
}
|
||||
var parameterTypeNodes =
|
||||
VmUtils.resolveParameterTypes(frame, getFrameDescriptor(), unresolvedParameterTypeNodes);
|
||||
|
||||
var returnTypeNode =
|
||||
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null;
|
||||
var typeParamterNodes = VmUtils.resolveTypeParameters(typeParameters);
|
||||
|
||||
functionNode =
|
||||
new FunctionNode(
|
||||
@@ -101,6 +107,7 @@ public final class ObjectMethodNode extends RegularMemberNode implements Method
|
||||
parameterTypeNodes,
|
||||
returnTypeNode,
|
||||
true,
|
||||
typeParamterNodes,
|
||||
bodyNode);
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,12 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
|
||||
import org.pkl.core.ast.expression.primary.GetTypeAliasModuleNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.runtime.VmTypeAlias;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
@@ -39,7 +38,7 @@ public final class TypeAliasNode extends ExpressionNode {
|
||||
private final int modifiers;
|
||||
private final String simpleName;
|
||||
private final String qualifiedName;
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
private @Child UnresolvedTypeNode typeAnnotationNode;
|
||||
|
||||
// use same caching scheme as ClassNode
|
||||
@@ -53,7 +52,7 @@ public final class TypeAliasNode extends ExpressionNode {
|
||||
int modifiers,
|
||||
String simpleName,
|
||||
String qualifiedName,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
UnresolvedTypeNode typeAnnotationNode) {
|
||||
super(sourceSection);
|
||||
this.headerSection = headerSection;
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.type.TypeParameterNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
|
||||
@@ -69,6 +70,7 @@ public final class UnresolvedFunctionNode extends PklNode {
|
||||
parameterTypeNodes,
|
||||
returnTypeNode,
|
||||
true,
|
||||
new TypeParameterNode[0],
|
||||
bodyNode);
|
||||
}
|
||||
}
|
||||
@@ -19,17 +19,16 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
|
||||
public final class UnresolvedMethodNode extends UnresolvedClassMemberNode {
|
||||
private final int parameterCount;
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedReturnTypeNode;
|
||||
private final boolean isReturnTypeChecked;
|
||||
@@ -46,7 +45,7 @@ public final class UnresolvedMethodNode extends UnresolvedClassMemberNode {
|
||||
Identifier name,
|
||||
String qualifiedName,
|
||||
int parameterCount,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
@Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes,
|
||||
@Nullable UnresolvedTypeNode unresolvedReturnTypeNode,
|
||||
boolean isReturnTypeChecked,
|
||||
@@ -92,6 +91,7 @@ public final class UnresolvedMethodNode extends UnresolvedClassMemberNode {
|
||||
VmUtils.resolveParameterTypes(frame, descriptor, unresolvedParameterTypeNodes);
|
||||
var returnTypeNode =
|
||||
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null;
|
||||
var typeParameterNodes = VmUtils.resolveTypeParameters(typeParameters);
|
||||
|
||||
String deprecation = null;
|
||||
for (var annotation : annotations) {
|
||||
@@ -124,6 +124,7 @@ public final class UnresolvedMethodNode extends UnresolvedClassMemberNode {
|
||||
parameterTypeNodes,
|
||||
returnTypeNode,
|
||||
isReturnTypeChecked,
|
||||
typeParameterNodes,
|
||||
bodyNode);
|
||||
|
||||
method.initFunctionNode(functionNode);
|
||||
|
||||
@@ -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.type;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
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.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.runtime.VmTypeArgument;
|
||||
|
||||
public abstract class CallTypeArgumentNode extends PklNode {
|
||||
|
||||
public CallTypeArgumentNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
}
|
||||
|
||||
public abstract Object execute(
|
||||
VirtualFrame frame, VmTypeArgument typeArgument, Object value, boolean lazy);
|
||||
|
||||
@Specialization(guards = {"typeArgument.getCallTarget() == cachedCallTarget"})
|
||||
protected Object evalDirect(
|
||||
@SuppressWarnings("unused") VmTypeArgument typeArgument,
|
||||
Object value,
|
||||
boolean lazy,
|
||||
@Cached("typeArgument.getCallTarget()") @SuppressWarnings("unused")
|
||||
CallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
var enclosingFrame = typeArgument.getEnclosingFrame();
|
||||
if (enclosingFrame == null) {
|
||||
return callNode.call(null, null, null, value, lazy);
|
||||
}
|
||||
|
||||
var arguments = enclosingFrame.getArguments();
|
||||
return callNode.call(arguments[0], arguments[1], enclosingFrame, value, lazy);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalDirect")
|
||||
protected Object eval(
|
||||
VmTypeArgument typeArgument,
|
||||
Object value,
|
||||
boolean lazy,
|
||||
@Cached("create()") IndirectCallNode callNode) {
|
||||
var enclosingFrame = typeArgument.getEnclosingFrame();
|
||||
if (enclosingFrame == null) {
|
||||
return callNode.call(typeArgument.getCallTarget(), null, null, null, value, lazy);
|
||||
}
|
||||
|
||||
var arguments = enclosingFrame.getArguments();
|
||||
return callNode.call(
|
||||
typeArgument.getCallTarget(), arguments[0], arguments[1], enclosingFrame, value, lazy);
|
||||
}
|
||||
}
|
||||
@@ -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.type;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.PklRootNode;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
|
||||
public final class TypeArgumentTypeNode extends PklRootNode {
|
||||
|
||||
private final SourceSection sourceSection;
|
||||
private final String qualifiedName;
|
||||
@Child private TypeNode typeNode;
|
||||
|
||||
public TypeArgumentTypeNode(
|
||||
VmLanguage language,
|
||||
FrameDescriptor frameDescriptor,
|
||||
SourceSection sourceSection,
|
||||
String qualifiedName,
|
||||
TypeNode typeNode) {
|
||||
super(language, frameDescriptor, true);
|
||||
this.sourceSection = sourceSection;
|
||||
this.qualifiedName = qualifiedName;
|
||||
this.typeNode = typeNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceSection getSourceSection() {
|
||||
return sourceSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
public TypeNode getTypeNode() {
|
||||
return typeNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object executeImpl(VirtualFrame frame) {
|
||||
var enclosingFrame = (MaterializedFrame) frame.getArguments()[2];
|
||||
var value = frame.getArguments()[3];
|
||||
var lazy = (boolean) frame.getArguments()[4];
|
||||
if (enclosingFrame == null) {
|
||||
return lazy ? typeNode.executeLazily(frame, value) : typeNode.executeEagerly(frame, value);
|
||||
}
|
||||
return lazy
|
||||
? typeNode.executeLazily(enclosingFrame, value)
|
||||
: typeNode.executeEagerly(enclosingFrame, value);
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.LoopNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.NodeUtil;
|
||||
import com.oracle.truffle.api.profiles.LoopConditionProfile;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -43,8 +41,6 @@ import java.util.function.Function;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.PType;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.*;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
||||
import org.pkl.core.ast.expression.primary.GetReceiverClassNode;
|
||||
@@ -57,12 +53,12 @@ import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.ast.member.UntypedObjectMemberNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter.OwnerType;
|
||||
import org.pkl.core.stdlib.VmObjectFactory;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.EconomicSets;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.core.util.MutableBoolean;
|
||||
import org.pkl.core.util.MutableReference;
|
||||
|
||||
public abstract class TypeNode extends PklNode {
|
||||
@CompilationFinal private @Nullable VmType type;
|
||||
@@ -167,10 +163,23 @@ public abstract class TypeNode extends PklNode {
|
||||
// header section of the property or method that carries the type annotation
|
||||
SourceSection headerSection,
|
||||
// qualified name of the property or method that carries the type annotation
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// method arguments are used when default value contains a root node
|
||||
public final @Nullable Object createDefaultValue(
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
// header section of the property or method that carries the type annotation
|
||||
SourceSection headerSection,
|
||||
// qualified name of the property or method that carries the type annotation
|
||||
String qualifiedName) {
|
||||
return createDefaultValue(frame, language, headerSection, qualifiedName, false, null);
|
||||
}
|
||||
|
||||
@Idempotent
|
||||
public final boolean isFinalType() {
|
||||
var ret = new MutableBoolean(true);
|
||||
@@ -178,7 +187,9 @@ public abstract class TypeNode extends PklNode {
|
||||
true,
|
||||
typeNode -> {
|
||||
// assumption: don't need to worry about `NonFinalClassTypeNode`
|
||||
if (typeNode instanceof NonFinalSelfTypeNode) {
|
||||
if (typeNode instanceof NonFinalSelfTypeNode
|
||||
|| (typeNode instanceof TypeVariableNode typeVariable
|
||||
&& typeVariable.ownerType == TypeParameter.OwnerType.METHOD)) {
|
||||
ret.set(false);
|
||||
return false;
|
||||
}
|
||||
@@ -201,6 +212,24 @@ public abstract class TypeNode extends PklNode {
|
||||
return ret.get();
|
||||
}
|
||||
|
||||
public final boolean getTypeArgumentRequiresFrame() {
|
||||
var ret = new MutableBoolean(false);
|
||||
acceptTypeNode(
|
||||
true,
|
||||
typeNode -> {
|
||||
if (typeNode instanceof ConstrainedTypeNode
|
||||
|| typeNode instanceof NonFinalSelfTypeNode
|
||||
|| typeNode instanceof ListingOrMappingTypeNode // lazy checks need captured frame
|
||||
|| (typeNode instanceof TypeVariableNode typeVar
|
||||
&& typeVar.ownerType == TypeParameter.OwnerType.METHOD)) {
|
||||
ret.set(true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return ret.get();
|
||||
}
|
||||
|
||||
/** Visit child type nodes of this type. */
|
||||
protected abstract boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer);
|
||||
|
||||
@@ -451,6 +480,7 @@ public abstract class TypeNode extends PklNode {
|
||||
return mirrorFactory.create(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
|
||||
return consumer.accept(this);
|
||||
}
|
||||
@@ -460,7 +490,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return TypeNode.createDefaultValue(clazz);
|
||||
}
|
||||
}
|
||||
@@ -530,7 +562,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
var clazz = ((VmObjectLike) getTargetNode.executeGeneric(frame)).getVmClass();
|
||||
return TypeNode.createDefaultValue(clazz);
|
||||
}
|
||||
@@ -571,7 +605,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return literal;
|
||||
}
|
||||
|
||||
@@ -625,7 +661,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return VmDynamic.empty();
|
||||
}
|
||||
|
||||
@@ -672,7 +710,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
|
||||
return TypeNode.createDefaultValue(clazz);
|
||||
}
|
||||
@@ -736,7 +776,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return TypeNode.createDefaultValue(clazz);
|
||||
}
|
||||
|
||||
@@ -777,9 +819,12 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return VmNull.withDefault(
|
||||
elementTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName));
|
||||
elementTypeNode.createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -849,12 +894,14 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
|
||||
return defaultIndex == -1
|
||||
? null
|
||||
: elementTypeNodes[defaultIndex].createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName);
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1190,7 +1237,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
if (defaultIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
@@ -1247,7 +1296,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return VmList.EMPTY;
|
||||
}
|
||||
|
||||
@@ -1330,7 +1381,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return VmList.EMPTY;
|
||||
}
|
||||
|
||||
@@ -1401,7 +1454,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return VmSet.EMPTY;
|
||||
}
|
||||
|
||||
@@ -1481,7 +1536,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
|
||||
return VmMap.EMPTY;
|
||||
}
|
||||
@@ -1554,7 +1611,7 @@ public abstract class TypeNode extends PklNode {
|
||||
return vmListing;
|
||||
}
|
||||
return new VmListing(
|
||||
vmListing.getEnclosingFrame(),
|
||||
frame.materialize(),
|
||||
vmListing,
|
||||
EconomicMaps.emptyMap(),
|
||||
vmListing.getLength(),
|
||||
@@ -1613,7 +1670,7 @@ public abstract class TypeNode extends PklNode {
|
||||
return vmMapping;
|
||||
}
|
||||
return new VmMapping(
|
||||
vmMapping.getEnclosingFrame(),
|
||||
frame.materialize(),
|
||||
vmMapping,
|
||||
EconomicMaps.emptyMap(),
|
||||
getValueTypeCastNode(frame.getFrameDescriptor()),
|
||||
@@ -1775,14 +1832,17 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
|
||||
if (valueTypeNode instanceof UnknownTypeNode) {
|
||||
return newEmptyListingOrMapping();
|
||||
}
|
||||
|
||||
var defaultMemberValue =
|
||||
valueTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
valueTypeNode.createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
|
||||
var defaultMember = createDefaultMember(headerSection, qualifiedName, defaultMemberValue);
|
||||
|
||||
@@ -1984,7 +2044,7 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class ReferenceTypeNode extends ValidatingObjectSlotTypeNode {
|
||||
public abstract static class ReferenceTypeNode extends ObjectSlotTypeNode {
|
||||
@Child private TypeNode domainTypeNode;
|
||||
@Child private TypeNode referentTypeNode;
|
||||
@Child private ExpressionNode getReceiverClassNode;
|
||||
@@ -1997,7 +2057,6 @@ public abstract class TypeNode extends PklNode {
|
||||
this.referentTypeNode = referentTypeNode;
|
||||
this.getReceiverClassNode = new GetReceiverClassNode(sourceSection);
|
||||
this.getModuleNode = new GetModuleNode(sourceSection);
|
||||
validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2006,55 +2065,29 @@ public abstract class TypeNode extends PklNode {
|
||||
RefModule.getReferenceClass(), domainTypeNode.getType(), referentTypeNode.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getValidationErrorKey() {
|
||||
return "invalidReferenceTypeAnnotationWithConstraint";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final @Nullable Node getViolatingNode() {
|
||||
// constraints may not be used in Reference type annotation referents
|
||||
// walk the type and throw if any part of the referent is constrained
|
||||
var violation = new MutableReference<Node>(null);
|
||||
referentTypeNode.acceptTypeNode(
|
||||
true,
|
||||
(typeNode) -> {
|
||||
if (typeNode instanceof ConstrainedTypeNode) {
|
||||
violation.set(typeNode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return violation.getOrNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isIncludedInTrace(Node node) {
|
||||
return node instanceof ReferenceTypeNode || node instanceof ConstrainedTypeNode;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected Object eval(VirtualFrame frame, VmReference value) {
|
||||
if (domainTypeNode.isNoopTypeCheck() && referentTypeNode.isNoopTypeCheck()) {
|
||||
return value;
|
||||
}
|
||||
|
||||
var realType = (VmType.ClassType) getType().reify(frame);
|
||||
try {
|
||||
domainTypeNode.execute(frame, value.getDomain());
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw typeMismatch(value, getType());
|
||||
throw typeMismatch(value, realType);
|
||||
}
|
||||
|
||||
var module = (VmTyped) getModuleNode.executeGeneric(frame);
|
||||
if (value.referentTypeIsSubtypeOf(
|
||||
referentTypeNode.getType(),
|
||||
realType.getTypeArguments()[1],
|
||||
(VmClass) getReceiverClassNode.executeGeneric(frame),
|
||||
module.getVmClass())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
throw typeMismatch(value, getType());
|
||||
throw typeMismatch(value, realType);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@@ -2151,7 +2184,9 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
.evalError("internalStdLibClass", "VarArgs")
|
||||
@@ -2173,24 +2208,34 @@ public abstract class TypeNode extends PklNode {
|
||||
|
||||
public static final class TypeVariableNode extends WriteFrameSlotTypeNode {
|
||||
private final TypeParameter typeParameter;
|
||||
private final int levelsUp;
|
||||
private final TypeParameter.OwnerType ownerType;
|
||||
@Child private CallTypeArgumentNode callTypeArgumentNode;
|
||||
|
||||
public TypeVariableNode(SourceSection sourceSection, TypeParameter typeParameter) {
|
||||
public TypeVariableNode(
|
||||
SourceSection sourceSection,
|
||||
TypeParameter typeParameter,
|
||||
TypeParameter.OwnerType ownerType,
|
||||
int levelsUp) {
|
||||
super(sourceSection);
|
||||
this.typeParameter = typeParameter;
|
||||
this.ownerType = ownerType;
|
||||
this.levelsUp = levelsUp;
|
||||
callTypeArgumentNode = CallTypeArgumentNodeGen.create(sourceSection);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VmType doGetType() {
|
||||
return new VmType.TypeVariableType(typeParameter);
|
||||
return new VmType.TypeVariableType(typeParameter, ownerType, levelsUp);
|
||||
}
|
||||
|
||||
public int getTypeParameterIndex() {
|
||||
return typeParameter.getIndex();
|
||||
public TypeParameter getTypeParameter() {
|
||||
return typeParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoopTypeCheck() {
|
||||
return true;
|
||||
return levelsUp < 0 || ownerType != TypeParameter.OwnerType.METHOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2202,16 +2247,77 @@ public abstract class TypeNode extends PklNode {
|
||||
return MirrorFactories.typeParameterFactory.create(typeParameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeEagerly(VirtualFrame frame, Object value) {
|
||||
return doExecute(frame, value, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
// do nothing
|
||||
return value;
|
||||
return doExecute(frame, value, true);
|
||||
}
|
||||
|
||||
private Object doExecute(VirtualFrame frame, Object value, boolean lazy) {
|
||||
if (ownerType != OwnerType.METHOD) return value;
|
||||
|
||||
var typeArg = VmUtils.getTypeArgumentOrNull(frame, typeParameter.slot(), levelsUp);
|
||||
if (typeArg == null) return value;
|
||||
|
||||
try {
|
||||
return callTypeArgumentNode.execute(frame, typeArg, value, lazy);
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
if (!VmUtils.sourceSectionContains(sourceSection, e.sourceSection)) {
|
||||
var rootNode = getRootNode();
|
||||
e.putInsertedStackFrame(
|
||||
rootNode.getCallTarget(),
|
||||
VmUtils.createStackFrame(sourceSection, rootNode.getName()));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
|
||||
return consumer.accept(this);
|
||||
}
|
||||
|
||||
private @Nullable VmTypeArgument getTypeArgumentForDefaultValue(
|
||||
VirtualFrame frame, boolean isPreCall, VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
// when creating default value for a method argument, the type node is 1 level _lower_ than
|
||||
// what was resolved lexically.
|
||||
//
|
||||
// edge case: if `levelsUp == 0`, we need to resolve the type argument from
|
||||
// `typeArgumentOverrides` rather than the frame.
|
||||
//
|
||||
// e.g.
|
||||
// ```
|
||||
// function foo<T>(arg: T)
|
||||
// res = foo::<Person>(new {}) // `Person` is passed in via `typeArgumentOverrides`
|
||||
// ```
|
||||
if (levelsUp == 0 && isPreCall) {
|
||||
return typeArgumentOverrides == null ? null : typeArgumentOverrides[typeParameter.index()];
|
||||
}
|
||||
return VmUtils.getTypeArgumentOrNull(frame, typeParameter.slot(), levelsUp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object createDefaultValue(
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
if (ownerType != OwnerType.METHOD) return null;
|
||||
|
||||
var typeArg = getTypeArgumentForDefaultValue(frame, isPreCall, typeArgumentOverrides);
|
||||
if (typeArg != null) {
|
||||
return typeArg.createDefaultValue(language, headerSection, qualifiedName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NonNullTypeAliasTypeNode extends WriteFrameSlotTypeNode {
|
||||
@@ -2418,13 +2524,6 @@ public abstract class TypeNode extends PklNode {
|
||||
this.typeAlias = typeAlias;
|
||||
this.typeArgumentNodes = typeArgumentNodes;
|
||||
aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes);
|
||||
aliasedTypeNode.accept(
|
||||
node -> {
|
||||
if (node instanceof ValidatingObjectSlotTypeNode typeNode) {
|
||||
typeNode.validate(this);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2521,12 +2620,15 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
if (typeAlias == BaseModule.getMixinTypeAlias()) {
|
||||
return newMixin(language, qualifiedName);
|
||||
}
|
||||
|
||||
return aliasedTypeNode.createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
return aliasedTypeNode.createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2613,9 +2715,11 @@ public abstract class TypeNode extends PklNode {
|
||||
VirtualFrame frame,
|
||||
VmLanguage language,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
|
||||
return childNode.createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
String qualifiedName,
|
||||
boolean isPreCall,
|
||||
VmTypeArgument @Nullable [] typeArgumentOverrides) {
|
||||
return childNode.createDefaultValue(
|
||||
frame, language, headerSection, qualifiedName, isPreCall, typeArgumentOverrides);
|
||||
}
|
||||
|
||||
public SourceSection getBaseTypeSection() {
|
||||
@@ -2914,78 +3018,6 @@ public abstract class TypeNode extends PklNode {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class ValidatingObjectSlotTypeNode extends ObjectSlotTypeNode {
|
||||
|
||||
protected ValidatingObjectSlotTypeNode(SourceSection sourceSection) {
|
||||
super(sourceSection);
|
||||
}
|
||||
|
||||
protected abstract String getValidationErrorKey();
|
||||
|
||||
protected abstract @Nullable Node getViolatingNode();
|
||||
|
||||
protected final void validate() {
|
||||
var violation = getViolatingNode();
|
||||
if (violation == null) return;
|
||||
throw exceptionBuilder()
|
||||
.evalError(getValidationErrorKey())
|
||||
.withLeadingStackFrames(buildLeadingFrames(violation, getSourceSection(), null))
|
||||
.build();
|
||||
}
|
||||
|
||||
public final void validate(TypeAliasTypeNode outermostAliasNode) {
|
||||
var violation = getViolatingNode();
|
||||
if (violation == null) return;
|
||||
|
||||
throw exceptionBuilder()
|
||||
.withLocation(outermostAliasNode)
|
||||
.evalError(getValidationErrorKey())
|
||||
.withLeadingStackFrames(
|
||||
buildLeadingFrames(
|
||||
violation,
|
||||
outermostAliasNode.getSourceSection(),
|
||||
outermostAliasNode.getTypeAlias()))
|
||||
.build();
|
||||
}
|
||||
|
||||
protected abstract boolean isIncludedInTrace(Node node);
|
||||
|
||||
private List<StackFrame> buildLeadingFrames(
|
||||
Node violatingNode, SourceSection usageSection, @Nullable VmTypeAlias outermostAlias) {
|
||||
var frames = new ArrayList<StackFrame>();
|
||||
for (var node = violatingNode; node != null; node = node.getParent()) {
|
||||
if (!(node instanceof TypeAliasTypeNode || isIncludedInTrace(node))) continue;
|
||||
var section = node.getSourceSection();
|
||||
if (section == null || !section.isAvailable() || isWithin(usageSection, section)) {
|
||||
continue;
|
||||
}
|
||||
var owner = ownerAlias(node, outermostAlias);
|
||||
if (owner != null) {
|
||||
frames.add(VmUtils.createStackFrame(section, owner.getQualifiedName()));
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type alias whose body contains {@code node}: the nearest enclosing alias, else the
|
||||
* outermost alias being instantiated (which is {@code null} when no alias is used).
|
||||
*/
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
private static @Nullable VmTypeAlias ownerAlias(
|
||||
Node node, @Nullable VmTypeAlias outermostAlias) {
|
||||
var parent = NodeUtil.findParent(node, TypeAliasTypeNode.class);
|
||||
//noinspection ConstantValue
|
||||
return parent != null ? parent.getTypeAlias() : outermostAlias;
|
||||
}
|
||||
|
||||
private static boolean isWithin(SourceSection outer, SourceSection inner) {
|
||||
return inner.getSource().equals(outer.getSource())
|
||||
&& inner.getCharIndex() >= outer.getCharIndex()
|
||||
&& inner.getCharEndIndex() <= outer.getCharEndIndex();
|
||||
}
|
||||
}
|
||||
|
||||
public static VmType[] toTypes(TypeNode[] typeNodes) {
|
||||
return toTypes(typeNodes, typeNodes.length);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.type;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
|
||||
public final class TypeParameterNode extends PklNode {
|
||||
|
||||
private final TypeParameter typeParameter;
|
||||
|
||||
public TypeParameterNode(TypeParameter typeParameter) {
|
||||
super(typeParameter.sourceSection());
|
||||
this.typeParameter = typeParameter;
|
||||
}
|
||||
|
||||
public void executeAndSet(VirtualFrame frame, Object arg) {
|
||||
frame.setObject(typeParameter.slot(), arg);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleOwnerNode;
|
||||
@@ -499,21 +498,29 @@ public abstract class UnresolvedTypeNode extends PklNode {
|
||||
|
||||
public static final class TypeVariable extends UnresolvedTypeNode {
|
||||
private final TypeParameter typeParameter;
|
||||
private final int levelsUp;
|
||||
private final TypeParameter.OwnerType ownerType;
|
||||
|
||||
public TypeVariable(SourceSection sourceSection, TypeParameter typeParameter) {
|
||||
public TypeVariable(
|
||||
SourceSection sourceSection,
|
||||
TypeParameter typeParameter,
|
||||
TypeParameter.OwnerType ownerType,
|
||||
int levelsUp) {
|
||||
super(sourceSection);
|
||||
this.typeParameter = typeParameter;
|
||||
this.ownerType = ownerType;
|
||||
this.levelsUp = levelsUp;
|
||||
}
|
||||
|
||||
public int getTypeParameterIndex() {
|
||||
return typeParameter.getIndex();
|
||||
public TypeParameter getTypeParameter() {
|
||||
return typeParameter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeNode execute(VirtualFrame frame) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
return new TypeVariableNode(sourceSection, typeParameter);
|
||||
return new TypeVariableNode(sourceSection, typeParameter, ownerType, levelsUp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.pkl.core.repl.ReplResponse.EvalSuccess;
|
||||
import org.pkl.core.repl.ReplResponse.InvalidRequest;
|
||||
import org.pkl.core.resource.ResourceReader;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.TypeParameter;
|
||||
import org.pkl.core.util.AnsiStringBuilder;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.IoUtils;
|
||||
@@ -478,7 +479,7 @@ public class ReplServer implements AutoCloseable {
|
||||
List.of(),
|
||||
VmModifier.NONE,
|
||||
classInfo,
|
||||
List.of(),
|
||||
new TypeParameter[0],
|
||||
module);
|
||||
if (parent != null) {
|
||||
var superclass = parent.getVmClass();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.member.ClassMethod;
|
||||
import org.pkl.core.ast.member.ClassProperty;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
@@ -209,11 +208,11 @@ public final class MirrorFactories {
|
||||
.addTypedProperty("type", Pair::getSecond);
|
||||
|
||||
typeParameterFactory
|
||||
.addStringProperty("name", TypeParameter::getName)
|
||||
.addStringProperty("name", TypeParameter::name)
|
||||
.addProperty(
|
||||
"variance",
|
||||
typeParameter ->
|
||||
switch (typeParameter.getVariance()) {
|
||||
switch (typeParameter.variance()) {
|
||||
case COVARIANT -> "out";
|
||||
case CONTRAVARIANT -> "in";
|
||||
default -> VmNull.withoutDefault();
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.runtime;
|
||||
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.pkl.core.TypeParameter.Variance;
|
||||
|
||||
public record TypeParameter(
|
||||
SourceSection sourceSection, int slot, int index, String name, Variance variance) {
|
||||
public org.pkl.core.TypeParameter export() {
|
||||
return new org.pkl.core.TypeParameter(variance, name, index);
|
||||
}
|
||||
|
||||
public static List<org.pkl.core.TypeParameter> export(TypeParameter[] typeParameters) {
|
||||
return Arrays.stream(typeParameters).map(TypeParameter::export).toList();
|
||||
}
|
||||
|
||||
public enum OwnerType {
|
||||
CLASS,
|
||||
METHOD,
|
||||
TYPEALIAS
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import org.pkl.core.Member.SourceLocation;
|
||||
import org.pkl.core.PClass;
|
||||
import org.pkl.core.PClassInfo;
|
||||
import org.pkl.core.PObject;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.*;
|
||||
import org.pkl.core.ast.member.*;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
@@ -51,7 +50,7 @@ public final class VmClass extends VmValue {
|
||||
private final List<VmTyped> annotations;
|
||||
private final int modifiers;
|
||||
private final PClassInfo<?> classInfo;
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
private final VmTyped prototype;
|
||||
|
||||
private final EconomicMap<Identifier, ClassProperty> declaredProperties = EconomicMaps.create();
|
||||
@@ -127,7 +126,7 @@ public final class VmClass extends VmValue {
|
||||
List<VmTyped> annotations,
|
||||
int modifiers,
|
||||
PClassInfo<?> classInfo,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
VmTyped prototype) {
|
||||
|
||||
this.sourceSection = sourceSection;
|
||||
@@ -190,6 +189,41 @@ public final class VmClass extends VmValue {
|
||||
return result;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private void checkMethodOverrides() {
|
||||
var methodCursor = declaredMethods.getEntries();
|
||||
while (methodCursor.advance()) {
|
||||
var method = methodCursor.getValue();
|
||||
if (method.isLocal()) continue;
|
||||
var name = methodCursor.getKey();
|
||||
ClassMethod parent = null;
|
||||
for (var clazz = superclass; clazz != null; clazz = clazz.superclass) {
|
||||
parent = clazz.getDeclaredMethod(name);
|
||||
if (parent != null) break;
|
||||
}
|
||||
if (parent == null) continue;
|
||||
|
||||
checkMethodOverride(method, parent);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMethodOverride(ClassMethod method, ClassMethod parent) {
|
||||
var typeParamCount = method.getTypeParameterCount();
|
||||
var parentTypeParamCount = parent.getTypeParameterCount();
|
||||
|
||||
if (typeParamCount != parentTypeParamCount) {
|
||||
throw new VmExceptionBuilder()
|
||||
.evalError(
|
||||
"methodTypeParameterCountMismatch",
|
||||
method.getQualifiedName(),
|
||||
parent.getQualifiedName(),
|
||||
parentTypeParamCount,
|
||||
typeParamCount)
|
||||
.withSourceSection(method.getHeaderSection())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public void addProperty(ClassProperty property) {
|
||||
prototype.addProperty(property.getInitializer());
|
||||
@@ -242,14 +276,15 @@ public final class VmClass extends VmValue {
|
||||
/** Called when the entire class hierarchy is completely initialized, including superclasses. */
|
||||
public void onFullyInitialized() {
|
||||
checkAbstractMethods();
|
||||
checkMethodOverrides();
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameters.size();
|
||||
return typeParameters.length;
|
||||
}
|
||||
|
||||
public List<TypeParameter> getTypeParameters() {
|
||||
public TypeParameter[] getTypeParameters() {
|
||||
return typeParameters;
|
||||
}
|
||||
|
||||
@@ -723,12 +758,12 @@ public final class VmClass extends VmValue {
|
||||
VmModifier.export(modifiers, true),
|
||||
exportedAnnotations,
|
||||
classInfo,
|
||||
typeParameters,
|
||||
TypeParameter.export(typeParameters),
|
||||
properties,
|
||||
methods,
|
||||
moduleClass);
|
||||
|
||||
for (var parameter : typeParameters) {
|
||||
for (var parameter : __pClass.getTypeParameters()) {
|
||||
parameter.initOwner(__pClass);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,13 @@ public abstract class VmListingOrMapping extends VmObject {
|
||||
if (obj.typeCastNode != null && obj.typeCastNode != prevTypeCastNode) {
|
||||
var callTarget = obj.typeCastNode.getCallTarget();
|
||||
try {
|
||||
result = callNode.call(callTarget, obj.typeCheckReceiver, obj.typeCheckOwner, result);
|
||||
result =
|
||||
callNode.call(
|
||||
callTarget,
|
||||
VmUtils.getReceiver(enclosingFrame),
|
||||
VmUtils.getOwner(enclosingFrame),
|
||||
obj.enclosingFrame,
|
||||
result);
|
||||
prevTypeCastNode = obj.typeCastNode;
|
||||
} catch (VmException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
@@ -109,7 +115,8 @@ public abstract class VmListingOrMapping extends VmObject {
|
||||
var callNode = IndirectCallNode.getUncached();
|
||||
var callTarget = typeCastNode.getCallTarget();
|
||||
try {
|
||||
result = callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result);
|
||||
result =
|
||||
callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, enclosingFrame, result);
|
||||
} catch (VmException e) {
|
||||
var member = VmUtils.findMember(parent, key);
|
||||
assert member != null; // already found the member's cached value
|
||||
@@ -133,6 +140,9 @@ public abstract class VmListingOrMapping extends VmObject {
|
||||
if (typeCastNode == null) {
|
||||
return false;
|
||||
}
|
||||
return typeCastNode.getTypeNode().getType().equals(typeNode.getType());
|
||||
var castTypeNode = typeCastNode.getTypeNode();
|
||||
// if cast type is not final, we can't know if typeNode is a subtype
|
||||
if (!castTypeNode.isFinalType()) return false;
|
||||
return castTypeNode.getType().equals(typeNode.getType());
|
||||
}
|
||||
}
|
||||
@@ -56,12 +56,8 @@ public final class VmReference extends VmValue {
|
||||
.toTyped(RefModule.getAccessClass());
|
||||
}
|
||||
|
||||
public VmReference(VmTyped domain, VmClass clazz, Object data) {
|
||||
this(
|
||||
domain,
|
||||
data,
|
||||
RrbTree.empty(),
|
||||
normalizeTypes(new VmType.ClassType(clazz), clazz, clazz.getModuleClass()));
|
||||
public VmReference(VmTyped domain, VmType referentType, Object data) {
|
||||
this(domain, data, RrbTree.empty(), normalizeTypes(referentType, null, null));
|
||||
}
|
||||
|
||||
public VmReference(VmTyped domain, Object data, ImRrbt<VmTyped> path, VmType referentType) {
|
||||
@@ -95,7 +91,8 @@ public final class VmReference extends VmValue {
|
||||
// * replace VmType.ModuleType with appropriate VmType.ClassType
|
||||
// * drop VmType.FunctionType and VmType.TypeVariableType
|
||||
@TruffleBoundary
|
||||
private static VmType normalizeTypes(VmType type, VmClass thisClass, VmClass moduleClass) {
|
||||
private static VmType normalizeTypes(
|
||||
VmType type, @Nullable VmClass thisClass, @Nullable VmClass moduleClass) {
|
||||
var types = new HashSet<VmType>();
|
||||
normalizeTypes(type, thisClass, moduleClass, types);
|
||||
return minimizeTypes(types);
|
||||
@@ -115,7 +112,7 @@ public final class VmReference extends VmValue {
|
||||
}
|
||||
|
||||
private static void normalizeTypes(
|
||||
VmType type, VmClass thisClass, VmClass moduleClass, Set<VmType> result) {
|
||||
VmType type, @Nullable VmClass thisClass, @Nullable VmClass moduleClass, Set<VmType> result) {
|
||||
if (type == UnknownType.INSTANCE
|
||||
|| type == NothingType.INSTANCE
|
||||
|| type instanceof VmType.StringLiteralType) {
|
||||
@@ -154,17 +151,19 @@ public final class VmReference extends VmValue {
|
||||
}
|
||||
} else if (type instanceof NonFinalThisType) {
|
||||
// there are 4 entrypoints here:
|
||||
// 1. init via the Reference constructor can only normalize an unparameterized PType.Class
|
||||
// 2. typecheck via ReferenceTypeNode erases self types to their actual PType.Class
|
||||
// 1. init via the Reference constructor, which pre-reifies the VmType
|
||||
// 2. typecheck via ReferenceTypeNode, which pre-reifies the VmType
|
||||
// 3. subscript access can only be achieved by first performing property access, at which time
|
||||
// self types are erased
|
||||
// 4. property access uses the enclosing receiver's class to substitute for these self types
|
||||
// only property access and typecheck can produce THIS or MODULE.
|
||||
// getCandidatePropertyType and referentTypeIsSubtypeOf always pass non-null `thisClass`.
|
||||
// getCandidatePropertyType always passes non-null `thisClass`.
|
||||
assert thisClass != null;
|
||||
result.add(new VmType.ClassType(thisClass));
|
||||
} else if (type instanceof NonFinalModuleType) {
|
||||
// this can be incorrect for usage of the module type in a class's property type annotation,
|
||||
// which is deprecated!!
|
||||
assert moduleClass != null;
|
||||
result.add(new VmType.ClassType(moduleClass));
|
||||
} else {
|
||||
// remaining types: PType.Function, PType.TypeVariable. no normalizing needed; TypeVariable
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
@@ -25,8 +26,8 @@ import org.pkl.core.PType.Constrained;
|
||||
import org.pkl.core.PType.StringLiteral;
|
||||
import org.pkl.core.PType.TypeVariable;
|
||||
import org.pkl.core.PType.Union;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ValueFormatter;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
||||
|
||||
public abstract sealed class VmType {
|
||||
|
||||
@@ -73,6 +74,15 @@ public abstract sealed class VmType {
|
||||
|| (other instanceof ConstrainedType ct && isSupertypeOf(ct.baseType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Where possible, substitute types for their "real" type. Self types become {@link ClassType}.
|
||||
* Where possible, {@link TypeVariableType} instances are replaced with the corresponding type
|
||||
* argument type.
|
||||
*
|
||||
* <p>Contract: when no substitutions were made, returns the identical type (same object).
|
||||
*/
|
||||
public abstract VmType reify(VirtualFrame frame);
|
||||
|
||||
public static final class UnknownType extends VmType {
|
||||
public static final UnknownType INSTANCE = new UnknownType();
|
||||
|
||||
@@ -99,6 +109,11 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NothingType extends VmType {
|
||||
@@ -127,6 +142,11 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "nothing";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class StringLiteralType extends VmType {
|
||||
@@ -166,6 +186,11 @@ public abstract sealed class VmType {
|
||||
public int hashCode() {
|
||||
return 31 * literal.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static sealed class AbstractClassType extends VmType {
|
||||
@@ -265,7 +290,7 @@ public abstract sealed class VmType {
|
||||
var newState = cSupertype.typeArguments.clone();
|
||||
for (var i = 0; i < newState.length; i++) {
|
||||
if (!(newState[i] instanceof TypeVariableType tvt)) continue;
|
||||
newState[i] = state[tvt.typeParameter.getIndex()];
|
||||
newState[i] = state[tvt.typeParameter.index()];
|
||||
}
|
||||
state = newState;
|
||||
}
|
||||
@@ -274,7 +299,7 @@ public abstract sealed class VmType {
|
||||
assert goalState.length == state.length;
|
||||
var params = clazz.getTypeParameters();
|
||||
for (var i = 0; i < goalState.length; i++) {
|
||||
if (!switch (params.get(i).getVariance()) {
|
||||
if (!switch (params[i].variance()) {
|
||||
case INVARIANT -> state[i].equals(goalState[i]);
|
||||
case COVARIANT -> state[i].isSubtypeOf(goalState[i]);
|
||||
case CONTRAVARIANT -> state[i].isSupertypeOf(goalState[i]);
|
||||
@@ -340,6 +365,18 @@ public abstract sealed class VmType {
|
||||
return appendTypeArguments(new StringBuilder(clazz.getDisplayName()), typeArguments)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var typeArgs = new VmType[typeArguments.length];
|
||||
var changed = false;
|
||||
for (var i = 0; i < typeArgs.length; i++) {
|
||||
typeArgs[i] = typeArguments[i].reify(frame);
|
||||
changed = changed || typeArguments[i] != typeArgs[i];
|
||||
}
|
||||
|
||||
return changed ? new ClassType(clazz, typeArgs) : this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FinalModuleType extends AbstractClassType {
|
||||
@@ -356,6 +393,11 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "module";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class FinalThisType extends AbstractClassType {
|
||||
@@ -372,6 +414,11 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "this";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static sealed class NonFinalSelfType extends VmType {
|
||||
@@ -416,6 +463,14 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "module";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var levelsUp = GetModuleNode.getLevelsUp(frame);
|
||||
return new ClassType(
|
||||
VmUtils.getClass(
|
||||
levelsUp == 0 ? VmUtils.getReceiver(frame) : VmUtils.getReceiver(frame, levelsUp)));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NonFinalThisType extends NonFinalSelfType {
|
||||
@@ -432,6 +487,11 @@ public abstract sealed class VmType {
|
||||
public String toString() {
|
||||
return "this";
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
return new ClassType(VmUtils.getClass(VmUtils.getReceiver(frame)));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class NullableType extends VmType {
|
||||
@@ -473,6 +533,12 @@ public abstract sealed class VmType {
|
||||
public int hashCode() {
|
||||
return 31 * elementType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var elem = elementType.reify(frame);
|
||||
return elem == elementType ? this : new NullableType(elem);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ConstrainedType extends VmType {
|
||||
@@ -522,6 +588,12 @@ public abstract sealed class VmType {
|
||||
public int hashCode() {
|
||||
return 31 * identity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var base = baseType.reify(frame);
|
||||
return base == baseType ? this : new ConstrainedType(base, constraints, identity);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class AliasType extends VmType {
|
||||
@@ -554,6 +626,18 @@ public abstract sealed class VmType {
|
||||
this.aliasedType = aliasedType;
|
||||
}
|
||||
|
||||
/** For reification */
|
||||
private AliasType(
|
||||
VmTypeAlias typeAlias,
|
||||
@Nullable VmClass clazz,
|
||||
VmType[] typeArguments,
|
||||
VmType aliasedType) {
|
||||
this.typeAlias = typeAlias;
|
||||
this.typeArguments = typeArguments;
|
||||
this.clazz = clazz;
|
||||
this.aliasedType = aliasedType;
|
||||
}
|
||||
|
||||
public VmTypeAlias getVmTypeAlias() {
|
||||
return typeAlias;
|
||||
}
|
||||
@@ -618,6 +702,14 @@ public abstract sealed class VmType {
|
||||
public int hashCode() {
|
||||
return aliasedType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var aliased = aliasedType.reify(frame);
|
||||
return aliased == aliasedType
|
||||
? this
|
||||
: new AliasType(typeAlias, clazz, typeArguments, aliased);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UnionType extends VmType {
|
||||
@@ -692,13 +784,30 @@ public abstract sealed class VmType {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
var elems = new VmType[elementTypes.length];
|
||||
var changed = false;
|
||||
for (var i = 0; i < elems.length; i++) {
|
||||
elems[i] = elementTypes[i].reify(frame);
|
||||
changed = changed || elementTypes[i] != elems[i];
|
||||
}
|
||||
|
||||
return changed ? new UnionType(defaultIndex, elems) : this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TypeVariableType extends VmType {
|
||||
private final TypeParameter typeParameter;
|
||||
private final TypeParameter.OwnerType ownerType;
|
||||
private final int levelsUp;
|
||||
|
||||
public TypeVariableType(TypeParameter typeParameter) {
|
||||
public TypeVariableType(
|
||||
TypeParameter typeParameter, TypeParameter.OwnerType ownerType, int levelsUp) {
|
||||
this.typeParameter = typeParameter;
|
||||
this.ownerType = ownerType;
|
||||
this.levelsUp = levelsUp;
|
||||
}
|
||||
|
||||
public TypeParameter getTypeParameter() {
|
||||
@@ -717,18 +826,30 @@ public abstract sealed class VmType {
|
||||
|
||||
@Override
|
||||
public PType export() {
|
||||
return new TypeVariable(typeParameter);
|
||||
return new TypeVariable(typeParameter.export());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return typeParameter.getName();
|
||||
return typeParameter.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return -318019;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VmType reify(VirtualFrame frame) {
|
||||
if (ownerType != TypeParameter.OwnerType.METHOD) return this;
|
||||
|
||||
var typeArg = VmUtils.getTypeArgumentOrNull(frame, typeParameter.slot(), levelsUp);
|
||||
if (typeArg != null) {
|
||||
return typeArg.reify();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean typesEquals(VmType[] a, VmType[] b) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.pkl.core.Member.SourceLocation;
|
||||
import org.pkl.core.PClassInfo;
|
||||
import org.pkl.core.PObject;
|
||||
import org.pkl.core.TypeAlias;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
@@ -47,7 +46,7 @@ public final class VmTypeAlias extends VmValue {
|
||||
private final String simpleName;
|
||||
private final VmTyped module;
|
||||
private final String qualifiedName;
|
||||
private final List<TypeParameter> typeParameters;
|
||||
private final TypeParameter[] typeParameters;
|
||||
private final MaterializedFrame enclosingFrame;
|
||||
|
||||
private @Nullable TypeNode typeNode;
|
||||
@@ -71,7 +70,7 @@ public final class VmTypeAlias extends VmValue {
|
||||
String simpleName,
|
||||
VmTyped module,
|
||||
String qualifiedName,
|
||||
List<TypeParameter> typeParameters,
|
||||
TypeParameter[] typeParameters,
|
||||
MaterializedFrame enclosingFrame) {
|
||||
this.sourceSection = sourceSection;
|
||||
this.headerSection = headerSection;
|
||||
@@ -170,7 +169,7 @@ public final class VmTypeAlias extends VmValue {
|
||||
}
|
||||
|
||||
public int getTypeParameterCount() {
|
||||
return typeParameters.size();
|
||||
return typeParameters.length;
|
||||
}
|
||||
|
||||
public TypeNode getTypeNode() {
|
||||
@@ -190,14 +189,14 @@ public final class VmTypeAlias extends VmValue {
|
||||
// * Fewer root nodes to call
|
||||
// * ControlFlowException used to implement union types doesn't escape root node
|
||||
|
||||
if (typeParameters.isEmpty()) return deepCopy(typeNode);
|
||||
if (typeParameters.length == 0) return deepCopy(typeNode);
|
||||
|
||||
// handle if alias root is itself a type variable (https://github.com/apple/pkl/issues/1711)
|
||||
if (typeNode instanceof TypeVariableNode typeVarNode) {
|
||||
// no need to run validation since the arg itself has already been checked
|
||||
return typeArgumentNodes.length == 0
|
||||
? new UnknownTypeNode(sourceSection)
|
||||
: typeArgumentNodes[typeVarNode.getTypeParameterIndex()];
|
||||
: typeArgumentNodes[typeVarNode.getTypeParameter().index()];
|
||||
}
|
||||
|
||||
var clone = (TypeNode) typeNode.deepCopy();
|
||||
@@ -206,7 +205,7 @@ public final class VmTypeAlias extends VmValue {
|
||||
@Override
|
||||
public boolean visit(com.oracle.truffle.api.nodes.Node node) {
|
||||
if (node instanceof TypeVariableNode typeVarNode) {
|
||||
var index = typeVarNode.getTypeParameterIndex();
|
||||
var index = typeVarNode.getTypeParameter().index();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnknownTypeNode(sourceSection)
|
||||
@@ -215,7 +214,7 @@ public final class VmTypeAlias extends VmValue {
|
||||
// Type variables inside constraint expressions (e.g. `every((it) -> it is T)`)
|
||||
// are still unresolved at instantiation time. Replace them with a resolved
|
||||
// unresolved type node that returns the concrete type argument.
|
||||
var index = unresolvedTypeVar.getTypeParameterIndex();
|
||||
var index = unresolvedTypeVar.getTypeParameter().index();
|
||||
node.replace(
|
||||
typeArgumentNodes.length == 0
|
||||
? new UnresolvedTypeNode.Unknown(sourceSection)
|
||||
@@ -258,10 +257,10 @@ public final class VmTypeAlias extends VmValue {
|
||||
simpleName,
|
||||
getModuleName(),
|
||||
qualifiedName,
|
||||
typeParameters,
|
||||
TypeParameter.export(typeParameters),
|
||||
module.getVmClass().export());
|
||||
|
||||
for (var parameter : typeParameters) {
|
||||
for (var parameter : __pTypeAlias.getTypeParameters()) {
|
||||
parameter.initOwner(__pTypeAlias);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.member.FunctionNode;
|
||||
import org.pkl.core.ast.type.TypeArgumentTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
|
||||
/**
|
||||
* Type argument information passed from a type argument (in a method call) as an argument to a
|
||||
* {@link FunctionNode}.
|
||||
*/
|
||||
public class VmTypeArgument {
|
||||
|
||||
private final @Nullable MaterializedFrame enclosingFrame;
|
||||
private final TypeArgumentTypeNode rootNode;
|
||||
|
||||
public VmTypeArgument(TypeArgumentTypeNode rootNode, @Nullable MaterializedFrame enclosingFrame) {
|
||||
this.enclosingFrame = enclosingFrame;
|
||||
this.rootNode = rootNode;
|
||||
}
|
||||
|
||||
public @Nullable MaterializedFrame getEnclosingFrame() {
|
||||
return enclosingFrame;
|
||||
}
|
||||
|
||||
public CallTarget getCallTarget() {
|
||||
return rootNode.getCallTarget();
|
||||
}
|
||||
|
||||
private TypeNode getTypeNode() {
|
||||
return rootNode.getTypeNode();
|
||||
}
|
||||
|
||||
public VmType reify() {
|
||||
var type = getTypeNode().getType();
|
||||
var frame = enclosingFrame != null ? enclosingFrame : VmUtils.createEmptyMaterializedFrame();
|
||||
return type.reify(frame);
|
||||
}
|
||||
|
||||
public @Nullable Object createDefaultValue(
|
||||
VmLanguage language, SourceSection headerSection, String qualifiedName) {
|
||||
var frame = enclosingFrame != null ? enclosingFrame : VmUtils.createEmptyMaterializedFrame();
|
||||
return getTypeNode().createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ import org.pkl.core.ast.expression.primary.CustomThisNode;
|
||||
import org.pkl.core.ast.expression.primary.ThisNode;
|
||||
import org.pkl.core.ast.member.*;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.TypeParameterNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.module.ModuleKey;
|
||||
import org.pkl.core.module.ModuleKeys;
|
||||
@@ -182,6 +183,21 @@ public final class VmUtils {
|
||||
return (VmTyped) getReceiver(frame);
|
||||
}
|
||||
|
||||
/** Returns the type arguments of the currently executing code. */
|
||||
public static @Nullable VmTypeArgument getTypeArgumentOrNull(Frame frame, int slot) {
|
||||
if (slot < 0) return null;
|
||||
return (VmTypeArgument) frame.getObject(slot);
|
||||
}
|
||||
|
||||
public static @Nullable VmTypeArgument getTypeArgumentOrNull(
|
||||
Frame frame, int slot, int levelsUp) {
|
||||
if (slot < 0) return null;
|
||||
if (levelsUp == 0) {
|
||||
return getTypeArgumentOrNull(frame, slot);
|
||||
}
|
||||
return getTypeArgumentOrNull(getEnclosingFrame(getOwner(frame), levelsUp), slot);
|
||||
}
|
||||
|
||||
/** Returns the owner of the currently executing code. */
|
||||
public static @Nullable VmObjectLike getOwnerOrNull(Frame frame) {
|
||||
return (VmObjectLike) frame.getArguments()[1];
|
||||
@@ -903,6 +919,14 @@ public final class VmUtils {
|
||||
return resolvedNodes;
|
||||
}
|
||||
|
||||
public static TypeParameterNode[] resolveTypeParameters(TypeParameter[] typeParameters) {
|
||||
var typeParameterNodes = new TypeParameterNode[typeParameters.length];
|
||||
for (var i = 0; i < typeParameters.length; i++) {
|
||||
typeParameterNodes[i] = new TypeParameterNode(typeParameters[i]);
|
||||
}
|
||||
return typeParameterNodes;
|
||||
}
|
||||
|
||||
public static void checkIsInstantiable(VmClass parentClass, Node parentNode, Node callerNode) {
|
||||
if (parentClass.isInstantiable()) return;
|
||||
|
||||
|
||||
@@ -17,17 +17,21 @@ package org.pkl.core.stdlib.ref;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmReference;
|
||||
import org.pkl.core.runtime.VmType;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.stdlib.ExternalMethod3Node;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.stdlib.ExternalMethod2Node;
|
||||
|
||||
public class RefNodes {
|
||||
public abstract static class Reference extends ExternalMethod3Node {
|
||||
public abstract static class Reference extends ExternalMethod2Node {
|
||||
@Specialization
|
||||
protected VmReference eval(
|
||||
VirtualFrame frame, VmTyped self, VmTyped domain, VmClass clazz, Object data) {
|
||||
return new VmReference(domain, clazz, data);
|
||||
VirtualFrame frame, @SuppressWarnings("unused") VmTyped self, VmTyped domain, Object data) {
|
||||
var referentTypeArg = VmUtils.getTypeArgumentOrNull(frame, 3);
|
||||
var referentType =
|
||||
referentTypeArg == null ? VmType.UnknownType.INSTANCE : referentTypeArg.reify();
|
||||
return new VmReference(domain, referentType, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,7 @@ cannotSpreadObject=\
|
||||
Cannot spread value of type `{0}` into object of type `{1}`.
|
||||
|
||||
cannotDeclareTypeParameter=\
|
||||
Only standard library members can have type parameters.
|
||||
Only standard library classes can have type parameters.
|
||||
|
||||
duplicateTypeParameter=\
|
||||
Duplicate type parameter `{0}`.
|
||||
@@ -1223,9 +1223,6 @@ Cannot follow redirect from ''https:'' URL to ''http:'' URL.\
|
||||
HTTP Request: `GET {0}`\n\
|
||||
Redirected to: `{1}`
|
||||
|
||||
invalidReferenceTypeAnnotationWithConstraint=\
|
||||
`Reference` referent type argument may not include type constraints.
|
||||
|
||||
parserError=\
|
||||
Could not parse Pkl source.
|
||||
|
||||
@@ -1254,6 +1251,9 @@ noImplementationForAbstractMethods=\
|
||||
Class `{0}` should either be declared `abstract`, or should implement the following methods:\n\
|
||||
{1}
|
||||
|
||||
methodTypeParameterCountMismatch=\
|
||||
Expected method `{0}` overriding `{1}` to have {2} type parameter{2,choice,0#s|1#|1<s}, but got {3}.
|
||||
|
||||
invalidModuleTypeInProperty=\
|
||||
Cannot reference `module` type from const property `{0}`.
|
||||
|
||||
|
||||
+15
-1
@@ -11,7 +11,7 @@ facts {
|
||||
isVerbose = false
|
||||
}
|
||||
local result = benchmark.run()
|
||||
|
||||
|
||||
result.iterations == 3
|
||||
result.repetitions >= 1
|
||||
result.samples == null
|
||||
@@ -21,6 +21,19 @@ facts {
|
||||
result.mean <= result.max
|
||||
}
|
||||
|
||||
["run microbenchmark (in generic method)"] {
|
||||
local function run<T>() =
|
||||
new Benchmark.Microbenchmark {
|
||||
expression = 123 is T
|
||||
iterations = 3
|
||||
iterationTime = 1.ms
|
||||
isVerbose = false
|
||||
}.run()
|
||||
|
||||
run::<Int>().iterations == 3
|
||||
run::<String>().iterations == 3
|
||||
}
|
||||
|
||||
["run output benchmark (in verbose mode)"] {
|
||||
local benchmark = new Benchmark.OutputBenchmark {
|
||||
sourceModule = import("../../input-helper/api/benchmark/moduleToBenchmark.pkl")
|
||||
@@ -84,6 +97,7 @@ facts {
|
||||
|
||||
local text = benchmark.output.text
|
||||
text.contains("[\"micro\"]")
|
||||
text.contains("[\"generic method\"]")
|
||||
text.contains("[\"output\"]")
|
||||
text.contains("[\"parser\"]")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import "pkl:math"
|
||||
import "pkl:ref"
|
||||
import "pkl:test"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String =
|
||||
let (data = reference.getData())
|
||||
let (root = if (data is Resource) data.name else data.toString())
|
||||
@@ -17,10 +17,11 @@ class D extends ref.Domain {
|
||||
|
||||
local const d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
const function Ref<T>(data: Any): Ref<T> = ref.Reference::<D, T>(d, data)
|
||||
|
||||
abstract class Resource {
|
||||
name: String
|
||||
hidden fixed $: Ref<this> = ref.Reference(d, getClass(), this)
|
||||
hidden fixed $: Ref<this> = Ref::<this>(this)
|
||||
}
|
||||
|
||||
class A extends Resource {
|
||||
@@ -122,28 +123,32 @@ kInterpolation = "\(k)"
|
||||
aValuesJoined = k.aValues.join("\n")
|
||||
|
||||
// ensure that type arguments that are unions are handled correctly
|
||||
typeArgs = ref.Reference(d, TypeHolder, null).prop as Ref<Listing<Number | Boolean | String>>
|
||||
typeArgs = Ref::<TypeHolder>(null).prop as Ref<Listing<Number | Boolean | String>>
|
||||
class TypeHolder {
|
||||
prop: Listing<String | Boolean | Number>
|
||||
mappingAnyKey: Mapping<Any, Int>
|
||||
mappingTypeArgKey: Mapping<Listing<Int>, Int>
|
||||
}
|
||||
typeArgs2 = Ref::<Listing<Number | Boolean | String>>(null) as Ref<Listing<Number | Boolean | String>>
|
||||
|
||||
class BadResource extends Resource {
|
||||
// return a reference with referent that isn't `this`
|
||||
fixed $ = ref.Reference(d, A, this)
|
||||
fixed $ = Ref::<A>(this)
|
||||
}
|
||||
|
||||
badThisRef = test.catch(() -> new BadResource { name = "bad" }.$.toString())
|
||||
|
||||
// test that a bare function type toStrings/exports okay
|
||||
bareFunctionType = test.catch(() -> ref.Reference(d, Function4, null) as Ref<String>)
|
||||
bareFunctionType = test.catch(() -> ref.Reference::<D, Function4>(d, null) as Ref<String>)
|
||||
|
||||
mappingAnyKey = ref.Reference(d, TypeHolder, null).mappingAnyKey["foo"]
|
||||
// test for allowing constraints during reference construction
|
||||
constraintInConstructor = Ref::<String(length > 5)>(null)
|
||||
|
||||
mappingAnyKey = Ref::<TypeHolder>(null).mappingAnyKey["foo"]
|
||||
|
||||
// this one should be an editor error but can't be caught in the runtime
|
||||
// because VmListing/VmMapping don't track their type args
|
||||
mappingTypeArgKey = ref.Reference(d, TypeHolder, null).mappingTypeArgKey[new Listing<String> { "foo" }]
|
||||
mappingTypeArgKey = Ref ::<TypeHolder>(null).mappingTypeArgKey[new Listing<String> { "foo" }]
|
||||
|
||||
output {
|
||||
renderer {
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
// module type in a final module
|
||||
|
||||
import "pkl:ref"
|
||||
import "reference30.pkl"
|
||||
import "referenceWithFinalModuleType.pkl"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(r: ref.Reference<this, Any>): String =
|
||||
r.getPath().map((it) -> it.property ?? it.key.toString()).join(".")
|
||||
}
|
||||
@@ -12,9 +12,9 @@ class D extends ref.Domain {
|
||||
hidden parent: module?
|
||||
hidden children: List<module>
|
||||
|
||||
typealias R = ref.Reference<D, reference30?>
|
||||
typealias R = ref.Reference<D, referenceWithFinalModuleType?>
|
||||
|
||||
res0 = ref.Reference(new D {}, getClass(), null)
|
||||
res0 = ref.Reference::<D, module>(new D {}, null)
|
||||
res1: R = res0.parent
|
||||
res2: R = res0.parent.parent
|
||||
res3: R = res0.children[0]
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
open module reference29
|
||||
open module referenceWithOpenModuleType
|
||||
|
||||
// interactions between references and self types
|
||||
// module type in an open module
|
||||
|
||||
import "pkl:ref"
|
||||
import "reference29.pkl"
|
||||
import "referenceWithOpenModuleType.pkl"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(r: ref.Reference<this, Any>): String =
|
||||
r.getPath().map((it) -> it.property ?? it.key.toString()).join(".")
|
||||
}
|
||||
@@ -14,9 +14,9 @@ class D extends ref.Domain {
|
||||
hidden parent: module?
|
||||
hidden children: List<module>
|
||||
|
||||
typealias R = ref.Reference<D, reference29?>
|
||||
typealias R = ref.Reference<D, referenceWithOpenModuleType?>
|
||||
|
||||
res0 = ref.Reference(new D {}, getClass(), null)
|
||||
res0 = ref.Reference::<D, module>(new D {}, null)
|
||||
res1: R = res0.parent
|
||||
res2: R = res0.parent.parent
|
||||
res3: R = res0.children[0]
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(r: ref.Reference<this, Any>): String =
|
||||
r.getPath().map((it) -> it.property ?? it.key.toString()).join(".")
|
||||
}
|
||||
@@ -22,14 +22,14 @@ open class Node2 {
|
||||
typealias R = ref.Reference<D, Node?>
|
||||
typealias R2 = ref.Reference<D, Node2?>
|
||||
|
||||
res0: R = ref.Reference(new D {}, Node, null)
|
||||
res0: R = ref.Reference::<D, Node>(new D {}, null)
|
||||
res1: R = res0.parent
|
||||
res2: R = res0.parent.parent
|
||||
res3: R = res0.children[0]
|
||||
res4: R = res0.children[0].parent
|
||||
res5: R = res0.children[0].children[0]
|
||||
|
||||
res0a: R2 = ref.Reference(new D {}, Node2, null)
|
||||
res0a: R2 = ref.Reference::<D, Node2>(new D {}, null)
|
||||
res1a: R2 = res0a.parent
|
||||
res2a: R2 = res0a.parent.parent
|
||||
res3a: R2 = res0a.children[0]
|
||||
@@ -2,7 +2,7 @@ amends "../snippetTest.pkl"
|
||||
|
||||
import "pkl:ref"
|
||||
|
||||
local class D extends ref.Domain {
|
||||
local class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = reference.getData().toString()
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ local class OtherHolder {
|
||||
constrainedType: String(isEmpty)
|
||||
}
|
||||
|
||||
local holderRef: Ref<Holder> = ref.Reference(d, Holder, null)
|
||||
local holderRef2: Ref<Holder> = ref.Reference(d, Holder, null)
|
||||
local otherHolderRef: Ref<OtherHolder> = ref.Reference(d, OtherHolder, null)
|
||||
local holderRef: Ref<Holder> = ref.Reference::<D, Holder>(d, null)
|
||||
local holderRef2: Ref<Holder> = ref.Reference::<D, Holder>(d, null)
|
||||
local otherHolderRef: Ref<OtherHolder> = ref.Reference::<D, OtherHolder>(d, null)
|
||||
|
||||
facts {
|
||||
["equality"] {
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
function foo<in T>() = null
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
obj {
|
||||
local function foo<in T>() = null
|
||||
}
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
local function x<T>() = x
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
open class A {
|
||||
function foo<A>() = null
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
function foo<A, B>() = null
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Int>
|
||||
test = ref.Reference::<D, String>(d, "") as Ref<Int>
|
||||
@@ -1,9 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Listing<String(true)>>
|
||||
@@ -1,12 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1>
|
||||
|
||||
typealias Alias1 = Int | Alias2
|
||||
typealias Alias2 = String(length < 5)
|
||||
@@ -1,12 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
typealias RefAlias1 = ref.Reference<D, Alias1?>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as RefAlias1
|
||||
|
||||
typealias Alias1 = Int | Alias2?
|
||||
typealias Alias2 = String(length < 5)
|
||||
@@ -1,8 +1,8 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, Mapping, "").default
|
||||
test = ref.Reference::<D, Mapping>(d, "").default
|
||||
@@ -1,8 +1,8 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, Listing, "").default
|
||||
test = ref.Reference::<D, Listing>(d, "").default
|
||||
@@ -1,8 +1,8 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, Dynamic, "").default
|
||||
test = ref.Reference::<D, Dynamic>(d, "").default
|
||||
@@ -2,9 +2,9 @@ import "pkl:ref"
|
||||
|
||||
import ".../input-helper/errors/ReferencedModule.pkl"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, ReferencedModule.getClass(), "").output
|
||||
test = ref.Reference::<D, ReferencedModule>(d, "").output
|
||||
@@ -2,9 +2,9 @@ import "pkl:ref"
|
||||
|
||||
import ".../input-helper/errors/ReferencedModuleWithOutputOverride.pkl"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, ReferencedModuleWithOutputOverride.getClass(), "").output
|
||||
test = ref.Reference::<D, ReferencedModuleWithOutputOverride>(d, "").output
|
||||
@@ -4,9 +4,9 @@ import ".../input-helper/errors/ReferencedModuleWithOutputOverride.pkl"
|
||||
|
||||
class ModuleSubclass extends ReferencedModuleWithOutputOverride
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, ModuleSubclass, "").output
|
||||
test = ref.Reference::<D, ModuleSubclass>(d, "").output
|
||||
@@ -4,9 +4,9 @@ class A {
|
||||
foo: String | Int | Boolean
|
||||
}
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, A, "").foo as ref.Reference<D, Int | Boolean>
|
||||
test = ref.Reference::<D, A>(d, "").foo as ref.Reference<D, Int | Boolean>
|
||||
@@ -1,9 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Boolean | Int>
|
||||
test = ref.Reference::<D, String>(d, "") as Ref<Boolean | Int>
|
||||
@@ -4,12 +4,12 @@ class A {
|
||||
foo: String | Int | Boolean
|
||||
}
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
|
||||
local test = ref.Reference(d, A, "").foo
|
||||
local test = ref.Reference::<D, A>(d, "").foo
|
||||
testInterpolation = "test:\(test)"
|
||||
|
||||
// this tests that the interpolation appears in the output when renderReference throws
|
||||
@@ -1,7 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
typealias Ref<T> = ref.Reference<ref.Domain, T>
|
||||
|
||||
typealias Ref2<T> = Ref<T>
|
||||
|
||||
foo: Ref2<String(isEmpty)>
|
||||
@@ -1,6 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
typealias Ref<T> = ref.Reference<ref.Domain, T>
|
||||
typealias Ref2<T> = Ref<T(isEmpty)>
|
||||
|
||||
foo: Ref2<String>
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
import "pkl:ref"
|
||||
|
||||
typealias Foo1 = Foo2
|
||||
typealias Foo2 = String(isEmpty)
|
||||
|
||||
foo: ref.Reference<ref.Domain, Foo1>
|
||||
@@ -1,6 +1,6 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(_): String = throw("not implemented")
|
||||
}
|
||||
|
||||
@@ -8,10 +8,6 @@ class Bird {
|
||||
name: String
|
||||
}
|
||||
|
||||
class Holder {
|
||||
$: Bird | Int
|
||||
}
|
||||
|
||||
myRef: ref.Reference<D, Bird | Int> = ref.Reference(new D {}, Holder, null).$
|
||||
myRef: ref.Reference<D, Bird | Int> = ref.Reference::<D, Bird | Int>(new D {}, null)
|
||||
|
||||
res = myRef.name
|
||||
@@ -1,13 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(_): String = throw("not implemented")
|
||||
}
|
||||
|
||||
class Holder {
|
||||
$: Duration | Listing<String>
|
||||
}
|
||||
|
||||
local myRef = ref.Reference(new D {}, Holder, null).$
|
||||
local myRef = ref.Reference::<D, Duration | Listing<String>>(new D {}, null)
|
||||
|
||||
res = myRef[0]
|
||||
@@ -1,9 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(_): String = throw("not implemented")
|
||||
}
|
||||
|
||||
local myRef = ref.Reference(new D {}, Listing, null)
|
||||
local myRef = ref.Reference::<D, Listing>(new D {}, null)
|
||||
|
||||
res = myRef.name
|
||||
@@ -1,13 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(_): String = throw("not implemented")
|
||||
}
|
||||
|
||||
class Holder {
|
||||
func: (Int) -> Boolean
|
||||
}
|
||||
|
||||
local myRef = ref.Reference(new D {}, Holder, null).func
|
||||
local myRef = ref.Reference::<D, (Int) -> Boolean>(new D {}, null)
|
||||
|
||||
res = myRef.name
|
||||
@@ -1,9 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Listing<String>>
|
||||
test = ref.Reference::<D, String>(d, "") as Ref<Listing<String>>
|
||||
@@ -1,12 +1,12 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1>
|
||||
test = ref.Reference::<D, String>(d, "") as Ref<Alias1>
|
||||
|
||||
typealias Alias1 = Int | Alias2
|
||||
typealias Alias2 = Boolean
|
||||
@@ -1,14 +1,14 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
class D2 extends ref.Domain {
|
||||
class D2 extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d2: D2 = new {}
|
||||
|
||||
test = ref.Reference(d2, String, "") as Ref<String>
|
||||
test = ref.Reference::<D2, String>(d2, "") as Ref<String>
|
||||
@@ -1,6 +1,6 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
@@ -10,4 +10,4 @@ class A {
|
||||
b: String
|
||||
}
|
||||
|
||||
test = ref.Reference(d, String, new A {}).c
|
||||
test = ref.Reference::<D, String>(d, new A {}).c
|
||||
@@ -1,9 +1,9 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
class D extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, List, List())["hi"]
|
||||
test = ref.Reference::<D, List>(d, List())["hi"]
|
||||
@@ -1,9 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<String(true)>
|
||||
@@ -1,9 +0,0 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(reference: ref.Reference<this, Any>): String = throw("not supported")
|
||||
}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<String(true) | Int>
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
class DoIt {
|
||||
function doit<A>(a: A): A = a
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
open module callWithTypeArguments
|
||||
|
||||
import "pkl:test"
|
||||
import "pkl:ref"
|
||||
|
||||
class Domain extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference) = "<ref>"
|
||||
}
|
||||
|
||||
directCall {
|
||||
pair = Pair::<List<String>, Set<String>>(List("foo"), Set("bar"))
|
||||
pairSelf = Pair::<module, module>(new module {}, new module {}).first.getClass().toString()
|
||||
pairErr = test.catch(() -> Pair::<List<String>, Set<String>>(List(0), Set("bar")))
|
||||
pairSelfErr = test.catch(() -> Pair::<module, module>(List(), new module {}))
|
||||
listMap = List("hi", "foo", "a").map::<Int>((it) -> it.length)
|
||||
listMapErr = test.catch(() -> List("hi", "foo", "a").map::<String>((it) -> it.length))
|
||||
wrongTypeArgCount = test.catch(() -> Pair::<String, Int, Boolean> ("foo", 10))
|
||||
nonGenericMethod = test.catch(() -> Regex::<String> ("foo"))
|
||||
}
|
||||
|
||||
open class A {
|
||||
function doIt<T>(arg: List): Boolean = arg.first is T
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
function doIt<T>(arg: List): Boolean = super.doIt::<T>(arg) || super.doIt::<List<T>>(arg)
|
||||
}
|
||||
|
||||
superCall {
|
||||
local function doIt(a: A, arg: List): Boolean =
|
||||
a.doIt::<String>(arg)
|
||||
|
||||
// should be true:
|
||||
doIt(new A {}, List(""))
|
||||
doIt(new A {}, List(""))
|
||||
doIt(new B {}, List(""))
|
||||
doIt(new B {}, List(List("")))
|
||||
// should be false:
|
||||
doIt(new A {}, List(0))
|
||||
doIt(new A {}, List(List(0)))
|
||||
doIt(new B {}, List(List(0)))
|
||||
}
|
||||
|
||||
local num = 5
|
||||
|
||||
objectMethod {
|
||||
local function foo<T>(): T = "hi"
|
||||
res = test.catch(() -> foo::<String(length > num)>())
|
||||
}
|
||||
|
||||
function lazy1<T>(it): Listing<T> = new Listing { it }
|
||||
local function lazy2<T>(it): Listing<T> = new Listing { it }
|
||||
lazyTypeCheck {
|
||||
local function lazy3<T>(it): Listing<T> = new Listing { it }
|
||||
res1 = test.catch(() -> lazy1::<String>(5))
|
||||
res2 = test.catch(() -> lazy2::<String>(5))
|
||||
res3 = test.catch(() -> lazy3::<String>(5))
|
||||
|
||||
eager {
|
||||
local myListing: Listing<String> = new { "Hello, world!" }
|
||||
local function myFunc<T>() = new {
|
||||
result = myListing is T
|
||||
}
|
||||
// should be false:
|
||||
res = myFunc::<Listing<Duration>>().result
|
||||
}
|
||||
|
||||
closure {
|
||||
local myListing: Listing<String> = new { "Hello, world!" }
|
||||
local function myFunc<T>() = new {
|
||||
result = myListing as T
|
||||
}
|
||||
res = test.catch(() -> myFunc::<Listing<Duration>>())
|
||||
}
|
||||
}
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
|
||||
function greet<A, B>() = "hi \(name)"
|
||||
}
|
||||
|
||||
typeDefaults {
|
||||
local function makeNamed<T>(val: String): T = new { name = val }
|
||||
local function makeNamedListing<T>(val: String): Listing<T> = new {
|
||||
new { name = val }
|
||||
}
|
||||
local function makeFromParam<T>(val: T): Listing<T> = new { val }
|
||||
res1 = makeNamed::<Person>("bob")
|
||||
res1a = res1.getClass().toString()
|
||||
res2 = makeNamedListing::<Person>("bob")
|
||||
res2a = res2.first.getClass().toString()
|
||||
res3 = makeFromParam::<Person>(new { name = "bob" })
|
||||
res3a = res3.first.getClass().toString()
|
||||
res3b = test.catch(() -> makeFromParam::<Person>(new Bird { name = "joe" }))
|
||||
}
|
||||
|
||||
class Bird {
|
||||
name: String
|
||||
|
||||
function greet<A>() = "hi \(name)"
|
||||
}
|
||||
|
||||
polymorphism {
|
||||
// test that method type parameters are considered non-final
|
||||
local function make<T>(): T = new T { name = "fred" }
|
||||
res1 = make::<Person>()
|
||||
res2 = make::<Bird>()
|
||||
|
||||
local function call(obj: Bird | Person) = obj.greet::<Int, String>()
|
||||
res3 = call(new Person { name = "fred" })
|
||||
res4 = test.catch(() -> call(new Bird { name = "pigeon" }))
|
||||
res5 = call(new Person { name = "bob" })
|
||||
}
|
||||
|
||||
typeCast {
|
||||
local function cast<U>(l: Listing<Any>): Listing<U> = l
|
||||
|
||||
res1 = cast::<String(startsWith("Bob"))>(new Listing { "Bobbert" })
|
||||
|
||||
res2 = test.catch(() -> cast::<String(length > 10)>(res1).toString())
|
||||
}
|
||||
|
||||
constraintReadsFrameSlot {
|
||||
local function id<T>(x) = x is T
|
||||
local function f(n: Int) = id::<String(length > n)>("hi")
|
||||
|
||||
res1 = f(1)
|
||||
res2 = let (n = 1) id::<String(length > n)>("hi")
|
||||
res3 = ((n) -> id::<String(length > n)>("hi")).apply(1)
|
||||
res4 = new Listing {
|
||||
for (n in List(1)) {
|
||||
id::<String(length > n)>("hi")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output {
|
||||
renderer {
|
||||
converters {
|
||||
[ref.Reference] = (it) -> it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
const function foo<T>(val): Boolean = val is T
|
||||
|
||||
local prefix1 = "hi"
|
||||
hidden prefix2 = "hi"
|
||||
function prefix3() = "hi"
|
||||
local function prefix4() = "hi"
|
||||
|
||||
class Bar {
|
||||
x: Int = 10
|
||||
function baz<T>(val): Boolean = foo::<T>(val)
|
||||
function qux<T>(val): Boolean = foo::<T(length < x)>(val)
|
||||
}
|
||||
|
||||
res {
|
||||
res1a = foo::<String(startsWith(prefix1))>("high five")
|
||||
res1b = foo::<String(startsWith(prefix2))>("high five")
|
||||
res1c = foo::<String(startsWith(prefix3()))>("high five")
|
||||
res1d = foo::<String(startsWith(prefix4()))>("high five")
|
||||
|
||||
res2a = new Bar {}.baz::<String(startsWith(prefix1))>("high five")
|
||||
res2b = new Bar {}.baz::<String(startsWith(prefix2))>("high five")
|
||||
res2c = new Bar {}.baz::<String(startsWith(prefix3()))>("high five")
|
||||
res2d = new Bar {}.baz::<String(startsWith(prefix4()))>("high five")
|
||||
|
||||
res3a = new Bar {}.qux::<String(startsWith(prefix1))>("high five")
|
||||
res3b = new Bar {}.qux::<String(startsWith(prefix2))>("high five")
|
||||
res3c = new Bar {}.qux::<String(startsWith(prefix3()))>("high five")
|
||||
res3d = new Bar {}.qux::<String(startsWith(prefix4()))>("high five")
|
||||
}
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
open module callWithTypeArguments
|
||||
|
||||
import "pkl:test"
|
||||
import "pkl:ref"
|
||||
|
||||
class Domain extends ref.AbstractDomain {
|
||||
function renderReference(reference: ref.Reference) = "<ref>"
|
||||
}
|
||||
|
||||
class Person {
|
||||
name: String
|
||||
|
||||
function greet<A, B>() = "hi \(name)"
|
||||
}
|
||||
|
||||
lexical {
|
||||
local function f<T>(): Dynamic = new { res = 5 as T }
|
||||
res1 = test.catch(() -> f::<String>().res)
|
||||
|
||||
local function g<T>(): Dynamic = new {
|
||||
local myProp: T = 5
|
||||
res = myProp
|
||||
}
|
||||
res2 = test.catch(() -> g::<String>().res)
|
||||
|
||||
local function nested<T>() = new Dynamic {
|
||||
local function inner<U>(x: Listing<T>, y: U) = x
|
||||
local function inner2<U>(x: T, y: U) = x
|
||||
local function inner3(x: T) = x
|
||||
local function inner4<U>() = new Dynamic {
|
||||
local function innest1<V>(x: T, y: U, z: V) = x
|
||||
local function innest2(x: T) = x
|
||||
res1 = innest1::<String>(new { name = "x" }, 1, "hi")
|
||||
res2 = innest1(new { name = "x" }, 1, "hi")
|
||||
res3 = test.catch(() -> innest2(1))
|
||||
}
|
||||
|
||||
res1 = inner::<Int>(new { new { name = "x" } }, 1)
|
||||
res2 = inner2::<Int>(new { name = "x" }, 1)
|
||||
res3 = inner(new { new { name = "x" } }, 1)
|
||||
res4 = inner2(new { name = "x" }, 1)
|
||||
res5 = test.catch(() -> inner3(1))
|
||||
nested = inner4::<Int>()
|
||||
}
|
||||
nested = nested::<Person>()
|
||||
|
||||
inEagerScope {
|
||||
local function k1<T>() = new {
|
||||
[let (elem = "key" as T) elem] = "value"
|
||||
}
|
||||
test.catch(() -> k1::<Int>())
|
||||
|
||||
local function k2<T>() = new {
|
||||
for (i in List(1, 2, 3)) {
|
||||
[let (elem = "key".repeat(i) as T) elem as T] = "value"
|
||||
}
|
||||
}
|
||||
k2::<String(length < 10)>()
|
||||
}
|
||||
|
||||
reify {
|
||||
local function makeMyObj<T>() = new {
|
||||
res = ref.Reference::<Domain, T>(new {}, null)
|
||||
}
|
||||
|
||||
res = makeMyObj::<Person>().res.name
|
||||
}
|
||||
}
|
||||
|
||||
local function nested<T>(foo) = new Dynamic {
|
||||
local function inner<U>(x: Listing<T>, y: U) = x
|
||||
local function inner2<U>(x: T, y: U) = x
|
||||
local function inner3(x: T) = x
|
||||
local function inner4<U>() = new Dynamic {
|
||||
local function innest1<V>(x: T, y: U, z: V) = x
|
||||
local function innest2(x: T) = x
|
||||
res1 = innest1::<String>(new { name = "x" }, 1, "hi")
|
||||
res2 = innest1(new { name = "x" }, 1, "hi")
|
||||
res3 = test.catch(() -> innest2(1))
|
||||
}
|
||||
res1 = inner::<Int>(new { new { name = "x" } }, foo)
|
||||
res2 = inner2::<Int>(new { name = "x" }, 1)
|
||||
res3 = inner(new { new { name = "x" } }, 1)
|
||||
res4 = inner2(new { name = "x" }, 1)
|
||||
res5 = test.catch(() -> inner3(1))
|
||||
nested = inner4::<Int>()
|
||||
}
|
||||
nested = nested::<Person>(1)
|
||||
|
||||
output {
|
||||
renderer {
|
||||
converters {
|
||||
[ref.Reference] = (it) -> it.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
pkl-core/src/test/files/LanguageSnippetTests/input/methods/callWithTypeArgumentsNestedWithOffset.pkl
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
local function make<T>(): Listing<T> = new Listing<T> { 1; 2 }
|
||||
local function amend<T, U>(l: Listing<Any>): Listing<U> = (l) { 3 } as Listing<U>
|
||||
|
||||
local parent = make::<Int>()
|
||||
local child = amend::<String, Int>(parent)
|
||||
res = child[0]
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
open class A {
|
||||
function foo<T, U, V>(val): List<Boolean> = List(val is T, val is U, val is V)
|
||||
function a<T, U>(val): List<Boolean> = this.foo::<T, U, this>(val)
|
||||
}
|
||||
|
||||
open class B extends A {
|
||||
function b1<T>(val): List<Boolean> = this.a::<T, this>(val)
|
||||
function b2<T>(val): List<Boolean> = super.a::<T, this>(val)
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
function c1(val): List<Boolean> = this.b1::<this>(val)
|
||||
function c2(val): List<Boolean> = super.b1::<this>(val)
|
||||
function c3(val): List<Boolean> = this.b2::<this>(val)
|
||||
function c4(val): List<Boolean> = super.b2::<this>(val)
|
||||
}
|
||||
|
||||
resAaA = new A {}.a(new A {})
|
||||
resAaB = new A {}.a(new B {})
|
||||
resAaC = new A {}.a(new C {})
|
||||
|
||||
resBaA = new B {}.a(new A {})
|
||||
resBaB = new B {}.a(new B {})
|
||||
resBaC = new B {}.a(new C {})
|
||||
|
||||
resBb1A = new B {}.b1(new A {})
|
||||
resBb1B = new B {}.b1(new B {})
|
||||
resBb1C = new B {}.b1(new C {})
|
||||
resBb2A = new B {}.b2(new A {})
|
||||
resBb2B = new B {}.b2(new B {})
|
||||
resBb2C = new B {}.b2(new C {})
|
||||
|
||||
resCaA = new C {}.a(new A {})
|
||||
resCaB = new C {}.a(new B {})
|
||||
resCaC = new C {}.a(new C {})
|
||||
|
||||
resCb1A = new C {}.b1(new A {})
|
||||
resCb1B = new C {}.b1(new B {})
|
||||
resCb1C = new C {}.b1(new C {})
|
||||
resCb2A = new C {}.b2(new A {})
|
||||
resCb2B = new C {}.b2(new B {})
|
||||
resCb2C = new C {}.b2(new C {})
|
||||
|
||||
resCc1A = new C {}.c1(new A {})
|
||||
resCc1B = new C {}.c1(new B {})
|
||||
resCc1C = new C {}.c1(new C {})
|
||||
resCc2A = new C {}.c2(new A {})
|
||||
resCc2B = new C {}.c2(new B {})
|
||||
resCc2C = new C {}.c2(new C {})
|
||||
resCc3A = new C {}.c3(new A {})
|
||||
resCc3B = new C {}.c3(new B {})
|
||||
resCc3C = new C {}.c3(new C {})
|
||||
resCc4A = new C {}.c4(new A {})
|
||||
resCc4B = new C {}.c4(new B {})
|
||||
resCc4C = new C {}.c4(new C {})
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
function foo<T>(bar) = bar as T
|
||||
|
||||
function baz<T>(qux) = foo::<List<T>>(qux)
|
||||
|
||||
res = baz::<String(throw("oops"))>(List("a"))
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
foo = 5
|
||||
|
||||
function bar<T>() = new {
|
||||
prop = foo as T
|
||||
}
|
||||
|
||||
res = bar::<Duration>()
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// when no function override (parent or child are local) then varying number of type params is okay
|
||||
|
||||
open class P {
|
||||
function f<T>(x) = x
|
||||
local function g<T>(x) = x
|
||||
a = f(1)
|
||||
b = g(2)
|
||||
}
|
||||
class C extends P {
|
||||
local function f(x) = x
|
||||
function g(x) = x
|
||||
c = f(2)
|
||||
d = g(3)
|
||||
}
|
||||
|
||||
ok = true
|
||||
@@ -8,6 +8,10 @@ facts {
|
||||
true
|
||||
true
|
||||
}
|
||||
["run microbenchmark (in generic method)"] {
|
||||
true
|
||||
true
|
||||
}
|
||||
["run output benchmark (in verbose mode)"] {
|
||||
true
|
||||
true
|
||||
@@ -29,6 +33,7 @@ facts {
|
||||
}
|
||||
["evaluate entire benchmark module"] {
|
||||
true
|
||||
false
|
||||
true
|
||||
true
|
||||
}
|
||||
|
||||
@@ -60,7 +60,9 @@ aValuesJoined = """
|
||||
${b.outputs.nonString}
|
||||
"""
|
||||
typeArgs = "${null.prop}"
|
||||
badThisRef = "Expected value of type `pkl.ref#Reference<reference#D, this>`, but got type `pkl.ref#Reference<reference#D, reference#A>`. Value: Reference(new reference#D {}, reference#A, new reference#BadResource { name =..."
|
||||
typeArgs2 = "${null}"
|
||||
badThisRef = "Expected value of type `pkl.ref#Reference<reference#D, reference#BadResource>`, but got type `pkl.ref#Reference<reference#D, reference#A>`. Value: Reference(new reference#D {}, reference#A, new reference#BadResource { name =..."
|
||||
bareFunctionType = "Expected value of type `pkl.ref#Reference<reference#D, String>`, but got type `pkl.ref#Reference<reference#D, Function4>`. Value: Reference(new reference#D {}, Function4, null)"
|
||||
constraintInConstructor = "${null}"
|
||||
mappingAnyKey = "${null.mappingAnyKey[foo]}"
|
||||
mappingTypeArgKey = "${null.mappingTypeArgKey[new Listing { \"foo\" }]}"
|
||||
Loaded 100 of 160 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user