Skip to content

Commit d7663a8

Browse files
committed
Implement Reference<T>
1 parent 7b7b51c commit d7663a8

File tree

27 files changed

+1047
-49
lines changed

27 files changed

+1047
-49
lines changed

docs/modules/ROOT/partials/component-attributes.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ endif::[]
147147
:uri-stdlib-Function4: {uri-stdlib-baseModule}/Function4
148148
:uri-stdlib-Function5: {uri-stdlib-baseModule}/Function5
149149
:uri-stdlib-Bytes: {uri-stdlib-baseModule}/Bytes
150+
:uri-stdlib-Reference: {uri-stdlib-baseModule}/Reference
151+
:uri-stdlib-ReferenceAccess: {uri-stdlib-baseModule}/ReferenceAccess
150152
:uri-stdlib-Resource: {uri-stdlib-baseModule}/Resource
151153
:uri-stdlib-outputFiles: {uri-stdlib-baseModule}/ModuleOutput#files
152154
:uri-stdlib-FileOutput: {uri-stdlib-baseModule}/FileOutput

docs/modules/bindings-specification/pages/binary-encoding.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,24 @@ The array's length is the number of slots that are filled. For example, xref:{ur
187187
|
188188
|
189189
|
190+
191+
|link:{uri-stdlib-Reference}[Reference]
192+
|`0x20`
193+
|`<value>`
194+
|Root value
195+
|link:{uri-messagepack-array}[array]
196+
|Array of link:{uri-stdlib-ReferenceAccess}[property access] values
197+
|
198+
|
199+
200+
|link:{uri-stdlib-ReferenceAccess}[ReferenceAccess]
201+
|`0x21`
202+
|link:{uri-messagepack-str}[str] (when a property access) or link:{uri-messagepack-str}[nil] (when a subscript access)
203+
|Property name
204+
|`<value>` or link:{uri-messagepack-str}[nil] (when a property access)
205+
|Key value
206+
|
207+
|
190208
|===
191209

192210
[[type-name-encoding]]

pkl-core/src/main/java/org/pkl/core/PClass.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ public final class PClass extends Member implements Value {
2727
private final List<TypeParameter> typeParameters;
2828
private final Map<String, Property> properties;
2929
private final Map<String, Method> methods;
30+
private final @Nullable PClass moduleClass;
3031

3132
private @Nullable PType supertype;
3233
private @Nullable PClass superclass;
@@ -42,12 +43,14 @@ public PClass(
4243
PClassInfo<?> classInfo,
4344
List<TypeParameter> typeParameters,
4445
Map<String, Property> properties,
45-
Map<String, Method> methods) {
46+
Map<String, Method> methods,
47+
@Nullable PClass moduleClass) {
4648
super(docComment, sourceLocation, modifiers, annotations, classInfo.getSimpleName());
4749
this.classInfo = classInfo;
4850
this.typeParameters = typeParameters;
4951
this.properties = properties;
5052
this.methods = methods;
53+
this.moduleClass = moduleClass;
5154
}
5255

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

125+
/** Returns the class's containing module's class, or this class if it is a module class. */
126+
public PClass getModuleClass() {
127+
return moduleClass != null ? moduleClass : this;
128+
}
129+
122130
@Override
123131
public void accept(ValueVisitor visitor) {
124132
visitor.visitClass(this);
@@ -138,6 +146,10 @@ public String toString() {
138146
return getDisplayName();
139147
}
140148

149+
public boolean isSubclassOf(PClass other) {
150+
return this == other || getSuperclass() != null && getSuperclass().isSubclassOf(other);
151+
}
152+
141153
public abstract static class ClassMember extends Member {
142154
@Serial private static final long serialVersionUID = 0L;
143155

pkl-core/src/main/java/org/pkl/core/TypeAlias.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public final class TypeAlias extends Member implements Value {
2828
private final String moduleName;
2929
private final String qualifiedName;
3030
private final List<TypeParameter> typeParameters;
31+
private final PClass moduleClass;
3132

3233
@LateInit private PType aliasedType;
3334

@@ -39,11 +40,13 @@ public TypeAlias(
3940
String simpleName,
4041
String moduleName,
4142
String qualifiedName,
42-
List<TypeParameter> typeParameters) {
43+
List<TypeParameter> typeParameters,
44+
PClass moduleClass) {
4345
super(docComment, sourceLocation, modifiers, annotations, simpleName);
4446
this.moduleName = moduleName;
4547
this.qualifiedName = qualifiedName;
4648
this.typeParameters = typeParameters;
49+
this.moduleClass = moduleClass;
4750
}
4851

4952
public void initAliasedType(PType type) {
@@ -78,6 +81,10 @@ public List<TypeParameter> getTypeParameters() {
7881
return typeParameters;
7982
}
8083

84+
public PClass getModuleClass() {
85+
return moduleClass;
86+
}
87+
8188
/** Returns the type that this type alias stands for. */
8289
public PType getAliasedType() {
8390
assert aliasedType != null;

pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ protected Object eval(
9898
return readMember(dynamic, key, callNode);
9999
}
100100

101+
@Specialization
102+
protected VmReference eval(VmReference reference, Object key) {
103+
var result = reference.withSubscriptAccess(key);
104+
if (result != null) return result;
105+
106+
CompilerDirectives.transferToInterpreter();
107+
// TODO
108+
throw exceptionBuilder()
109+
.adhocEvalError("unabled to index reference<FooBarBaz> with key {0}", key)
110+
.build();
111+
}
112+
101113
@Specialization
102114
protected long eval(VmBytes receiver, long index) {
103115
if (index < 0 || index >= receiver.getLength()) {

pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadPropertyNode.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,19 @@ protected ReadPropertyNode(SourceSection sourceSection, Identifier propertyName)
6161
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
6262
}
6363

64+
@Specialization
65+
protected VmReference evalReference(VmReference receiver) {
66+
assert lookupMode == MemberLookupMode.EXPLICIT_RECEIVER;
67+
var result = receiver.withPropertyAccess(propertyName);
68+
if (result != null) return result;
69+
70+
CompilerDirectives.transferToInterpreter();
71+
// TODO
72+
throw exceptionBuilder()
73+
.adhocEvalError("Cannot find property `{0}` on Reference<FooBarBaz>", propertyName)
74+
.build();
75+
}
76+
6477
// This method effectively covers `VmObject receiver` but is implemented in a more
6578
// efficient way. See:
6679
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces

pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2121
import com.oracle.truffle.api.dsl.Cached;
2222
import com.oracle.truffle.api.dsl.Fallback;
23+
import com.oracle.truffle.api.dsl.ImportStatic;
2324
import com.oracle.truffle.api.dsl.Specialization;
2425
import com.oracle.truffle.api.frame.Frame;
2526
import com.oracle.truffle.api.frame.FrameDescriptor;
@@ -2057,6 +2058,80 @@ protected boolean isParametric() {
20572058
}
20582059
}
20592060

2061+
@ImportStatic(BaseModule.class)
2062+
public abstract static class ReferenceTypeNode extends ObjectSlotTypeNode {
2063+
private TypeNode valueTypeNode;
2064+
@Child private ExpressionNode getModuleNode;
2065+
2066+
public ReferenceTypeNode(SourceSection sourceSection, TypeNode valueTypeNode) {
2067+
super(sourceSection);
2068+
this.valueTypeNode = valueTypeNode;
2069+
this.getModuleNode = new GetModuleNode(sourceSection);
2070+
}
2071+
2072+
@SuppressWarnings("unused")
2073+
@Specialization(guards = "value.getVmClass() == getReferenceClass()")
2074+
protected Object eval(VirtualFrame frame, VmReference value) {
2075+
if (valueTypeNode.isNoopTypeCheck()) {
2076+
return value;
2077+
}
2078+
2079+
var module = (VmTyped) getModuleNode.executeGeneric(frame);
2080+
if (value.checkType(TypeNode.export(valueTypeNode), module.getVmClass().export())) {
2081+
return value;
2082+
}
2083+
// TODO better exceptions?
2084+
throw new VmExceptionBuilder()
2085+
.adhocEvalError(
2086+
"reference type mismatch Reference<%s> and %s",
2087+
value.getCandidateTypes(), TypeNode.export(valueTypeNode))
2088+
.build();
2089+
}
2090+
2091+
@Fallback
2092+
protected Object fallback(Object value) {
2093+
throw typeMismatch(value, BaseModule.getReferenceClass());
2094+
}
2095+
2096+
@Override
2097+
protected boolean acceptTypeNode(TypeNodeConsumer consumer) {
2098+
return consumer.accept(this);
2099+
}
2100+
2101+
@Override
2102+
public VmClass getVmClass() {
2103+
return BaseModule.getReferenceClass();
2104+
}
2105+
2106+
@Override
2107+
public VmList getTypeArgumentMirrors() {
2108+
return VmList.of(valueTypeNode.getMirror());
2109+
}
2110+
2111+
@Override
2112+
protected boolean doIsEquivalentTo(TypeNode other) {
2113+
if (!(other instanceof ReferenceTypeNode referenceTypeNode)) {
2114+
return false;
2115+
}
2116+
return valueTypeNode.isEquivalentTo(referenceTypeNode.valueTypeNode);
2117+
}
2118+
2119+
@Override
2120+
public boolean isNoopTypeCheck() {
2121+
return valueTypeNode.isNoopTypeCheck();
2122+
}
2123+
2124+
@Override
2125+
protected PType doExport() {
2126+
return new PType.Class(BaseModule.getReferenceClass().export(), valueTypeNode.doExport());
2127+
}
2128+
2129+
@Override
2130+
protected boolean isParametric() {
2131+
return true;
2132+
}
2133+
}
2134+
20602135
public static final class PairTypeNode extends ObjectSlotTypeNode {
20612136
@Child private TypeNode firstTypeNode;
20622137
@Child private TypeNode secondTypeNode;

pkl-core/src/main/java/org/pkl/core/ast/type/UnresolvedTypeNode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ public TypeNode execute(VirtualFrame frame) {
287287
return new VarArgsTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
288288
}
289289

290+
if (clazz.isReferenceClass()) {
291+
return ReferenceTypeNodeGen.create(sourceSection, typeArgumentNodes[0].execute(frame));
292+
}
293+
290294
throw exceptionBuilder()
291295
.evalError("notAParameterizableClass", clazz.getDisplayName())
292296
.withSourceSection(typeArgumentNodes[0].sourceSection)

pkl-core/src/main/java/org/pkl/core/runtime/BaseModule.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.oracle.truffle.api.CompilerDirectives;
2121
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
22+
import java.util.Set;
2223

2324
public final class BaseModule extends StdLibModule {
2425
static final VmTyped instance = VmUtils.createEmptyModule();
@@ -207,6 +208,14 @@ public static VmClass getResourceClass() {
207208
return ResourceClass.instance;
208209
}
209210

211+
public static VmClass getReferenceClass() {
212+
return ReferenceClass.instance;
213+
}
214+
215+
public static VmClass getReferenceAccessClass() {
216+
return ReferenceAccessClass.instance;
217+
}
218+
210219
public static VmTypeAlias getNonNullTypeAlias() {
211220
return NonNullTypeAlias.instance;
212221
}
@@ -231,6 +240,29 @@ public static VmTypeAlias getUInt8TypeAlias() {
231240
return UInt8TypeAlias.instance;
232241
}
233242

243+
public static VmTypeAlias getUInt16TypeAlias() {
244+
return UInt16TypeAlias.instance;
245+
}
246+
247+
public static VmTypeAlias getUInt32TypeAlias() {
248+
return UInt32TypeAlias.instance;
249+
}
250+
251+
public static VmTypeAlias getUIntTypeAlias() {
252+
return UIntTypeAlias.instance;
253+
}
254+
255+
public static Set<VmTypeAlias> getIntTypeAliases() {
256+
return Set.of(
257+
getInt8TypeAlias(),
258+
getInt16TypeAlias(),
259+
getInt32TypeAlias(),
260+
getUInt8TypeAlias(),
261+
getUInt16TypeAlias(),
262+
getUInt32TypeAlias(),
263+
getUIntTypeAlias());
264+
}
265+
234266
private static final class AnyClass {
235267
static final VmClass instance = loadClass("Any");
236268
}
@@ -359,6 +391,14 @@ private static final class ResourceClass {
359391
static final VmClass instance = loadClass("Resource");
360392
}
361393

394+
private static final class ReferenceClass {
395+
static final VmClass instance = loadClass("Reference");
396+
}
397+
398+
private static final class ReferenceAccessClass {
399+
static final VmClass instance = loadClass("ReferenceAccess");
400+
}
401+
362402
private static final class FunctionClass {
363403
static final VmClass instance = loadClass("Function");
364404
}
@@ -407,6 +447,18 @@ private static final class UInt8TypeAlias {
407447
static final VmTypeAlias instance = loadTypeAlias("UInt8");
408448
}
409449

450+
private static final class UInt16TypeAlias {
451+
static final VmTypeAlias instance = loadTypeAlias("UInt16");
452+
}
453+
454+
private static final class UInt32TypeAlias {
455+
static final VmTypeAlias instance = loadTypeAlias("UInt32");
456+
}
457+
458+
private static final class UIntTypeAlias {
459+
static final VmTypeAlias instance = loadTypeAlias("UInt");
460+
}
461+
410462
private static final class MixinTypeAlias {
411463
static final VmTypeAlias instance = loadTypeAlias("Mixin");
412464
}

0 commit comments

Comments
 (0)