google / google/jsonnet

Cyclic object asserts sometimes recurse infinitely

Open
#451 14 comments 2 reactions 0 assignees View on GitHub
bug
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.