Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/modules/ROOT/partials/component-attributes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ endif::[]
:uri-stdlib-Function4: {uri-stdlib-baseModule}/Function4
:uri-stdlib-Function5: {uri-stdlib-baseModule}/Function5
:uri-stdlib-Bytes: {uri-stdlib-baseModule}/Bytes
:uri-stdlib-Reference: {uri-stdlib-baseModule}/Reference
:uri-stdlib-ReferenceAccess: {uri-stdlib-baseModule}/ReferenceAccess
:uri-stdlib-Resource: {uri-stdlib-baseModule}/Resource
:uri-stdlib-outputFiles: {uri-stdlib-baseModule}/ModuleOutput#files
:uri-stdlib-FileOutput: {uri-stdlib-baseModule}/FileOutput
Expand Down
18 changes: 18 additions & 0 deletions docs/modules/bindings-specification/pages/binary-encoding.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,24 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
|
|

|link:{uri-stdlib-Reference}[Reference]
|`0x20`
|`<value>`
|Root value
|link:{uri-messagepack-array}[array]
|Array of link:{uri-stdlib-ReferenceAccess}[property access] values
|
|

|link:{uri-stdlib-ReferenceAccess}[ReferenceAccess]
|`0x21`
|link:{uri-messagepack-str}[str] (when a property access) or link:{uri-messagepack-str}[nil] (when a subscript access)
|Property name
|`<value>` or link:{uri-messagepack-str}[nil] (when a property access)
|Key value
|
|
|===

[[type-name-encoding]]
Expand Down
16 changes: 14 additions & 2 deletions pkl-core/src/main/java/org/pkl/core/PClass.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
Expand Down Expand Up @@ -27,6 +27,7 @@ public final class PClass extends Member implements Value {
private final List<TypeParameter> typeParameters;
private final Map<String, Property> properties;
private final Map<String, Method> methods;
private final @Nullable PClass moduleClass;

private @Nullable PType supertype;
private @Nullable PClass superclass;
Expand All @@ -42,12 +43,14 @@ public PClass(
PClassInfo<?> classInfo,
List<TypeParameter> typeParameters,
Map<String, Property> properties,
Map<String, Method> methods) {
Map<String, Method> methods,
@Nullable PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, classInfo.getSimpleName());
this.classInfo = classInfo;
this.typeParameters = typeParameters;
this.properties = properties;
this.methods = methods;
this.moduleClass = moduleClass;
}

public void initSupertype(PType supertype, PClass superclass) {
Expand Down Expand Up @@ -119,6 +122,11 @@ public Map<String, Method> getAllMethods() {
return allMethods;
}

/** Returns the class's containing module's class, or this class if it is a module class. */
public PClass getModuleClass() {
return moduleClass != null ? moduleClass : this;
}

@Override
public void accept(ValueVisitor visitor) {
visitor.visitClass(this);
Expand All @@ -138,6 +146,10 @@ public String toString() {
return getDisplayName();
}

public boolean isSubclassOf(PClass other) {
return this == other || getSuperclass() != null && getSuperclass().isSubclassOf(other);
}

public abstract static class ClassMember extends Member {
@Serial private static final long serialVersionUID = 0L;

Expand Down
9 changes: 8 additions & 1 deletion pkl-core/src/main/java/org/pkl/core/TypeAlias.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public final class TypeAlias extends Member implements Value {
private final String moduleName;
private final String qualifiedName;
private final List<TypeParameter> typeParameters;
private final PClass moduleClass;

@LateInit private PType aliasedType;

Expand All @@ -39,11 +40,13 @@ public TypeAlias(
String simpleName,
String moduleName,
String qualifiedName,
List<TypeParameter> typeParameters) {
List<TypeParameter> typeParameters,
PClass moduleClass) {
super(docComment, sourceLocation, modifiers, annotations, simpleName);
this.moduleName = moduleName;
this.qualifiedName = qualifiedName;
this.typeParameters = typeParameters;
this.moduleClass = moduleClass;
}

public void initAliasedType(PType type) {
Expand Down Expand Up @@ -78,6 +81,10 @@ public List<TypeParameter> getTypeParameters() {
return typeParameters;
}

public PClass getModuleClass() {
return moduleClass;
}

/** Returns the type that this type alias stands for. */
public PType getAliasedType() {
assert aliasedType != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ protected Object eval(
return readMember(dynamic, key, callNode);
}

@Specialization
protected VmReference eval(VmReference reference, Object key) {
var result = reference.withSubscriptAccess(key);
if (result != null) return result;

CompilerDirectives.transferToInterpreter();
// TODO
throw exceptionBuilder()
.adhocEvalError("unabled to index reference<FooBarBaz> with key {0}", key)
.build();
}

@Specialization
protected long eval(VmBytes receiver, long index) {
if (index < 0 || index >= receiver.getLength()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2025 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.
Expand Down Expand Up @@ -61,6 +61,19 @@ protected ReadPropertyNode(SourceSection sourceSection, Identifier propertyName)
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
}

@Specialization
protected VmReference evalReference(VmReference receiver) {
assert lookupMode == MemberLookupMode.EXPLICIT_RECEIVER;
var result = receiver.withPropertyAccess(propertyName);
if (result != null) return result;

CompilerDirectives.transferToInterpreter();
// TODO
throw exceptionBuilder()
.adhocEvalError("Cannot find property `{0}` on Reference<FooBarBaz>", propertyName)
.build();
}

// This method effectively covers `VmObject receiver` but is implemented in a more
// efficient way. See:
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces
Expand Down
76 changes: 76 additions & 0 deletions pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameDescriptor;
Expand Down Expand Up @@ -2057,6 +2058,81 @@ protected boolean isParametric() {
}
}

@ImportStatic(BaseModule.class)
public abstract static class ReferenceTypeNode extends ObjectSlotTypeNode {
private TypeNode referentTypeNode;
@Child private ExpressionNode getModuleNode;

public ReferenceTypeNode(SourceSection sourceSection, TypeNode referentTypeNode) {
super(sourceSection);
this.referentTypeNode = referentTypeNode;
this.getModuleNode = new GetModuleNode(sourceSection);
}

@SuppressWarnings("unused")
@Specialization(guards = "value.getVmClass() == getReferenceClass()")
protected Object eval(VirtualFrame frame, VmReference value) {
if (referentTypeNode.isNoopTypeCheck()) {
return value;
}

var module = (VmTyped) getModuleNode.executeGeneric(frame);
if (value.checkType(TypeNode.export(referentTypeNode), module.getVmClass().export())) {
return value;
}
// TODO better exceptions?
throw new VmExceptionBuilder()
.adhocEvalError(
"reference type mismatch Reference<%s> and %s",
value.getCandidateTypes(), TypeNode.export(referentTypeNode))
.build();
}

@Fallback
protected Object fallback(Object value) {
throw typeMismatch(value, BaseModule.getReferenceClass());
}

@Override
protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
if (visitTypeArguments) return consumer.accept(this) && consumer.accept(referentTypeNode);
return consumer.accept(this);
}

@Override
public VmClass getVmClass() {
return BaseModule.getReferenceClass();
}

@Override
public VmList getTypeArgumentMirrors() {
return VmList.of(referentTypeNode.getMirror());
}

@Override
protected boolean doIsEquivalentTo(TypeNode other) {
if (!(other instanceof ReferenceTypeNode referenceTypeNode)) {
return false;
}
return referentTypeNode.isEquivalentTo(referenceTypeNode.referentTypeNode);
}

@Override
public boolean isNoopTypeCheck() {
return referentTypeNode.isNoopTypeCheck();
}

@Override
protected PType doExport() {
return new PType.Class(BaseModule.getReferenceClass().export(), referentTypeNode.doExport());
}

@Override
protected boolean isParametric() {
return true;
}
}

public static final class PairTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode firstTypeNode;
@Child private TypeNode secondTypeNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ public TypeNode execute(VirtualFrame frame) {
return new VarArgsTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
}

if (clazz.isReferenceClass()) {
return ReferenceTypeNodeGen.create(sourceSection, typeArgumentNodes[0].execute(frame));
}

throw exceptionBuilder()
.evalError("notAParameterizableClass", clazz.getDisplayName())
.withSourceSection(typeArgumentNodes[0].sourceSection)
Expand Down
52 changes: 52 additions & 0 deletions pkl-core/src/main/java/org/pkl/core/runtime/BaseModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import java.util.Set;

public final class BaseModule extends StdLibModule {
static final VmTyped instance = VmUtils.createEmptyModule();
Expand Down Expand Up @@ -207,6 +208,14 @@ public static VmClass getResourceClass() {
return ResourceClass.instance;
}

public static VmClass getReferenceClass() {
return ReferenceClass.instance;
}

public static VmClass getReferenceAccessClass() {
return ReferenceAccessClass.instance;
}

public static VmTypeAlias getNonNullTypeAlias() {
return NonNullTypeAlias.instance;
}
Expand All @@ -231,6 +240,29 @@ public static VmTypeAlias getUInt8TypeAlias() {
return UInt8TypeAlias.instance;
}

public static VmTypeAlias getUInt16TypeAlias() {
return UInt16TypeAlias.instance;
}

public static VmTypeAlias getUInt32TypeAlias() {
return UInt32TypeAlias.instance;
}

public static VmTypeAlias getUIntTypeAlias() {
return UIntTypeAlias.instance;
}

public static Set<VmTypeAlias> getIntTypeAliases() {
return Set.of(
getInt8TypeAlias(),
getInt16TypeAlias(),
getInt32TypeAlias(),
getUInt8TypeAlias(),
getUInt16TypeAlias(),
getUInt32TypeAlias(),
getUIntTypeAlias());
}

private static final class AnyClass {
static final VmClass instance = loadClass("Any");
}
Expand Down Expand Up @@ -359,6 +391,14 @@ private static final class ResourceClass {
static final VmClass instance = loadClass("Resource");
}

private static final class ReferenceClass {
static final VmClass instance = loadClass("Reference");
}

private static final class ReferenceAccessClass {
static final VmClass instance = loadClass("ReferenceAccess");
}

private static final class FunctionClass {
static final VmClass instance = loadClass("Function");
}
Expand Down Expand Up @@ -407,6 +447,18 @@ private static final class UInt8TypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt8");
}

private static final class UInt16TypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt16");
}

private static final class UInt32TypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt32");
}

private static final class UIntTypeAlias {
static final VmTypeAlias instance = loadTypeAlias("UInt");
}

private static final class MixinTypeAlias {
static final VmTypeAlias instance = loadTypeAlias("Mixin");
}
Expand Down
Loading
Loading