Implement power assertions (#1384)

This adds power assertions to Pkl!

This implements the SPICE described in
https://github.com/apple/pkl-evolution/pull/29

This follows the power assertions style of reporting also found in
Groovy, Kotlin, and others.

* Literal values are not emitted in the diagram
* Stdlib constructors of literals like `List(1, 2)` are also considered
  literals

Power assertions are added to:

* Failing type constraints
* Failing test facts

Power assertions are implemented as a truffle instrument to observe
execution.
When an assertion fails, the instrument is created and the assertion is
run again to observe facts.
This incurs runtime overhead to collect facts, but has no impact on code
in the non-error case.

---------

Co-authored-by: Islon Scherer <islonscherer@gmail.com>
This commit is contained in:
Daniel Chao
2026-01-20 21:41:33 -08:00
committed by GitHub
parent 3cd294b62a
commit 03a7676e64
107 changed files with 2133 additions and 290 deletions
@@ -0,0 +1 @@
foo: String(this == "foo") = "bar"
@@ -0,0 +1,3 @@
local list = List(1, 2, 3)
foo: Dynamic(this == new Dynamic { ...list })
@@ -0,0 +1,4 @@
local list = List(1)
foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
@@ -0,0 +1 @@
foo: String(true, this.length > 5) = "bob"
@@ -0,0 +1,3 @@
name: String = "Bub"
foo: String(this == "Hello, \(name)") = "Hello, Patty"
@@ -0,0 +1,9 @@
open class Foo {
name: String = "Foo"
}
class Bar extends Foo {
bub: String(startsWith(super.name)) = "Bubby"
}
bar: Bar
@@ -0,0 +1 @@
foo: String(this == "foo") | String(this == "qux") = "bar"
@@ -0,0 +1,10 @@
class Bird {
name: String?
}
// this will create `NullPropagatingOperationNode` and `InvokeVirtualMethodNode` for
// `name?.contains("Bob")`.
// this tests that we don't have two duplicate values in the diagram.
p: Bird(name?.contains("Bob") ?? false) = new {
name = "Pigeon"
}
@@ -0,0 +1,4 @@
local name = "Bob"
local function greet(name: String) = "Hello, \(name)"
foo: Any(greet(name) == "Hello, \(name)!") = null
@@ -0,0 +1,7 @@
foo: String(startsWith(bub.name)) = "Huzzah"
bub: Person = new { name = "Bub" }
class Person {
name: String
}
@@ -0,0 +1,10 @@
foo: Int(this
+ two
/ three
== four) = 4
local two = 2
local three = 3
local four = 4
@@ -0,0 +1,3 @@
foo: List(fold(0, (a, b) -> a + b) == 5) = myList
myList = List(1, 2, 3)
@@ -0,0 +1,3 @@
foo: String(isCapitalized) = "hello"
local isCapitalized = (it: String) -> it == it.capitalize()
@@ -0,0 +1,5 @@
foo: String(isCapitalized) = "hello"
local isCapitalized = (it: String) ->
let (first = it.take(1).toUpperCase())
it == "\(first)\(it.drop(1))"
@@ -0,0 +1 @@
foo: UInt8 = -5
@@ -0,0 +1 @@
foo: NonNull = null
@@ -0,0 +1,10 @@
// stdlib constructors are treated as literals
foo: Int(
IntSeq(1, 5).toList().contains(this)
|| Map(1, 2, 3, 4).values.contains(this)
|| List(1, 2, 3, 4).contains(this)
|| Set(1, 2, 3, 4).contains(this)
|| Pair(1, 2).second == this
|| Bytes(1, 2, 3, 4).toList().contains(this)
|| Regex("1234").toString().length == this
) = 10
@@ -0,0 +1,12 @@
local num = 1
// stdlib constructors that have a non-literal are not treated as literals
foo: Int(
IntSeq(num, 5).toList().contains(this)
|| Map(num, 2, 3, 4).values.contains(this)
|| List(num, 2, 3, 4).contains(this)
|| Set(num, 2, 3, 4).contains(this)
|| Pair(num, 2).second == this
|| Bytes(num, 2, 3, 4).toList().contains(this)
|| Regex("\(num)234").toString().length == this
) = 10
@@ -1,5 +1,10 @@
amends "pkl:Project"
dependencies {
["badLocalProject"] = import("../badLocalProject/PklProject")
["badLocalProject"] = import("PklProject")
}
// hack: override the project file URI so that our tests render the same error message
// across different machines (it will truncate at 100 chars, and otherwise would be influenced by
// where the pkl project is placed within the file tree).
projectFileUri = "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
@@ -2,6 +2,11 @@
Type constraint `this >= min` violated.
Value: 3
this >= min
│ │ │
3 │ 4
false
x | max: Int(this >= min)
^^^^^^^^^^^
at constraints5#Gauge.max (file:///$snippetsDir/input/classes/constraints5.pkl)
@@ -10,6 +10,10 @@ at unionTypesErrorDifferent2#X.a (file:///$snippetsDir/input/classes/unionTypesE
Type constraint `length > 3` violated.
Value: List(1, 2, 3)
length > 3
│ │
3 false
x | a = List(1, 2, 3)
^^^^^^^^^^^^^
at unionTypesErrorDifferent2#res1.a (file:///$snippetsDir/input/classes/unionTypesErrorDifferent2.pkl)
@@ -2,6 +2,11 @@
Type constraint `!isEmpty` violated.
Value: List()
!isEmpty
││
│true
false
x | names: List<String>(!isEmpty)
^^^^^^^^
at wrongType6#Person.names (file:///$snippetsDir/input/classes/wrongType6.pkl)
@@ -2,6 +2,13 @@
Type constraint `firstOneIsSandy` violated.
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
(it: Listing<Bird>) -> it[0].name == "Sandy"
│ │ │ │
│ │ │ false
│ │ "Bob"
│ new Bird { name = "Bob" }
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
x | birds: Listing(firstOneIsSandy) = new {
^^^^^^^^^^^^^^^
at constraintDetails1#birds (file:///$snippetsDir/input/errors/constraintDetails1.pkl)
@@ -3,6 +3,17 @@ Type constraint `let (myself: Listing<Bird> = this)
myself[0].name == "Sandy"` violated.
Value: new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
let (myself: Listing<Bird> = this)
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
myself[0].name == "Sandy"
│ │ │ │
│ │ │ false
│ │ "Bob"
│ new Bird { name = "Bob" }
new Listing { new Bird { name = "Bob" }; new Bird { name = ? } }
x | let (myself: Listing<Bird> = this)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at constraintDetails2#birds (file:///$snippetsDir/input/errors/constraintDetails2.pkl)
@@ -2,6 +2,11 @@
Type constraint `toList().every((it: Listing<Bird>) -> it[0].name == "Bob")` violated.
Value: new Listing { new Listing { new Bird { name = "Eagle" }; new Bird { name = ? ...
toList().every((it: Listing<Bird>) -> it[0].name == "Bob")
│ │
│ false
List(new Listing { new Bird { name = "Eagle" }; new Bird { name = ? } })
x | foo: Listing(toList().every((it: Listing<Bird>) -> it[0].name == "Bob")) = new {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at constraintDetails3#foo (file:///$snippetsDir/input/errors/constraintDetails3.pkl)
@@ -2,6 +2,10 @@
Type constraint `isBetween(0, 65535)` violated.
Value: -1
isBetween(0, 65535)
false
xxxx | typealias UInt16 = Int(isBetween(0, 65535))
^^^^^^^^^^^^^^^^^^^
at intrinsifiedTypeAlias2#res1 (pkl:base)
@@ -2,6 +2,10 @@
Type constraint `!(this is Null)` violated.
Value: null
!(this is Null)
false
xx | typealias NonNull = Any(!(this is Null))
^^^^^^^^^^^^^^^
at intrinsifiedTypeAlias3#res1 (pkl:base)
@@ -2,6 +2,10 @@
Type constraint `isBetween(0, 255)` violated.
Value: 65535
isBetween(0, 255)
false
xxxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes1#foo (pkl:base)
@@ -2,6 +2,10 @@
Type constraint `isBetween(0, 255)` violated.
Value: 4095
isBetween(0, 255)
false
xxxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes2#res (pkl:base)
@@ -2,6 +2,10 @@
Type constraint `isBetween(0, 255)` violated.
Value: 65535
isBetween(0, 255)
false
xxxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at invalidBytes3#bytes (pkl:base)
@@ -2,6 +2,10 @@
Type constraint `isBetween(0, 255)` violated.
Value: 65535
isBetween(0, 255)
false
xxxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at pkl.base#List.toBytes (pkl:base)
@@ -2,6 +2,11 @@
Type constraint `!isEmpty` violated.
Value: ""
!isEmpty
││
│true
false
x | res: Listing<String(!isEmpty)> = new Listing<String> {
^^^^^^^^
at listingTypeCheckError3#res (file:///$snippetsDir/input/errors/listingTypeCheckError3.pkl)
@@ -2,6 +2,10 @@
Type constraint `endsWith("ga")` violated.
Value: "hola"
endsWith("ga")
false
x | res: Listing<String(!isEmpty)> = new Listing<String(endsWith("ga"))> {
^^^^^^^^^^^^^^
at listingTypeCheckError4#res (file:///$snippetsDir/input/errors/listingTypeCheckError4.pkl)
@@ -2,6 +2,10 @@
Type constraint `length == 5` violated.
Value: "hi"
length == 5
│ │
2 false
xx | res: Mapping<Listing<String(length == 5)>, String> = bar
^^^^^^^^^^^
at mappingTypeCheckError6#res (file:///$snippetsDir/input/errors/mappingTypeCheckError6.pkl)
@@ -2,6 +2,10 @@
Type constraint `length == 5` violated.
Value: "hi"
length == 5
│ │
2 false
x | res = new Mapping<Listing<String(length == 5)>, String> {
^^^^^^^^^^^
at mappingTypeCheckError7#res (file:///$snippetsDir/input/errors/mappingTypeCheckError7.pkl)
@@ -0,0 +1,24 @@
–– Pkl Error ––
Type constraint `this == "foo"` violated.
Value: "bar"
this == "foo"
│ │
│ false
"bar"
x | foo: String(this == "foo") = "bar"
^^^^^^^^^^^^^
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.pkl)
x | foo: String(this == "foo") = "bar"
^^^^^
at typeConstraints1#foo (file:///$snippetsDir/input/errors/power/typeConstraints1.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)
@@ -0,0 +1,26 @@
–– Pkl Error ––
Type constraint `this == new Dynamic { ...list }` violated.
Value: new Dynamic {}
this == new Dynamic { ...list }
│ │ │ │
│ │ │ List(1, 2, 3)
│ │ new Dynamic { 1; 2; 3 }
│ false
new Dynamic {}
x | foo: Dynamic(this == new Dynamic { ...list })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.pkl)
x | foo: Dynamic(this == new Dynamic { ...list })
^^^
at typeConstraints10#foo (file:///$snippetsDir/input/errors/power/typeConstraints10.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)
@@ -0,0 +1,25 @@
–– Pkl Error ––
Type constraint `this == new Dynamic { for (elem in list) { elem + 1 } }` violated.
Value: new Dynamic {}
this == new Dynamic { for (elem in list) { elem + 1 } }
│ │ │ │
│ │ new Dynamic { 2 } List(1)
│ false
new Dynamic {}
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.pkl)
x | foo: Dynamic(this == new Dynamic { for (elem in list) { elem + 1 } })
^^^
at typeConstraints11#foo (file:///$snippetsDir/input/errors/power/typeConstraints11.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)
@@ -0,0 +1,24 @@
–– Pkl Error ––
Type constraint `this.length > 5` violated.
Value: "bob"
this.length > 5
│ │ │
│ 3 false
"bob"
x | foo: String(true, this.length > 5) = "bob"
^^^^^^^^^^^^^^^
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.pkl)
x | foo: String(true, this.length > 5) = "bob"
^^^^^
at typeConstraints12#foo (file:///$snippetsDir/input/errors/power/typeConstraints12.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)
@@ -0,0 +1,26 @@
–– Pkl Error ––
Type constraint `this == "Hello, \(name)"` violated.
Value: "Hello, Patty"
this == "Hello, \(name)"
│ │ │ │
│ │ │ "Bub"
│ │ "Hello, Bub"
│ false
"Hello, Patty"
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.pkl)
x | foo: String(this == "Hello, \(name)") = "Hello, Patty"
^^^^^^^^^^^^^^
at typeConstraints13#foo (file:///$snippetsDir/input/errors/power/typeConstraints13.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)
@@ -0,0 +1,23 @@
–– Pkl Error ––
Type constraint `startsWith(super.name)` violated.
Value: "Bubby"
startsWith(super.name)
│ │
false "Foo"
x | bub: String(startsWith(super.name)) = "Bubby"
^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.pkl)
x | bub: String(startsWith(super.name)) = "Bubby"
^^^^^^^
at typeConstraints14#Bar.bub (file:///$snippetsDir/input/errors/power/typeConstraints14.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)
@@ -0,0 +1,37 @@
–– Pkl Error ––
Expected value of type `String(this == "foo") | String(this == "qux")`, but got a different `String`.
Value: "bar"
x | foo: String(this == "foo") | String(this == "qux") = "bar"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.pkl)
* Value is not of type `String(this == "foo")` because:
Type constraint `this == "foo"` violated.
Value: "bar"
this == "foo"
│ │
│ false
"bar"
* Value is not of type `String(this == "qux")` because:
Type constraint `this == "qux"` violated.
Value: "bar"
this == "qux"
│ │
│ false
"bar"
x | foo: String(this == "foo") | String(this == "qux") = "bar"
^^^^^
at typeConstraints15#foo (file:///$snippetsDir/input/errors/power/typeConstraints15.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)
@@ -0,0 +1,24 @@
–– Pkl Error ––
Type constraint `name?.contains("Bob") ?? false` violated.
Value: new Bird { name = "Pigeon" }
name?.contains("Bob") ?? false
│ │ │
│ false false
"Pigeon"
x | p: Bird(name?.contains("Bob") ?? false) = new {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.pkl)
x | p: Bird(name?.contains("Bob") ?? false) = new {
^^^^^
at typeConstraints16#p (file:///$snippetsDir/input/errors/power/typeConstraints16.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)
@@ -0,0 +1,27 @@
–– Pkl Error ––
Type constraint `greet(name) == "Hello, \(name)!"` violated.
Value: null
greet(name) == "Hello, \(name)!"
│ │ │ │ │
│ │ │ │ "Bob"
│ │ │ "Hello, Bob!"
│ │ false
│ "Bob"
"Hello, Bob"
x | foo: Any(greet(name) == "Hello, \(name)!") = null
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.pkl)
x | foo: Any(greet(name) == "Hello, \(name)!") = null
^^^^
at typeConstraints17#foo (file:///$snippetsDir/input/errors/power/typeConstraints17.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)
@@ -0,0 +1,24 @@
–– Pkl Error ––
Type constraint `startsWith(bub.name)` violated.
Value: "Huzzah"
startsWith(bub.name)
│ │ │
false │ "Bub"
new Person { name = "Bub" }
x | foo: String(startsWith(bub.name)) = "Huzzah"
^^^^^^^^^^^^^^^^^^^^
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.pkl)
x | foo: String(startsWith(bub.name)) = "Huzzah"
^^^^^^^^
at typeConstraints2#foo (file:///$snippetsDir/input/errors/power/typeConstraints2.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)
@@ -0,0 +1,41 @@
–– Pkl Error ––
Type constraint `this
+ two
/ three
== four` violated.
Value: 4
this
4
+ two
│ │
│ 2
4.666666666666667
/ three
│ │
│ 3
0.6666666666666666
== four
│ │
│ 4
false
x | foo: Int(this
^^^^
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.pkl)
x | == four) = 4
^
at typeConstraints3#foo (file:///$snippetsDir/input/errors/power/typeConstraints3.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)
@@ -0,0 +1,23 @@
–– Pkl Error ––
Type constraint `fold(0, (a, b) -> a + b) == 5` violated.
Value: List(1, 2, 3)
fold(0, (a, b) -> a + b) == 5
│ │
6 false
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.pkl)
x | foo: List(fold(0, (a, b) -> a + b) == 5) = myList
^^^^^^
at typeConstraints4#foo (file:///$snippetsDir/input/errors/power/typeConstraints4.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)
@@ -0,0 +1,26 @@
–– Pkl Error ––
Type constraint `isCapitalized` violated.
Value: "hello"
(it: String) -> it == it.capitalize()
│ │ │ │
│ │ │ "Hello"
│ │ "hello"
│ false
"hello"
x | foo: String(isCapitalized) = "hello"
^^^^^^^^^^^^^
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.pkl)
x | foo: String(isCapitalized) = "hello"
^^^^^^^
at typeConstraints5#foo (file:///$snippetsDir/input/errors/power/typeConstraints5.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)
@@ -0,0 +1,32 @@
–– Pkl Error ––
Type constraint `isCapitalized` violated.
Value: "hello"
(it: String) ->
let (first = it.take(1).toUpperCase())
│ │ │
│ "h" "H"
"hello"
it == "\(first)\(it.drop(1))"
│ │ │ │ │ │
│ │ │ "H" │ "ello"
│ │ "Hello" "hello"
│ false
"hello"
x | foo: String(isCapitalized) = "hello"
^^^^^^^^^^^^^
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.pkl)
x | foo: String(isCapitalized) = "hello"
^^^^^^^
at typeConstraints6#foo (file:///$snippetsDir/input/errors/power/typeConstraints6.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)
@@ -0,0 +1,23 @@
–– Pkl Error ––
Type constraint `isBetween(0, 255)` violated.
Value: -5
isBetween(0, 255)
false
xxxx | typealias UInt8 = Int(isBetween(0, 255))
^^^^^^^^^^^^^^^^^
at typeConstraints7#foo (pkl:base)
x | foo: UInt8 = -5
^^
at typeConstraints7#foo (file:///$snippetsDir/input/errors/power/typeConstraints7.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)
@@ -0,0 +1,23 @@
–– Pkl Error ––
Type constraint `!(this is Null)` violated.
Value: null
!(this is Null)
false
xx | typealias NonNull = Any(!(this is Null))
^^^^^^^^^^^^^^^
at typeConstraints8#foo (pkl:base)
x | foo: NonNull = null
^^^^
at typeConstraints8#foo (file:///$snippetsDir/input/errors/power/typeConstraints8.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)
@@ -0,0 +1,59 @@
–– Pkl Error ––
Type constraint `IntSeq(1, 5).toList().contains(this)
|| Map(1, 2, 3, 4).values.contains(this)
|| List(1, 2, 3, 4).contains(this)
|| Set(1, 2, 3, 4).contains(this)
|| Pair(1, 2).second == this
|| Bytes(1, 2, 3, 4).toList().contains(this)
|| Regex("1234").toString().length == this` violated.
Value: 10
IntSeq(1, 5).toList().contains(this)
│ │ │
│ false 10
List(1, 2, 3, 4, 5)
|| Map(1, 2, 3, 4).values.contains(this)
│ │ │ │
false │ false 10
List(2, 4)
|| List(1, 2, 3, 4).contains(this)
│ │ │
false false 10
|| Set(1, 2, 3, 4).contains(this)
│ │ │
false false 10
|| Pair(1, 2).second == this
│ │ │ │
false 2 │ 10
false
|| Bytes(1, 2, 3, 4).toList().contains(this)
│ │ │ │
false │ false 10
List(1, 2, 3, 4)
|| Regex("1234").toString().length == this
│ │ │ │ │
false │ 13 │ 10
│ false
"Regex(\"1234\")"
x | IntSeq(1, 5).toList().contains(this)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.pkl)
xx | ) = 10
^^
at typeConstraints9#foo (file:///$snippetsDir/input/errors/power/typeConstraints9.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)
@@ -0,0 +1,67 @@
–– Pkl Error ––
Type constraint `IntSeq(num, 5).toList().contains(this)
|| Map(num, 2, 3, 4).values.contains(this)
|| List(num, 2, 3, 4).contains(this)
|| Set(num, 2, 3, 4).contains(this)
|| Pair(num, 2).second == this
|| Bytes(num, 2, 3, 4).toList().contains(this)
|| Regex("\(num)234").toString().length == this` violated.
Value: 10
IntSeq(num, 5).toList().contains(this)
│ │ │ │ │
│ 1 │ false 10
IntSeq(1, 5) List(1, 2, 3, 4, 5)
|| Map(num, 2, 3, 4).values.contains(this)
│ │ │ │ │ │
│ │ 1 │ false 10
│ Map(1, 2, 3, 4) List(2, 4)
false
|| List(num, 2, 3, 4).contains(this)
│ │ │ │ │
│ │ 1 false 10
│ List(1, 2, 3, 4)
false
|| Set(num, 2, 3, 4).contains(this)
│ │ │ │ │
│ │ 1 false 10
│ Set(1, 2, 3, 4)
false
|| Pair(num, 2).second == this
│ │ │ │ │ │
│ │ 1 2 │ 10
│ Pair(1, 2) false
false
|| Bytes(num, 2, 3, 4).toList().contains(this)
│ │ │ │ │ │
│ │ 1 │ false 10
│ Bytes(1, 2, 3, 4) List(1, 2, 3, 4)
false
|| Regex("\(num)234").toString().length == this
│ │ │ │ │ │ │ │
│ │ │ 1 │ 13 │ 10
│ │ "1234" │ false
│ Regex("1234") "Regex(\"1234\")"
false
x | IntSeq(num, 5).toList().contains(this)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.pkl)
xx | ) = 10
^^
at typeConstraints9a#foo (file:///$snippetsDir/input/errors/power/typeConstraints9a.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)
@@ -2,6 +2,11 @@
Type constraint `!isEmpty` violated.
Value: ""
!isEmpty
││
│true
false
x | res1: String(!isEmpty) = ""
^^^^^^^^
at typedModuleProperties3#res1 (file:///$snippetsDir/input/modules/typedModuleProperties3.pkl)
@@ -1,6 +1,6 @@
–– Pkl Error ––
Expected value of type `*RemoteDependency | Project(isValidLoadDependency)`, but got a different `pkl.Project`.
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetting...
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] ...
xxx | dependencies: Mapping<String(!contains("/")), *RemoteDependency | Project(isValidLoadDependency)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,8 +8,32 @@ at pkl.Project#dependencies (pkl:Project)
* Value is not of type `Project(isValidLoadDependency)` because:
Type constraint `isValidLoadDependency` violated.
Value: new ModuleClass { package = null; tests {}; dependencies {}; evaluatorSetti...
Value: new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"...
x | ["badLocalProject"] = import("../badLocalProject/PklProject")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(it: Project) ->
isUriLocal(projectFileUri, it.projectFileUri)
│ │ │ │
true │ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
"file:///$snippetsDir/projects/badProjectDeps4/PklProject"
&& it.projectFileUri.endsWith("/PklProject")
│ │ │ │
│ │ │ true
│ │ "file:///$snippetsDir/projects/badProjectDeps4/PklProject"
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
true
&& it != module
│ │ │
│ │ false
│ new ModuleClass { package = ?; tests = ?; dependencies { ["badLocalProject"] = ? }; evaluatorSett...
false
&& it.package != null
false
x | ["badLocalProject"] = import("PklProject")
^^^^^^^^^^^^^^^^^^^^
at PklProject#dependencies["badLocalProject"] (file:///$snippetsDir/input/projects/badProjectDeps4/PklProject)
@@ -2,6 +2,10 @@
Type constraint `endsWith(lastName)` violated.
Value: "Jimmy Bird"
endsWith(lastName)
│ │
false "Birdo"
x | typealias Birds = Listing<String(endsWith(lastName))>
^^^^^^^^^^^^^^^^^^
at typeAliasContext#res (file:///$snippetsDir/input/types/helpers/originalTypealias.pkl)
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-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.
@@ -88,10 +88,26 @@ class EvaluateTestsTest {
assertThat(res.failures.size).isEqualTo(2)
val fail1 = res.failures[0]
assertThat(fail1.message).isEqualTo("1 == 2 (repl:text)")
assertThat(fail1.message)
.isEqualTo(
"""
1 == 2 (repl:text)
false
"""
.trimIndent()
)
val fail2 = res.failures[1]
assertThat(fail2.message).isEqualTo(""""foo" == "bar" (repl:text)""")
assertThat(fail2.message)
.isEqualTo(
"""
"foo" == "bar" (repl:text)
false
"""
.trimIndent()
)
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-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.
@@ -192,7 +192,7 @@ class StackTraceRendererTest {
val loop = StackTraceRenderer.StackFrameLoop(loopFrames, 1)
val frames = listOf(createFrame("bar", 1), createFrame("baz", 2), loop)
val formatter = AnsiStringBuilder(false)
renderer.doRender(frames, null, formatter, "", true)
renderer.doRender(frames, null, null, formatter, "", true)
val renderedFrames = formatter.toString()
assertThat(renderedFrames)
.isEqualTo(