Compare commits

..

1 Commits

Author SHA1 Message Date
Dan Chao a6b02692bb Enforce that abstract members are implemented
This adds a check that abstract members must be implemented.
If any members lack an implementation, an error is thrown describing
the missing members.

Also: we have a bug in `pkl:base`; class `Set` does not implement all
members of class `Collection`.
2026-06-14 21:15:52 -07:00
128 changed files with 666 additions and 3077 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: nothing
steps:
- name: EnricoMi/publish-unit-test-result-action@v2
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
- name: actions/checkout@v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: actions/create-github-app-token@v2
+1 -1
View File
@@ -949,7 +949,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -1018,7 +1018,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -947,7 +947,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -1043,7 +1043,7 @@ jobs:
pattern: test-results-xml-*
- name: Publish test results
if: '!cancelled()'
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
with:
comment_mode: 'off'
files: test-results-xml-*/**/*.xml
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
name_is_regexp: true
run_id: ${{ github.event.workflow_run.id }}
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2
uses: EnricoMi/publish-unit-test-result-action@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2
with:
commit: ${{ github.event.workflow_run.head_sha }}
comment_mode: 'off'
+19 -78
View File
@@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.*
import java.util.zip.ZipInputStream
import javax.inject.Inject
import kotlin.io.path.createDirectories
import org.gradle.api.DefaultTask
@@ -44,14 +41,19 @@ constructor(
@TaskAction
@Suppress("unused")
fun run() {
// minimize chance of corruption by extract-to-random-dir-and-publish
// minimize chance of corruption by extract-to-random-dir-and-flip-symlink
val distroDir = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
try {
distroDir.createDirectories()
println("Extracting ${graalVm.get().downloadFile} into $distroDir")
val os = org.gradle.internal.os.OperatingSystem.current()
if (os.isWindows) extractZip(distroDir) else extractTarGz(distroDir)
// faster and more reliable than Gradle's `copy { from tarTree() }`
execOperations.exec {
workingDir = distroDir.toFile()
executable = "tar"
args("--strip-components=1", "-xzf", graalVm.get().downloadFile)
}
val os = org.gradle.internal.os.OperatingSystem.current()
val distroBinDir =
if (os.isMacOsX) distroDir.resolve("Contents/Home/bin") else distroDir.resolve("bin")
@@ -68,7 +70,17 @@ constructor(
}
}
publishInstallDir(distroDir, os)
println("Creating symlink ${graalVm.get().installDir} for $distroDir")
val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
Files.createSymbolicLink(tempLink, distroDir)
try {
Files.move(tempLink, graalVm.get().installDir.toPath(), StandardCopyOption.ATOMIC_MOVE)
} catch (e: Exception) {
try {
fileOperations.delete(tempLink.toFile())
} catch (ignored: Exception) {}
throw e
}
} catch (e: Exception) {
try {
fileOperations.delete(distroDir)
@@ -76,75 +88,4 @@ constructor(
throw e
}
}
private fun extractTarGz(distroDir: Path) {
// faster and more reliable than Gradle's `copy { from tarTree() }`
execOperations.exec {
workingDir = distroDir.toFile()
executable = "tar"
args("--strip-components=1", "-xzf", graalVm.get().downloadFile)
}
}
private fun extractZip(distroDir: Path) {
val targetDir = distroDir.toAbsolutePath().normalize()
ZipInputStream(graalVm.get().downloadFile.inputStream().buffered()).use { zipInput ->
var entry = zipInput.nextEntry
while (entry != null) {
try {
val strippedPath = stripFirstPathComponent(entry.name)
if (strippedPath != null) {
val target = targetDir.resolve(strippedPath).normalize()
require(target.startsWith(targetDir)) {
"GraalVM archive entry escapes destination directory: ${entry.name}"
}
if (entry.isDirectory) {
target.createDirectories()
} else {
target.parent?.createDirectories()
Files.copy(zipInput, target)
}
}
} finally {
zipInput.closeEntry()
}
entry = zipInput.nextEntry
}
}
}
private fun stripFirstPathComponent(path: String): String? {
val normalizedPath = path.replace('\\', '/').trimStart('/')
val separatorIndex = normalizedPath.indexOf('/')
if (separatorIndex == -1) return null
return normalizedPath.substring(separatorIndex + 1).takeIf { it.isNotEmpty() }
}
private fun publishInstallDir(distroDir: Path, os: org.gradle.internal.os.OperatingSystem) {
if (os.isWindows) {
println("Installing ${graalVm.get().installDir} from $distroDir")
moveAtomicallyOrRegularly(distroDir, graalVm.get().installDir.toPath())
return
}
println("Creating symlink ${graalVm.get().installDir} for $distroDir")
val tempLink = Paths.get(graalVm.get().homeDir, UUID.randomUUID().toString())
Files.createSymbolicLink(tempLink, distroDir)
try {
moveAtomicallyOrRegularly(tempLink, graalVm.get().installDir.toPath())
} catch (e: Exception) {
try {
fileOperations.delete(tempLink.toFile())
} catch (ignored: Exception) {}
throw e
}
}
private fun moveAtomicallyOrRegularly(source: Path, target: Path) {
try {
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE)
} catch (e: AtomicMoveNotSupportedException) {
Files.move(source, target)
}
}
}
@@ -69,7 +69,6 @@ endif::[]
:uri-stdlib-baseModule: {uri-pkl-stdlib-docs}/base
:uri-stdlib-CommandModule: {uri-pkl-stdlib-docs}/Command
:uri-stdlib-refModule: {uri-pkl-stdlib-docs}/ref
:uri-stdlib-analyzeModule: {uri-pkl-stdlib-docs}/analyze
:uri-stdlib-jsonnetModule: {uri-pkl-stdlib-docs}/jsonnet
:uri-stdlib-reflectModule: {uri-pkl-stdlib-docs}/reflect
@@ -161,8 +160,6 @@ endif::[]
:uri-stdlib-Command-CountedFlag: {uri-stdlib-CommandModule}/CountedFlag
:uri-stdlib-Command-Argument: {uri-stdlib-CommandModule}/Argument
:uri-stdlib-Command-Import: {uri-stdlib-CommandModule}/Import
:uri-stdlib-ref-Reference: {uri-stdlib-baseModule}/Reference
:uri-stdlib-ref-Access: {uri-stdlib-baseModule}/Access
:uri-messagepack: https://msgpack.org/index.html
:uri-messagepack-spec: https://github.com/msgpack/msgpack/blob/master/spec.md
@@ -187,15 +187,7 @@ The array's length is the number of slots that are filled. For example, xref:{ur
|
|
|
|link:{uri-stdlib-ref-Reference}[Reference]
|`0x20`
|`<value>` (Typed)
|Domain
|`<value>`
|Data
|link:{uri-messagepack-array}[array]
|Array of link:{uri-stdlib-ref-Access}[`pkl.ref#Access`] values
|===
[[type-name-encoding]]
[NOTE]
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -5,7 +5,7 @@ checksumPlugin = "1.4.0"
clikt = "5.0.3"
commonMark = "0.28.0"
downloadTaskPlugin = "5.7.0"
errorProne = "2.50.0"
errorProne = "2.49.0"
errorPronePlugin = "5.1.0"
geantyref = "2.0.1"
#noinspection UnusedVersionCatalogEntry
@@ -54,7 +54,7 @@ ktfmt = "0.62"
log4j = "2.17.1"
msgpack = "0.9.12"
nexusPublishPlugin = "2.0.0"
nullaway = "0.13.6"
nullaway = "0.13.4"
nullawayPlugin = "3.0.0"
nuValidator = "26.5.29"
paguro = "3.10.3"
@@ -27,7 +27,6 @@ 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;
@@ -43,14 +42,12 @@ public final class PClass extends Member implements Value {
PClassInfo<?> classInfo,
List<TypeParameter> typeParameters,
Map<String, Property> properties,
Map<String, Method> methods,
@Nullable PClass moduleClass) {
Map<String, Method> methods) {
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) {
@@ -60,7 +57,7 @@ public final class PClass extends Member implements Value {
/**
* Returns the name of the module that this class is declared in. Note that a module name is not
* guaranteed to be unique, especially if it is not declared but inferred from the module URI.
* guaranteed to be unique, especially if it not declared but inferred from the module URI.
*/
public String getModuleName() {
return classInfo.getModuleName();
@@ -122,11 +119,6 @@ public final class PClass extends Member implements Value {
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);
@@ -146,10 +138,6 @@ public final class PClass extends Member implements Value {
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;
@@ -39,7 +39,6 @@ public final class PClassInfo<T> implements Serializable {
public static final URI pklSemverUri = URI.create("pkl:semver");
public static final URI pklSettingsUri = URI.create("pkl:settings");
public static final URI pklProjectUri = URI.create("pkl:Project");
public static final URI pklRefUri = URI.create("pkl:ref");
public static final PClassInfo<Void> Any = pklBaseClassInfo("Any", Void.class);
public static final PClassInfo<PNull> Null = pklBaseClassInfo("Null", PNull.class);
@@ -83,11 +82,9 @@ public final class PClassInfo<T> implements Serializable {
public static final PClassInfo<PObject> Version =
new PClassInfo<>("pkl.semver", "Version", PObject.class, pklSemverUri);
public static final PClassInfo<PObject> Project =
new PClassInfo<>("pkl.Project", MODULE_CLASS_NAME, PObject.class, pklProjectUri);
new PClassInfo<>("pkl.Project", "ModuleClass", PObject.class, pklProjectUri);
public static final PClassInfo<PObject> Settings =
new PClassInfo<>("pkl.settings", MODULE_CLASS_NAME, PObject.class, pklSettingsUri);
public static final PClassInfo<Reference> Reference =
new PClassInfo<>("pkl.ref", "Reference", Reference.class, pklRefUri);
new PClassInfo<>("pkl.settings", "ModuleClass", PObject.class, pklSettingsUri);
public static final PClassInfo<Object> Unavailable =
new PClassInfo<>("unavailable", "unavailable", Object.class, URI.create("pkl:unavailable"));
+1 -177
View File
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024 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.
@@ -18,7 +18,6 @@ package org.pkl.core;
import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/** A Pkl type as used in type annotations. */
public abstract class PType implements Serializable {
@@ -28,33 +27,18 @@ public abstract class PType implements Serializable {
public static final PType UNKNOWN =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "unknown";
}
};
/** The bottom type. */
public static final PType NOTHING =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "nothing";
}
};
/** The type of the enclosing module. */
public static final PType MODULE =
new PType() {
@Serial private static final long serialVersionUID = 0L;
@Override
public String toString() {
return "module";
}
};
private PType() {}
@@ -75,22 +59,6 @@ public abstract class PType implements Serializable {
public String getLiteral() {
return literal;
}
@Override
public String toString() {
return ValueFormatter.basic().formatStringValue(literal, "");
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof StringLiteral that && literal.equals(that.literal);
}
@Override
public int hashCode() {
return literal.hashCode();
}
}
public static final class Class extends PType {
@@ -124,31 +92,6 @@ public abstract class PType implements Serializable {
public List<PType> getTypeArguments() {
return typeArguments;
}
@Override
public String toString() {
var result = pClass.getDisplayName();
if (!typeArguments.isEmpty()) {
result +=
"<"
+ typeArguments.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ">";
}
return result;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Class that
&& pClass.equals(that.pClass)
&& typeArguments.equals(that.typeArguments);
}
@Override
public int hashCode() {
return 31 * pClass.hashCode() + typeArguments.hashCode();
}
}
public static final class Nullable extends PType {
@@ -163,24 +106,6 @@ public abstract class PType implements Serializable {
public PType getBaseType() {
return baseType;
}
@Override
public String toString() {
return baseType instanceof Function || baseType instanceof Union
? "(" + baseType + ")?"
: baseType + "?";
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Nullable that && baseType.equals(that.baseType);
}
@Override
public int hashCode() {
return baseType.hashCode();
}
}
public static final class Constrained extends PType {
@@ -201,29 +126,6 @@ public abstract class PType implements Serializable {
public List<String> getConstraints() {
return constraints;
}
@Override
public String toString() {
return (baseType instanceof Function || baseType instanceof Union
? "(" + baseType + ")"
: baseType)
+ "("
+ String.join(", ", constraints)
+ ")";
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Constrained that
&& baseType.equals(that.baseType)
&& constraints.equals(that.constraints);
}
@Override
public int hashCode() {
return 31 * baseType.hashCode() + constraints.hashCode();
}
}
public static final class Alias extends PType {
@@ -259,31 +161,6 @@ public abstract class PType implements Serializable {
public PType getAliasedType() {
return aliasedType;
}
@Override
public String toString() {
var result = typeAlias.getDisplayName();
if (!typeArguments.isEmpty()) {
result +=
"<"
+ typeArguments.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ">";
}
return result;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Alias that
&& typeAlias.equals(that.typeAlias)
&& typeArguments.equals(that.typeArguments);
}
@Override
public int hashCode() {
return 31 * typeAlias.hashCode() + typeArguments.hashCode();
}
}
public static final class Function extends PType {
@@ -304,27 +181,6 @@ public abstract class PType implements Serializable {
public PType getReturnType() {
return returnType;
}
@Override
public String toString() {
return "("
+ parameterTypes.stream().map(Object::toString).collect(Collectors.joining(", "))
+ ") -> "
+ returnType;
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Function that
&& parameterTypes.equals(that.parameterTypes)
&& returnType.equals(that.returnType);
}
@Override
public int hashCode() {
return 31 * parameterTypes.hashCode() + returnType.hashCode();
}
}
public static final class Union extends PType {
@@ -339,22 +195,6 @@ public abstract class PType implements Serializable {
public List<PType> getElementTypes() {
return elementTypes;
}
@Override
public String toString() {
return elementTypes.stream().map(Object::toString).collect(Collectors.joining(" | "));
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof Union that && elementTypes.equals(that.elementTypes);
}
@Override
public int hashCode() {
return elementTypes.hashCode();
}
}
public static final class TypeVariable extends PType {
@@ -373,21 +213,5 @@ public abstract class PType implements Serializable {
public TypeParameter getTypeParameter() {
return typeParameter;
}
@Override
public String toString() {
return typeParameter.getName();
}
@Override
public boolean equals(@org.jspecify.annotations.Nullable Object obj) {
if (obj == this) return true;
return obj instanceof TypeVariable that && typeParameter.equals(that.typeParameter);
}
@Override
public int hashCode() {
return typeParameter.hashCode();
}
}
}
@@ -177,13 +177,6 @@ final class PropertiesRenderer implements ValueRenderer {
"Values of type `Regex` cannot be rendered as Properties. Value: %s", value));
}
@Override
public String convertReference(Reference value) {
throw new RendererException(
String.format(
"Values of type `Reference` cannot be rendered as Properties. Value: %s", value));
}
private void doVisitMap(@Nullable String keyPrefix, Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
doVisitKeyAndValue(keyPrefix, entry.getKey(), entry.getValue());
@@ -1,101 +0,0 @@
/*
* 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;
import java.io.Serial;
import java.util.List;
public class Reference implements Value {
@Serial private static final long serialVersionUID = 0L;
private final Composite domain;
private final Object data;
private final List<Composite> path;
private final PType referentType;
/** Constructs a reference. */
public Reference(Composite domain, Object data, List<Composite> path, PType referentType) {
this.domain = domain;
this.data = data;
this.path = path;
this.referentType = referentType;
}
/** Returns the domain object of this reference. */
public Composite getDomain() {
return domain;
}
/** Returns the data object of this reference. */
public Object getData() {
return data;
}
/**
* Returns the access path of this reference.
*
* <p>All elements are exported {@code pkl.ref#Access} instances.
*/
public List<Composite> getPath() {
return path;
}
/** Returns the referent type of this reference. */
public PType getReferentType() {
return referentType;
}
@Override
public void accept(ValueVisitor visitor) {
visitor.visitReference(this);
}
@Override
public <T> T accept(ValueConverter<T> converter) {
return converter.convertReference(this);
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Reference reference)) {
return false;
}
return domain.equals(reference.domain)
&& data.equals(reference.data)
&& path.equals(reference.path)
&& referentType.equals(reference.referentType);
}
@Override
public PClassInfo<?> getClassInfo() {
return PClassInfo.Reference;
}
@Override
public int hashCode() {
int result = domain.hashCode();
result = 31 * result + data.hashCode();
result = 31 * result + path.hashCode();
result = 31 * result + referentType.hashCode();
return result;
}
@Override
public String toString() {
return super.toString();
}
}
@@ -28,7 +28,6 @@ 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;
@@ -40,13 +39,11 @@ public final class TypeAlias extends Member implements Value {
String simpleName,
String moduleName,
String qualifiedName,
List<TypeParameter> typeParameters,
PClass moduleClass) {
List<TypeParameter> typeParameters) {
super(docComment, sourceLocation, modifiers, annotations, simpleName);
this.moduleName = moduleName;
this.qualifiedName = qualifiedName;
this.typeParameters = typeParameters;
this.moduleClass = moduleClass;
}
public void initAliasedType(PType type) {
@@ -82,10 +79,6 @@ public final class TypeAlias extends Member implements Value {
return typeParameters;
}
public PClass getModuleClass() {
return moduleClass;
}
/** Returns the type that this type alias stands for. */
public PType getAliasedType() {
//noinspection ConstantValue
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 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.
@@ -57,8 +57,6 @@ public interface ValueConverter<T> {
T convertRegex(Pattern value);
T convertReference(Reference value);
default T convert(Object value) {
if (value instanceof Value v) {
return (v.accept(this));
@@ -93,10 +93,6 @@ public interface ValueVisitor {
visitDefault(value);
}
default void visitReference(Reference value) {
visitDefault(value);
}
default void visit(Object value) {
if (value instanceof Value v) {
v.accept(this);
@@ -1476,6 +1476,8 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
var scope = (ModuleScope) symbolTable.getCurrentScope();
scope.setModifiers(modifiers);
checkAbstractMembersAllowed(modifiers, mod.getProperties(), mod.getMethods());
// visit imports first so that we already have the object member name available
var imports = mod.getImports();
var importMembers = new ObjectMember[imports.size()];
@@ -1720,6 +1722,7 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
List<ClassProperty> properties = bodyNode != null ? bodyNode.getProperties() : List.of();
List<ClassMethod> methods = bodyNode != null ? bodyNode.getMethods() : List.of();
registerClassScopeNames(scope, properties, methods);
checkAbstractMembersAllowed(modifiers, properties, methods);
var supertypeCtx = clazz.getSuperClass();
@@ -1810,6 +1813,30 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
};
}
private void checkAbstractMembersAllowed(
int enclosingModifiers, List<ClassProperty> properties, List<ClassMethod> methods) {
if (VmModifier.isAbstract(enclosingModifiers)) {
return;
}
for (var property : properties) {
checkMemberNotAbstract(property.getModifiers());
}
for (var method : methods) {
checkMemberNotAbstract(method.getModifiers());
}
}
private void checkMemberNotAbstract(List<Modifier> modifiers) {
for (var modifier : modifiers) {
if (modifier.getValue() == ModifierValue.ABSTRACT) {
throw exceptionBuilder()
.evalError("abstractMemberInNonAbstractClass")
.withSourceSection(createSourceSection(modifier.span()))
.build();
}
}
}
private UnresolvedPropertyNode[] doVisitClassProperties(
List<ClassProperty> propertyContexts, Set<String> propertyNames) {
var propertyNodes = new UnresolvedPropertyNode[propertyContexts.size()];
@@ -100,20 +100,6 @@ public abstract class SubscriptNode extends BinaryExpressionNode {
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();
throw exceptionBuilder()
.evalError(
"operatorNotDefined2", getShortName(), reference.exportType(), VmUtils.getClass(key))
.withProgramValue("Left operand", reference)
.withProgramValue("Right operand", key)
.build();
}
@Specialization
protected long eval(VmBytes receiver, long index) {
if (index < 0 || index >= receiver.getLength()) {
@@ -61,18 +61,6 @@ public abstract class ReadPropertyNode extends ExpressionNode {
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();
throw exceptionBuilder()
.evalError("cannotFindPropertyInReference", propertyName, receiver.exportType())
.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
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 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.
@@ -20,7 +20,6 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
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.pkl.core.ast.ExpressionNode;
import org.pkl.core.ast.MemberLookupMode;
@@ -63,16 +62,8 @@ public abstract class ToStringNode extends UnaryExpressionNode {
VmTyped value,
@Cached(value = "createInvokeNode()", neverDefault = true)
InvokeMethodVirtualNode invokeNode) {
return (String) invokeNode.executeWith(frame, value, value.getVmClass());
}
@Specialization
protected String evalReference(
VirtualFrame frame,
VmReference value,
@Cached(value = "createReferenceCallNode(value)", neverDefault = true)
DirectCallNode callNode) {
return (String) callNode.call(value, value.getVmClass().getPrototype());
return (String) invokeNode.executeWith(frame, value, value.getVmClass());
}
@Fallback
@@ -83,6 +74,7 @@ public abstract class ToStringNode extends UnaryExpressionNode {
}
protected InvokeMethodVirtualNode createInvokeNode() {
//noinspection ConstantConditions
return InvokeMethodVirtualNodeGen.create(
sourceSection,
Identifier.TO_STRING,
@@ -91,10 +83,4 @@ public abstract class ToStringNode extends UnaryExpressionNode {
null,
null);
}
protected DirectCallNode createReferenceCallNode(VmReference reference) {
var toStringMethod = reference.getVmClass().getDeclaredMethod(Identifier.TO_STRING);
assert toStringMethod != null;
return DirectCallNode.create(toStringMethod.getCallTarget());
}
}
@@ -24,7 +24,7 @@ import org.pkl.core.ast.VmModifier;
import org.pkl.core.runtime.*;
public final class ClassProperty extends ClassMember {
private final @Nullable PropertyTypeNode typeNode;
private @Nullable PropertyTypeNode typeNode;
private final ObjectMember initializer;
public ClassProperty(
@@ -64,6 +64,11 @@ public final class ClassProperty extends ClassMember {
return VmModifier.getMirrors(mods, false);
}
public void lateInitTypeNodeAndModifiers(@Nullable PropertyTypeNode typeNode, int modifiers) {
this.typeNode = typeNode;
this.modifiers = modifiers;
}
public @Nullable PropertyTypeNode getTypeNode() {
return typeNode;
}
@@ -25,7 +25,7 @@ public abstract class Member {
protected final SourceSection headerSection;
protected final int modifiers;
protected int modifiers;
protected final @Nullable Identifier name;
@@ -2127,125 +2127,6 @@ public abstract class TypeNode extends PklNode {
}
}
public abstract static class ReferenceTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode domainTypeNode;
@Child private TypeNode referentTypeNode;
@Child private ExpressionNode getModuleNode;
public ReferenceTypeNode(
SourceSection sourceSection, TypeNode domainTypeNode, TypeNode referentTypeNode) {
super(sourceSection);
this.domainTypeNode = domainTypeNode;
this.referentTypeNode = referentTypeNode;
this.getModuleNode = new GetModuleNode(sourceSection);
validateTypeArguments(sourceSection);
}
@Specialization
protected Object eval(VirtualFrame frame, VmReference value) {
if (domainTypeNode.isNoopTypeCheck() && referentTypeNode.isNoopTypeCheck()) {
return value;
}
try {
domainTypeNode.execute(frame, value.getDomain());
} catch (VmTypeMismatchException e) {
CompilerDirectives.transferToInterpreter();
throw new VmTypeMismatchException.Reference(
sourceSection,
value,
TypeNode.export(domainTypeNode),
TypeNode.export(referentTypeNode));
}
var module = (VmTyped) getModuleNode.executeGeneric(frame);
return doEval(value, module);
}
@TruffleBoundary
private Object doEval(VmReference value, VmTyped module) {
var referentType = TypeNode.export(referentTypeNode);
if (value.referentTypeIsSubtypeOf(referentType, module.getVmClass().export())) {
return value;
}
throw new VmTypeMismatchException.Reference(
sourceSection, value, TypeNode.export(domainTypeNode), referentType);
}
public void validateTypeArguments(@Nullable SourceSection aliasSourceSection) {
// constraints may not be used in Reference type annotation referents
// walk the type and throw if any part of the referent is constrained
// TODO improve error message when this type node and/or referent constraint are behind type
// aliases
referentTypeNode.acceptTypeNode(
true,
(typeNode) -> {
if (typeNode instanceof ConstrainedTypeNode) {
CompilerDirectives.transferToInterpreter();
var err =
exceptionBuilder().evalError("invalidReferenceTypeAnnotationWithConstraint");
if (aliasSourceSection != null) {
err.withSourceSection(aliasSourceSection);
}
throw err.build();
}
return true;
});
}
@Fallback
protected Object fallback(Object value) {
throw typeMismatch(value, RefModule.getReferenceClass());
}
@Override
protected boolean acceptTypeNode(boolean visitTypeArguments, TypeNodeConsumer consumer) {
if (visitTypeArguments)
return consumer.accept(this)
&& consumer.accept(domainTypeNode)
&& consumer.accept(referentTypeNode);
return consumer.accept(this);
}
@Override
public VmClass getVmClass() {
return RefModule.getReferenceClass();
}
@Override
public VmList getTypeArgumentMirrors() {
return VmList.of(domainTypeNode.getMirror(), 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 domainTypeNode.isNoopTypeCheck() && referentTypeNode.isNoopTypeCheck();
}
@Override
protected PType doExport() {
return new PType.Class(
RefModule.getReferenceClass().export(),
domainTypeNode.doExport(),
referentTypeNode.doExport());
}
@Override
protected boolean isParametric() {
return true;
}
}
public static final class PairTypeNode extends ObjectSlotTypeNode {
@Child private TypeNode firstTypeNode;
@Child private TypeNode secondTypeNode;
@@ -2703,7 +2584,7 @@ public abstract class TypeNode extends PklNode {
this.typeAlias = typeAlias;
this.typeArgumentNodes = typeArgumentNodes;
aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes, sourceSection);
aliasedTypeNode = typeAlias.instantiate(typeArgumentNodes);
}
public TypeNode getAliasedTypeNode() {
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 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.
@@ -287,13 +287,6 @@ public abstract class UnresolvedTypeNode extends PklNode {
return new VarArgsTypeNode(sourceSection, typeArgumentNodes[0].execute(frame));
}
if (clazz.isReferenceClass()) {
return ReferenceTypeNodeGen.create(
sourceSection,
typeArgumentNodes[0].execute(frame),
typeArgumentNodes[1].execute(frame));
}
throw exceptionBuilder()
.evalError("notAParameterizableClass", clazz.getDisplayName())
.withSourceSection(typeArgumentNodes[0].sourceSection)
@@ -23,7 +23,6 @@ import com.oracle.truffle.api.source.SourceSection;
import java.util.*;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PType;
import org.pkl.core.StackFrame;
import org.pkl.core.ValueFormatter;
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
@@ -334,44 +333,4 @@ public abstract class VmTypeMismatchException extends ControlFlowException {
return false;
}
}
public static final class Reference extends VmTypeMismatchException {
private final PType expectedDomainType;
private final PType expectedReferentType;
public Reference(
SourceSection sourceSection,
VmReference actualValue,
PType expectedDomainType,
PType expectedReferentType) {
super(sourceSection, actualValue);
this.expectedDomainType = expectedDomainType;
this.expectedReferentType = expectedReferentType;
}
@Override
public void buildMessage(
AnsiStringBuilder builder, String indent, boolean withPowerAssertions) {
builder
.append(
ErrorMessages.createIndented(
"typeMismatch",
indent,
new PType.Class(
RefModule.getReferenceClass().export(),
expectedDomainType,
expectedReferentType),
((VmReference) actualValue).exportType()))
.append("\n")
.append(indent)
.append("Value: ")
.append(VmValueRenderer.singleLine(80 - indent.length()).render(actualValue));
}
@Override
protected Boolean hasHint() {
return false;
}
}
}
@@ -45,7 +45,6 @@ import java.util.stream.StreamSupport;
import java.util.zip.ZipInputStream;
import org.graalvm.collections.EconomicMap;
import org.jspecify.annotations.Nullable;
import org.pkl.core.PklBugException;
import org.pkl.core.SecurityManager;
import org.pkl.core.SecurityManagerException;
import org.pkl.core.http.HttpClient;
@@ -457,17 +456,10 @@ final class PackageResolvers {
}
private Path getRelativePath(PackageUri uri) {
var relativePath =
Path.of(
CACHE_DIR_PREFIX,
IoUtils.encodePath(uri.getUri().getAuthority()),
getEffectivePackageUriPath(uri));
// ensure the derived path cannot escape the cache directory
var resolved = cacheDir.resolve(relativePath).normalize();
if (!resolved.startsWith(cacheDir.normalize())) {
throw new PklBugException("Package URI escapes the cache directory");
}
return relativePath;
return Path.of(
CACHE_DIR_PREFIX,
IoUtils.encodePath(uri.getUri().getAuthority()),
getEffectivePackageUriPath(uri));
}
private String getLastSegmentName(PackageUri packageUri) {
@@ -61,13 +61,6 @@ public final class PackageUri {
throw new URISyntaxException(
uri.toString(), ErrorMessages.create("missingPathInPackageUri", uri));
}
// reject `..` segments, percent-encoded or not
for (var segment : path.split("/", -1)) {
if (segment.equals("..")) {
throw new URISyntaxException(
uri.toString(), ErrorMessages.create("invalidRelativePathInPackageUri"));
}
}
var versionIdx = path.lastIndexOf('@');
if (versionIdx == -1) {
throw new URISyntaxException(
@@ -165,10 +165,6 @@ public final class Identifier implements Comparable<Identifier> {
public static final Identifier ILLEGAL = get("`");
// members of pkl.ref
public static final Identifier PROPERTY = get("property");
public static final Identifier KEY = get("key");
// common in lambdas etc
public static final Identifier IT = get("it");
@@ -104,8 +104,6 @@ public final class ModuleCache {
return PlatformModule.getModule();
case "project":
return ProjectModule.getModule();
case "ref":
return RefModule.getModule();
case "reflect":
return ReflectModule.getModule();
case "release":
@@ -1,53 +0,0 @@
/*
* 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.CompilerDirectives.TruffleBoundary;
import java.net.URI;
public class RefModule extends StdLibModule {
private static final VmTyped instance = VmUtils.createEmptyModule();
static {
loadModule(URI.create("pkl:ref"), instance);
}
public static VmClass getReferenceClass() {
return ReferenceClass.instance;
}
public static VmClass getAccessClass() {
return AccessClass.instance;
}
private static final class ReferenceClass {
static final VmClass instance = loadClass("Reference");
}
private static final class AccessClass {
static final VmClass instance = loadClass("Access");
}
@TruffleBoundary
private static VmClass loadClass(String className) {
var theModule = getModule();
return (VmClass) VmUtils.readMember(theModule, Identifier.get(className));
}
public static VmTyped getModule() {
return instance;
}
}
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.Idempotent;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.source.SourceSection;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
import org.graalvm.collections.*;
import org.jspecify.annotations.Nullable;
@@ -84,6 +85,13 @@ public final class VmClass extends VmValue {
private final Object allHiddenPropertyNamesLock = new Object();
@GuardedBy("finalizersLock")
private @Nullable List<Runnable> __finalizers = null;
private final Object finalizersLock = new Object();
private final AtomicInteger uninitializedSuperclassCount = new AtomicInteger(0);
// Helps to overcome recursive initialization issues
// between classes and annotations in pkl.base.
@CompilationFinal private volatile boolean isInitialized;
@@ -150,6 +158,83 @@ public final class VmClass extends VmValue {
prototype.lateInitParent(superclass.getPrototype());
}
@TruffleBoundary
private void checkAbstractMembers() {
if (this.isAbstract()) return;
// minimize allocations in the non-error case
if (!hasAbstractMember()) return;
var abstractMembers = getAbstractMembers();
if (abstractMembers.size() == 1) {
var member = abstractMembers.get(0);
var message =
member instanceof ClassProperty
? "noImplementationForAbstractProperty"
: "noImplementationForAbstractMethod";
throw new VmExceptionBuilder()
.evalError(message, getDisplayName(), member.getName().toString())
.withSourceSection(getHeaderSection())
.build();
}
var memberList = new StringBuilder();
var isFirst = true;
for (var member : abstractMembers) {
if (isFirst) {
isFirst = false;
} else {
memberList.append('\n');
}
memberList.append("* ");
if (member instanceof ClassProperty) {
memberList.append("property ");
} else {
memberList.append("method ");
}
memberList.append('`').append(member.getName()).append('`');
}
throw new VmExceptionBuilder()
.evalError("noImplementationForAbstractMembers", getDisplayName(), memberList.toString())
.withSourceSection(getHeaderSection())
.build();
}
private boolean hasAbstractMember() {
var propertyCursor = getAllProperties().getEntries();
while (propertyCursor.advance()) {
var property = propertyCursor.getValue();
if (property.isAbstract()) {
return true;
}
}
var methodCursor = getAllMethods().getEntries();
while (methodCursor.advance()) {
var method = methodCursor.getValue();
if (method.isAbstract()) {
return true;
}
}
return false;
}
private List<ClassMember> getAbstractMembers() {
assert this.superclass != null;
var result = new ArrayList<ClassMember>();
var propertyCursor = getAllProperties().getEntries();
while (propertyCursor.advance()) {
var property = propertyCursor.getValue();
if (property.isAbstract()) {
result.add(property);
}
}
var methodCursor = getAllMethods().getEntries();
while (methodCursor.advance()) {
var method = methodCursor.getValue();
if (method.isAbstract()) {
result.add(method);
}
}
return result;
}
@TruffleBoundary
public void addProperty(ClassProperty property) {
prototype.addProperty(property.getInitializer());
@@ -190,8 +275,46 @@ public final class VmClass extends VmValue {
}
}
private void onInitialized(Runnable runnable) {
synchronized (finalizersLock) {
if (this.__finalizers == null) {
this.__finalizers = new ArrayList<>();
}
this.__finalizers.add(runnable);
}
}
// Note: Superclasses may not have finished their initialization when this method is called.
public void notifyInitialized() {
var sc = superclass;
var isAllInitialized = true;
var uninitializedCount = 0;
while (sc != null) {
if (!sc.isInitialized) {
sc.onInitialized(
() -> {
var count = uninitializedSuperclassCount.decrementAndGet();
if (count == 0) {
checkAbstractMembers();
}
});
uninitializedCount++;
isAllInitialized = false;
}
sc = sc.superclass;
}
uninitializedSuperclassCount.set(uninitializedCount);
if (isAllInitialized) {
checkAbstractMembers();
}
lock:
synchronized (finalizersLock) {
if (__finalizers == null) break lock;
for (var finalizer : __finalizers) {
finalizer.run();
}
this.__finalizers = null;
}
isInitialized = true;
}
@@ -445,11 +568,6 @@ public final class VmClass extends VmValue {
return isClass(BaseModule.getVarArgsClass(), "pkl.base#VarArgs");
}
@Idempotent
public boolean isReferenceClass() {
return isClass(RefModule.getReferenceClass(), "pkl.ref#Reference");
}
private boolean isClass(@Nullable VmClass clazz, String qualifiedClassName) {
// may be null during evaluation of base module
return clazz != null ? this == clazz : getQualifiedName().equals(qualifiedClassName);
@@ -626,62 +744,49 @@ public final class VmClass extends VmValue {
public PClass export() {
synchronized (pClassLock) {
//noinspection ConstantValue
if (__pClass != null) {
return __pClass;
}
if (__pClass == null) {
var exportedAnnotations = new ArrayList<PObject>();
var properties =
CollectionUtils.<String, PClass.Property>newLinkedHashMap(
EconomicMaps.size(declaredProperties));
var methods =
CollectionUtils.<String, PClass.Method>newLinkedHashMap(
EconomicMaps.size(declaredMethods));
// if this is not a module class, export this class's module's class first to break the cycle
PClass moduleClass = null;
if (!classInfo.isModuleClass()) {
moduleClass = getModule().getVmClass().export();
}
// then if the cached value is still null, initialize it
if (__pClass != null) {
return __pClass;
}
// set pClass before exporting class members to prevent
// infinite recursion in case of cyclic references
__pClass =
new PClass(
VmUtils.exportDocComment(docComment),
new SourceLocation(headerSection.getStartLine(), sourceSection.getEndLine()),
VmModifier.export(modifiers, true),
exportedAnnotations,
classInfo,
typeParameters,
properties,
methods);
var exportedAnnotations = new ArrayList<PObject>();
var properties =
CollectionUtils.<String, PClass.Property>newLinkedHashMap(
EconomicMaps.size(declaredProperties));
var methods =
CollectionUtils.<String, PClass.Method>newLinkedHashMap(
EconomicMaps.size(declaredMethods));
// set pClass before exporting class members to prevent
// infinite recursion in case of cyclic references
__pClass =
new PClass(
VmUtils.exportDocComment(docComment),
new SourceLocation(headerSection.getStartLine(), sourceSection.getEndLine()),
VmModifier.export(modifiers, true),
exportedAnnotations,
classInfo,
typeParameters,
properties,
methods,
moduleClass);
for (var parameter : typeParameters) {
parameter.initOwner(__pClass);
}
if (supertypeNode != null) {
assert superclass != null;
__pClass.initSupertype(TypeNode.export(supertypeNode), superclass.export());
}
VmUtils.exportAnnotations(annotations, exportedAnnotations);
for (var property : EconomicMaps.getValues(declaredProperties)) {
if (isClassPropertyDefinition(property)) {
properties.put(property.getName().toString(), property.export(__pClass));
for (var parameter : typeParameters) {
parameter.initOwner(__pClass);
}
}
for (var method : EconomicMaps.getValues(declaredMethods)) {
if (method.isLocal()) continue;
methods.put(method.getName().toString(), method.export(__pClass));
if (supertypeNode != null) {
assert superclass != null;
__pClass.initSupertype(TypeNode.export(supertypeNode), superclass.export());
}
VmUtils.exportAnnotations(annotations, exportedAnnotations);
for (var property : EconomicMaps.getValues(declaredProperties)) {
if (isClassPropertyDefinition(property)) {
properties.put(property.getName().toString(), property.export(__pClass));
}
}
for (var method : EconomicMaps.getValues(declaredMethods)) {
if (method.isLocal()) continue;
methods.put(method.getName().toString(), method.export(__pClass));
}
}
return __pClass;
@@ -753,13 +858,27 @@ public final class VmClass extends VmValue {
for (var property : EconomicMaps.getValues(declaredProperties)) {
if (property.isLocal()) continue;
// A property is considered a class property definition
// if it has a type annotation or has no superdefinition (ad-hoc case).
// A property is considered a class property definition if any of the following are true:
//
// 1. It has a type annotation.
// 2. It has no superdefinition.
// 3. It is not abstract but its superclass is.
//
// Otherwise, it is considered an object property definition,
// which means it affects the class prototype but not the class itself.
// An example for the latter is when `Module.output` is overridden with `output { ... }`.
if (property.getTypeNode() != null || !EconomicMaps.containsKey(result, property.getName())) {
if (property.getTypeNode() != null) {
EconomicMaps.put(result, property.getName(), property);
} else {
var existingProperty = EconomicMaps.get(result, property.getName());
if (existingProperty != null && existingProperty.isAbstract() && !this.isAbstract()) {
property.lateInitTypeNodeAndModifiers(
existingProperty.getTypeNode(),
existingProperty.getModifiers() & ~VmModifier.ABSTRACT);
EconomicMaps.put(result, property.getName(), property);
} else if (existingProperty == null) {
EconomicMaps.put(result, property.getName(), property);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024 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.
@@ -87,9 +87,4 @@ public final class VmObjectBuilder {
members,
elementCount);
}
public VmTyped toTyped(VmClass clazz) {
return new VmTyped(
VmUtils.createEmptyMaterializedFrame(), clazz.getPrototype(), clazz, members);
}
}
@@ -324,22 +324,6 @@ public class VmPklBinaryEncoder extends AbstractRenderer {
}
}
@Override
public void visitReference(VmReference value) {
try {
packer.packArrayHeader(4);
packCode(PklBinaryCode.REFERENCE);
visit(value.getDomain());
visit(value.getData());
packer.packArrayHeader(value.getPath().size());
for (var access : value.getPath()) {
visit(access);
}
} catch (IOException e) {
throw PklBugException.unreachableCode();
}
}
@Override
protected void visitEntryKey(Object key, boolean isFirst) {
visit(key);
@@ -1,484 +0,0 @@
/*
* Copyright © 2025-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.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.DirectCallNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.pkl.core.Composite;
import org.pkl.core.PClass;
import org.pkl.core.PClassInfo;
import org.pkl.core.PType;
import org.pkl.core.Reference;
import org.pkl.core.TypeAlias;
import org.pkl.core.util.paguro.RrbTree;
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
public final class VmReference extends VmValue {
private final VmTyped domain;
private final Object data;
private final ImRrbt<VmTyped> path;
// candidate types can only be: PType.Class, PType.Alias (only preservedAliasTypes),
// PType.StringLiteral, PType.UNKNOWN, or PType.Union (containing only the previous; flattened)
private final PType referentType;
private boolean forced = false;
private static VmTyped newAccess(@Nullable String property, @Nullable Object key) {
return new VmObjectBuilder()
.addProperty(Identifier.PROPERTY, property == null ? VmNull.withoutDefault() : property)
.addProperty(Identifier.KEY, key == null ? VmNull.withoutDefault() : key)
.toTyped(RefModule.getAccessClass());
}
@TruffleBoundary
public VmReference(VmTyped domain, VmClass clazz, Object data) {
this(
domain,
data,
RrbTree.empty(),
normalizeTypes(new PType.Class(clazz.export()), clazz.getModule().getVmClass().export()));
}
public VmReference(VmTyped domain, Object data, ImRrbt<VmTyped> path, PType referentType) {
this.domain = domain;
this.data = data;
this.referentType = referentType;
this.path = path;
}
public VmTyped getDomain() {
return domain;
}
public Object getData() {
return data;
}
public List<VmTyped> getPath() {
return path;
}
public PType getReferentType() {
return referentType;
}
// simplifies a type by:
// * erasing constraints
// * transforming T? into T|Null
// * dereferencing aliases (except for well-known stdlib alias types)
// * flattening unions
// * when moduleClass is supplied, replace PType.MODULE with appropriate PType.Class
// * drop PType.Function and PType.TypeVariable
private static PType normalizeTypes(PType type, PClass moduleClass) {
var types = new HashSet<PType>();
normalizeTypes(type, moduleClass, types);
return minimizeTypes(types);
}
private static PType minimizeTypes(Set<PType> types) {
if (types.size() == 1) return types.iterator().next();
// optimization: unknown allows all references, erase all candidates to only unknown
if (types.contains(PType.UNKNOWN)) return PType.UNKNOWN;
// optimization: All allows all references, erase all candidates to only All
if (containsClass(types, BaseModule.getAnyClass().export()))
return new PType.Class(BaseModule.getAnyClass().export());
var typesList = new ArrayList<>(types);
typesList.sort(Comparator.comparing(Object::toString));
return new PType.Union(typesList);
}
private static void normalizeTypes(PType type, PClass moduleClass, Set<PType> result) {
if (type == PType.UNKNOWN || type == PType.NOTHING || type instanceof PType.StringLiteral) {
result.add(type);
} else if (type instanceof PType.Class clazz) {
if (clazz.getTypeArguments().isEmpty()) {
// if a generic type is used without type arguments, it needs to be normalized so all args
// are unknown; i.e. with bare List/Map/etc. type annotations (via FinalClassTypeNode).
var typeParameterCount = clazz.getPClass().getTypeParameters().size();
result.add(
typeParameterCount == 0
? clazz
: new PType.Class(
clazz.getPClass(), Collections.nCopies(typeParameterCount, PType.UNKNOWN)));
} else {
var typeArgs = new ArrayList<PType>(clazz.getTypeArguments().size());
for (var arg : clazz.getTypeArguments()) {
typeArgs.add(normalizeTypes(arg, moduleClass));
}
result.add(new PType.Class(clazz.getPClass(), typeArgs));
}
} else if (type instanceof PType.Nullable nullable) {
normalizeTypes(nullable.getBaseType(), moduleClass, result);
result.add(new PType.Class(BaseModule.getNullClass().export()));
} else if (type instanceof PType.Constrained constrained) {
normalizeTypes(constrained.getBaseType(), moduleClass, result);
} else if (type instanceof PType.Alias alias) {
if (isPreservedTypeAlias(alias.getTypeAlias())) {
result.add(alias);
} else {
normalizeTypes(alias.getAliasedType(), alias.getTypeAlias().getModuleClass(), result);
}
} else if (type instanceof PType.Union union) {
for (var t : union.getElementTypes()) {
normalizeTypes(t, moduleClass, result);
}
} else if (type == PType.MODULE) {
result.add(new PType.Class(moduleClass));
}
}
private static Iterable<PType> iterateTypes(PType t) {
if (t instanceof PType.Union union) return union.getElementTypes();
return Collections.singleton(t);
}
public @Nullable VmReference withPropertyAccess(Identifier property) {
var propString = property.toString();
return withAccess(
(t, candidates) -> getCandidatePropertyType(t, propString, candidates),
() -> newAccess(property.toString(), null));
}
public @Nullable VmReference withSubscriptAccess(Object key) {
return withAccess(
(t, candidates) -> getCandidateSubscriptType(t, key, candidates),
() -> newAccess(null, key));
}
@TruffleBoundary
private @Nullable VmReference withAccess(
BiConsumer<PType, Set<PType>> checkCandidate, Supplier<VmTyped> makeAccess) {
Set<PType> candidates = new HashSet<>();
for (var t : iterateTypes(referentType)) {
checkCandidate.accept(t, candidates);
}
if (candidates.isEmpty()) {
return null; // no valid access found
}
return new VmReference(domain, data, path.append(makeAccess.get()), minimizeTypes(candidates));
}
@SuppressWarnings("DuplicatedCode")
private static void getCandidatePropertyType(PType type, String property, Set<PType> result) {
if (type == PType.UNKNOWN) {
result.add(type);
return;
}
// restriction: only class types can have their properties referenced
if (!(type instanceof PType.Class clazz)) return;
// restriction: cannot reference properties of external classes
if (clazz.getPClass().isExternal()) return;
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
// restriction: cannot reference Dynamic.default
if (!property.equals("default")) result.add(PType.UNKNOWN);
return;
}
// restriction: cannot reference Listing/Mapping.default
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|| clazz.getPClass().getInfo() == PClassInfo.Mapping) {
return;
}
// restriction: cannot reference Module.output.
// generalized: properties originally defined in external classes; the only extant example.
// This is implemented specifically because this is the only case where an external class
// containing a property can be subclassed.
// And this can't check prop.getOwner().isExternal() because fully overriding the property with
// a new type annotation means the owner isn't Module.
if (clazz.getPClass().isSubclassOf(BaseModule.getModuleClass().export())
&& property.equals("output")) return;
var prop = clazz.getPClass().getAllProperties().get(property);
// restriction: cannot reference external properties
if (prop == null || prop.isExternal()) {
return;
}
normalizeTypes(prop.getType(), clazz.getPClass().getModuleClass(), result);
}
@SuppressWarnings("DuplicatedCode")
private static void getCandidateSubscriptType(PType type, Object key, Set<PType> result) {
if (type == PType.UNKNOWN) {
result.add(type);
return;
}
if (!(type instanceof PType.Class clazz)) {
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) {
result.add(PType.UNKNOWN);
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Listing
|| clazz.getPClass().getInfo() == PClassInfo.List) {
if (key instanceof Long) {
normalizeTypes(clazz.getTypeArguments().get(0), clazz.getPClass().getModuleClass(), result);
}
return;
}
if (clazz.getPClass().getInfo() == PClassInfo.Mapping
|| clazz.getPClass().getInfo() == PClassInfo.Map) {
var typeArgs = clazz.getTypeArguments();
var keyTypes = normalizeTypes(typeArgs.get(0), clazz.getPClass().getModuleClass());
for (var kt : iterateTypes(keyTypes)) {
if (kt == PType.UNKNOWN
|| (kt instanceof PType.Class klazz
&& klazz.getPClass().getInfo() == PClassInfo.forValue(VmValue.export(key)))
|| (kt instanceof PType.StringLiteral stringLiteral
&& stringLiteral.getLiteral().equals(key))) {
normalizeTypes(typeArgs.get(1), clazz.getPClass().getModuleClass(), result);
return;
}
}
}
}
/**
* Tells if this reference's referent type is a subtype of {@code type}. Does not check domain.
*/
public boolean referentTypeIsSubtypeOf(PType type, PClass moduleClass) {
// fast path: if referent is unknown it can match any type check
if (referentType == PType.UNKNOWN) {
return true;
}
var checkType = normalizeTypes(type, moduleClass);
// fast path: short circuit if any referent is accepted
if (checkType == PType.UNKNOWN || isClass(checkType, BaseModule.getAnyClass().export())) {
return true;
}
// fast path: short circuit if nothing is accepted
if (checkType == PType.NOTHING) {
return false;
}
return isSubtype(referentType, checkType);
}
private static boolean containsClass(Set<PType> types, PClass pClass) {
for (var t : types) {
if (isClass(t, pClass)) return true;
}
return false;
}
private static boolean isClass(PType t, PClass pClass) {
return t instanceof PType.Class clazz && clazz.getPClass() == pClass;
}
private static boolean isSubtype(PType a, PType b) {
// checks if A is a subtype of B
// cases (A -> B)
// * A == B
// * StringLiteral -> StringLiteral: if literals are the same
// * StringLiteral -> Class: B is String
// * Char Alias -> Char Alias, StringLiteral (known single character)
// * Int Alias -> Class: B is a subtype of Number (Int|Float|Number)
// * Int Alias -> Alias
// * same alias
// * Int8 is Int16|Int32
// * Int16 is Int32
// * UInt8 is Int16|Int32|Uint16|UInt32|UInt
// * UInt16 is Int32|UInt32|UInt
// * UInt32 is UInt
// * Class -> Class: if same class or A is a subclass of B
// * if type args are present, must have equal number of them
// * for each pair of type args, check variance
// * invariant: A_i must be identical to B_i
// * covariant: A_i must be a subtype of B_i
// * contravariant: B_i must be a subtype of A_i
// * Union -> Union: Each elem of A must be a subtype of at least one elem of B
// * Non-union -> Union: A must be a subtype of at least one elem of B
if (a == b) return true;
if (a instanceof PType.StringLiteral aStr) {
if (b instanceof PType.StringLiteral bStr) {
return aStr.getLiteral().equals(bStr.getLiteral());
} else if (b instanceof PType.Class bClass) {
return bClass.getPClass() == BaseModule.getStringClass().export();
}
} else if (a instanceof PType.Alias aAlias) {
var aa = aAlias.getTypeAlias();
if (isIntTypeAlias(aa)) {
// special casing for stdlib Int typealiases
if (b instanceof PType.Class bClass) {
// A is an int alias, B is a Number (sub)class
return bClass.getPClass().isSubclassOf(BaseModule.getNumberClass().export());
} else if (b instanceof PType.Alias bAlias) {
var bb = bAlias.getTypeAlias();
if (aa == bb) {
return true;
}
if (aa == BaseModule.getInt8TypeAlias().export()) {
return bb == BaseModule.getInt16TypeAlias().export()
|| bb == BaseModule.getInt32TypeAlias().export();
} else if (aa == BaseModule.getInt16TypeAlias().export()) {
return bb == BaseModule.getInt32TypeAlias().export();
} else if (aa == BaseModule.getUInt8TypeAlias().export()) {
return bb == BaseModule.getInt16TypeAlias().export()
|| bb == BaseModule.getInt32TypeAlias().export()
|| bb == BaseModule.getUInt16TypeAlias().export()
|| bb == BaseModule.getUInt32TypeAlias().export()
|| bb == BaseModule.getUIntTypeAlias().export();
} else if (aa == BaseModule.getUInt16TypeAlias().export()) {
return bb == BaseModule.getInt32TypeAlias().export()
|| bb == BaseModule.getUInt32TypeAlias().export()
|| bb == BaseModule.getUIntTypeAlias().export();
} else if (aa == BaseModule.getUInt32TypeAlias().export()) {
return bb == BaseModule.getUIntTypeAlias().export();
}
}
}
} else if (a instanceof PType.Class aClass && b instanceof PType.Class bClass) {
if (!aClass.getPClass().isSubclassOf(bClass.getPClass())) {
return false;
}
var aArgs = aClass.getTypeArguments();
var bArgs = bClass.getTypeArguments();
var bParams = bClass.getPClass().getTypeParameters();
if (aArgs.size() != bArgs.size()) {
return false;
}
// check variance of type args pairwise
for (var i = 0; i < aArgs.size(); i++) {
if (!switch (bParams.get(i).getVariance()) {
case INVARIANT -> aArgs.get(i) == bArgs.get(i);
case COVARIANT -> isSubtype(aArgs.get(i), bArgs.get(i));
case CONTRAVARIANT -> isSubtype(bArgs.get(i), aArgs.get(i));
}) {
return false;
}
}
return true;
} else if (b instanceof PType.Union bUnion) {
if (a instanceof PType.Union aUnion) {
a:
for (var aElem : aUnion.getElementTypes()) {
for (var bElem : bUnion.getElementTypes()) {
if (isSubtype(aElem, bElem)) continue a;
}
return false;
}
return true;
} else {
for (var bElem : bUnion.getElementTypes()) {
if (isSubtype(a, bElem)) return true;
}
}
}
return false;
}
private static boolean isIntTypeAlias(TypeAlias t) {
return t == BaseModule.getInt8TypeAlias().export()
|| t == BaseModule.getInt16TypeAlias().export()
|| t == BaseModule.getInt32TypeAlias().export()
|| t == BaseModule.getUInt8TypeAlias().export()
|| t == BaseModule.getUInt16TypeAlias().export()
|| t == BaseModule.getUInt32TypeAlias().export()
|| t == BaseModule.getUIntTypeAlias().export();
}
private static boolean isPreservedTypeAlias(TypeAlias t) {
return isIntTypeAlias(t);
}
@Override
public VmClass getVmClass() {
return RefModule.getReferenceClass();
}
@Override
public void force(boolean allowUndefinedValues) {
if (forced) return;
forced = true;
domain.force(allowUndefinedValues);
VmValue.force(data, allowUndefinedValues);
for (var elem : path) {
elem.force(allowUndefinedValues);
}
}
@Override
public Reference export() {
var pathList = new ArrayList<Composite>(path.size());
for (var elem : path) {
pathList.add(elem.export());
}
return new Reference(domain.export(), VmValue.export(data), pathList, getReferentType());
}
public PType exportType() {
return new PType.Class(
RefModule.getReferenceClass().export(),
new PType.Class(domain.getVmClass().export()),
getReferentType());
}
@Override
public void accept(VmValueVisitor visitor) {
visitor.visitReference(this);
}
@Override
public <T> T accept(VmValueConverter<T> converter, Iterable<Object> path) {
return converter.convertReference(this, path);
}
@Override
public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof VmReference that)) {
return false;
}
return domain.equals(that.domain)
&& data.equals(that.data)
&& path.equals(that.path)
&& referentType.equals(that.referentType);
}
@Override
public int hashCode() {
int result = domain.hashCode();
result = 31 * result + data.hashCode();
result = 31 * result + path.hashCode();
result = 31 * result + referentType.hashCode();
return result;
}
// in-language calls _should_ all go through `ToStringNode`.
// however, some calls escape through to here currently (e.g. `Listing.join`).
@Override
public String toString() {
var toStringMethod = getVmClass().getDeclaredMethod(Identifier.TO_STRING);
assert toStringMethod != null;
var callNode = DirectCallNode.create(toStringMethod.getCallTarget());
return (String) callNode.call(this, getVmClass().getPrototype());
}
}
@@ -32,7 +32,6 @@ import org.pkl.core.TypeParameter;
import org.pkl.core.ast.VmModifier;
import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode;
import org.pkl.core.ast.type.TypeNode.ReferenceTypeNode;
import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
import org.pkl.core.util.LateInit;
@@ -178,8 +177,7 @@ public final class VmTypeAlias extends VmValue {
}
@TruffleBoundary
public TypeNode instantiate(
TypeNode[] typeArgumentNodes, SourceSection typeAliasTypeNodeSourceSection) {
public TypeNode instantiate(TypeNode[] typeArgumentNodes) {
// Cloning the type node means that the entire type check remains within a single root node,
// which should be good for interpreted and compiled performance alike:
// * Fewer root nodes to call
@@ -201,13 +199,6 @@ public final class VmTypeAlias extends VmValue {
}
return true;
});
clone.accept(
node -> {
if (node instanceof ReferenceTypeNode referenceTypeNode) {
referenceTypeNode.validateTypeArguments(typeAliasTypeNodeSourceSection);
}
return true;
});
return clone;
}
@@ -237,8 +228,7 @@ public final class VmTypeAlias extends VmValue {
simpleName,
getModuleName(),
qualifiedName,
typeParameters,
module.getVmClass().export());
typeParameters);
for (var parameter : typeParameters) {
parameter.initOwner(__pTypeAlias);
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024 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.
@@ -40,7 +40,6 @@ import com.oracle.truffle.api.dsl.TypeSystem;
VmRegex.class,
VmTypeAlias.class,
VmObjectLike.class,
VmReference.class,
VmValue.class,
VmValue.class
})
public class VmTypes {}
@@ -85,8 +85,6 @@ public interface VmValueConverter<T> {
T convertFunction(VmFunction value, Iterable<Object> path);
T convertReference(VmReference value, Iterable<Object> path);
/** Returns with an empty identifier if the second value is a RenderDirective */
Pair<Identifier, T> convertProperty(ClassProperty property, Object value, Iterable<Object> path);
@@ -271,30 +271,6 @@ public final class VmValueRenderer {
append("null");
}
@Override
public void visitReference(VmReference value) {
contexts.push(Context.EXPLICIT);
append("Reference(");
visit(value.getDomain());
append(", ");
append(value.getReferentType());
append(", ");
visit(value.getData());
append(")");
for (var elem : value.getPath()) {
var property = VmUtils.readMember(elem, Identifier.PROPERTY);
if (property instanceof String propName) {
append(".");
writeIdentifier(propName);
} else {
append("[");
visit(VmUtils.readMember(elem, Identifier.KEY));
append("]");
}
}
contexts.pop();
}
private void append(Object value) {
builder.append(value);
checkLengthLimit();
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 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.
@@ -60,8 +60,6 @@ public interface VmValueVisitor {
void visitFunction(VmFunction value);
void visitReference(VmReference value);
default void visit(Object value) {
Objects.requireNonNull(value, "Value to be visited must be non-null.");
@@ -34,7 +34,6 @@ import org.pkl.core.runtime.VmListing;
import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.runtime.VmSet;
import org.pkl.core.runtime.VmTypeAlias;
import org.pkl.core.runtime.VmTyped;
@@ -398,11 +397,6 @@ public abstract class AbstractRenderer implements VmValueVisitor {
currSourceSection = prevSourceSection;
}
@Override
public void visitReference(VmReference value) {
cannotRenderTypeAddConverter(value);
}
protected void cannotRenderTypeAddConverter(VmValue value) {
var builder =
new VmExceptionBuilder()
@@ -46,7 +46,6 @@ public final class PklConverter implements VmValueConverter<Object> {
private final @Nullable VmFunction nullConverter;
private final @Nullable VmFunction classConverter;
private final @Nullable VmFunction typeAliasConverter;
private final @Nullable VmFunction referenceConverter;
private PklConverter(
VmMapping converters, VmMapping convertPropertyTransformers, Object rendererOrParser) {
@@ -77,7 +76,6 @@ public final class PklConverter implements VmValueConverter<Object> {
nullConverter = typeConverters.get(BaseModule.getNullClass());
classConverter = typeConverters.get(BaseModule.getClassClass());
typeAliasConverter = typeConverters.get(BaseModule.getTypeAliasClass());
referenceConverter = typeConverters.get(RefModule.getReferenceClass());
}
public static final PklConverter NOOP =
@@ -201,11 +199,6 @@ public final class PklConverter implements VmValueConverter<Object> {
return doConvert(value, path, nullConverter);
}
@Override
public Object convertReference(VmReference value, Iterable<Object> path) {
return doConvert(value, path, referenceConverter);
}
@Override
public Pair<Identifier, Object> convertProperty(
ClassProperty property, Object value, Iterable<Object> path) {
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024 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.
@@ -15,7 +15,6 @@
*/
package org.pkl.core.stdlib.base;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import java.net.URI;
@@ -31,16 +30,15 @@ public final class ModuleClassNodes {
@Specialization
@TruffleBoundary
protected VmList eval(VmObjectLike self, VmObjectLike other) {
if (!self.isModuleObject()) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().evalError("expectedModuleAsReceiver").build();
}
if (!other.isModuleObject()) {
CompilerDirectives.transferToInterpreter();
throw exceptionBuilder().evalError("expectedModuleAsArgument").build();
}
var selfKey = VmUtils.getModuleInfo(self).getModuleKey();
var selfUri = selfKey.getUri();
if (!other.isModuleObject()) {
throw exceptionBuilder()
.evalError("expectedModule")
// No meaningful SourceSection available, in this case.
.build();
}
var otherKey = VmUtils.getModuleInfo(other).getModuleKey();
var otherUri = otherKey.getUri();
@@ -59,7 +59,6 @@ import org.pkl.core.runtime.VmMap;
import org.pkl.core.runtime.VmMapping;
import org.pkl.core.runtime.VmNull;
import org.pkl.core.runtime.VmPair;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.runtime.VmRegex;
import org.pkl.core.runtime.VmSet;
import org.pkl.core.runtime.VmTyped;
@@ -555,12 +554,6 @@ public final class RendererNodes {
builder.append(value);
}
@Override
public void visitReference(VmReference value) {
writePropertyName();
builder.append(value);
}
/**
* Resolves types for the purpose of protobuf rendering. "Sees through" nullable types and type
* aliases, simplifies variations of {@code Int} and {@code String} types (literate string
@@ -1,33 +0,0 @@
/*
* 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.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.VmTyped;
import org.pkl.core.stdlib.ExternalMethod3Node;
public class RefNodes {
public abstract static class Reference extends ExternalMethod3Node {
@Specialization
protected VmReference eval(
VirtualFrame frame, VmTyped self, VmTyped domain, VmClass clazz, Object data) {
return new VmReference(domain, clazz, data);
}
}
}
@@ -1,46 +0,0 @@
/*
* 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.stdlib.ref;
import com.oracle.truffle.api.dsl.Specialization;
import org.pkl.core.runtime.VmList;
import org.pkl.core.runtime.VmReference;
import org.pkl.core.stdlib.ExternalMethod0Node;
public class ReferenceNodes {
private ReferenceNodes() {}
public abstract static class getDomain extends ExternalMethod0Node {
@Specialization
protected Object eval(VmReference self) {
return self.getDomain();
}
}
public abstract static class getData extends ExternalMethod0Node {
@Specialization
protected Object eval(VmReference self) {
return self.getData();
}
}
public abstract static class getPath extends ExternalMethod0Node {
@Specialization
protected VmList eval(VmReference self) {
return VmList.create(self.getPath());
}
}
}
@@ -1,4 +0,0 @@
@NullMarked
package org.pkl.core.stdlib.ref;
import org.jspecify.annotations.NullMarked;
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024 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.
@@ -32,10 +32,7 @@ public final class ReflectNodes {
@TruffleBoundary
protected VmTyped eval(VmTyped self, VmTyped module) {
if (!module.isModuleObject()) {
throw exceptionBuilder()
.evalError("expectedModuleAsArgument")
.withLocation(getArg1Node())
.build();
throw exceptionBuilder().evalError("expectedModule").withLocation(getArg1Node()).build();
}
return module.getModuleMirror();
}
@@ -33,7 +33,6 @@ public enum PklBinaryCode {
TYPEALIAS((byte) 0x0D),
FUNCTION((byte) 0x0E),
BYTES((byte) 0x0F),
REFERENCE((byte) 0x20),
PROPERTY((byte) 0x10),
ENTRY((byte) 0x11),
@@ -66,7 +65,6 @@ public enum PklBinaryCode {
case 0x0D -> PklBinaryCode.TYPEALIAS;
case 0x0E -> PklBinaryCode.FUNCTION;
case 0x0F -> PklBinaryCode.BYTES;
case 0x20 -> PklBinaryCode.REFERENCE;
case 0x10 -> PklBinaryCode.PROPERTY;
case 0x11 -> PklBinaryCode.ENTRY;
@@ -203,10 +203,7 @@ Class `{0}` does not have type parameters.
notASubclassOfTyped=\
Class `{0}` is not a subtype of `Typed`.
expectedModuleAsReceiver=\
Expected a module as the receiver, but got an object that amends a module.
expectedModuleAsArgument=\
expectedModule=\
Expected a module as argument, but got an object that amends a module.
wrongTypeArgumentCount=\
@@ -289,6 +286,11 @@ External members cannot have a body.
abstractMemberCannotHaveBody=\
Abstract members cannot have a body.
abstractMemberInNonAbstractClass=\
Cannot define an abstract member in a non-abstract class.\n\
\n\
A member can only be `abstract` if its enclosing class is also `abstract`.
methodNotDefined1=\
Method `{0}` is not defined for argument type `{1}`.
@@ -388,9 +390,6 @@ Cannot find property `{0}` in object of type `{1}`.\n\
Did you mean any of the following?\n\
{2}
cannotFindPropertyInReference=\
Cannot find property `{0}` in object of type `{1}`.
cannotFindMethodInScope=\
Cannot find method `{0}`.
@@ -862,9 +861,6 @@ Package URIs must have an authority component.\n\
\n\
For example, `example.com` in URI `project://example.com/my/package@1.0.0`.
invalidRelativePathInPackageUri=\
Package URIs cannot contain `..` segments in the URI's "path" component.
unexpectedChecksumInPackageUri=\
Did not expect to find a checksum component in this package URI.
@@ -1181,5 +1177,12 @@ 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.
noImplementationForAbstractProperty=\
Class `{0}` should either be declared `abstract`, or should implement property `{1}`.
noImplementationForAbstractMethod=\
Class `{0}` should either be declared `abstract`, or should implement method `{1}`.
noImplementationForAbstractMembers=\
Class `{0}` should either be declared `abstract`, or implement the following members:\n\
{1}
@@ -0,0 +1,3 @@
abstract module Foo
abstract bar: Int
@@ -1 +0,0 @@
foo: String
@@ -1,7 +0,0 @@
open module ReferencedModuleWithOutputOverride
foo: String
hidden output: ModuleOutput = new {
text = foo
}
@@ -1,134 +0,0 @@
import "pkl:math"
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String =
let (data = reference.getData())
let (root = if (data is Resource) data.name else data.toString())
let (
path =
reference
.getPath()
.map((elem) -> if (elem.isProperty) ".\(elem.property)" else "[\(elem.key)]")
)
"${\(root)\(path.join(""))}"
}
local const d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
abstract class Resource {
name: String
hidden fixed $: Ref<Resource>
}
class A extends Resource {
id: String
hidden outputs: AProperties
hidden fixed $: Ref<A> = ref.Reference(d, A, this)
}
/// Test doc comment
class AProperties {
foo: Int
someMapping: Mapping<String, Int?>
someMap: Map<MapKey, Int>
someListing: Listing<Int>
someList: List<Int>
nonInt: String
}
class B extends Resource {
id: String
hidden outputs: BProperties
hidden fixed $: Ref<B> = ref.Reference(d, B, this)
}
class BProperties {
foo: String
someMapping: Mapping<String, String?>
someMap: Map<MapKey, String>
someListing: Listing<String>
someList: List<String>
nonString: Int
}
class MapKey {
k: Int
function toString(): String = "key:\(k)"
}
class K {
aId: String | *Ref<String>?
bId: String | *Ref<String>?
aProperties: AProperties | *Ref<AProperties>?
bProperties: BProperties | *Ref<BProperties>?
aValues: Listing<Int | Ref<Int?>>?
bValues: Listing<String | Ref<String?>>?
splitUnion: Ref<Listing<String> | Listing<Int>>?
}
a: A = new {
name = "a"
id = "some-a-value"
}
b: B = new {
name = "b"
id = "some-b-value"
}
aOrB: A | B = a
aRef: Ref<A> = a.$
bRef: Ref<B> = b.$
unknownRef: Ref<A | B> = aRef
unknownRef2: Ref<A> | Ref<B> = aRef
unknownRef3: Ref<B | A> = aOrB.$
k: K = new {
aId = aRef.id
bId = bRef.id
aProperties = aRef.outputs
bProperties = bRef.outputs
aValues {
aRef.outputs.foo
aRef.outputs.someMapping["key"]
aRef.outputs.someMap[new MapKey { k = 123 }]
aRef.outputs.someListing[0]
aRef.outputs.someList[math.maxInt]
bRef.outputs.nonString
}
bValues {
bRef.outputs.foo
bRef.outputs.someMapping["key"]
bRef.outputs.someMap[new MapKey { k = 123 }]
bRef.outputs.someListing[0]
bRef.outputs.someList[math.maxInt]
aRef.outputs.nonInt
}
}
j: K = new {
local aRef2 = unknownRef as Ref<A>
aProperties = aRef2.outputs
splitUnion = unknownRef.outputs.someListing
}
refInterpolation = "\(aRef.outputs.someListing[1])"
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>>
class TypeHolder {
prop: Listing<String | Boolean | Number>
}
output {
renderer {
converters {
[ref.Reference] = (it) -> it.toString()
}
}
}
@@ -1,55 +0,0 @@
amends "../snippetTest.pkl"
import "pkl:ref"
local class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = reference.getData().toString()
}
local const d: D = new {}
local typealias Ref<T> = ref.Reference<D, T>
local class Holder {
num: Int
text: String
lit: "literal"
small: Int8
optional: Mapping<String, Int?>
listing: Listing<Int>
union: Listing<String> | Listing<Int>
}
local const h: Holder = new {
num = 1
text = "t"
lit = "literal"
small = 8
optional { ["k"] = 5 }
listing { 1; 2 }
union = new Listing<Int> { 1 }
}
local hRef1: Ref<Holder> = ref.Reference(d, Holder, h)
local hRef2: Ref<Holder> = ref.Reference(d, Holder, h)
facts {
["equality"] {
hRef1 == hRef2
hRef1.num == hRef2.num
hRef1.lit == hRef2.lit
hRef1.small == hRef2.small
hRef1.optional["k"] == hRef2.optional["k"]
hRef1.listing[0] == hRef2.listing[0]
hRef1.union == hRef2.union
}
["inequality"] {
hRef1.num != hRef2.text
}
["set deduplication"] {
Set(hRef1.num, hRef2.num).length == 1
Set(hRef1.union, hRef2.union).length == 1
Set(hRef1.num, hRef2.text).length == 2
}
}
@@ -0,0 +1,5 @@
class Foo {
abstract bar: Int
}
res = new Foo { bar = 5 }
@@ -0,0 +1 @@
abstract foo: Int
@@ -0,0 +1,19 @@
abstract class AbstractProperty {
abstract foo: Int
}
abstract class AbstractMethod {
abstract function foo(): Int
}
abstract class AbstractPropertiesAndMethods {
abstract foo: Int
abstract bar: Int
abstract function foo(): Int
abstract function bar(): Int
}
class MyClass extends AbstractProperty {
}
foo: MyClass
@@ -0,0 +1,9 @@
abstract class AbstractMethod {
abstract function foo(): Int
}
class MyClass extends AbstractMethod {
}
foo: MyClass
@@ -0,0 +1,10 @@
abstract class AbstractPropertiesAndMethods {
abstract foo: Int
abstract bar: Int
abstract function foo(): Int
abstract function bar(): Int
}
class MyClass extends AbstractPropertiesAndMethods {}
foo: MyClass
@@ -0,0 +1 @@
extends "../../input-helper/classes/AbstractModule.pkl"
@@ -0,0 +1,5 @@
class Foo {
abstract function bar(): Int
}
res = new Foo {}
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
test = ref.Reference(d, String, "") as Ref<Int>
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, 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<D, 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<D, 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 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, Mapping, "").default
@@ -1,8 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, Listing, "").default
@@ -1,8 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, Dynamic, "").default
@@ -1,10 +0,0 @@
import "pkl:ref"
import ".../input-helper/errors/ReferencedModule.pkl"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, ReferencedModule.getClass(), "").output
@@ -1,10 +0,0 @@
import "pkl:ref"
import ".../input-helper/errors/ReferencedModuleWithOutputOverride.pkl"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, ReferencedModuleWithOutputOverride.getClass(), "").output
@@ -1,12 +0,0 @@
import "pkl:ref"
import ".../input-helper/errors/ReferencedModuleWithOutputOverride.pkl"
class ModuleSubclass extends ReferencedModuleWithOutputOverride
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, ModuleSubclass, "").output
@@ -1,12 +0,0 @@
import "pkl:ref"
class A {
foo: String | Int | Boolean
}
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
test = ref.Reference(d, A, "").foo as ref.Reference<D, Int | Boolean>
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, 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>
@@ -1,15 +0,0 @@
import "pkl:ref"
class A {
foo: String | Int | Boolean
}
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
local test = ref.Reference(d, A, "").foo
testInterpolation = "test:\(test)"
// this tests that the interpolation appears in the output when referenceToString throws
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, 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>>
@@ -1,12 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
test = ref.Reference(d, String, "") as Ref<Alias1?>
typealias Alias1 = Int | Alias2?
typealias Alias2 = Boolean
@@ -1,14 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
class D2 extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d2: D2 = new {}
test = ref.Reference(d2, String, "") as Ref<String>
@@ -1,13 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
class A {
b: String
}
test = ref.Reference(d, String, new A {}).c
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, Any>): String = throw("not supported")
}
local d: D = new {}
typealias Ref<T> = ref.Reference<D, T>
test = ref.Reference(d, List, List())["hi"]
@@ -1,9 +0,0 @@
import "pkl:ref"
class D extends ref.Domain {
function renderReference(reference: ref.Reference<D, 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<D, 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>
@@ -1,7 +0,0 @@
local objectThatAmendsModule = (module) {
// add a local property to force this into a new object amends;
// empty object amends are optimized away.
local qux = 1
}
res = objectThatAmendsModule.relativePathTo(module)
@@ -1,8 +0,0 @@
local objectThatAmendsModule = (module) {
// add a local property to force this into a new object amends;
// empty object amends are optimized away.
local qux = 1
}
res = module.relativePathTo(objectThatAmendsModule)
@@ -1,2 +0,0 @@
// relative path in package URI
res = import("package://localhost:0/%2e%2e/birds@0.5.0#/catalog/Bird.pkl")
@@ -1,60 +0,0 @@
a {
name = "a"
id = "some-a-value"
}
b {
name = "b"
id = "some-b-value"
}
aOrB {
name = "a"
id = "some-a-value"
}
aRef = "${a}"
bRef = "${b}"
unknownRef = "${a}"
unknownRef2 = "${a}"
unknownRef3 = "${a}"
k {
aId = "${a.id}"
bId = "${b.id}"
aProperties = "${a.outputs}"
bProperties = "${b.outputs}"
aValues {
"${a.outputs.foo}"
"${a.outputs.someMapping[key]}"
"${a.outputs.someMap[key:123]}"
"${a.outputs.someListing[0]}"
"${a.outputs.someList[9223372036854775807]}"
"${b.outputs.nonString}"
}
bValues {
"${b.outputs.foo}"
"${b.outputs.someMapping[key]}"
"${b.outputs.someMap[key:123]}"
"${b.outputs.someListing[0]}"
"${b.outputs.someList[9223372036854775807]}"
"${a.outputs.nonInt}"
}
splitUnion = null
}
j {
aId = null
bId = null
aProperties = "${a.outputs}"
bProperties = null
aValues = null
bValues = null
splitUnion = "${a.outputs.someListing}"
}
refInterpolation = "${a.outputs.someListing[1]}"
kInterpolation = "new K { aId = Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).id; bId = Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).id; aProperties = Reference(new D {}, reference#AProperties, new A { name = \"a\"; id = \"some-a-value\" }).outputs; bProperties = Reference(new D {}, reference#BProperties, new B { name = \"b\"; id = \"some-b-value\" }).outputs; aValues { Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.foo; Reference(new D {}, Int | Null, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someListing[0]; Reference(new D {}, Int, new A { name = \"a\"; id = \"some-a-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, Int, new B { name = \"b\"; id = \"some-b-value\" }).outputs.nonString }; bValues { Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.foo; Reference(new D {}, Null | String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMapping[\"key\"]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someMap[new MapKey { k = 123 }]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someListing[0]; Reference(new D {}, String, new B { name = \"b\"; id = \"some-b-value\" }).outputs.someList[9223372036854775807]; Reference(new D {}, String, new A { name = \"a\"; id = \"some-a-value\" }).outputs.nonInt }; splitUnion = null }"
aValuesJoined = """
${a.outputs.foo}
${a.outputs.someMapping[key]}
${a.outputs.someMap[key:123]}
${a.outputs.someListing[0]}
${a.outputs.someList[9223372036854775807]}
${b.outputs.nonString}
"""
typeArgs = "${null.prop}"
@@ -1,19 +0,0 @@
facts {
["equality"] {
true
true
true
true
true
true
true
}
["inequality"] {
true
}
["set deduplication"] {
true
true
true
}
}
@@ -0,0 +1,8 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
x | abstract bar: Int
^^^^^^^^
at abstractMemberInNonAbstractClass#Foo (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractClass.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -0,0 +1,8 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
x | abstract foo: Int
^^^^^^^^
at abstractMemberInNonAbstractModule (file:///$snippetsDir/input/errors/abstractMemberInNonAbstractModule.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -0,0 +1,10 @@
–– Pkl Error ––
Class `abstractMemberNotImplemented1#MyClass` should either be declared `abstract`, or should implement property `foo`.
xx | class MyClass extends AbstractProperty {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at abstractMemberNotImplemented1#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented1.pkl)
xx | foo: MyClass
^^^^^^^
at abstractMemberNotImplemented1 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented1.pkl)
@@ -0,0 +1,10 @@
–– Pkl Error ––
Class `abstractMemberNotImplemented2#MyClass` should either be declared `abstract`, or should implement method `foo`.
x | class MyClass extends AbstractMethod {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at abstractMemberNotImplemented2#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented2.pkl)
x | foo: MyClass
^^^^^^^
at abstractMemberNotImplemented2 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented2.pkl)
@@ -0,0 +1,14 @@
–– Pkl Error ––
Class `abstractMemberNotImplemented3#MyClass` should either be declared `abstract`, or implement the following members:
* property `foo`
* property `bar`
* method `foo`
* method `bar`
x | class MyClass extends AbstractPropertiesAndMethods {}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at abstractMemberNotImplemented3#MyClass (file:///$snippetsDir/input/errors/abstractMemberNotImplemented3.pkl)
xx | foo: MyClass
^^^^^^^
at abstractMemberNotImplemented3 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented3.pkl)
@@ -0,0 +1,6 @@
–– Pkl Error ––
Class `abstractMemberNotImplemented4` should either be declared `abstract`, or should implement property `bar`.
x | extends "../../input-helper/classes/AbstractModule.pkl"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at abstractMemberNotImplemented4 (file:///$snippetsDir/input/errors/abstractMemberNotImplemented4.pkl)
@@ -0,0 +1,8 @@
–– Pkl Error ––
Cannot define an abstract member in a non-abstract class.
x | abstract function bar(): Int
^^^^^^^^
at abstractMethodInNonAbstractClass#Foo (file:///$snippetsDir/input/errors/abstractMethodInNonAbstractClass.pkl)
A member can only be `abstract` if its enclosing class is also `abstract`.
@@ -0,0 +1,10 @@
–– Pkl Error ––
Class `abstractMethodsNotImplemented#MyClass` either needs to be abstract, or should implement property `foo`.
x | class MyClass extends MyAbstractClass {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at abstractMethodsNotImplemented#MyClass (file:///$snippetsDir/input/errors/abstractMethodsNotImplemented.pkl)
x | foo: MyClass
^^^^^^^
at abstractMethodsNotImplemented (file:///$snippetsDir/input/errors/abstractMethodsNotImplemented.pkl)
@@ -20,7 +20,6 @@ pkl:pklbinary
pkl:platform
pkl:Project
pkl:protobuf
pkl:ref
pkl:reflect
pkl:release
pkl:semver
@@ -1,15 +0,0 @@
–– Pkl Error ––
Expected value of type `pkl.ref#Reference<reference1#D, Int>`, but got type `pkl.ref#Reference<reference1#D, String>`.
Value: Reference(new D {}, String, "")
x | typealias Ref<T> = ref.Reference<D, T>
^^^^^^^^^^^^^^^^^^^
at reference1#test (file:///$snippetsDir/input/errors/reference1.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)
@@ -1,14 +0,0 @@
–– Pkl Error ––
`Reference` referent type argument may not include type constraints.
x | test = ref.Reference(d, String, "") as Ref<Listing<String(true)>>
^^^^^^^^^^^^^^^^^^^^^^^^^^
at reference10#test (file:///$snippetsDir/input/errors/reference10.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)

Some files were not shown because too many files have changed in this diff Show More