Improve partial-evaluation when amending null parents (#1836)

GraalVM's partial evaulator can't handle recursive calls into the same
node.

The current implementation around evaluating `VmNull` as a parent causes
the partial evaluator to bail out, leaving Pkl stuck in interpreter
mode.

This rewrites the various object literals to add individual
specializations for each type of default value we can see from a VmNull
parent.

Also:
* Fix `isTypeObjectClass` impl
* Fix bug when amending with generator object literal node with object params
This commit is contained in:
Daniel Chao
2026-09-02 18:18:55 +00:00
committed by GitHub
parent 2c2f50113d
commit 0035199176
29 changed files with 1038 additions and 41 deletions
@@ -0,0 +1,9 @@
class Person { name: String }
hidden x: Mixin<Person>?
local y = (x) {
name = "Fred"
}
res = y.apply(new Person {})
@@ -26,6 +26,25 @@ res2 = new Listing {
new Mapping { ["one"] = 1; ["two"] = 2 }
}
res2n = (Null(new Listing {})) {
null
true
42
1.23
"foo"
Regex("bar")
5.gb
3.min
Pair(1, 2)
List(1, 2, 3)
Set(1, 2, 3)
Map("one", 1, "two", 2)
new Dynamic { name = "Pigeon"; age = 42 }
new Person { name = "Pigeon"; age = 42 }
new Listing { 1; 2; 3 }
new Mapping { ["one"] = 1; ["two"] = 2 }
}
res3 = new Listing {
id(null)
id(true)
@@ -44,3 +63,22 @@ res3 = new Listing {
id(new Listing { 1; 2; 3 })
id(new Mapping { ["one"] = 1; ["two"] = 2 })
}
res3n = (Null(new Listing {})) {
id(null)
id(true)
id(42)
id(1.23)
id("foo")
id(Regex("bar"))
id(5.gb)
id(3.min)
id(Pair(1, 2))
id(List(1, 2, 3))
id(Set(1, 2, 3))
id(Map("one", 1, "two", 2))
id(new Dynamic { name = "Pigeon"; age = 42 })
id(new Person { name = "Pigeon"; age = 42 })
id(new Listing { 1; 2; 3 })
id(new Mapping { ["one"] = 1; ["two"] = 2 })
}
@@ -73,11 +73,20 @@ res8 = test.catch(() -> (x) { ["wrong type"] = "value" })
res8a = test.catch(() -> (x) { [id("wrong type")] = id("value") })
res9 = test.catch(() -> new Listing { foo = "bar" })
res9n = test.catch(() -> (Null(new Listing {})) { foo = "bar" })
res10 = test.catch(() -> (x) { foo = "foo" })
res10n = test.catch(() -> (Null(x)) { foo = "foo" })
res11 = test.catch(() -> new Listing { "one"; foo = "foo" })
res11n = test.catch(() -> (Null(new Listing { "one" })) { foo = "foo" })
res12 = test.catch(() -> (x) { "four"; foo = "foo" })
res12n = test.catch(() -> (Null(x)) { "four"; foo = "foo" })
res12a = test.catch(() -> (x) { id("four"); foo = "foo" })
res12an = test.catch(() -> (Null(x)) { id("four"); foo = "foo" })
res13 = test.catch(() -> (x) { [1] = "updated two"; foo = "foo" })
res13n = test.catch(() -> (Null(x)) { [1] = "updated two"; foo = "foo" })
res13a = test.catch(() -> (x) { [id(1)] = id("updated two"); foo = "foo" })
res13an = test.catch(() -> (Null(x)) { [id(1)] = id("updated two"); foo = "foo" })
res14 = test.catch(() -> (x) { "four"; [1] = "updated two"; foo = "foo" })
res14n = test.catch(() -> (Null(x)) { "four"; [1] = "updated two"; foo = "foo" })
res14a = test.catch(() -> (x) { id("four"); [id(1)] = id("updated two"); foo = "foo" })
res14an = test.catch(() -> (Null(x)) { id("four"); [id(1)] = id("updated two"); foo = "foo" })
@@ -26,6 +26,25 @@ res2 = new Mapping {
[new Mapping { ["one"] = 1; ["two"] = 2 }] = new Mapping { ["one"] = 1; ["two"] = 2 }
}
res2n = (Null(new Mapping {})) {
[null] = null
[true] = true
[42] = 42
[1.23] = 1.23
["foo"] = "foo"
[Regex("bar")] = Regex("bar")
[5.gb] = 5.gb
[3.min] = 3.min
[Pair(1, 2)] = Pair(1, 2)
[List(1, 2, 3)] = List(1, 2, 3)
[Set(1, 2, 3)] = Set(1, 2, 3)
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
[new Dynamic { name = "Pigeon"; age = 42 }] = new Dynamic { name = "Pigeon"; age = 42 }
[new Person { name = "Pigeon"; age = 42 }] = new Person { name = "Pigeon"; age = 42 }
[new Listing { 1; 2; 3 }] = new Listing { 1; 2; 3 }
[new Mapping { ["one"] = 1; ["two"] = 2 }] = new Mapping { ["one"] = 1; ["two"] = 2 }
}
res3 = new Mapping {
[id(null)] = id(null)
[id(true)] = id(true)
@@ -44,3 +63,34 @@ res3 = new Mapping {
[id(new Listing { 1; 2; 3 })] = id(new Listing { 1; 2; 3 })
[id(new Mapping { ["one"] = 1; ["two"] = 2 })] = id(new Mapping { ["one"] = 1; ["two"] = 2 })
}
res3n = (Null(new Mapping {})) {
[id(null)] = id(null)
[id(true)] = id(true)
[id(42)] = id(42)
[id(1.23)] = id(1.23)
[id("foo")] = id("foo")
[id(Regex("bar"))] = id(Regex("bar"))
[id(5.gb)] = id(5.gb)
[id(3.min)] = id(3.min)
[id(Pair(1, 2))] = id(Pair(1, 2))
[id(List(1, 2, 3))] = id(List(1, 2, 3))
[id(Set(1, 2, 3))] = id(Set(1, 2, 3))
[id(Map("one", 1, "two", 2))] = id(Map("one", 1, "two", 2))
[id(new Dynamic { name = "Pigeon"; age = 42 })] = id(new Dynamic { name = "Pigeon"; age = 42 })
[id(new Person { name = "Pigeon"; age = 42 })] = id(new Person { name = "Pigeon"; age = 42 })
[id(new Listing { 1; 2; 3 })] = id(new Listing { 1; 2; 3 })
[id(new Mapping { ["one"] = 1; ["two"] = 2 })] = id(new Mapping { ["one"] = 1; ["two"] = 2 })
}
// ConstantEntriesLiteralNode
res4 = new Mapping {
["foo"] = "1"
["bar"] = "2"
}
// ConstantEntriesLiteralNode
res4n = (Null(new Mapping {})) {
["foo"] = "1"
["bar"] = "2"
}
@@ -0,0 +1,47 @@
// Covers ConstantEntriesLiteralNode's specializations for amending a `VmNull` whose default value
// is each of the underlying parent types it supports. All entry keys below are constants, so
// these bodies compile to ConstantEntriesLiteralNode rather than EntriesLiteralNode.
import "pkl:test"
// evalNullWithMappingDefault
local mapping = Null(new Mapping { ["a"] = 1 })
res1 = (mapping) {
["b"] = 2
}
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { a = 1 })
res2 = (dynamic) {
["b"] = 2
}
// evalNullWithListingDefault
local listing = Null(new Listing { "a"; "b" })
res3 = (listing) {
[0] = "updated a"
}
// evalNullWithFunctionDefault
local fun = Null(() -> new Mapping { ["a"] = 1 })
local amendedFun = (fun) {
["b"] = 2
}
res4 = amendedFun.apply()
// error: listing entry index out of range
res5 = test.catch(() -> (Null(new Listing { "a" })) { [5] = "value" })
// error: listing cannot have a property (other than `default`)
res6 = test.catch(() -> (Null(new Listing { "a" })) { [0] = "updated a"; foo = "bar" })
// error: mapping cannot have a property (other than `default`)
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { ["b"] = 2; foo = "bar" })
// error: function amendment with wrong parameter count
local twoArgFun = (x, y) -> new Mapping {}
res8 = test.catch(() -> (Null(twoArgFun)) { x -> ["a"] = 1 })
hidden foo: "foo"?
// error: default value's type has no matching specialization; falls back to the generic error
res9 = test.catch(() -> (foo) { ["a"] = 1 })
@@ -0,0 +1,37 @@
// Covers ElementsLiteralNode's specializations for amending a `VmNull` whose default value is
// each of the underlying parent types it supports.
import "pkl:test"
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { a = 1 })
res1 = (dynamic) {
"b"
"c"
}
// evalNullWithListingDefault
local listing = Null(new Listing { "a" })
res2 = (listing) {
"b"
"c"
}
// evalNullWithFunctionDefault
local fun = Null(() -> new Listing { "a" })
local amendedFun = (fun) {
"b"
}
res3 = amendedFun.apply()
// error: mapping cannot have an element
res4 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { "b" })
// error: listing cannot have a property (other than `default`)
res5 = test.catch(() -> (Null(new Listing { "a" })) { "b"; foo = "bar" })
// error: function amendment with wrong parameter count
local twoArgFun = (x, y) -> new Listing {}
res6 = test.catch(() -> (Null(twoArgFun)) { x -> "b" })
// error: default value's type has no matching specialization; falls back to the generic error
res7 = test.catch(() -> (Null(42)) { "b" })
@@ -0,0 +1,45 @@
// Covers ElementsEntriesLiteralNode's specializations for amending a `VmNull` whose default value
// is each of the underlying parent types it supports. Element and entry keys are mixed, and at
// least one key is non-constant (via `id()`), matching this node's shape.
import "pkl:test"
function id(x) = x
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { "a" })
res1 = (dynamic) {
[id(0)] = "updated a"
"b"
}
// evalNullWithListingDefault
local listing = Null(new Listing { "a"; "b" })
res2 = (listing) {
"c"
[id(0)] = "updated a"
}
// evalNullWithFunctionDefault
local fun = Null(() -> new Listing { "a" })
local amendedFun = (fun) {
"b"
[id(0)] = "updated a"
}
res3 = amendedFun.apply()
// error: mapping cannot have an element
res4 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { "b"; [id("c")] = 2 })
// error: listing entry index out of range
res5 = test.catch(() -> (Null(new Listing { "a" })) { "b"; [id(5)] = "value" })
// error: listing cannot have a property (other than `default`)
res6 = test.catch(() -> (Null(new Listing { "a" })) { "b"; [id(0)] = "updated a"; foo = "bar" })
// error: function amendment with wrong parameter count
local twoArgFun = (x, y) -> new Listing {}
res7 = test.catch(() -> (Null(twoArgFun)) { x -> "b"; [id(0)] = "updated a" })
// error: default value's type has no matching specialization; falls back to the generic error
res8 = test.catch(() -> (Null(42)) { "b"; [id(0)] = "updated a" })
@@ -0,0 +1,53 @@
// Covers EntriesLiteralNode's specializations for amending a `VmNull` whose default value is
// each of the underlying parent types it supports. At least one entry key below is non-constant
// (via `id()`) so that these bodies compile to EntriesLiteralNode rather than
// ConstantEntriesLiteralNode.
import "pkl:test"
function id(x) = x
// evalNullableMapping
local mapping = Null(new Mapping { ["a"] = 1 })
res1 = (mapping) {
[id("b")] = 2
}
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { a = 1 })
res2 = (dynamic) {
[id("b")] = 2
}
// evalNullWithListingDefault
local listing = Null(new Listing { "a"; "b" })
res3 = (listing) {
[id(0)] = "updated a"
}
// evalNullWithFunctionDefault
local fun = Null(() -> new Mapping { ["a"] = 1 })
local amendedFun = (fun) {
[id("b")] = 2
}
res4 = amendedFun.apply()
// error: listing entry with wrong key type
res5 = test.catch(() -> (Null(new Listing { "a" })) { [id("wrong type")] = "value" })
// error: listing entry index out of range
res6 = test.catch(() -> (Null(new Listing { "a" })) { [id(5)] = "value" })
// error: mapping cannot have a property (other than `default`)
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { foo = "bar"; [id("b")] = 2 })
// error: duplicate key
res8 = test.catch(() -> (Null(new Mapping {})) { [id("a")] = 1; [id("a")] = 2 })
// error: function amendment with wrong parameter count
local twoArgFun = (x, y) -> new Mapping {}
res9 = test.catch(() -> (Null(twoArgFun)) { x -> [id("a")] = 1 })
hidden foo: "foo"?
// error: default value's type has no matching specialization; falls back to the generic error
res10 = test.catch(() -> (foo) { [id("a")] = 1 })
@@ -0,0 +1,68 @@
// Covers GeneratorObjectLiteralNode's specializations for amending a `VmNull` whose default value
// is each of the underlying parent types it supports. Each body contains a `for`- or
// when-generator, which is what forces this node type instead of ElementsLiteralNode/
// EntriesLiteralNode/PropertiesLiteralNode.
import "pkl:test"
local class Person {
name: String
age: Int = 0
}
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { a = 1 })
res1 = (dynamic) {
for (i in List(2, 3)) {
i
}
}
// evalNullWithTypedDefault
local person = Null(new Person { name = "Pigeon" })
res2 = (person) {
when (true) {
age = 10
}
}
// evalNullWithListingDefault
local listing = Null(new Listing { "a" })
res3 = (listing) {
for (i in List(2, 3)) {
i
}
}
// evalNullableMapping
local mapping = Null(new Mapping { ["a"] = 1 })
res4 = (mapping) {
for (i in List(2, 3)) {
[i] = i
}
}
// evalNullWithFunctionDefault
local fun = Null(() -> new Dynamic { a = 1 })
local amendedFun = (fun) {
for (i in List(2, 3)) {
i
}
}
res5 = amendedFun.apply()
// error: listing amendment cannot have parameters
res6 = test.catch(() -> (Null(new Listing { "a" })) { x -> for (i in List(2)) { i } })
// error: mapping amendment cannot have parameters
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { x -> for (i in List(2)) { [i] = i } })
// error: object amendment cannot have parameters
res8 = test.catch(() -> (Null(new Dynamic { a = 1 })) { x -> for (i in List(2)) { i } })
hidden foo: "foo"?
// error: default value's type has no matching specialization; falls back to the generic error
res9 = test.catch(() -> (foo) { for (i in List(2)) { i } })
hidden x: List<Int>?
res10 = test.catch(() -> ((x) { when (true) { foo = 1 } }))
@@ -0,0 +1,63 @@
// Covers PropertiesLiteralNode's specializations for amending a `VmNull` whose default value is
// each of the underlying parent types it supports.
import "pkl:test"
local class Person {
name: String
age: Int = 0
}
// evalNullWithTypedDefault
local person = Null(new Person { name = "Pigeon" })
res1 = (person) {
age = 3
}
// evalNullWithDynamicDefault
local dynamic = Null(new Dynamic { name = "Pigeon" })
res2 = (dynamic) {
age = 3
}
// evalNullWithListingDefault
local listing = Null(new Listing { "one"; "two" })
res3 = (listing) {
default = (_) -> "three"
}.getOrDefault(5)
// evalNullableMapping
local mapping = Null(new Mapping { ["one"] = 1 })
res4 = (mapping) {
default = (_) -> 0
}.getOrDefault("missing")
// evalNullWithFunctionDefault
local fun = Null(() -> new Dynamic { zero = 0 })
local amendedFun = (fun) {
amended = "amended"
}
res5 = amendedFun.apply()
// error: typed object amendment with an unknown property
res6 = test.catch(() -> (Null(new Person { name = "Pigeon" })) { nickname = "Pidge" })
// error: listing cannot have a property (other than `default`)
res7 = test.catch(() -> (Null(new Listing { "one" })) { foo = "bar" })
// error: mapping cannot have a property (other than `default`)
res8 = test.catch(() -> (Null(new Mapping { ["one"] = 1 })) { foo = "bar" })
// error: function amendment with wrong parameter count
local twoArgFun = (x, y) -> new Dynamic {}
res9 = test.catch(() -> (Null(twoArgFun)) { x -> prop = x })
hidden foo: "foo"?
// error: default value's type has no matching specialization; falls back to the generic error
res10 = test.catch(() -> (foo) { foo = "bar" })
hidden clazz: Class?
res11 = (clazz) {
x = 1
}
@@ -0,0 +1,3 @@
res {
name = "Fred"
}
@@ -30,6 +30,37 @@ res2 {
["two"] = 2
}
}
res2n {
null
true
42
1.23
"foo"
Regex("bar")
5.gb
3.min
Pair(1, 2)
List(1, 2, 3)
Set(1, 2, 3)
Map("one", 1, "two", 2)
new {
name = "Pigeon"
age = 42
}
new {
name = "Pigeon"
age = 42
}
new {
1
2
3
}
new {
["one"] = 1
["two"] = 2
}
}
res3 {
null
true
@@ -61,3 +92,34 @@ res3 {
["two"] = 2
}
}
res3n {
null
true
42
1.23
"foo"
Regex("bar")
5.gb
3.min
Pair(1, 2)
List(1, 2, 3)
Set(1, 2, 3)
Map("one", 1, "two", 2)
new {
name = "Pigeon"
age = 42
}
new {
name = "Pigeon"
age = 42
}
new {
1
2
3
}
new {
["one"] = 1
["two"] = 2
}
}
@@ -55,11 +55,20 @@ res7a = "Element index `-1` is out of range `0`..`2`."
res8 = "Expected key of type `Int`, but got type `String`."
res8a = "Expected key of type `Int`, but got type `String`."
res9 = "Object of type `Listing` cannot have a property (other than `default`)."
res9n = "Object of type `Listing` cannot have a property (other than `default`)."
res10 = "Object of type `Listing` cannot have a property (other than `default`)."
res10n = "Object of type `Listing` cannot have a property (other than `default`)."
res11 = "Object of type `Listing` cannot have a property (other than `default`)."
res11n = "Object of type `Listing` cannot have a property (other than `default`)."
res12 = "Object of type `Listing` cannot have a property (other than `default`)."
res12n = "Object of type `Listing` cannot have a property (other than `default`)."
res12a = "Object of type `Listing` cannot have a property (other than `default`)."
res12an = "Object of type `Listing` cannot have a property (other than `default`)."
res13 = "Object of type `Listing` cannot have a property (other than `default`)."
res13n = "Object of type `Listing` cannot have a property (other than `default`)."
res13a = "Object of type `Listing` cannot have a property (other than `default`)."
res13an = "Object of type `Listing` cannot have a property (other than `default`)."
res14 = "Object of type `Listing` cannot have a property (other than `default`)."
res14n = "Object of type `Listing` cannot have a property (other than `default`)."
res14a = "Object of type `Listing` cannot have a property (other than `default`)."
res14an = "Object of type `Listing` cannot have a property (other than `default`)."
@@ -43,6 +43,50 @@ res2 {
["two"] = 2
}
}
res2n {
[null] = null
[true] = true
[42] = 42
[1.23] = 1.23
["foo"] = "foo"
[Regex("bar")] = Regex("bar")
[5.gb] = 5.gb
[3.min] = 3.min
[Pair(1, 2)] = Pair(1, 2)
[List(1, 2, 3)] = List(1, 2, 3)
[Set(1, 2, 3)] = Set(1, 2, 3)
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
[new {
name = "Pigeon"
age = 42
}] {
name = "Pigeon"
age = 42
}
[new {
name = "Pigeon"
age = 42
}] {
name = "Pigeon"
age = 42
}
[new {
1
2
3
}] {
1
2
3
}
[new {
["one"] = 1
["two"] = 2
}] {
["one"] = 1
["two"] = 2
}
}
res3 {
[null] = null
[true] = true
@@ -87,3 +131,55 @@ res3 {
["two"] = 2
}
}
res3n {
[null] = null
[true] = true
[42] = 42
[1.23] = 1.23
["foo"] = "foo"
[Regex("bar")] = Regex("bar")
[5.gb] = 5.gb
[3.min] = 3.min
[Pair(1, 2)] = Pair(1, 2)
[List(1, 2, 3)] = List(1, 2, 3)
[Set(1, 2, 3)] = Set(1, 2, 3)
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
[new {
name = "Pigeon"
age = 42
}] {
name = "Pigeon"
age = 42
}
[new {
name = "Pigeon"
age = 42
}] {
name = "Pigeon"
age = 42
}
[new {
1
2
3
}] {
1
2
3
}
[new {
["one"] = 1
["two"] = 2
}] {
["one"] = 1
["two"] = 2
}
}
res4 {
["foo"] = "1"
["bar"] = "2"
}
res4n {
["foo"] = "1"
["bar"] = "2"
}
@@ -0,0 +1,21 @@
res1 {
["a"] = 1
["b"] = 2
}
res2 {
a = 1
["b"] = 2
}
res3 {
"updated a"
"b"
}
res4 {
["a"] = 1
["b"] = 2
}
res5 = "Element index `5` is out of range `0`..`0`."
res6 = "Object of type `Listing` cannot have a property (other than `default`)."
res7 = "Object of type `Mapping` cannot have a property (other than `default`)."
res8 = "Expected a function with 1 parameters, but got 2."
res9 = "Cannot instantiate, or amend an instance of, external class `String`."
@@ -0,0 +1,18 @@
res1 {
a = 1
"b"
"c"
}
res2 {
"a"
"b"
"c"
}
res3 {
"a"
"b"
}
res4 = "Object of type `Mapping` cannot have an element."
res5 = "Object of type `Listing` cannot have a property (other than `default`)."
res6 = "Expected a function with 1 parameters, but got 2."
res7 = "Expected value of type `Object | Function<Object>`, but got type `Int`. Value: 42"
@@ -0,0 +1,18 @@
res1 {
"updated a"
"b"
}
res2 {
"updated a"
"b"
"c"
}
res3 {
"updated a"
"b"
}
res4 = "Object of type `Mapping` cannot have an element."
res5 = "Element index `5` is out of range `0`..`0`."
res6 = "Object of type `Listing` cannot have a property (other than `default`)."
res7 = "Expected a function with 1 parameters, but got 2."
res8 = "Expected value of type `Object | Function<Object>`, but got type `Int`. Value: 42"
@@ -0,0 +1,22 @@
res1 {
["a"] = 1
["b"] = 2
}
res2 {
a = 1
["b"] = 2
}
res3 {
"updated a"
"b"
}
res4 {
["a"] = 1
["b"] = 2
}
res5 = "Expected key of type `Int`, but got type `String`."
res6 = "Element index `5` is out of range `0`..`0`."
res7 = "Object of type `Mapping` cannot have a property (other than `default`)."
res8 = "Duplicate definition of member `\"a\"`."
res9 = "Expected a function with 1 parameters, but got 2."
res10 = "Cannot instantiate, or amend an instance of, external class `String`."
@@ -0,0 +1,29 @@
res1 {
a = 1
2
3
}
res2 {
name = "Pigeon"
age = 10
}
res3 {
"a"
2
3
}
res4 {
["a"] = 1
[2] = 2
[3] = 3
}
res5 {
a = 1
2
3
}
res6 = "Expected a function that returns a `Listing`, but got a `Listing`."
res7 = "Expected a function that returns a `Mapping`, but got a `Mapping`."
res8 = "Expected a function that returns an object, but got an object."
res9 = "Cannot instantiate, or amend an instance of, external class `String`."
res10 = "Cannot instantiate, or amend an instance of, external class `List`."
@@ -0,0 +1,22 @@
res1 {
name = "Pigeon"
age = 3
}
res2 {
name = "Pigeon"
age = 3
}
res3 = "three"
res4 = 0
res5 {
zero = 0
amended = "amended"
}
res6 = "Cannot find property `nickname` in object of type `properties#Person`."
res7 = "Object of type `Listing` cannot have a property (other than `default`)."
res8 = "Object of type `Mapping` cannot have a property (other than `default`)."
res9 = "Expected a function with 1 parameters, but got 2."
res10 = "Cannot instantiate, or amend an instance of, external class `String`."
res11 {
x = 1
}