Cyclic object asserts sometimes recurse infinitely
- Dominant language
- Jsonnet
- Stars
- 7.6k
- Forks
- 475
- PR merge metrics
- No merged PRs in 30d
Description
Simple example of a cyclic assert that logically should be OK:
```
{
A: {
assert self.f == $.B.f,
f: 1,
},
B: {
assert self.f == $.A.f,
f: 1,
},
}
```
What's happening here is in the assert statement, $.B is generating a new object, and accessing .f of that object triggers a fresh set of assertion evaluations.
Factoring out the $.A does not help:
```
{
local a = self.A,
local b = self.B,
A: {
assert self.f == b.f,
f: 1,
},
B: {
assert self.f == a.f,
f: 1,
},
}
```
However leaning on the mutually recursive local is a work around:
```
local
A = {
assert self.f == B.f,
f: 1,
},
B = {
assert self.f == A.f,
f: 1,
};
B
```
And this can be done inside the object too:
```
{
local outer = self,
local A = {
assert self.f == B.f,
f: 1,
},
local B = {
assert self.f == A.f,
f: 1,
},
A: A,
B: B,
}
```
However, these kinds of refactorings ought not to affect the termination behavior of Jsonnet. What is happening is that buried in the code there is a trick to break the assertion cycle if an object is already having its assertions evaluated. However that test uses the reference of the object, which is not valid in a functional language.
An even more trivial example is:
```
{
assert self.f == 1, // works
assert (self {}).f == 1, // doesn't work
f: 1,
}
```
This problem has been observed in a real config written by @rossmomax.
Contributor guide
Assessment
This issue has not been assessed yet.