mirror of
https://github.com/apple/pkl.git
synced 2026-09-13 21:31:52 +02:00
Enforce stricter rules on reference access (#1718)
This changes the following: 1. If given a union type, the member must exist on each member of that type 2. If accessing a member off `Reference<D, Null>`, give a `Reference<D, Null>` Also: * Improve error messages thrown during member access. * Add test around accessing members off of a function type
This commit is contained in:
@@ -36,6 +36,7 @@ class AProperties {
|
||||
someListing: Listing<Int>
|
||||
someList: List<Int>
|
||||
nonInt: String
|
||||
nullProp: Null
|
||||
}
|
||||
|
||||
class B extends Resource {
|
||||
@@ -64,6 +65,7 @@ class K {
|
||||
bId: String | *Ref<String>?
|
||||
aProperties: AProperties | *Ref<AProperties>?
|
||||
bProperties: BProperties | *Ref<BProperties>?
|
||||
nullProp: Ref<Null>?
|
||||
aValues: Listing<Int | Ref<Int?>>?
|
||||
bValues: Listing<String | Ref<String?>>?
|
||||
splitUnion: Ref<Listing<String> | Listing<Int>>?
|
||||
@@ -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"]
|
||||
|
||||
@@ -17,39 +17,40 @@ local class Holder {
|
||||
optional: Mapping<String, Int?>
|
||||
listing: Listing<Int>
|
||||
union: Listing<String> | Listing<Int>
|
||||
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<Int> { 1 }
|
||||
local class OtherHolder {
|
||||
num: Int?
|
||||
constrainedType: String(isEmpty)
|
||||
}
|
||||
|
||||
local hRef1: Ref<Holder> = ref.Reference(d, Holder, h)
|
||||
local hRef2: Ref<Holder> = ref.Reference(d, Holder, h)
|
||||
local holderRef: Ref<Holder> = ref.Reference(d, Holder, null)
|
||||
local holderRef2: Ref<Holder> = ref.Reference(d, Holder, null)
|
||||
local otherHolderRef: Ref<OtherHolder> = ref.Reference(d, OtherHolder, null)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ class D extends ref.Domain {
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
local d: D = new {}
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1?>
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1>
|
||||
|
||||
typealias Alias1 = Int | Alias2?
|
||||
typealias Alias1 = Int | Alias2
|
||||
typealias Alias2 = String(length < 5)
|
||||
|
||||
@@ -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<D, Bird | Int> = ref.Reference(new D {}, Holder, null).$
|
||||
|
||||
res = myRef.name
|
||||
@@ -0,0 +1,13 @@
|
||||
import "pkl:ref"
|
||||
|
||||
class D extends ref.Domain {
|
||||
function renderReference(_): String = throw("not implemented")
|
||||
}
|
||||
|
||||
class Holder {
|
||||
$: Duration | Listing<String>
|
||||
}
|
||||
|
||||
local myRef = ref.Reference(new D {}, Holder, null).$
|
||||
|
||||
res = myRef[0]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -6,7 +6,7 @@ class D extends ref.Domain {
|
||||
local d: D = new {}
|
||||
typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1?>
|
||||
test = ref.Reference(d, String, "") as Ref<Alias1>
|
||||
|
||||
typealias Alias1 = Int | Alias2?
|
||||
typealias Alias1 = Int | Alias2
|
||||
typealias Alias2 = Boolean
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -8,6 +8,9 @@ facts {
|
||||
true
|
||||
true
|
||||
}
|
||||
["equality -- constraint gets erased"] {
|
||||
true
|
||||
}
|
||||
["inequality"] {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -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<T> = ref.Reference<D, T>
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
at reference11#Ref (file:///$snippetsDir/input/errors/reference11.pkl)
|
||||
|
||||
x | test = ref.Reference(d, String, "") as Ref<Alias1?>
|
||||
^^^^^^^^^^^^
|
||||
x | test = ref.Reference(d, String, "") as Ref<Alias1>
|
||||
^^^^^^^^^^^
|
||||
at reference11#test (file:///$snippetsDir/input/errors/reference11.pkl)
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
–– Pkl Error ––
|
||||
Cannot find property `name` in object of type `pkl.ref#Reference<reference24#D, Int | reference24#Bird>`.
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,19 @@
|
||||
–– Pkl Error ––
|
||||
Operator `[]` is not defined for operand types `pkl.ref#Reference<reference25#D, Duration | Listing<String>>` and `Int`.
|
||||
Left operand : Reference(new D {}, Duration | Listing<String>, 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<String>` 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)
|
||||
@@ -0,0 +1,16 @@
|
||||
–– Pkl Error ––
|
||||
Cannot find property `name` in object of type `pkl.ref#Reference<reference26#D, Listing<unknown>>`.
|
||||
|
||||
x | res = myRef.name
|
||||
^^^^^^^^^^
|
||||
at reference26#res (file:///$snippetsDir/input/errors/reference26.pkl)
|
||||
|
||||
Cannot find property `name` in type `Listing<unknown>`.
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,16 @@
|
||||
–– Pkl Error ––
|
||||
Cannot find property `name` in object of type `pkl.ref#Reference<reference27#D, (Int) -> 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)
|
||||
@@ -1,5 +1,5 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `pkl.ref#Reference<reference4#D, reference4#Alias1?>`, but got type `pkl.ref#Reference<reference4#D, String>`.
|
||||
Expected value of type `pkl.ref#Reference<reference4#D, reference4#Alias1>`, but got type `pkl.ref#Reference<reference4#D, String>`.
|
||||
Value: Reference(new D {}, String, "")
|
||||
|
||||
x | typealias Ref<T> = ref.Reference<D, T>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<unknown>` and `String`.
|
||||
|
||||
xxx | renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
|
||||
Reference in New Issue
Block a user