internal/core/eval: fix bug in cycle handling
Basically, the dereference list was grown incorrectly.
This was exposed when a cyclic structure is
referenced from within a more than 1 deeply
nested other cyclic structure.
Fixes #555
Fixes #534
Change-Id: I8aa71a37eb19b5efc75fc3bd75c8e00d254d88d7
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7481
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/cycle/structural.txtar b/cue/testdata/cycle/structural.txtar
index fabbba1..7c24827 100644
--- a/cue/testdata/cycle/structural.txtar
+++ b/cue/testdata/cycle/structural.txtar
@@ -61,6 +61,33 @@
a: [a]
}
+// Issue #555
+b8: {
+ x: a
+ a: f: b
+ b: a | string
+}
+
+// Issue #555
+b9: {
+ #a: string | #b | #ref
+ #b: {
+ c: [#a, #a, #a]
+ }
+ #ref: ref: string
+ x: #b | #ref
+}
+
+// Issue #534
+b10: {
+ a: close({
+ b: string | a | c
+ })
+ c: close({
+ d: string | a
+ })
+}
+
c1: {
a: {
b: {}
@@ -236,43 +263,43 @@
e2.a.c: structural cycle
e2.b.c: structural cycle
e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
- ./in.cue:114:8
- ./in.cue:115:8
+ ./in.cue:141:8
+ ./in.cue:142:8
e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
- ./in.cue:114:8
- ./in.cue:115:8
+ ./in.cue:141:8
+ ./in.cue:142:8
e3.a.0: structural cycle
e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
- ./in.cue:114:8
- ./in.cue:115:8
+ ./in.cue:141:8
+ ./in.cue:142:8
e3.a.c: structural cycle
e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:117:8
- ./in.cue:118:8
+ ./in.cue:144:8
+ ./in.cue:145:8
e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:117:8
- ./in.cue:118:8
+ ./in.cue:144:8
+ ./in.cue:145:8
e3.b.0: structural cycle
e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:117:8
- ./in.cue:118:8
+ ./in.cue:144:8
+ ./in.cue:145:8
e3.b.c: structural cycle
e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- ./in.cue:122:13
- ./in.cue:123:9
+ ./in.cue:149:13
+ ./in.cue:150:9
e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- ./in.cue:125:9
- ./in.cue:126:13
+ ./in.cue:152:9
+ ./in.cue:153:13
z1.z.f.h.h: structural cycle
z1.z.g.h: structural cycle
b4.x.y.0: structural cycle:
./in.cue:41:8
d2.a.b.c.d.t: structural cycle:
- ./in.cue:79:8
+ ./in.cue:106:8
d2.r: structural cycle:
- ./in.cue:79:8
+ ./in.cue:106:8
0: structural cycle:
- ./in.cue:89:19
+ ./in.cue:116:19
Result:
(_|_){
@@ -420,6 +447,61 @@
}
}
}
+ b8: (struct){
+ x: (struct){
+ f: (string){ string }
+ }
+ a: (struct){
+ f: (string){ string }
+ }
+ b: (string){ string }
+ }
+ b9: (struct){
+ #a: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ #b: (#struct){
+ c: (#list){
+ 0: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ 1: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ 2: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ }
+ }
+ #ref: (#struct){
+ ref: (string){ string }
+ }
+ x: (struct){ |((#struct){
+ c: (#list){
+ 0: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ 1: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ 2: ((string|struct)){ |((string){ string }, (#struct){
+ ref: (string){ string }
+ }) }
+ }
+ }, (#struct){
+ ref: (string){ string }
+ }) }
+ }
+ b10: (struct){
+ a: (#struct){
+ b: ((string|struct)){ |((string){ string }, (#struct){
+ d: (string){ string }
+ }) }
+ }
+ c: (#struct){
+ d: (string){ string }
+ }
+ }
c1: (_|_){
// [structural cycle]
a: (_|_){
@@ -483,11 +565,11 @@
// [structural cycle]
x: (_|_){
// [structural cycle] d2.a.b.c.d.t: structural cycle:
- // ./in.cue:79:8
+ // ./in.cue:106:8
}
r: (_|_){
// [structural cycle] d2.r: structural cycle:
- // ./in.cue:79:8
+ // ./in.cue:106:8
c: (_|_){// {
// d: {
// h: int
@@ -530,13 +612,13 @@
// [structural cycle]
c: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:89:19
+ // ./in.cue:116:19
}
}
}
indirect: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:89:19
+ // ./in.cue:116:19
}
i: (int){ 1 }
}
@@ -548,13 +630,13 @@
// [structural cycle]
c: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:89:19
+ // ./in.cue:116:19
}
}
}
indirect: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:89:19
+ // ./in.cue:116:19
}
i: (int){ 0 }
}
@@ -601,12 +683,12 @@
// [eval]
a: (_|_){
// [eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:114:8
- // ./in.cue:115:8
+ // ./in.cue:141:8
+ // ./in.cue:142:8
c: (_|_){
// [eval] e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:114:8
- // ./in.cue:115:8
+ // ./in.cue:141:8
+ // ./in.cue:142:8
// e3.a.c: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -615,8 +697,8 @@
}
0: (_|_){
// [eval] e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:114:8
- // ./in.cue:115:8
+ // ./in.cue:141:8
+ // ./in.cue:142:8
// e3.a.0: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -626,12 +708,12 @@
}
b: (_|_){
// [eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:117:8
- // ./in.cue:118:8
+ // ./in.cue:144:8
+ // ./in.cue:145:8
c: (_|_){
// [eval] e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:117:8
- // ./in.cue:118:8
+ // ./in.cue:144:8
+ // ./in.cue:145:8
// e3.b.c: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -640,8 +722,8 @@
}
0: (_|_){
// [eval] e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:117:8
- // ./in.cue:118:8
+ // ./in.cue:144:8
+ // ./in.cue:145:8
// e3.b.0: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -656,8 +738,8 @@
// [eval]
0: (_|_){
// [eval] e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- // ./in.cue:122:13
- // ./in.cue:123:9
+ // ./in.cue:149:13
+ // ./in.cue:150:9
0: (struct){
c: (int){ 1 }
}
@@ -667,8 +749,8 @@
// [eval]
0: (_|_){
// [eval] e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- // ./in.cue:125:9
- // ./in.cue:126:13
+ // ./in.cue:152:9
+ // ./in.cue:153:13
0: (struct){
c: (int){ 1 }
}
@@ -779,8 +861,6 @@
// [structural cycle]
h: (_|_){
// [structural cycle] z1.z.f.h.h: structural cycle
- h: (_|_){// 〈1;g〉
- }
}
}
}
@@ -932,6 +1012,35 @@
〈0;a〉,
]
}
+ b8: {
+ x: 〈0;a〉
+ a: {
+ f: 〈1;b〉
+ }
+ b: (〈0;a〉|string)
+ }
+ b9: {
+ #a: (string|〈0;#b〉|〈0;#ref〉)
+ #b: {
+ c: [
+ 〈1;#a〉,
+ 〈1;#a〉,
+ 〈1;#a〉,
+ ]
+ }
+ #ref: {
+ ref: string
+ }
+ x: (〈0;#b〉|〈0;#ref〉)
+ }
+ b10: {
+ a: close({
+ b: (string|〈1;a〉|〈1;c〉)
+ })
+ c: close({
+ d: (string|〈1;a〉)
+ })
+ }
c1: {
a: {
b: {}
diff --git a/internal/core/eval/closed.go b/internal/core/eval/closed.go
index ea96a32..8fbd14d 100644
--- a/internal/core/eval/closed.go
+++ b/internal/core/eval/closed.go
@@ -374,7 +374,7 @@
if n != nil {
for _, c := range v.Conjuncts {
- c = updateCyclic(c, cyclic, nil)
+ c = updateCyclic(c, cyclic, nil, nil)
c.CloseID += id
n.addExprConjunct(c)
}
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index ec45c9b..98ec358 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -1237,7 +1237,11 @@
ctx.Unify(ctx, arc, adt.Finalized)
for _, c := range arc.Conjuncts {
- c = updateCyclic(c, cyclic, arc)
+ var a []*adt.Vertex
+ if v.Env != nil {
+ a = v.Env.Deref
+ }
+ c = updateCyclic(c, cyclic, arc, a)
c.CloseID = closeID
n.addExprConjunct(c)
}
@@ -1298,12 +1302,11 @@
// a structural cycle.
func (n *nodeContext) updateCyclicStatus(env *adt.Environment) {
if env == nil || !env.Cyclic {
- // fmt.Printf("%p -- %d hasNonCycle\n", n.node, n.node.Label)
n.hasNonCycle = true
}
}
-func updateCyclic(c adt.Conjunct, cyclic bool, deref *adt.Vertex) adt.Conjunct {
+func updateCyclic(c adt.Conjunct, cyclic bool, deref *adt.Vertex, a []*adt.Vertex) adt.Conjunct {
env := c.Env
switch {
case env == nil:
@@ -1311,7 +1314,7 @@
return c
}
env = &adt.Environment{Cyclic: cyclic}
- case deref == nil && env.Cyclic == cyclic:
+ case deref == nil && env.Cyclic == cyclic && len(a) == 0:
return c
default:
// The conjunct may still be in use in other fields, so we should
@@ -1320,8 +1323,15 @@
e.Cyclic = e.Cyclic || cyclic
env = &e
}
+ if deref != nil || len(a) > 0 {
+ cp := make([]*adt.Vertex, 0, len(a)+1)
+ cp = append(cp, a...)
+ if deref != nil {
+ cp = append(cp, deref)
+ }
+ env.Deref = cp
+ }
if deref != nil {
- env.Deref = append(env.Deref, deref)
env.Cycles = append(env.Cycles, deref)
}
return adt.MakeConjunct(env, c.Expr(), c.CloseID)
@@ -1354,7 +1364,7 @@
}
for _, c := range x.Conjuncts {
- c = updateCyclic(c, cyclic, nil)
+ c = updateCyclic(c, cyclic, nil, nil)
c.CloseID = id
n.addExprConjunct(c) // TODO: Pass from eval
}
@@ -1370,7 +1380,7 @@
case *adt.StructMarker:
for _, a := range x.Arcs {
c := adt.MakeConjunct(nil, a, id)
- c = updateCyclic(c, cyclic, nil)
+ c = updateCyclic(c, cyclic, nil, nil)
n.insertField(a.Label, c)
}