diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java index f0259f573..4ced12cbe 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java @@ -23,7 +23,11 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; +import org.jspecify.annotations.Nullable; import org.pkl.core.runtime.*; +import org.pkl.core.runtime.VmReference.VmReferenceAccessError; +import org.pkl.core.runtime.VmReference.VmReferenceAccessErrorType; +import org.pkl.core.util.ErrorMessages; @NodeInfo(shortName = "[]") public abstract class SubscriptNode extends BinaryExpressionNode { @@ -100,18 +104,32 @@ public abstract class SubscriptNode extends BinaryExpressionNode { return readMember(dynamic, key, callNode); } + private @Nullable String getReferenceHint( + VmReference reference, VmReferenceAccessError err, Object key) { + var myType = reference.getReferentType(); + if (err.getErrorType() != VmReferenceAccessErrorType.CANNOT_FIND_MEMBER) { + return null; + } + return err.getType().equals(myType) + ? ErrorMessages.create("operatorNotDefined2", getShortName(), myType, VmUtils.getClass(key)) + : ErrorMessages.create( + "operatorNotDefined3", getShortName(), myType, err.getType(), VmUtils.getClass(key)); + } + @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(); + try { + return reference.withSubscriptAccess(key); + } catch (VmReferenceAccessError err) { + CompilerDirectives.transferToInterpreter(); + throw exceptionBuilder() + .evalError( + "operatorNotDefined2", getShortName(), reference.exportType(), VmUtils.getClass(key)) + .withProgramValue("Left operand", reference) + .withProgramValue("Right operand", key) + .withHint(getReferenceHint(reference, err, key)) + .build(); + } } @Specialization diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadPropertyNode.java index db8014103..9746b9ef2 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ReadPropertyNode.java @@ -24,10 +24,13 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; import org.jspecify.annotations.Nullable; +import org.pkl.core.PType; import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.MemberLookupMode; import org.pkl.core.ast.member.ClassProperty; import org.pkl.core.runtime.*; +import org.pkl.core.runtime.VmReference.VmReferenceAccessError; +import org.pkl.core.util.ErrorMessages; @NodeInfo(shortName = ".") @ImportStatic(BaseModule.class) @@ -61,16 +64,38 @@ public abstract class ReadPropertyNode extends ExpressionNode { this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false); } + protected String getReferenceErrorHint(VmReference reference, VmReferenceAccessError err) { + var myType = reference.getReferentType(); + return switch (err.getErrorType()) { + case CANNOT_FIND_MEMBER -> + err.getType().equals(myType) + ? ErrorMessages.create("cannotFindPropertyInType", propertyName, myType) + : ErrorMessages.create( + "cannotFindPropertyInType2", propertyName, myType, err.getType()); + case EXTERNAL_MEMBER -> + ErrorMessages.create("cannotReferenceExternalProperty", propertyName, err.getType()); + case DEFAULT_MEMBER -> + ErrorMessages.create( + "cannotReferenceDefaultProperty", + ((PType.Class) err.getType()).getPClass().getSimpleName()); + case EXTERNAL_CLASS -> + ErrorMessages.create( + "cannotReferencePropertyInExternalClass", propertyName, err.getType()); + }; + } + @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(); + try { + return receiver.withPropertyAccess(propertyName); + } catch (VmReferenceAccessError err) { + CompilerDirectives.transferToInterpreter(); + throw exceptionBuilder() + .evalError("cannotFindPropertyInObjectNoHint", propertyName, receiver.exportType()) + .withHint(getReferenceErrorHint(receiver, err)) + .build(); + } } // This method effectively covers `VmObject receiver` but is implemented in a more diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmReference.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmReference.java index 853a8f248..09e56ea5b 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmReference.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmReference.java @@ -42,7 +42,8 @@ public final class VmReference extends VmValue { private final Object data; private final ImRrbt path; // candidate types can only be: PType.Class, PType.Alias (only preservedAliasTypes), - // PType.StringLiteral, PType.UNKNOWN, or PType.Union (containing only the previous; flattened) + // PType.StringLiteral, PType.UNKNOWN, PType.Function, PType.TypeVariable, or PType.Union + // (containing only the previous; flattened) private final PType referentType; private boolean forced = false; @@ -131,9 +132,12 @@ public final class VmReference extends VmValue { } result.add(new PType.Class(clazz.getPClass(), typeArgs)); } - } else if (type instanceof PType.Nullable nullable) { + } + // normalize `T?` to `T | Null` + else if (type instanceof PType.Nullable nullable) { normalizeTypes(nullable.getBaseType(), moduleClass, result); result.add(new PType.Class(BaseModule.getNullClass().export())); + // erase `T(someConstraint)` to `T` } else if (type instanceof PType.Constrained constrained) { normalizeTypes(constrained.getBaseType(), moduleClass, result); } else if (type instanceof PType.Alias alias) { @@ -148,6 +152,11 @@ public final class VmReference extends VmValue { } } else if (type == PType.MODULE) { result.add(new PType.Class(moduleClass)); + } else { + // remaining types: PType.Function, PType.TypeVariable. no normalizing needed; TypeVariable + // gets replaced upon instantiation, and Function can bubble up to users as a reference error + // if accessed. + result.add(type); } } @@ -156,29 +165,26 @@ public final class VmReference extends VmValue { return Collections.singleton(t); } - public @Nullable VmReference withPropertyAccess(Identifier property) { + public 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) { + public VmReference withSubscriptAccess(Object key) { return withAccess( (t, candidates) -> getCandidateSubscriptType(t, key, candidates), () -> newAccess(null, key)); } @TruffleBoundary - private @Nullable VmReference withAccess( + private VmReference withAccess( BiConsumer> checkCandidate, Supplier makeAccess) { Set 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)); } @@ -189,33 +195,54 @@ public final class VmReference extends VmValue { 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 (!(type instanceof PType.Class clazz)) { + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER); + } if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) { - // restriction: cannot reference Dynamic.default - if (!property.equals("default")) result.add(PType.UNKNOWN); + if (property.equals("default")) { + // restriction: cannot reference Dynamic.default + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.DEFAULT_MEMBER); + } + result.add(PType.UNKNOWN); return; } // restriction: cannot reference Listing/Mapping.default if (clazz.getPClass().getInfo() == PClassInfo.Listing || clazz.getPClass().getInfo() == PClassInfo.Mapping) { - return; + var errorType = + property.equals("default") + ? VmReferenceAccessErrorType.DEFAULT_MEMBER + : VmReferenceAccessErrorType.CANNOT_FIND_MEMBER; + throw new VmReferenceAccessError(type, errorType); } + var baseModule = BaseModule.getModuleClass().export(); // 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; + if (clazz.getPClass().isSubclassOf(baseModule) && property.equals("output")) { + throw new VmReferenceAccessError( + new PType.Class(baseModule), VmReferenceAccessErrorType.EXTERNAL_CLASS); + } - var prop = clazz.getPClass().getAllProperties().get(property); - // restriction: cannot reference external properties - if (prop == null || prop.isExternal()) { + // dot access on `Reference` gives `Reference` + if (clazz.getPClass().getInfo() == PClassInfo.Null) { + result.add(clazz); return; } + + var prop = clazz.getPClass().getAllProperties().get(property); + if (prop == null) { + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER); + } + + // restriction: cannot reference external properties + if (prop.isExternal()) { + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.EXTERNAL_MEMBER); + } + normalizeTypes(prop.getType(), clazz.getPClass().getModuleClass(), result); } @@ -235,7 +262,7 @@ public final class VmReference extends VmValue { return; } if (!(type instanceof PType.Class clazz)) { - return; + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER); } if (clazz.getPClass().getInfo() == PClassInfo.Dynamic) { result.add(PType.UNKNOWN); @@ -243,9 +270,10 @@ public final class VmReference extends VmValue { } if (clazz.getPClass().getInfo() == PClassInfo.Listing || clazz.getPClass().getInfo() == PClassInfo.List) { - if (key instanceof Long) { - normalizeTypes(clazz.getTypeArguments().get(0), clazz.getPClass().getModuleClass(), result); + if (!(key instanceof Long)) { + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER); } + normalizeTypes(clazz.getTypeArguments().get(0), clazz.getPClass().getModuleClass(), result); return; } if (clazz.getPClass().getInfo() == PClassInfo.Mapping @@ -262,6 +290,14 @@ public final class VmReference extends VmValue { } } } + + // subscript access on `Reference` gives `Reference` + if (clazz.getPClass().getInfo() == PClassInfo.Null) { + result.add(clazz); + return; + } + + throw new VmReferenceAccessError(type, VmReferenceAccessErrorType.CANNOT_FIND_MEMBER); } /** @@ -490,4 +526,29 @@ public final class VmReference extends VmValue { var callNode = DirectCallNode.create(toStringMethod.getCallTarget()); return (String) callNode.call(this, getVmClass().getPrototype()); } + + public enum VmReferenceAccessErrorType { + CANNOT_FIND_MEMBER, + EXTERNAL_MEMBER, + DEFAULT_MEMBER, + EXTERNAL_CLASS + } + + public static final class VmReferenceAccessError extends RuntimeException { + private final PType type; + private final VmReferenceAccessErrorType errorType; + + public VmReferenceAccessError(PType type, VmReferenceAccessErrorType errorType) { + this.type = type; + this.errorType = errorType; + } + + public PType getType() { + return type; + } + + public VmReferenceAccessErrorType getErrorType() { + return errorType; + } + } } diff --git a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties index f72d2946a..30bdff601 100644 --- a/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties +++ b/pkl-core/src/main/resources/org/pkl/core/errorMessages.properties @@ -319,6 +319,10 @@ Operator `{0}` is not defined for operand type `{1}`. operatorNotDefined2=\ Operator `{0}` is not defined for operand types `{1}` and `{2}`. +operatorNotDefined3=\ +Operator `{0}` is not defined for operand types `{1}` and `{3}`.\n\ +\t└─ Operator `{0}` is not defined for operand types `{2}` and `{3}`. + operatorNotDefinedLeft=\ Operator `{0}` is not defined for left operand type `{1}`. @@ -388,9 +392,25 @@ Cannot find property `{0}` in object of type `{1}`.\n\ Did you mean any of the following?\n\ {2} -cannotFindPropertyInReference=\ +cannotFindPropertyInObjectNoHint=\ Cannot find property `{0}` in object of type `{1}`. +cannotFindPropertyInType=\ +Cannot find property `{0}` in type `{1}`. + +cannotFindPropertyInType2=\ +Cannot find property `{0}` in type `{1}`.\n\ +\t└─ Cannot find property `{0}` in type `{2}`. + +cannotReferenceExternalProperty=\ +Cannot reference external property `{0}` in class `{1}`. + +cannotReferenceDefaultProperty=\ +Cannot reference property `default` in class `{0}`. + +cannotReferencePropertyInExternalClass=\ +Cannot reference property `{0}` because it is defined inside external class `{1}`. + cannotFindMethodInScope=\ Cannot find method `{0}`. diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/api/reference.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/api/reference.pkl index c8451edd1..dddfb0712 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/api/reference.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/api/reference.pkl @@ -36,6 +36,7 @@ class AProperties { someListing: Listing someList: List nonInt: String + nullProp: Null } class B extends Resource { @@ -64,6 +65,7 @@ class K { bId: String | *Ref? aProperties: AProperties | *Ref? bProperties: BProperties | *Ref? + nullProp: Ref? aValues: Listing>? bValues: Listing>? splitUnion: Ref | Listing>? @@ -91,6 +93,7 @@ k: K = new { bId = bRef.id aProperties = aRef.outputs bProperties = bRef.outputs + nullProp = aRef.outputs.nullProp.prop1.prop2["foo"].prop3 aValues { aRef.outputs.foo aRef.outputs.someMapping["key"] diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/reference.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/reference.pkl index f23589f8c..336f0165e 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/basic/reference.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/basic/reference.pkl @@ -17,39 +17,40 @@ local class Holder { optional: Mapping listing: Listing union: Listing | Listing + constrainedType: String(isEmpty) } -local const h: Holder = new { - num = 1 - text = "t" - lit = "literal" - small = 8 - optional { ["k"] = 5 } - listing { 1; 2 } - union = new Listing { 1 } +local class OtherHolder { + num: Int? + constrainedType: String(isEmpty) } -local hRef1: Ref = ref.Reference(d, Holder, h) -local hRef2: Ref = ref.Reference(d, Holder, h) +local holderRef: Ref = ref.Reference(d, Holder, null) +local holderRef2: Ref = ref.Reference(d, Holder, null) +local otherHolderRef: Ref = ref.Reference(d, OtherHolder, null) 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 + holderRef == holderRef2 + holderRef.num == holderRef2.num + holderRef.lit == holderRef2.lit + holderRef.small == holderRef2.small + holderRef.optional["k"] == holderRef2.optional["k"] + holderRef.listing[0] == holderRef2.listing[0] + holderRef.union == holderRef2.union + } + + ["equality -- constraint gets erased"] { + holderRef.constrainedType == otherHolderRef.constrainedType } ["inequality"] { - hRef1.num != hRef2.text + holderRef.num != holderRef2.text } ["set deduplication"] { - Set(hRef1.num, hRef2.num).length == 1 - Set(hRef1.union, hRef2.union).length == 1 - Set(hRef1.num, hRef2.text).length == 2 + Set(holderRef.num, holderRef2.num).length == 1 + Set(holderRef.union, holderRef2.union).length == 1 + Set(holderRef.num, holderRef2.text).length == 2 } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference11.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference11.pkl index 8bb1c9141..8e1daebc4 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference11.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference11.pkl @@ -6,7 +6,7 @@ class D extends ref.Domain { typealias Ref = ref.Reference local d: D = new {} -test = ref.Reference(d, String, "") as Ref +test = ref.Reference(d, String, "") as Ref -typealias Alias1 = Int | Alias2? +typealias Alias1 = Int | Alias2 typealias Alias2 = String(length < 5) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference24.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference24.pkl new file mode 100644 index 000000000..3877f6208 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference24.pkl @@ -0,0 +1,17 @@ +import "pkl:ref" + +class D extends ref.Domain { + function renderReference(_): String = throw("not implemented") +} + +class Bird { + name: String +} + +class Holder { + $: Bird | Int +} + +myRef: ref.Reference = ref.Reference(new D {}, Holder, null).$ + +res = myRef.name diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference25.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference25.pkl new file mode 100644 index 000000000..8d6e3d8a1 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference25.pkl @@ -0,0 +1,13 @@ +import "pkl:ref" + +class D extends ref.Domain { + function renderReference(_): String = throw("not implemented") +} + +class Holder { + $: Duration | Listing +} + +local myRef = ref.Reference(new D {}, Holder, null).$ + +res = myRef[0] diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference26.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference26.pkl new file mode 100644 index 000000000..e0ecf6479 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference26.pkl @@ -0,0 +1,9 @@ +import "pkl:ref" + +class D extends ref.Domain { + function renderReference(_): String = throw("not implemented") +} + +local myRef = ref.Reference(new D {}, Listing, null) + +res = myRef.name diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference27.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference27.pkl new file mode 100644 index 000000000..96d883165 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference27.pkl @@ -0,0 +1,13 @@ +import "pkl:ref" + +class D extends ref.Domain { + function renderReference(_): String = throw("not implemented") +} + +class Holder { + func: (Int) -> Boolean +} + +local myRef = ref.Reference(new D {}, Holder, null).func + +res = myRef.name diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference4.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference4.pkl index d264d65f5..a48fa5dab 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference4.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/reference4.pkl @@ -6,7 +6,7 @@ class D extends ref.Domain { local d: D = new {} typealias Ref = ref.Reference -test = ref.Reference(d, String, "") as Ref +test = ref.Reference(d, String, "") as Ref -typealias Alias1 = Int | Alias2? +typealias Alias1 = Int | Alias2 typealias Alias2 = Boolean diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/api/reference.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/api/reference.pcf index 6875f4b09..edd5f67aa 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/api/reference.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/api/reference.pcf @@ -20,6 +20,7 @@ k { bId = "${b.id}" aProperties = "${a.outputs}" bProperties = "${b.outputs}" + nullProp = "${a.outputs.nullProp.prop1.prop2[foo].prop3}" aValues { "${a.outputs.foo}" "${a.outputs.someMapping[key]}" @@ -43,12 +44,13 @@ j { bId = null aProperties = "${a.outputs}" bProperties = null + nullProp = 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 }" +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; nullProp = Reference(new D {}, Null, new A { name = \"a\"; id = \"some-a-value\" }).outputs.nullProp.prop1.prop2[\"foo\"].prop3; 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]} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/reference.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/reference.pcf index 4c176b165..f070ac2eb 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/basic/reference.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/basic/reference.pcf @@ -8,6 +8,9 @@ facts { true true } + ["equality -- constraint gets erased"] { + true + } ["inequality"] { true } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err index 819e5efaf..4c2455e20 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference11.err @@ -5,7 +5,7 @@ xx | typealias Alias2 = String(length < 5) ^^^^^^^^^^^^^^^^^^ at reference11#Alias2 (file:///$snippetsDir/input/errors/reference11.pkl) -xx | typealias Alias1 = Int | Alias2? +xx | typealias Alias1 = Int | Alias2 ^^^^^^ at reference11#Alias1 (file:///$snippetsDir/input/errors/reference11.pkl) @@ -13,8 +13,8 @@ x | typealias Ref = ref.Reference ^^^^^^^^^^^^^^^^^^^ at reference11#Ref (file:///$snippetsDir/input/errors/reference11.pkl) -x | test = ref.Reference(d, String, "") as Ref - ^^^^^^^^^^^^ +x | test = ref.Reference(d, String, "") as Ref + ^^^^^^^^^^^ at reference11#test (file:///$snippetsDir/input/errors/reference11.pkl) xxx | renderer.renderDocument(value) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference13.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference13.err index e689a38a9..a7ed01eec 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference13.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference13.err @@ -5,6 +5,8 @@ x | test = ref.Reference(d, Mapping, "").default ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference13#test (file:///$snippetsDir/input/errors/reference13.pkl) +Cannot reference property `default` in class `Mapping`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference14.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference14.err index bebf392d1..0ab4e342d 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference14.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference14.err @@ -5,6 +5,8 @@ x | test = ref.Reference(d, Listing, "").default ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference14#test (file:///$snippetsDir/input/errors/reference14.pkl) +Cannot reference property `default` in class `Listing`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference15.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference15.err index 7c833d88c..d46c8ad1f 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference15.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference15.err @@ -5,6 +5,8 @@ x | test = ref.Reference(d, Dynamic, "").default ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference15#test (file:///$snippetsDir/input/errors/reference15.pkl) +Cannot reference property `default` in class `Dynamic`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference16.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference16.err index 13ea3afe3..01c7b2fde 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference16.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference16.err @@ -5,6 +5,8 @@ xx | test = ref.Reference(d, ReferencedModule.getClass(), "").output ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference16#test (file:///$snippetsDir/input/errors/reference16.pkl) +Cannot reference property `output` because it is defined inside external class `Module`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference17.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference17.err index e68974a6e..b04be664a 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference17.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference17.err @@ -5,6 +5,8 @@ xx | test = ref.Reference(d, ReferencedModuleWithOutputOverride.getClass(), ""). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference17#test (file:///$snippetsDir/input/errors/reference17.pkl) +Cannot reference property `output` because it is defined inside external class `Module`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference18.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference18.err index 05087f5ae..be4744eef 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference18.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference18.err @@ -5,6 +5,8 @@ xx | test = ref.Reference(d, ModuleSubclass, "").output ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference18#test (file:///$snippetsDir/input/errors/reference18.pkl) +Cannot reference property `output` because it is defined inside external class `Module`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference24.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference24.err new file mode 100644 index 000000000..4b0238326 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference24.err @@ -0,0 +1,17 @@ +–– Pkl Error –– +Cannot find property `name` in object of type `pkl.ref#Reference`. + +xx | res = myRef.name + ^^^^^^^^^^ +at reference24#res (file:///$snippetsDir/input/errors/reference24.pkl) + +Cannot find property `name` in type `Int | reference24#Bird`. + └─ Cannot find property `name` in type `Int`. + +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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference25.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference25.err new file mode 100644 index 000000000..8cf2422d1 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference25.err @@ -0,0 +1,19 @@ +–– Pkl Error –– +Operator `[]` is not defined for operand types `pkl.ref#Reference>` and `Int`. +Left operand : Reference(new D {}, Duration | Listing, null).$ +Right operand: 0 + +xx | res = myRef[0] + ^^^^^^^^ +at reference25#res (file:///$snippetsDir/input/errors/reference25.pkl) + +Operator `[]` is not defined for operand types `Duration | Listing` and `Int`. + └─ Operator `[]` is not defined for operand types `Duration` and `Int`. + +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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference26.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference26.err new file mode 100644 index 000000000..383bcc149 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference26.err @@ -0,0 +1,16 @@ +–– Pkl Error –– +Cannot find property `name` in object of type `pkl.ref#Reference>`. + +x | res = myRef.name + ^^^^^^^^^^ +at reference26#res (file:///$snippetsDir/input/errors/reference26.pkl) + +Cannot find property `name` in type `Listing`. + +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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference27.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference27.err new file mode 100644 index 000000000..0f60c0e6e --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference27.err @@ -0,0 +1,16 @@ +–– Pkl Error –– +Cannot find property `name` in object of type `pkl.ref#Reference Boolean>`. + +xx | res = myRef.name + ^^^^^^^^^^ +at reference27#res (file:///$snippetsDir/input/errors/reference27.pkl) + +Cannot find property `name` in type `(Int) -> Boolean`. + +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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference4.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference4.err index 7d90d28ac..46c6a8e2c 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference4.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference4.err @@ -1,5 +1,5 @@ –– Pkl Error –– -Expected value of type `pkl.ref#Reference`, but got type `pkl.ref#Reference`. +Expected value of type `pkl.ref#Reference`, but got type `pkl.ref#Reference`. Value: Reference(new D {}, String, "") x | typealias Ref = ref.Reference diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference6.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference6.err index 1dd136837..4312d1351 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference6.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference6.err @@ -5,6 +5,8 @@ xx | test = ref.Reference(d, String, new A {}).c ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference6#test (file:///$snippetsDir/input/errors/reference6.pkl) +Cannot find property `c` in type `String`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference7.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference7.err index 426b680ec..165f4bc30 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference7.err +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/reference7.err @@ -7,6 +7,8 @@ x | test = ref.Reference(d, List, List())["hi"] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at reference7#test (file:///$snippetsDir/input/errors/reference7.pkl) +Operator `[]` is not defined for operand types `List` and `String`. + xxx | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (pkl:base)