Skip to content

Commit

Permalink
internal/core/compile: fix mutual dependent let clauses
Browse files Browse the repository at this point in the history
Allow let clauses to refer to each other, as long
as they don't introduce a cycle.

Change-Id: Ia7f2aa8188bb29e308fce8df8c0a81f8838b65bf
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7601
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Nov 15, 2020
1 parent aa87887 commit b886b0f
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 10 deletions.
49 changes: 49 additions & 0 deletions cue/testdata/compile/let.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-- in.cue --
a: {
let X = Y
let Y = c

b: X
c: 5
}

b: {
let X = Y
let Y = X

b: X
c: 5
}
-- out/compile --
--- in.cue
{
a: {
b: 〈0;let X〉
c: 5
}
b: {
b: 〈0;let X〉
c: 5
}
}
-- out/eval --
Errors:
b.let[]: cyclic references in let clause or alias:
./in.cue:10:13

Result:
(_|_){
// [eval]
a: (struct){
b: (int){ 5 }
c: (int){ 5 }
}
b: (_|_){
// [eval]
b: (_|_){
// [eval] b.let[]: cyclic references in let clause or alias:
// ./in.cue:10:13
}
c: (int){ 5 }
}
}
3 changes: 3 additions & 0 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ func (x *LetReference) Source() ast.Node {
func (x *LetReference) resolve(c *OpContext) *Vertex {
e := c.Env(x.UpCount)
label := e.Vertex.Label
if x.X == nil {
panic("nil expression")
}
// Anonymous arc.
return &Vertex{Parent: nil, Label: label, Conjuncts: []Conjunct{{e, x.X, 0}}}
}
Expand Down
48 changes: 38 additions & 10 deletions internal/core/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@ type frame struct {
}

type aliasEntry struct {
expr adt.Expr
source ast.Node
used bool
label labeler
srcExpr ast.Expr
expr adt.Expr
source ast.Node
used bool
}

func (c *compiler) insertAlias(id *ast.Ident, a aliasEntry) *adt.Bottom {
Expand Down Expand Up @@ -178,6 +180,8 @@ func (c *compiler) updateAlias(id *ast.Ident, expr adt.Expr) {

x := m[id.Name]
x.expr = expr
x.label = nil
x.srcExpr = nil
m[id.Name] = x
}

Expand All @@ -192,6 +196,21 @@ func (c compiler) lookupAlias(k int, id *ast.Ident) aliasEntry {
return aliasEntry{expr: err}
}

switch {
case entry.label != nil:
if entry.srcExpr == nil {
entry.expr = c.errf(id, "cyclic references in let clause or alias")
break
}

src := entry.srcExpr
entry.srcExpr = nil // mark to allow detecting cycles
m[name] = entry

entry.expr = c.labeledExpr(nil, entry.label, src)
entry.label = nil
}

entry.used = true
m[name] = entry
return entry
Expand Down Expand Up @@ -483,11 +502,19 @@ func (c *compiler) markAlias(d ast.Decl) {
}

case *ast.LetClause:
a := aliasEntry{source: x}
a := aliasEntry{
label: (*letScope)(x),
srcExpr: x.Expr,
source: x,
}
c.insertAlias(x.Ident, a)

case *ast.Alias:
a := aliasEntry{source: x}
a := aliasEntry{
label: (*deprecatedAliasScope)(x),
srcExpr: x.Expr,
source: x,
}
c.insertAlias(x.Ident, a)
}
}
Expand Down Expand Up @@ -609,14 +636,12 @@ func (c *compiler) addLetDecl(d ast.Decl) {
// Cache the parsed expression. Creating a unique expression for each
// reference allows the computation to be shared given that we don't
// have fields for expressions. This, in turn, prevents exponential
// blowup in x2: x1+x1, x3: x2+x2, ... patterns.

// blowup in x2: x1+x1, x3: x2+x2, ... patterns.
expr := c.labeledExpr(nil, (*letScope)(x), x.Expr)
c.updateAlias(x.Ident, expr)

case *ast.Alias:
// TODO(legacy): deprecated, remove this use of Alias

expr := c.labeledExpr(nil, (*deprecatedAliasScope)(x), x.Expr)
c.updateAlias(x.Ident, expr)
}
Expand Down Expand Up @@ -733,11 +758,14 @@ func (c *compiler) labeledExpr(f *ast.Field, lab labeler, expr ast.Expr) adt.Exp
if c.stack[k].field != nil {
panic("expected nil field")
}
saved := c.stack[k]

c.stack[k].label = lab
c.stack[k].field = f

value := c.expr(expr)
c.stack[k].label = nil
c.stack[k].field = nil

c.stack[k] = saved
return value
}

Expand Down

0 comments on commit b886b0f

Please sign in to comment.