internal/core/eval: fix spurious cycle for bulk constraints
Fixes #502
Change-Id: I697007d0f7d0fabfedb919c10f4a044b1a6b45de
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7501
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/cycle/issue502.txtar b/cue/testdata/cycle/issue502.txtar
new file mode 100644
index 0000000..7888450
--- /dev/null
+++ b/cue/testdata/cycle/issue502.txtar
@@ -0,0 +1,236 @@
+-- in.cue --
+#T : {
+ config: _
+ body: _
+ mas: [string]: {
+ cfg: _
+ _link: #T
+ link: _link & {config: cfg}
+ }
+}
+
+#a: #T & {
+ config: a: int
+ body: config.a
+ mas: one: {
+ cfg: b: config.a
+ _link: {
+ config: b: int
+ body: config.b
+ mas: two : {
+ cfg: c: config.b
+ _link: {
+ config: c: int
+ body: config.c
+ }
+ }
+ }
+ }
+}
+
+a: #a & {config: a: 34}
+-- out/eval --
+(struct){
+ #T: (#struct){
+ config: (_){ _ }
+ body: (_){ _ }
+ mas: (#struct){
+ }
+ }
+ #a: (#struct){
+ config: (#struct){
+ a: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ one: (#struct){
+ cfg: (#struct){
+ b: (int){ int }
+ }
+ _link: (#struct){
+ config: (#struct){
+ b: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ two: (#struct){
+ cfg: (#struct){
+ c: (int){ int }
+ }
+ _link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ }
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ b: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ two: (#struct){
+ cfg: (#struct){
+ c: (int){ int }
+ }
+ _link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ a: (#struct){
+ config: (#struct){
+ a: (int){ 34 }
+ }
+ body: (int){ 34 }
+ mas: (#struct){
+ one: (#struct){
+ cfg: (#struct){
+ b: (int){ 34 }
+ }
+ _link: (#struct){
+ config: (#struct){
+ b: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ two: (#struct){
+ cfg: (#struct){
+ c: (int){ int }
+ }
+ _link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ }
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ b: (int){ 34 }
+ }
+ body: (int){ 34 }
+ mas: (#struct){
+ two: (#struct){
+ cfg: (#struct){
+ c: (int){ 34 }
+ }
+ _link: (#struct){
+ config: (#struct){
+ c: (int){ int }
+ }
+ body: (int){ int }
+ mas: (#struct){
+ }
+ }
+ link: (#struct){
+ config: (#struct){
+ c: (int){ 34 }
+ }
+ body: (int){ 34 }
+ mas: (#struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+-- out/compile --
+--- in.cue
+{
+ #T: {
+ config: _
+ body: _
+ mas: {
+ [string]: {
+ cfg: _
+ _link: 〈3;#T〉
+ link: (〈0;_link〉 & {
+ config: 〈1;cfg〉
+ })
+ }
+ }
+ }
+ #a: (〈0;#T〉 & {
+ config: {
+ a: int
+ }
+ body: 〈0;config〉.a
+ mas: {
+ one: {
+ cfg: {
+ b: 〈3;config〉.a
+ }
+ _link: {
+ config: {
+ b: int
+ }
+ body: 〈0;config〉.b
+ mas: {
+ two: {
+ cfg: {
+ c: 〈3;config〉.b
+ }
+ _link: {
+ config: {
+ c: int
+ }
+ body: 〈0;config〉.c
+ }
+ }
+ }
+ }
+ }
+ }
+ })
+ a: (〈0;#a〉 & {
+ config: {
+ a: 34
+ }
+ })
+}
diff --git a/cue/testdata/cycle/structural.txtar b/cue/testdata/cycle/structural.txtar
index aaccc3c..ddbba75 100644
--- a/cue/testdata/cycle/structural.txtar
+++ b/cue/testdata/cycle/structural.txtar
@@ -125,6 +125,85 @@
}
}
+// Issue #502 -- unused bulk constraints are not cyclic
+p1: {
+ #T: {
+ a: [string]: link: #T
+ }
+
+ a: #T & {
+ a: one: link: a: two: {}
+ }
+}
+
+// Issue #502 -- but they are if it is invoked within the struct.
+p2: {
+ #T: {
+ a: [string]: link: #T
+ a: b: {}
+ }
+
+ a: #T & {
+ a: one: link: a: two: {}
+ }
+}
+
+// Issue #502 -- or added later.
+p3: {
+ #S: #T: {
+ a: [string]: link: #T
+ }
+
+ #U: {
+ #S
+ #T: a: b: {}
+ }
+
+ a: #U.#T & {
+ a: one: link: a: two: {}
+ }
+}
+
+// Issue #502 -- unused bulk constraints are not cyclic
+p4: {
+ #T: {
+ a: [...{link: #T}]
+ }
+
+ a: #T & {
+ a: [{link: a: [{}]}]
+ }
+}
+
+// Issue #502 -- but they are if it is invoked within the struct.
+p5: {
+ #T: {
+ a: [...{link: #T}]
+ a: [{}]
+ }
+
+ a: #T & {
+ a: [{link: a: [{}]}]
+ }
+}
+
+// Issue #502 -- or added later.
+p6: {
+ #S: #T: {
+ a: [...{link: #T}]
+ }
+
+ #U: {
+ #S
+ #T: a: [{}]
+ }
+
+ a: #U.#T & {
+ a: [{link: a: [{}]}]
+ }
+}
+
+
c1: {
a: {
b: {}
@@ -300,43 +379,46 @@
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:178:8
- ./in.cue:179:8
+ ./in.cue:257:8
+ ./in.cue:258:8
e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
- ./in.cue:178:8
- ./in.cue:179:8
+ ./in.cue:257:8
+ ./in.cue:258:8
e3.a.0: structural cycle
e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
- ./in.cue:178:8
- ./in.cue:179:8
+ ./in.cue:257:8
+ ./in.cue:258:8
e3.a.c: structural cycle
e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:181:8
- ./in.cue:182:8
+ ./in.cue:260:8
+ ./in.cue:261:8
e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:181:8
- ./in.cue:182:8
+ ./in.cue:260:8
+ ./in.cue:261:8
e3.b.0: structural cycle
e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
- ./in.cue:181:8
- ./in.cue:182:8
+ ./in.cue:260:8
+ ./in.cue:261:8
e3.b.c: structural cycle
e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- ./in.cue:186:13
- ./in.cue:187:9
+ ./in.cue:265:13
+ ./in.cue:266:9
e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- ./in.cue:189:9
- ./in.cue:190:13
-z1.z.f.h.h: structural cycle
+ ./in.cue:268:9
+ ./in.cue:269:13
+p2.#T.a.b.link: structural cycle
+p3.#U.#T.a.b.link: structural cycle
+p5.#T.a.0.link: structural cycle
+p6.#U.#T.a.0.link: 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:143:8
+ ./in.cue:222:8
d2.r: structural cycle:
- ./in.cue:143:8
+ ./in.cue:222:8
0: structural cycle:
- ./in.cue:153:19
+ ./in.cue:232:19
Result:
(_|_){
@@ -470,8 +552,10 @@
}
b7: (_|_){
// [structural cycle]
- b: (#list){
- 0: (#list){
+ b: (_|_){
+ // [structural cycle]
+ 0: (_|_){
+ // [structural cycle] b7.a.0: structural cycle
0: (int){ 1 }
}
}
@@ -568,6 +652,212 @@
sum: (int){ 10 }
}
}
+ p1: (struct){
+ #T: (#struct){
+ a: (#struct){
+ }
+ }
+ a: (#struct){
+ a: (#struct){
+ one: (#struct){
+ link: (#struct){
+ a: (#struct){
+ two: (#struct){
+ link: (#struct){
+ a: (#struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p2: (_|_){
+ // [structural cycle]
+ #T: (_|_){
+ // [structural cycle]
+ a: (_|_){
+ // [structural cycle]
+ b: (_|_){
+ // [structural cycle]
+ link: (_|_){
+ // [structural cycle] p2.#T.a.b.link: structural cycle
+ a: (_|_){// {
+ // [string]: {
+ // link: 〈3;#T〉
+ // }
+ // } & {
+ // b: {}
+ // }
+ }
+ }
+ }
+ }
+ }
+ a: (_|_){
+ // [structural cycle]
+ a: (struct){
+ one: (struct){
+ link: (struct){
+ a: (struct){
+ two: (struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p3: (_|_){
+ // [structural cycle]
+ #S: (#struct){
+ #T: (#struct){
+ a: (#struct){
+ }
+ }
+ }
+ #U: (_|_){
+ // [structural cycle]
+ #T: (_|_){
+ // [structural cycle]
+ a: (_|_){
+ // [structural cycle]
+ b: (_|_){
+ // [structural cycle]
+ link: (_|_){
+ // [structural cycle] p3.#U.#T.a.b.link: structural cycle
+ a: (_|_){// {
+ // [string]: {
+ // link: 〈3;#T〉
+ // }
+ // } & {
+ // b: {}
+ // }
+ }
+ }
+ }
+ }
+ }
+ }
+ a: (_|_){
+ // [structural cycle] p3.#U.#T.a.b.link: structural cycle
+ a: (struct){
+ one: (struct){
+ link: (struct){
+ a: (struct){
+ two: (struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p4: (struct){
+ #T: (#struct){
+ a: (list){
+ }
+ }
+ a: (#struct){
+ a: (#list){
+ 0: (#struct){
+ link: (#struct){
+ a: (#list){
+ 0: (#struct){
+ link: (#struct){
+ a: (list){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p5: (_|_){
+ // [structural cycle]
+ #T: (_|_){
+ // [structural cycle]
+ a: (_|_){
+ // [structural cycle]
+ 0: (_|_){
+ // [structural cycle]
+ link: (_|_){
+ // [structural cycle] p5.#T.a.0.link: structural cycle
+ a: (_|_){// [
+ // ...{
+ // link: 〈2;#T〉
+ // },
+ // ] & [
+ // {},
+ // ]
+ }
+ }
+ }
+ }
+ }
+ a: (_|_){
+ // [structural cycle]
+ a: (#list){
+ 0: (struct){
+ link: (struct){
+ a: (#list){
+ 0: (struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p6: (_|_){
+ // [structural cycle]
+ #S: (#struct){
+ #T: (#struct){
+ a: (list){
+ }
+ }
+ }
+ #U: (_|_){
+ // [structural cycle]
+ #T: (_|_){
+ // [structural cycle]
+ a: (_|_){
+ // [structural cycle]
+ 0: (_|_){
+ // [structural cycle]
+ link: (_|_){
+ // [structural cycle] p6.#U.#T.a.0.link: structural cycle
+ a: (_|_){// [
+ // ...{
+ // link: 〈2;#T〉
+ // },
+ // ] & [
+ // {},
+ // ]
+ }
+ }
+ }
+ }
+ }
+ }
+ a: (_|_){
+ // [structural cycle] p6.#U.#T.a.0.link: structural cycle
+ a: (#list){
+ 0: (struct){
+ link: (struct){
+ a: (#list){
+ 0: (struct){
+ }
+ }
+ }
+ }
+ }
+ }
+ }
c1: (_|_){
// [structural cycle]
a: (_|_){
@@ -631,11 +921,11 @@
// [structural cycle]
x: (_|_){
// [structural cycle] d2.a.b.c.d.t: structural cycle:
- // ./in.cue:143:8
+ // ./in.cue:222:8
}
r: (_|_){
// [structural cycle] d2.r: structural cycle:
- // ./in.cue:143:8
+ // ./in.cue:222:8
c: (_|_){// {
// d: {
// h: int
@@ -678,32 +968,19 @@
// [structural cycle]
c: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:153:19
+ // ./in.cue:232:19
}
}
}
indirect: (_|_){
// [structural cycle] 0: structural cycle:
- // ./in.cue:153:19
+ // ./in.cue:232:19
}
i: (int){ 1 }
}
x: (_|_){
- // [structural cycle]
- a: (_|_){
- // [structural cycle]
- b: (_|_){
- // [structural cycle]
- c: (_|_){
- // [structural cycle] 0: structural cycle:
- // ./in.cue:153:19
- }
- }
- }
- indirect: (_|_){
- // [structural cycle] 0: structural cycle:
- // ./in.cue:153:19
- }
+ // [structural cycle] 0: structural cycle:
+ // ./in.cue:232:19
i: (int){ 0 }
}
}
@@ -749,12 +1026,12 @@
// [eval]
a: (_|_){
// [eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:178:8
- // ./in.cue:179:8
+ // ./in.cue:257:8
+ // ./in.cue:258:8
c: (_|_){
// [eval] e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:178:8
- // ./in.cue:179:8
+ // ./in.cue:257:8
+ // ./in.cue:258:8
// e3.a.c: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -763,8 +1040,8 @@
}
0: (_|_){
// [eval] e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct):
- // ./in.cue:178:8
- // ./in.cue:179:8
+ // ./in.cue:257:8
+ // ./in.cue:258:8
// e3.a.0: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -774,12 +1051,12 @@
}
b: (_|_){
// [eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:181:8
- // ./in.cue:182:8
+ // ./in.cue:260:8
+ // ./in.cue:261:8
c: (_|_){
// [eval] e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:181:8
- // ./in.cue:182:8
+ // ./in.cue:260:8
+ // ./in.cue:261:8
// e3.b.c: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -788,8 +1065,8 @@
}
0: (_|_){
// [eval] e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct):
- // ./in.cue:181:8
- // ./in.cue:182:8
+ // ./in.cue:260:8
+ // ./in.cue:261:8
// e3.b.0: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -804,8 +1081,8 @@
// [eval]
0: (_|_){
// [eval] e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- // ./in.cue:186:13
- // ./in.cue:187:9
+ // ./in.cue:265:13
+ // ./in.cue:266:9
0: (struct){
c: (int){ 1 }
}
@@ -815,8 +1092,8 @@
// [eval]
0: (_|_){
// [eval] e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct):
- // ./in.cue:189:9
- // ./in.cue:190:13
+ // ./in.cue:268:9
+ // ./in.cue:269:13
0: (struct){
c: (int){ 1 }
}
@@ -926,7 +1203,7 @@
h: (_|_){
// [structural cycle]
h: (_|_){
- // [structural cycle] z1.z.f.h.h: structural cycle
+ // [structural cycle] z1.z.g.h: structural cycle
}
}
}
@@ -1138,6 +1415,152 @@
}
}
}
+ p1: {
+ #T: {
+ a: {
+ [string]: {
+ link: 〈3;#T〉
+ }
+ }
+ }
+ a: (〈0;#T〉 & {
+ a: {
+ one: {
+ link: {
+ a: {
+ two: {}
+ }
+ }
+ }
+ }
+ })
+ }
+ p2: {
+ #T: {
+ a: {
+ [string]: {
+ link: 〈3;#T〉
+ }
+ }
+ a: {
+ b: {}
+ }
+ }
+ a: (〈0;#T〉 & {
+ a: {
+ one: {
+ link: {
+ a: {
+ two: {}
+ }
+ }
+ }
+ }
+ })
+ }
+ p3: {
+ #S: {
+ #T: {
+ a: {
+ [string]: {
+ link: 〈3;#T〉
+ }
+ }
+ }
+ }
+ #U: {
+ 〈1;#S〉
+ #T: {
+ a: {
+ b: {}
+ }
+ }
+ }
+ a: (〈0;#U〉.#T & {
+ a: {
+ one: {
+ link: {
+ a: {
+ two: {}
+ }
+ }
+ }
+ }
+ })
+ }
+ p4: {
+ #T: {
+ a: [
+ ...{
+ link: 〈2;#T〉
+ },
+ ]
+ }
+ a: (〈0;#T〉 & {
+ a: [
+ {
+ link: {
+ a: [
+ {},
+ ]
+ }
+ },
+ ]
+ })
+ }
+ p5: {
+ #T: {
+ a: [
+ ...{
+ link: 〈2;#T〉
+ },
+ ]
+ a: [
+ {},
+ ]
+ }
+ a: (〈0;#T〉 & {
+ a: [
+ {
+ link: {
+ a: [
+ {},
+ ]
+ }
+ },
+ ]
+ })
+ }
+ p6: {
+ #S: {
+ #T: {
+ a: [
+ ...{
+ link: 〈2;#T〉
+ },
+ ]
+ }
+ }
+ #U: {
+ 〈1;#S〉
+ #T: {
+ a: [
+ {},
+ ]
+ }
+ }
+ a: (〈0;#U〉.#T & {
+ a: [
+ {
+ link: {
+ a: [
+ {},
+ ]
+ }
+ },
+ ]
+ })
+ }
c1: {
a: {
b: {}
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 7511b51..f0fd940 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -328,6 +328,10 @@
return nil, err
}
+ if arc.ChildErrors != nil && arc.ChildErrors.Code == StructuralCycleError {
+ return nil, arc.ChildErrors
+ }
+
return arc, err
}
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index e078801..39d8597 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -388,6 +388,7 @@
ci := closedInfo.clone()
v.Closed = ci
// TODO(performance): use closedInfo.Compact.
+ // TODO: should be clear the list flag here?
}
v.UpdateStatus(adt.Evaluating)
@@ -1174,11 +1175,6 @@
}
status := arc.Status()
- for _, d := range v.Env.Deref {
- if d == arc {
- status = adt.EvaluatingArcs
- }
- }
switch status {
case adt.Evaluating:
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index 7edabca..674f7ef 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -117,6 +117,8 @@
bulkEnv := *env
bulkEnv.DynamicLabel = arc.Label
+ bulkEnv.Deref = nil
+ bulkEnv.Cycles = nil
// match bulk optional fields / pattern properties
matched := false
@@ -132,9 +134,13 @@
return
}
+ addEnv := *env
+ addEnv.Deref = nil
+ addEnv.Cycles = nil
+
// match others
for _, x := range o.additional {
- arc.AddConjunct(adt.MakeConjunct(env, x, o.id))
+ arc.AddConjunct(adt.MakeConjunct(&addEnv, x, o.id))
}
}