internal/core/eval: allow lists resulting from dependent expressions
Fixes #494
Change-Id: I4d9e92193156e39e8677ba091250a73881d141b0
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6962
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/testdata/cycle/issue494.txtar b/cue/testdata/cycle/issue494.txtar
new file mode 100644
index 0000000..7b1d905
--- /dev/null
+++ b/cue/testdata/cycle/issue494.txtar
@@ -0,0 +1,190 @@
+-- in.cue --
+_Q : [{pos: 0},{pos: 1}]
+
+a: [rn=string]: _Q[0:len(a[rn])]
+a: ben: [{}]
+
+b: [rn=string]: _Q[0:1]
+b: ben: [{}]
+
+c: [rn=string]: [...{l: len(a[rn])}]
+c: ben: [{}]
+
+#d: [rn=string]: [...{pos:uint}] & _Q[0:len(#d[rn])]
+#d: ben: [{}]
+
+d: #d
+
+e: [rn=string]: _Q[0:len(a[rn])+1]
+e: ben: [{}, ...]
+
+f: [rn=string]: _Q[0:len(a[rn])+1]
+f: ben: [{}]
+
+g: [rn=string]: _Q[0:len(a[rn])]
+g: ben: [{}, {}]
+
+-- out/eval --
+Errors:
+f.ben: incompatible list lengths (1 and 2)
+g.ben: incompatible list lengths (1 and 2)
+
+Result:
+(_|_){
+ // [eval]
+ _Q: (#list){
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ 1: (struct){
+ pos: (int){ 1 }
+ }
+ }
+ a: (struct){
+ ben: (#list){
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ }
+ }
+ b: (struct){
+ ben: (#list){
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ }
+ }
+ c: (struct){
+ ben: (#list){
+ 0: (struct){
+ l: (int){ 1 }
+ }
+ }
+ }
+ #d: (#struct){
+ ben: (#list){
+ 0: (#struct){
+ pos: (int){ 0 }
+ }
+ }
+ }
+ d: (#struct){
+ ben: (#list){
+ 0: (#struct){
+ pos: (int){ 0 }
+ }
+ }
+ }
+ e: (struct){
+ ben: (#list){
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ 1: (struct){
+ pos: (int){ 1 }
+ }
+ }
+ }
+ f: (_|_){
+ // [eval]
+ ben: (_|_){
+ // [eval] f.ben: incompatible list lengths (1 and 2)
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ 1: (struct){
+ pos: (int){ 1 }
+ }
+ }
+ }
+ g: (_|_){
+ // [eval]
+ ben: (_|_){
+ // [eval] g.ben: incompatible list lengths (1 and 2)
+ 0: (struct){
+ pos: (int){ 0 }
+ }
+ 1: (struct){
+ }
+ }
+ }
+}
+-- out/compile --
+--- in.cue
+{
+ _Q: [
+ {
+ pos: 0
+ },
+ {
+ pos: 1
+ },
+ ]
+ a: {
+ [string]: 〈1;_Q〉[0:len(〈1;a〉[〈0;-〉])]
+ }
+ a: {
+ ben: [
+ {},
+ ]
+ }
+ b: {
+ [string]: 〈1;_Q〉[0:1]
+ }
+ b: {
+ ben: [
+ {},
+ ]
+ }
+ c: {
+ [string]: [
+ ...{
+ l: len(〈2;a〉[〈1;-〉])
+ },
+ ]
+ }
+ c: {
+ ben: [
+ {},
+ ]
+ }
+ #d: {
+ [string]: ([
+ ...{
+ pos: &(int, >=0)
+ },
+ ] & 〈1;_Q〉[0:len(〈1;#d〉[〈0;-〉])])
+ }
+ #d: {
+ ben: [
+ {},
+ ]
+ }
+ d: 〈0;#d〉
+ e: {
+ [string]: 〈1;_Q〉[0:(len(〈1;a〉[〈0;-〉]) + 1)]
+ }
+ e: {
+ ben: [
+ {},
+ ...,
+ ]
+ }
+ f: {
+ [string]: 〈1;_Q〉[0:(len(〈1;a〉[〈0;-〉]) + 1)]
+ }
+ f: {
+ ben: [
+ {},
+ ]
+ }
+ g: {
+ [string]: 〈1;_Q〉[0:len(〈1;a〉[〈0;-〉])]
+ }
+ g: {
+ ben: [
+ {},
+ {},
+ ]
+ }
+}
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 3298f7d..647d4b8 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -420,13 +420,18 @@
func (n *nodeContext) postDisjunct() {
ctx := n.ctx
- // Use maybeSetCache for cycle breaking
- for n.maybeSetCache(); n.expandOne(); n.maybeSetCache() {
+ for {
+ // Use maybeSetCache for cycle breaking
+ for n.maybeSetCache(); n.expandOne(); n.maybeSetCache() {
+ }
+
+ if aList := n.addLists(ctx); aList != nil {
+ n.updateNodeType(adt.ListKind, aList)
+ } else {
+ break
+ }
}
- if aList := n.addLists(ctx); aList != nil {
- n.updateNodeType(adt.ListKind, aList)
- }
if n.aStruct != nil {
n.updateNodeType(adt.StructKind, n.aStruct)
}
@@ -1600,6 +1605,11 @@
max := 0
var maxNode adt.Expr
+ if m, ok := n.node.Value.(*adt.ListMarker); ok {
+ isOpen = m.IsOpen
+ max = len(n.node.Arcs)
+ }
+
for _, l := range n.vLists {
oneOfTheLists = l
@@ -1747,10 +1757,21 @@
n.openList = isOpen
- n.node.SetValue(c, adt.Partial, &adt.ListMarker{
- Src: ast.NewBinExpr(token.AND, sources...),
- IsOpen: isOpen,
- })
+ if m, ok := n.node.Value.(*adt.ListMarker); !ok {
+ n.node.SetValue(c, adt.Partial, &adt.ListMarker{
+ Src: ast.NewBinExpr(token.AND, sources...),
+ IsOpen: isOpen,
+ })
+ } else {
+ if expr, _ := m.Src.(ast.Expr); expr != nil {
+ sources = append(sources, expr)
+ }
+ m.Src = ast.NewBinExpr(token.AND, sources...)
+ m.IsOpen = m.IsOpen && isOpen
+ }
+
+ n.lists = n.lists[:0]
+ n.vLists = n.vLists[:0]
return oneOfTheLists
}