Skip to content

Commit

Permalink
cue/testdata/cycle: fix another cycle bug
Browse files Browse the repository at this point in the history
The special case here is that the cycle passes over a
reference into a builtin.

Fixes #655

Change-Id: Ibc650932ccdf63aa3163a81bf6af101c7d942271
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8283
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
  • Loading branch information
mpvl committed Jan 23, 2021
1 parent c58e0ff commit a8de61c
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 25 deletions.
179 changes: 179 additions & 0 deletions cue/testdata/cycle/builtins.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
-- in.cue --
import "regexp"

// Issue #655
// When evaluating a value into a struct, and then back into a value, the
// evaluation mode flips from Partial to AllArcs to Back. This is typically
// not an issue, but if a referred field is within a struct generated by a
// builtin, effectively the entire struct needs to be evaluated and special care
// should be taking to not evaluate too early.
builtinCyclePerm0: {
X: "example.com"

Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}

X: Y.host
}

builtinCyclePerm1: {
X: Y.host

Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}

X: "example.com"
}

builtinCyclePerm2: {
Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}

X: Y.host
X: "example.com"
}

builtinCyclePerm3: {
Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}

X: "example.com"
X: Y.host
}

builtinCyclePerm4: {
X: "example.com"
X: Y.host

Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}
}

builtinCyclePerm5: {
X: Y.host
X: "example.com"

Y: {
#components: regexp.FindNamedSubmatch(#"^(?P<host>[[:alnum:].]+)$"#, X)
host: #components.host
}
}
-- out/eval --
(struct){
builtinCyclePerm0: (struct){
X: (string){ "example.com" }
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
}
builtinCyclePerm1: (struct){
X: (string){ "example.com" }
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
}
builtinCyclePerm2: (struct){
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
X: (string){ "example.com" }
}
builtinCyclePerm3: (struct){
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
X: (string){ "example.com" }
}
builtinCyclePerm4: (struct){
X: (string){ "example.com" }
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
}
builtinCyclePerm5: (struct){
X: (string){ "example.com" }
Y: (struct){
#components: (#struct){
host: (string){ "example.com" }
}
host: (string){ "example.com" }
}
}
}
-- out/compile --
--- in.cue
{
builtinCyclePerm0: {
X: "example.com"
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
X: 〈0;Y〉.host
}
builtinCyclePerm1: {
X: 〈0;Y〉.host
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
X: "example.com"
}
builtinCyclePerm2: {
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
X: 〈0;Y〉.host
X: "example.com"
}
builtinCyclePerm3: {
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
X: "example.com"
X: 〈0;Y〉.host
}
builtinCyclePerm4: {
X: "example.com"
X: 〈0;Y〉.host
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
}
builtinCyclePerm5: {
X: 〈0;Y〉.host
X: "example.com"
Y: {
#components: 〈import;regexp〉.FindNamedSubmatch("^(?P<host>[[:alnum:].]+)$", 〈1;X〉)
host: 〈0;#components〉.host
}
}
}
59 changes: 34 additions & 25 deletions internal/core/adt/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ func (e *Unifier) Unify(c *OpContext, v *Vertex, state VertexStatus) {
n.doNotify()

if !n.done() {
if len(n.disjunctions) > 0 && v.BaseValue == cycle {
switch {
case len(n.disjunctions) > 0 && v.BaseValue == cycle:
// We disallow entering computations of disjunctions with
// incomplete data.
if state == Finalized {
Expand All @@ -276,12 +277,16 @@ func (e *Unifier) Unify(c *OpContext, v *Vertex, state VertexStatus) {
n.node.UpdateStatus(Partial)
}
return
}
}

if !n.done() && state <= Partial {
n.node.UpdateStatus(Partial)
return
case state <= Partial:
n.node.UpdateStatus(Partial)
return

case state <= AllArcs:
c.AddBottom(n.incompleteErrors())
n.node.UpdateStatus(Partial)
return
}
}

if s := v.Status(); state <= s {
Expand Down Expand Up @@ -416,25 +421,7 @@ func (n *nodeContext) postDisjunct(state VertexStatus) {
default:
if n.node.BaseValue == cycle {
if !n.done() {
// collect incomplete errors.
var err *Bottom // n.incomplete
for _, d := range n.dynamicFields {
err = CombineErrors(nil, err, d.err)
}
for _, c := range n.forClauses {
err = CombineErrors(nil, err, c.err)
}
for _, c := range n.ifClauses {
err = CombineErrors(nil, err, c.err)
}
for _, x := range n.exprs {
err = CombineErrors(nil, err, x.err)
}
if err == nil {
// safeguard.
err = incompleteSentinel
}
n.node.BaseValue = err
n.node.BaseValue = n.incompleteErrors()
} else {
n.node.BaseValue = nil
}
Expand Down Expand Up @@ -538,6 +525,28 @@ func (n *nodeContext) postDisjunct(state VertexStatus) {
n.completeArcs(state)
}

func (n *nodeContext) incompleteErrors() *Bottom {
// collect incomplete errors.
var err *Bottom // n.incomplete
for _, d := range n.dynamicFields {
err = CombineErrors(nil, err, d.err)
}
for _, c := range n.forClauses {
err = CombineErrors(nil, err, c.err)
}
for _, c := range n.ifClauses {
err = CombineErrors(nil, err, c.err)
}
for _, x := range n.exprs {
err = CombineErrors(nil, err, x.err)
}
if err == nil {
// safeguard.
err = incompleteSentinel
}
return err
}

func (n *nodeContext) completeArcs(state VertexStatus) {

if state <= AllArcs {
Expand Down

0 comments on commit a8de61c

Please sign in to comment.