internal/core/eval: track fieldSets for data values

This is, unfortunately, necessary because of
acceptor.isClosed. This tracking can be removed
once we get rid of that value.

Fixes #530

Change-Id: I34fe6d8af0667200595e000c02ca5898a4ab9f8b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7745
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
index e5aeba4..6d160d9 100644
--- a/internal/core/adt/composite.go
+++ b/internal/core/adt/composite.go
@@ -565,7 +565,11 @@
 		return a
 	}
 	a = appendPath(a, v.Parent)
-	return append(a, v.Label)
+	if v.Label != 0 {
+		// A Label may be 0 for programmatically inserted nodes.
+		a = append(a, v.Label)
+	}
+	return a
 }
 
 func (v *Vertex) appendListArcs(arcs []*Vertex) (err *Bottom) {
diff --git a/internal/core/convert/go.go b/internal/core/convert/go.go
index 1f2b5b7..aedd6ed 100644
--- a/internal/core/convert/go.go
+++ b/internal/core/convert/go.go
@@ -402,8 +402,7 @@
 
 		case reflect.Struct:
 			obj := &adt.StructLit{Src: src}
-			v := &adt.Vertex{}
-			v.AddConjunct(adt.MakeRootConjunct(nil, obj))
+			v := &adt.Vertex{Structs: []*adt.StructLit{obj}}
 			v.SetValue(ctx, adt.Finalized, &adt.StructMarker{})
 
 			t := value.Type()
@@ -440,10 +439,7 @@
 				if sf.Anonymous && name == "" {
 					arc, ok := sub.(*adt.Vertex)
 					if ok {
-						for _, a := range arc.Arcs {
-							obj.Decls = append(obj.Decls, &adt.Field{Label: a.Label, Value: a.Value})
-							v.Arcs = append(v.Arcs, a)
-						}
+						v.Arcs = append(v.Arcs, arc.Arcs...)
 					}
 					continue
 				}
@@ -455,6 +451,7 @@
 					arc.Label = f
 				} else {
 					arc = &adt.Vertex{Label: f, Value: sub}
+					arc.UpdateStatus(adt.Finalized)
 					arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
 				}
 				v.Arcs = append(v.Arcs, arc)
@@ -463,9 +460,7 @@
 			return v
 
 		case reflect.Map:
-			obj := &adt.StructLit{Src: src}
 			v := &adt.Vertex{Value: &adt.StructMarker{}}
-			v.AddConjunct(adt.MakeRootConjunct(nil, obj))
 			v.SetValue(ctx, adt.Finalized, &adt.StructMarker{})
 
 			t := value.Type()
@@ -497,15 +492,12 @@
 
 					s := fmt.Sprint(k)
 					f := ctx.StringLabel(s)
-					obj.Decls = append(obj.Decls, &adt.Field{
-						Label: f,
-						Value: sub,
-					})
 					arc, ok := sub.(*adt.Vertex)
 					if ok {
 						arc.Label = f
 					} else {
 						arc = &adt.Vertex{Label: f, Value: sub}
+						arc.UpdateStatus(adt.Finalized)
 						arc.AddConjunct(adt.MakeRootConjunct(nil, sub))
 					}
 					v.Arcs = append(v.Arcs, arc)
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 3fa7a60..db3e806 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -1393,21 +1393,37 @@
 			return
 
 		case *adt.StructMarker:
+			// TODO: this would not be necessary if acceptor.isClose were
+			// not used. See comment at acceptor.
+			opt := fieldSet{env: env, id: id}
+
+			// Keep ordering of Go struct for topological sort.
+			n.node.Structs = append(n.node.Structs, x.Structs...)
 			for _, a := range x.Arcs {
 				c := adt.MakeConjunct(nil, a, id)
 				c = updateCyclic(c, cyclic, nil, nil)
 				n.insertField(a.Label, c)
+				opt.MarkField(ctx, a.Label)
 			}
 
+			closedInfo(n.node).insertFieldSet(id, &opt)
+
 		default:
 			n.addValueConjunct(env, v, id)
 
+			// TODO: this would not be necessary if acceptor.isClose were
+			// not used. See comment at acceptor.
+			opt := fieldSet{env: env, id: id}
+
 			for _, a := range x.Arcs {
 				// TODO(errors): report error when this is a regular field.
-				n.insertField(a.Label, adt.MakeConjunct(nil, a, id))
-				// sub, _ := n.node.GetArc(a.Label)
-				// sub.Add(a)
+				c := adt.MakeConjunct(nil, a, id)
+				c = updateCyclic(c, cyclic, nil, nil)
+				n.insertField(a.Label, c)
+				opt.MarkField(ctx, a.Label)
 			}
+
+			closedInfo(n.node).insertFieldSet(id, &opt)
 		}
 
 		return
@@ -1434,7 +1450,7 @@
 
 	case *adt.Top:
 		n.hasTop = true
-		// TODO: Is this correct. Needed for elipsis, but not sure for others.
+		// TODO: Is this correct? Needed for elipsis, but not sure for others.
 		// n.optionals = append(n.optionals, fieldSet{env: env, id: id, isOpen: true})
 		if a, _ := n.node.Closed.(*acceptor); a != nil {
 			// Embedding top indicates that now all possible values are allowed
@@ -1604,7 +1620,7 @@
 	for _, d := range s.Decls {
 		switch x := d.(type) {
 		case *adt.Field:
-			opt.MarkField(ctx, x)
+			opt.MarkField(ctx, x.Label)
 			// handle in next iteration.
 
 		case *adt.OptionalField:
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index 674f7ef..765e341 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -153,9 +153,9 @@
 	return -1
 }
 
-func (o *fieldSet) MarkField(c *adt.OpContext, x *adt.Field) {
-	if o.fieldIndex(x.Label) < 0 {
-		o.fields = append(o.fields, field{label: x.Label})
+func (o *fieldSet) MarkField(c *adt.OpContext, f adt.Feature) {
+	if o.fieldIndex(f) < 0 {
+		o.fields = append(o.fields, field{label: f})
 	}
 }
 
diff --git a/internal/core/eval/vertex_test.go b/internal/core/eval/vertex_test.go
new file mode 100644
index 0000000..04dcb13
--- /dev/null
+++ b/internal/core/eval/vertex_test.go
@@ -0,0 +1,142 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package eval_test
+
+import (
+	"testing"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal/core/adt"
+	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/convert"
+	"cuelang.org/go/internal/core/debug"
+	"cuelang.org/go/internal/core/eval"
+)
+
+// TestVertex tests the use of programmatically generated Vertex values that
+// may have different properties than the one generated.
+func TestVertex(t *testing.T) {
+	r := cue.NewRuntime()
+	ctx := eval.NewContext(r, nil)
+
+	goValue := func(x interface{}) *adt.Vertex {
+		v := convert.GoValueToValue(ctx, x, true)
+		return v.(*adt.Vertex)
+	}
+
+	goType := func(x interface{}) *adt.Vertex {
+		v, err := convert.GoTypeToExpr(ctx, x)
+		if err != nil {
+			t.Fatal(err)
+		}
+		n := &adt.Vertex{}
+		n.AddConjunct(adt.MakeRootConjunct(nil, v))
+		n.Finalize(ctx)
+		return n
+	}
+
+	schema := func(field, config string) *adt.Vertex {
+		v := build(t, ctx, config)
+		f := adt.MakeIdentLabel(r, field, "")
+		return v.Lookup(f)
+	}
+
+	testCases := []struct {
+		name    string
+		a, b    *adt.Vertex
+		want    string
+		verbose bool
+	}{{
+
+		// Issue #530
+		a: schema("Steps", `
+			Steps: [...#Step]
+
+			#Step: Args: _
+			`),
+		b: goValue([]*struct{ Args interface{} }{{
+			Args: map[string]interface{}{
+				"Message": "Hello, world!",
+			},
+		}}),
+		want: `[{Args:{Message:"Hello, world!"}}]`,
+	}, {
+
+		name: "list of values",
+		a:    goValue([]int{1, 2, 3}),
+		b:    goType([]int{}),
+		want: `[1,2,3]`,
+	}, {
+		name: "list of list of values",
+		a:    goValue([][]int{{1, 2, 3}}),
+		b:    goType([][]int{}),
+		want: `[[1,2,3]]`,
+	}, {
+		a: schema("Steps", `
+		Steps: [...#Step]
+
+		#Step: Args: _
+		`),
+		b: schema("a", `
+		a: [{Args: { Message: "hello" }}]
+	`),
+		want: `[{Args:{Message:"hello"}}]`,
+	}, {
+
+		// Issue #530
+		a: schema("Steps", `
+		Steps: [...#Step]
+
+		#Step: Args: _
+		`),
+		b: goValue([]*struct{ Args interface{} }{{
+			Args: map[string]interface{}{
+				"Message": "Hello, world!",
+			},
+		}}),
+		want: `[{Args:{Message:"Hello, world!"}}]`,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+
+			n := &adt.Vertex{}
+			n.AddConjunct(adt.MakeRootConjunct(nil, tc.a))
+			n.AddConjunct(adt.MakeRootConjunct(nil, tc.b))
+			n.Finalize(ctx)
+
+			got := debug.NodeString(r, n, &debug.Config{Compact: !tc.verbose})
+			if got != tc.want {
+				t.Errorf("got:\n%s\nwant:\n%s", got, tc.want)
+			}
+		})
+	}
+}
+
+func build(t *testing.T, ctx *adt.OpContext, schema string) *adt.Vertex {
+	f, err := parser.ParseFile("test", schema)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	v, err := compile.Files(nil, ctx, "test", f)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	v.Finalize(ctx)
+
+	return v
+}
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index 52bce8a..57eb3f4 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -87,7 +87,7 @@
 			}
 			return convert.GoValueToValue(ctx, in, false), nil
 		},
-		out: `{Terminals: [{Description: "Desc", Name: "Name"}]}`,
+		out: `{Terminals: [{Name: "Name", Description: "Desc"}]}`,
 	}, {
 		in: func(ctx *adt.OpContext) (adt.Expr, error) {
 			in := &C{