mirror of
https://github.com/apple/pkl.git
synced 2026-05-28 01:29:15 +02:00
Improve lazy type checking of listings and mappings (#789)
Motivation:
- simplify implementation of lazy type checking
- fix correctness issues of lazy type checking (#785)
Changes:
- implement listing/mapping type cast via amendment (`parent`) instead of delegation (`delegate`)
- handle type checking of *computed* elements/entries in the same way as type checking of computed properties
- ElementOrEntryNode is the equivalent of TypeCheckedPropertyNode
- remove fields VmListingOrMapping.delegate/typeNodeFrame/cachedMembers/checkedMembers
- fix #785 by executing all type casts between a member's owner and receiver
- fix #823 by storing owner and receiver directly
instead of storing the mutable frame containing them (typeNodeFrame)
- remove overrides of VmObject methods that are no longer required
- good for Truffle partial evaluation and JVM inlining
- revert a85a173faa except for added tests
- move `VmUtils.setOwner` and `VmUtils.setReceiver` and make them private
- these methods aren't generally safe to use
Result:
- simpler code with greater optimization potential
- VmListingOrMapping can now have both a type node and new members
- fewer changes to surrounding code
- smaller memory footprint
- better performance in some cases
- fixes https://github.com/apple/pkl/issues/785
- fixes https://github.com/apple/pkl/issues/823
Potential future optimizations:
- avoid lazy type checking overhead for untyped listings/mappings
- improve efficiency of forcing a typed listing/mapping
- currently, lazy type checking will traverse the parent chain once per member,
reducing the performance benefit of shallow-forcing
a listing/mapping over evaluating each member individually
- avoid creating an intermediate untyped listing/mapping in the following cases:
- `new Listing<X> {...}`
- amendment of `property: Listing<X>`
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
function isValid(str): Boolean = str.startsWith("a")
|
||||
|
||||
foo: Listing<String(isValid(this))>(isDistinct)
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
local a = new Listing { new Listing { 0 } }
|
||||
local b = a as Listing<Listing<String>>
|
||||
local c = (b) { new Listing { 1 } }
|
||||
local d = c as Listing<Listing<Int>>
|
||||
|
||||
result = d
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local a = new Listing { new Foo {} }
|
||||
local b = (a) { new Bar {} }
|
||||
local c = b as Listing<Bar>
|
||||
local d = (c) { new Foo {} }
|
||||
local e = d as Listing<Foo>
|
||||
|
||||
result = e
|
||||
|
||||
open class Foo
|
||||
class Bar extends Foo
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
local a = new Mapping { [0] = new Foo {} }
|
||||
local b = (a) { [1] = new Bar {} }
|
||||
local c = b as Mapping<Int, Bar>
|
||||
local d = (c) { [2] = new Foo {} }
|
||||
local e = d as Mapping<Int, Foo>
|
||||
|
||||
result = e
|
||||
|
||||
open class Foo
|
||||
class Bar extends Foo
|
||||
@@ -0,0 +1,7 @@
|
||||
foo1: Listing<String> = new { "hello" }
|
||||
foo2: Listing<String|Int> = foo1
|
||||
|
||||
res1 = foo1.isDistinct
|
||||
// steals isDistinct from foo1's VmListing.cachedValues but must not
|
||||
// perform a String|Int type check because isDistinct is not an element
|
||||
res2 = foo2.isDistinct
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
amends "../../input-helper/listings/cacheStealingTypeCheck.pkl"
|
||||
|
||||
// this test covers a regression where the wrong receiver
|
||||
// and owner was used to typecheck a stolen value
|
||||
foo {
|
||||
"abcdx"
|
||||
"ax"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
const local lastName = "Birdo"
|
||||
|
||||
typealias Birds = Listing<String(endsWith(lastName))>
|
||||
|
||||
typealias Birds2 = Pair<Listing<String(endsWith(lastName))>, Int>
|
||||
@@ -1,5 +1,7 @@
|
||||
import "pkl:test"
|
||||
|
||||
import "helpers/originalTypealias.pkl"
|
||||
|
||||
typealias Simple = String
|
||||
const function simple(arg: Simple): Simple = arg
|
||||
|
||||
@@ -105,3 +107,8 @@ res19: LocalTypeAlias = "abc"
|
||||
typealias VeryComposite = Pair<Composite, Composite>
|
||||
|
||||
res20: VeryComposite = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
||||
|
||||
// typealiases should be executed in their original context
|
||||
res21: originalTypealias.Birds = new { "John Birdo" }
|
||||
|
||||
res22: originalTypealias.Birds2 = Pair(new Listing { "John Birdo" }, 0)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import "helpers/originalTypealias.pkl"
|
||||
|
||||
res: originalTypealias.Birds = new { "Jimmy Bird" }
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `String`, but got type `Int`.
|
||||
Value: 0
|
||||
|
||||
x | local b = a as Listing<Listing<String>>
|
||||
^^^^^^
|
||||
at listingTypeCheckError8#b (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
|
||||
|
||||
x | local a = new Listing { new Listing { 0 } }
|
||||
^
|
||||
at listingTypeCheckError8#a[#1][#1] (file:///$snippetsDir/input/errors/listingTypeCheckError8.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `listingTypeCheckError9#Bar`, but got type `listingTypeCheckError9#Foo`.
|
||||
Value: new Foo {}
|
||||
|
||||
x | local c = b as Listing<Bar>
|
||||
^^^
|
||||
at listingTypeCheckError9#c (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
|
||||
|
||||
x | local a = new Listing { new Foo {} }
|
||||
^^^^^^^^^^
|
||||
at listingTypeCheckError9#a[#1] (file:///$snippetsDir/input/errors/listingTypeCheckError9.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Expected value of type `mappingTypeCheckError11#Bar`, but got type `mappingTypeCheckError11#Foo`.
|
||||
Value: new Foo {}
|
||||
|
||||
x | local c = b as Mapping<Int, Bar>
|
||||
^^^
|
||||
at mappingTypeCheckError11#c (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
|
||||
|
||||
x | local a = new Mapping { [0] = new Foo {} }
|
||||
^^^^^^^^^^
|
||||
at mappingTypeCheckError11#a[0] (file:///$snippetsDir/input/errors/mappingTypeCheckError11.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
foo1 {
|
||||
"hello"
|
||||
}
|
||||
foo2 {
|
||||
"hello"
|
||||
}
|
||||
res1 = true
|
||||
res2 = true
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
foo {
|
||||
"abcdx"
|
||||
"ax"
|
||||
}
|
||||
@@ -38,3 +38,9 @@ res18 = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb
|
||||
res18b = "Expected value of type `Duration`, but got type `DataSize`. Value: 5.mb"
|
||||
res19 = "abc"
|
||||
res20 = Pair(Map("abc", List("def")), Map("abc", List("def")))
|
||||
res21 {
|
||||
"John Birdo"
|
||||
}
|
||||
res22 = Pair(new {
|
||||
"John Birdo"
|
||||
}, 0)
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
–– Pkl Error ––
|
||||
Type constraint `endsWith(lastName)` violated.
|
||||
Value: "Jimmy Bird"
|
||||
|
||||
x | typealias Birds = Listing<String(endsWith(lastName))>
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
|
||||
|
||||
x | res: originalTypealias.Birds = new { "Jimmy Bird" }
|
||||
^^^^^^^^^^^^
|
||||
at typeAliasContext#res[#1] (file:///$snippetsDir/input/types/typeAliasContext.pkl)
|
||||
|
||||
xxx | text = renderer.renderDocument(value)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
at pkl.base#Module.output.text (pkl:base)
|
||||
Reference in New Issue
Block a user