cue: fix lookup for cue API

Fixes analogue of commit c937e7a0e47b.

Fixes #157
Issue #202

Change-Id: Ieb3e37206b5cbe1ddddebfb2712f07b9e9efccf6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4220
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/instance.go b/cue/instance.go
index 9b7955e..60ceffb 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -257,7 +257,7 @@
 		i.setError(val.toErr(err))
 		return i
 	}
-	i.scope = v.n
+	i.scope = v.obj
 
 	if err := resolveFiles(idx, p); err != nil {
 		i.setError(err)
diff --git a/cue/types.go b/cue/types.go
index e62d628..8f7c30b 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -135,20 +135,21 @@
 type structValue struct {
 	ctx  *context
 	path *valueData
-	n    *structLit
+	obj  *structLit
+	arcs arcs
 }
 
 // Len reports the number of fields in this struct.
 func (o *structValue) Len() int {
-	if o.n == nil {
+	if o.obj == nil {
 		return 0
 	}
-	return len(o.n.arcs)
+	return len(o.arcs)
 }
 
 // At reports the key and value of the ith field, i < o.Len().
 func (o *structValue) At(i int) (key string, v Value) {
-	a := o.n.arcs[i]
+	a := o.arcs[i]
 	v = newChildValue(o, i)
 	return o.ctx.labelStr(a.feature), v
 }
@@ -160,19 +161,17 @@
 	i := 0
 	len := o.Len()
 	for ; i < len; i++ {
-		if o.n.arcs[i].feature == f {
+		if o.arcs[i].feature == f {
 			break
 		}
 	}
 	if i == len {
 		// TODO: better message.
 		ctx := o.ctx
-		x := ctx.mkErr(o.n, codeNotExist, "value %q not found", key)
+		x := ctx.mkErr(o.obj, codeNotExist, "value %q not found", key)
 		v := x.evalPartial(ctx)
 		return Value{ctx.index, &valueData{o.path, 0, arc{cache: v, v: x}}}
 	}
-	// v, _ := o.n.lookup(o.ctx, f)
-	// v = o.ctx.manifest(v)
 	return newChildValue(o, i)
 }
 
@@ -606,8 +605,26 @@
 }
 
 func newChildValue(obj *structValue, i int) Value {
-	a := obj.n.arcs[i]
-	a.cache = obj.n.at(obj.ctx, i)
+	a := obj.arcs[i]
+	for j, b := range obj.obj.arcs {
+		if b.feature == a.feature {
+			a = obj.obj.iterAt(obj.ctx, j)
+			// TODO: adding more technical debt here. The evaluator should be
+			// rewritten.
+			x := obj.obj
+			ctx := obj.ctx
+			if x.optionals != nil {
+				name := ctx.labelStr(x.arcs[i].feature)
+				arg := &stringLit{x.baseValue, name, nil}
+
+				val, _ := x.optionals.constraint(ctx, arg)
+				if val != nil {
+					a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
+				}
+			}
+			break
+		}
+	}
 
 	return Value{obj.ctx.index, &valueData{obj.path, uint32(i), a}}
 }
@@ -640,7 +657,8 @@
 	if v.path == nil {
 		return v
 	}
-	return remakeValue(v, v.path.v.evalPartial(v.ctx()))
+	fmt.Println(v)
+	return remakeValue(v, v.path.v)
 }
 
 // Default reports the default value and whether it existed. It returns the
@@ -1148,17 +1166,9 @@
 			k++
 		}
 		arcs = arcs[:k]
-		obj = &structLit{
-			obj.baseValue,   // baseValue
-			obj.emit,        // emit
-			obj.optionals,   // template
-			obj.closeStatus, // closeStatus
-			nil,             // comprehensions
-			arcs,            // arcs
-			nil,             // attributes
-		}
+		return structValue{ctx, v.path, obj, arcs}, nil
 	}
-	return structValue{ctx, v.path, obj}, nil
+	return structValue{ctx, v.path, obj, obj.arcs}, nil
 }
 
 // Struct returns the underlying struct of a value or an error if the value
@@ -1206,6 +1216,19 @@
 	a := s.s.arcs[i]
 	a.cache = s.s.at(ctx, i)
 
+	// TODO: adding more technical debt here. The evaluator should be
+	// rewritten.
+	x := s.s
+	if x.optionals != nil {
+		name := ctx.labelStr(x.arcs[i].feature)
+		arg := &stringLit{x.baseValue, name, nil}
+
+		val, _ := x.optionals.constraint(ctx, arg)
+		if val != nil {
+			a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
+		}
+	}
+
 	v := Value{ctx.index, &valueData{s.v.path, uint32(i), a}}
 	str := ctx.labelStr(a.feature)
 	return FieldInfo{str, i, v, a.definition, a.optional, a.feature&hidden != 0}
@@ -1237,7 +1260,16 @@
 	if err != nil {
 		return Iterator{ctx: ctx}, v.toErr(err)
 	}
-	return Iterator{ctx: ctx, val: v, iter: obj.n, len: len(obj.n.arcs)}, nil
+	n := &structLit{
+		obj.obj.baseValue,   // baseValue
+		obj.obj.emit,        // emit
+		obj.obj.optionals,   // template
+		obj.obj.closeStatus, // closeStatus
+		nil,                 // comprehensions
+		obj.arcs,            // arcs
+		nil,                 // attributes
+	}
+	return Iterator{ctx: ctx, val: v, iter: n, len: len(n.arcs)}, nil
 }
 
 // Lookup reports the value at a path starting from v.
@@ -1665,7 +1697,7 @@
 		for i := 0; i < obj.Len(); i++ {
 			_, v := obj.At(i)
 			opts := opts
-			if obj.n.arcs[i].definition {
+			if obj.arcs[i].definition {
 				opts.concrete = false
 			}
 			x.walk(v, opts)
diff --git a/cue/types_test.go b/cue/types_test.go
index 010808a..bbebffd 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"go/parser"
 	"io/ioutil"
 	"math"
 	"math/big"
@@ -687,6 +688,69 @@
 	}
 }
 
+func TestLookup(t *testing.T) {
+	_ = parser.ParseFile
+	var runtime = new(Runtime)
+	inst, err := runtime.Compile("x.cue", `
+V :: {
+	x: int
+}
+X :: {
+	<_>: int64
+} & V
+v: X
+`)
+	if err != nil {
+		t.Fatalf("compile: %v", err)
+	}
+	// expr, err := parser.ParseExpr("lookup.cue", `v`, parser.DeclarationErrors, parser.AllErrors)
+	// if err != nil {
+	// 	log.Fatalf("parseExpr: %v", err)
+	// }
+	// v := inst.Eval(expr)
+	testCases := []struct {
+		ref  []string
+		raw  string
+		eval string
+	}{{
+		ref:  []string{"v", "x"},
+		raw:  "(int & <=9223372036854775807 & int & >=-9223372036854775808)",
+		eval: "int64",
+	}}
+	for _, tc := range testCases {
+		v := inst.Lookup(tc.ref...)
+
+		if got := fmt.Sprint(v); got != tc.raw {
+			t.Errorf("got %v; want %v", got, tc.raw)
+		}
+
+		if got := fmt.Sprint(v.Eval().Syntax()); got != tc.eval {
+			t.Errorf("got %v; want %v", got, tc.eval)
+		}
+
+		v = inst.Lookup()
+		for _, ref := range tc.ref {
+			s, err := v.Struct()
+			if err != nil {
+				t.Fatal(err)
+			}
+			fi, err := s.FieldByName(ref)
+			if err != nil {
+				t.Fatal(err)
+			}
+			v = fi.Value
+		}
+
+		if got := fmt.Sprint(v); got != tc.raw {
+			t.Errorf("got %v; want %v", got, tc.raw)
+		}
+
+		if got := fmt.Sprint(v.Eval().Syntax()); got != tc.eval {
+			t.Errorf("got %v; want %v", got, tc.eval)
+		}
+	}
+}
+
 func TestDefaults(t *testing.T) {
 	testCases := []struct {
 		value string
@@ -1745,7 +1809,7 @@
 	return doc
 }
 
-func TestMashalJSON(t *testing.T) {
+func TestMarshalJSON(t *testing.T) {
 	testCases := []struct {
 		value string
 		json  string