this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue: allow cyclic references in lists

This is done by making lists considerably less efficient,
but that is for later concern. Still better, probably,
than creating a linked list.

Errors are now retained at the value level within lists.
This is okay, as validation descends into list values
as well.

Change-Id: Ifcd35d383f061dab82164d8da36f693dbea64847
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1900
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>

authored by

Marcel van Lohuizen and committed by
Marcel van Lohuizen
9ee652dd 6c58f258

+117 -112
+5 -3
cue/ast.go
··· 421 421 } 422 422 423 423 case *ast.ListLit: 424 - list := &list{baseValue: newExpr(n)} 425 - for _, e := range n.Elts { 426 - list.a = append(list.a, v.walk(e)) 424 + arcs := []arc{} 425 + for i, e := range n.Elts { 426 + arcs = append(arcs, arc{feature: label(i), v: v.walk(e)}) 427 427 } 428 + s := &structLit{baseValue: newExpr(n), arcs: arcs} 429 + list := &list{baseValue: newExpr(n), elem: s} 428 430 list.initLit() 429 431 if n.Ellipsis != token.NoPos || n.Type != nil { 430 432 list.len = &bound{list.baseValue, opGeq, list.len}
+40 -37
cue/binop.go
··· 926 926 src = mkBin(ctx, src.Pos(), op, x, other) 927 927 return ctx.mkErr(src, "incompatible list lengths: %v", n) 928 928 } 929 - var a, rest []value 930 - var rtyp value 931 - nx, ny := len(x.a), len(y.a) 932 - if nx < ny { 933 - a = make([]value, nx, ny) 934 - rest = y.a[nx:] 935 - rtyp = x.typ 929 + sx := x.elem.arcs 930 + xa := sx 931 + sy := y.elem.arcs 932 + ya := sy 933 + for len(xa) < len(ya) { 934 + xa = append(xa, arc{feature: label(len(xa)), v: x.typ}) 935 + } 936 + for len(ya) < len(xa) { 937 + ya = append(ya, arc{feature: label(len(ya)), v: y.typ}) 938 + } 936 939 937 - } else { 938 - a = make([]value, ny, nx) 939 - rest = x.a[ny:] 940 - rtyp = y.typ 941 - } 942 940 typ := x.typ 943 941 max, ok := n.(*numLit) 944 - if !ok || len(a)+len(rest) < max.intValue(ctx) { 942 + if !ok || len(xa) < max.intValue(ctx) { 945 943 typ = unify(ctx, src, x.typ.(evaluated), y.typ.(evaluated)) 946 944 if isBottom(typ) { 947 945 src = mkBin(ctx, src.Pos(), op, x, other) ··· 949 947 } 950 948 } 951 949 952 - for i := range a { 953 - ai := unify(ctx, src, x.at(ctx, i).evalPartial(ctx), y.at(ctx, i).evalPartial(ctx)) 954 - if isBottom(ai) { 955 - return ai 956 - } 957 - a[i] = ai 958 - } 959 - for _, n := range rest { 960 - an := unify(ctx, src, n.evalPartial(ctx), rtyp.(evaluated)) 961 - if isBottom(an) { 962 - return an 963 - } 964 - a = append(a, an) 965 - } 966 - return &list{baseValue: binSrc(src.Pos(), op, x, other), a: a, typ: typ, len: n} 950 + // TODO: use forwarding instead of this mild hack. 951 + x.elem.arcs = xa 952 + y.elem.arcs = ya 953 + s := unify(ctx, src, x.elem, y.elem).(*structLit) 954 + x.elem.arcs = sx 955 + y.elem.arcs = sy 956 + 957 + base := binSrc(src.Pos(), op, x, other) 958 + return &list{baseValue: base, elem: s, typ: typ, len: n} 967 959 968 960 case opEql, opNeq: 969 961 y, ok := other.(*list) 970 962 if !ok { 971 963 break 972 964 } 973 - if len(x.a) != len(y.a) { 965 + if len(x.elem.arcs) != len(y.elem.arcs) { 974 966 return boolTonode(src, false) 975 967 } 976 - for i := range x.a { 968 + for i := range x.elem.arcs { 977 969 if !test(ctx, src, op, x.at(ctx, i), y.at(ctx, i)) { 978 970 return boolTonode(src, false) 979 971 } ··· 986 978 break 987 979 } 988 980 n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: y.typ} 989 - n.a = append(x.a, y.a...) 981 + arcs := []arc{} 982 + for _, v := range x.elem.arcs { 983 + arcs = append(arcs, arc{feature: label(len(arcs)), v: v.v}) 984 + } 985 + for _, v := range y.elem.arcs { 986 + arcs = append(arcs, arc{feature: label(len(arcs)), v: v.v}) 987 + } 990 988 switch v := y.len.(type) { 991 989 case *numLit: 992 990 // Closed list 993 991 ln := &numLit{numBase: v.numBase} 994 - ln.v.SetInt64(int64(len(n.a))) 992 + ln.v.SetInt64(int64(len(arcs))) 995 993 n.len = ln 996 994 default: 997 995 // Open list 998 - n.len = y.len 996 + n.len = y.len // TODO: add length of x? 999 997 } 998 + n.elem = &structLit{baseValue: n.baseValue, arcs: arcs} 1000 999 return n 1001 1000 1002 1001 case opMul: ··· 1005 1004 panic("multiplication must be int type") 1006 1005 } 1007 1006 n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: x.typ} 1008 - if len(x.a) > 0 { 1007 + arcs := []arc{} 1008 + if len(x.elem.arcs) > 0 { 1009 1009 if !k.isGround() { 1010 1010 // should never reach here. 1011 1011 break ··· 1013 1013 if ln := other.(*numLit).intValue(ctx); ln > 0 { 1014 1014 for i := 0; i < ln; i++ { 1015 1015 // TODO: copy values 1016 - n.a = append(n.a, x.a...) 1016 + for _, a := range x.elem.arcs { 1017 + arcs = append(arcs, arc{feature: label(len(arcs)), v: a.v}) 1018 + } 1017 1019 } 1018 1020 } else if ln < 0 { 1019 1021 return ctx.mkErr(src, "negative number %d multiplies list", ln) ··· 1023 1025 case *numLit: 1024 1026 // Closed list 1025 1027 ln := &numLit{numBase: v.numBase} 1026 - ln.v.SetInt64(int64(len(n.a))) 1028 + ln.v.SetInt64(int64(len(arcs))) 1027 1029 n.len = ln 1028 1030 default: 1029 1031 // Open list 1030 - n.len = x.len 1032 + n.len = x.len // TODO: multiply length? 1031 1033 } 1034 + n.elem = &structLit{baseValue: n.baseValue, arcs: arcs} 1032 1035 return n 1033 1036 } 1034 1037 return ctx.mkIncompatible(src, op, x, other)
+5 -5
cue/debug.go
··· 363 363 case *top, *basicType: 364 364 open = true 365 365 } 366 - if !ok || ln > len(x.a) { 366 + if !ok || ln > len(x.elem.arcs) { 367 367 if !open && !isTop(x.typ) { 368 368 p.debugStr(x.len) 369 369 write("*[") 370 370 p.debugStr(x.typ) 371 371 write("]") 372 - if len(x.a) == 0 { 372 + if len(x.elem.arcs) == 0 { 373 373 break 374 374 } 375 375 write("(") ··· 378 378 ellipsis = true 379 379 } 380 380 write("[") 381 - for i, a := range x.a { 382 - p.debugStr(a) 383 - if i < len(x.a)-1 { 381 + for i, a := range x.elem.arcs { 382 + p.debugStr(a.v) 383 + if i < len(x.elem.arcs)-1 { 384 384 write(",") 385 385 } 386 386 }
+9 -19
cue/eval.go
··· 219 219 if err := firstBottom(n, t); err != nil { 220 220 return err 221 221 } 222 - a := make([]value, len(x.a)) 223 - changed := false 224 - for i, v := range x.a { 225 - // TODO: don't evaluate now. List elements may refer to other list 226 - // elements. Evaluating them here will cause a cycle evaluating the 227 - // struct field. 228 - e := v.evalPartial(ctx) 229 - changed = changed || e != v 230 - switch e.(type) { 231 - case *bottom: 232 - return e 233 - case value: 234 - a[i] = e 235 - } 236 - } 237 - if !changed && n == x.len && t == x.typ { 222 + s := x.elem.evalPartial(ctx).(*structLit) 223 + if s == x.elem && n == x.len && t == x.typ { 238 224 return x 239 225 } 240 - return &list{x.baseValue, a, t, n} 226 + return &list{x.baseValue, s, t, n} 241 227 } 242 228 243 229 func (x *listComprehension) evalPartial(ctx *context) evaluated { 244 - list := &list{baseValue: x.baseValue} 230 + s := &structLit{baseValue: x.baseValue} 231 + list := &list{baseValue: x.baseValue, elem: s} 245 232 result := x.clauses.yield(ctx, func(k, v evaluated, _ bool) *bottom { 246 233 if !k.kind().isAnyOf(intKind) { 247 234 return ctx.mkErr(k, "key must be of type int") 248 235 } 249 - list.a = append(list.a, v.evalPartial(ctx)) 236 + list.elem.arcs = append(list.elem.arcs, arc{ 237 + feature: label(len(list.elem.arcs)), 238 + v: v.evalPartial(ctx), 239 + }) 250 240 return nil 251 241 }) 252 242 switch {
+7 -3
cue/export.go
··· 411 411 case *list: 412 412 list := &ast.ListLit{} 413 413 var expr ast.Expr = list 414 - for _, e := range x.a { 415 - list.Elts = append(list.Elts, p.expr(e)) 414 + for _, e := range x.elem.arcs { 415 + if doEval(p.mode) { 416 + list.Elts = append(list.Elts, p.expr(e.v.evalPartial(p.ctx))) 417 + } else { 418 + list.Elts = append(list.Elts, p.expr(e.v)) 419 + } 416 420 } 417 421 max := maxNum(x.len) 418 422 num, ok := max.(*numLit) ··· 430 434 case *top, *basicType: 431 435 open = true 432 436 } 433 - if !ok || ln > len(x.a) { 437 + if !ok || ln > len(x.elem.arcs) { 434 438 list.Type = p.expr(x.typ) 435 439 if !open && !isTop(x.typ) { 436 440 expr = &ast.BinaryExpr{
+4 -2
cue/go.go
··· 346 346 347 347 case reflect.Slice, reflect.Array: 348 348 list := &list{baseValue: src.base()} 349 + arcs := []arc{} 349 350 for i := 0; i < value.Len(); i++ { 350 351 x := convert(ctx, src, value.Index(i).Interface()) 351 352 if isBottom(x) { 352 353 return x 353 354 } 354 - list.a = append(list.a, x) 355 + arcs = append(arcs, arc{feature: label(len(arcs)), v: x}) 355 356 } 357 + list.elem = &structLit{baseValue: list.baseValue, arcs: arcs} 356 358 list.initLit() 357 359 // There is no need to set the type of the list, as the list will 358 360 // be of fixed size and all elements will already have a defined ··· 522 524 if t.Kind() == reflect.Array { 523 525 ln = toInt(ctx, baseValue{}, int64(t.Len())) 524 526 } 525 - e = &list{typ: elem, len: ln} 527 + e = &list{elem: &structLit{}, typ: elem, len: ln} 526 528 } 527 529 if k == reflect.Slice { 528 530 e = wrapOrNull(e)
+14 -4
cue/resolve_test.go
··· 337 337 e4: [1, 2, ...>=4 & <=5] & [1, 2, 4, 8] 338 338 e5: [1, 2, 4, 8] & [1, 2, ...>=4 & <=5] 339 339 `, 340 - out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):unsupported op &(list, number)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: _|_((<=5 & 8):8 not within bound <=5), e5: _|_((<=5 & 8):8 not within bound <=5)}`, 340 + out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):unsupported op &(list, number)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: [1,2,4,_|_((<=5 & 8):8 not within bound <=5)], e5: [1,2,4,_|_((<=5 & 8):8 not within bound <=5)]}`, 341 341 }, { 342 342 desc: "list arithmetic", 343 343 in: ` ··· 473 473 474 474 c: [c[1], c[0]] 475 475 `, 476 - out: `<0>{a: _|_(cycle detected), b: _|_(cycle detected), c: _|_(cycle detected)}`, 476 + out: `<0>{a: _|_(cycle detected), b: _|_(cycle detected), c: [_|_(cycle detected),_|_(cycle detected)]}`, 477 477 }, { 478 478 desc: "resolved self-reference cycles", 479 479 in: ` ··· 481 481 b: a + 100 482 482 b: 200 483 483 484 - c: [c[1], a] // TODO: should be allowed 484 + c: [c[1], a] 485 485 486 486 s1: s2 & {a: 1} 487 487 s2: s3 & {b: 2} 488 488 s3: s1 & {c: 3} 489 489 `, 490 - out: `<0>{a: 100, b: 200, c: _|_(cycle detected), s1: <1>{a: 1, b: 2, c: 3}, s2: <2>{a: 1, b: 2, c: 3}, s3: <3>{a: 1, b: 2, c: 3}}`, 490 + out: `<0>{a: 100, b: 200, c: [100,100], s1: <1>{a: 1, b: 2, c: 3}, s2: <2>{a: 1, b: 2, c: 3}, s3: <3>{a: 1, b: 2, c: 3}}`, 491 491 }, { 492 492 desc: "resolved self-reference cycles: Issue 19", 493 493 in: ` ··· 1028 1028 `ne0: true, ne1: true, ne2: true, ne3: true, ne4: false, ne5: false, ne6: false, ne7: false, ne8: false, ne9: false, ne10: false, ne11: false, ` + 1029 1029 `feq0: false, feq1: false, feq2: false, feq3: false, feq4: false, feq5: false, feq6: false, feq7: false, feq8: false, feq9: false, feq10: false, feq11: false, ` + 1030 1030 `fne0: false, fne1: false, fne2: false, fne3: false, fne4: false, fne5: false, fne6: false, fne7: false, fne8: false, fne9: false, fne10: false, fne11: false}`, 1031 + }, { 1032 + desc: "list unification", 1033 + in: ` 1034 + a: { l: ["foo", v], v: l[1] } 1035 + b: a & { l: [_, "bar"] } 1036 + `, 1037 + out: `<0>{` + 1038 + `a: <1>{l: ["foo",_|_(cycle detected)], ` + 1039 + `v: _|_(cycle detected)}, ` + 1040 + `b: <2>{l: ["foo","bar"], v: "bar"}}`, 1031 1041 }, { 1032 1042 desc: "correct error messages", 1033 1043 // Tests that it is okay to partially evaluate structs.
+3 -8
cue/rewrite.go
··· 105 105 } 106 106 107 107 func (x *list) rewrite(ctx *context, fn rewriteFunc) value { 108 - a := make([]value, len(x.a)) 109 - changed := false 110 - for i, e := range x.a { 111 - a[i] = rewrite(ctx, e, fn) 112 - changed = changed || a[i] != e 113 - } 108 + elem := rewrite(ctx, x.elem, fn).(*structLit) 114 109 typ := rewrite(ctx, x.typ, fn) 115 110 len := rewrite(ctx, x.len, fn) 116 - if !changed && typ == x.typ && len == x.len { 111 + if elem == x.elem && typ == x.typ && len == x.len { 117 112 return x 118 113 } 119 - return &list{x.baseValue, a, typ, len} 114 + return &list{x.baseValue, elem, typ, len} 120 115 } 121 116 122 117 func (x *sliceExpr) rewrite(ctx *context, fn rewriteFunc) value {
+2 -5
cue/rewrite_test.go
··· 93 93 obj := &structLit{x.baseValue, emit, t, nil, arcs, nil} 94 94 return obj 95 95 case *list: 96 - a := make([]value, len(x.a)) 97 - for i := range x.a { 98 - a[i] = rewriteRec(ctx, x.a[i], x.at(ctx, i), m) 99 - } 96 + elm := rewriteRec(ctx, x.elem, x.elem, m).(*structLit) 100 97 len := rewriteRec(ctx, x.len, x.len.(evaluated), m) 101 98 typ := rewriteRec(ctx, x.typ, x.typ.(evaluated), m) 102 - return &list{x.baseValue, a, typ, len} 99 + return &list{x.baseValue, elm, typ, len} 103 100 default: 104 101 return eval 105 102 }
+8 -9
cue/subsume.go
··· 239 239 if !subsumes(ctx, x.len, y.len, mode) { 240 240 return false 241 241 } 242 - n := len(x.a) 243 - if len(y.a) < n { 244 - n = len(y.a) 242 + if !subsumes(ctx, x.elem, y.elem, mode) { 243 + return false 245 244 } 246 - for i, a := range x.a[:n] { 247 - if !subsumes(ctx, a, y.a[i], mode) { 248 - return false 249 - } 245 + n := len(x.elem.arcs) 246 + if len(y.elem.arcs) < n { 247 + n = len(y.elem.arcs) 250 248 } 251 249 if y.isOpen() { 252 250 return subsumes(ctx, x.typ, y.typ, 0) 253 251 } 254 - for i := range y.a[n:] { 255 - if !subsumes(ctx, x.typ, y.a[i], mode) { 252 + for _, a := range y.elem.arcs[n:] { 253 + // TODO: evaluate? 254 + if !subsumes(ctx, x.typ, a.v, mode) { 256 255 return false 257 256 } 258 257 }
+3 -3
cue/types.go
··· 568 568 return json.Marshal(x.(*bytesLit).b) 569 569 case listKind: 570 570 l := x.(*list) 571 - i := Iterator{ctx: ctx, val: v, iter: l, len: len(l.a)} 571 + i := Iterator{ctx: ctx, val: v, iter: l, len: len(l.elem.arcs)} 572 572 return marshalList(&i) 573 573 case structKind: 574 574 obj, _ := v.structVal(ctx) ··· 730 730 return Iterator{ctx: ctx}, err 731 731 } 732 732 l := v.eval(ctx).(*list) 733 - return Iterator{ctx: ctx, val: v, iter: l, len: len(l.a)}, nil 733 + return Iterator{ctx: ctx, val: v, iter: l, len: len(l.elem.arcs)}, nil 734 734 } 735 735 736 736 // Null reports an error if v is not null. ··· 1096 1096 func isGroundRecursive(ctx *context, v value) error { 1097 1097 switch x := v.(type) { 1098 1098 case *list: 1099 - for i := 0; i < len(x.a); i++ { 1099 + for i := 0; i < len(x.elem.arcs); i++ { 1100 1100 v := ctx.manifest(x.at(ctx, i)) 1101 1101 if err := isGroundRecursive(ctx, v); err != nil { 1102 1102 return err
+2 -5
cue/validate.go
··· 29 29 } 30 30 } 31 31 case *list: 32 - for _, v := range x.a { 33 - if err := validate(ctx, v); err != nil { 34 - return err 35 - } 36 - } 32 + // TODO: also validate types for open lists? 33 + return validate(ctx, x.elem) 37 34 } 38 35 return nil 39 36 }
+15 -9
cue/value.go
··· 438 438 439 439 type list struct { 440 440 baseValue 441 - // TODO: Elements in a list are nodes to allow for cycle detection. 442 - a []value // TODO: could be arc? 441 + elem *structLit 443 442 444 443 typ value 445 444 ··· 451 450 // initLit initializes a literal list. 452 451 func (x *list) initLit() { 453 452 n := newNum(x, intKind) 454 - n.v.SetInt64(int64(len(x.a))) 453 + n.v.SetInt64(int64(len(x.elem.arcs))) 455 454 x.len = n 456 455 x.typ = &top{x.baseValue} 457 456 } ··· 481 480 v := ctx.mkErr(x, "index %d out of bounds", i) 482 481 return arc{cache: v} 483 482 } 484 - if i < len(x.a) { 485 - return arc{cache: x.a[i].evalPartial(ctx), v: x.a[i]} 483 + if i < len(x.elem.arcs) { 484 + a := x.elem.iterAt(ctx, i) 485 + a.feature = 0 486 + return a 486 487 } 487 488 max := maxNum(x.len.(evaluated)) 488 489 if max.kind().isGround() { ··· 504 505 505 506 // lo and hi must be nil or a ground integer. 506 507 func (x *list) slice(ctx *context, lo, hi *numLit) evaluated { 507 - a := x.a 508 + a := x.elem.arcs 508 509 max := maxNum(x.len).evalPartial(ctx) 509 510 if hi != nil { 510 511 n := hi.intValue(ctx) ··· 535 536 if n < len(a) { 536 537 a = a[n:] 537 538 } else { 538 - a = []value{} 539 + a = []arc{} 539 540 } 540 541 } 541 - return &list{baseValue: x.baseValue, a: a, typ: x.typ, len: max} 542 + arcs := make([]arc, len(a)) 543 + for i, a := range a { 544 + arcs[i] = arc{feature: label(i), v: a.v} 545 + } 546 + s := &structLit{baseValue: x.baseValue, arcs: arcs} 547 + return &list{baseValue: x.baseValue, elem: s, typ: x.typ, len: max} 542 548 } 543 549 544 550 // An structLit is a single structLit in the configuration tree. ··· 1205 1211 return nil 1206 1212 1207 1213 case *list: 1208 - for i := range src.a { 1214 + for i := range src.elem.arcs { 1209 1215 idx := newNum(x, intKind) 1210 1216 idx.v.SetInt64(int64(i)) 1211 1217 v := fn.call(ctx, x, idx, src.at(ctx, i))