cue: eliminate context type

Change-Id: Ifa11b59cd5f96e6a70cbf13aa380c84970e8d323
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9364
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/build.go b/cue/build.go
index 2101032..02ef7a6 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -50,7 +50,7 @@
 
 	internal.CheckAndForkRuntime = func(runtime, value interface{}) interface{} {
 		r := runtime.(*Runtime)
-		idx := value.(Value).ctx().index
+		idx := value.(Value).idx
 		if idx != r.idx {
 			panic("value not from same runtime")
 		}
diff --git a/cue/builtin.go b/cue/builtin.go
index ef2754b..ee88ab1 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -38,7 +38,6 @@
 	// TODO: unroll this function. Should no longer be necessary to be internal.
 	internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
 		v := val.(Value)
-		ctx := v.ctx()
 
 		p := strings.Split(kind, ".")
 		pkg, name := p[0], p[1]
@@ -46,7 +45,7 @@
 		if s == nil {
 			return v
 		}
-		a := s.Lookup(ctx.Label(name, false))
+		a := s.Lookup(v.idx.Label(name, false))
 		if a == nil {
 			return v
 		}
diff --git a/cue/context.go b/cue/context.go
index 48a7d09..df444a1 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -20,62 +20,35 @@
 	"cuelang.org/go/internal/core/eval"
 )
 
-// context manages evaluation state.
-type context struct {
-	opCtx *adt.OpContext
-	*index
-}
-
 // newContext returns a new evaluation context.
-func newContext(idx *index) *context {
-	c := &context{
-		index: idx,
+func newContext(idx *index) *adt.OpContext {
+	if idx == nil {
+		return nil
 	}
-	if idx != nil {
-		c.opCtx = eval.NewContext(idx.Runtime, nil)
-	}
-	return c
+	return eval.NewContext(idx.Runtime, nil)
 }
 
-func debugStr(ctx *context, v adt.Node) string {
-	return debug.NodeString(ctx.opCtx, v, nil)
+func debugStr(ctx *adt.OpContext, v adt.Node) string {
+	return debug.NodeString(ctx, v, nil)
 }
 
-func (c *context) str(v adt.Node) string {
+func str(c *adt.OpContext, v adt.Node) string {
 	return debugStr(c, v)
 }
 
-func (c *context) mkErr(src adt.Node, args ...interface{}) *adt.Bottom {
-	return c.index.mkErr(src, args...)
-}
-
-func (c *context) vertex(v *adt.Vertex) *adt.Vertex {
-	return v
-}
-
-// vertex returns the evaluated vertex of v.
-func (v Value) vertex(ctx *context) *adt.Vertex {
-	return ctx.vertex(v.v)
-}
-
 // eval returns the evaluated value. This may not be the vertex.
 //
 // Deprecated: use ctx.value
-func (v Value) eval(ctx *context) adt.Value {
+func (v Value) eval(ctx *adt.OpContext) adt.Value {
 	if v.v == nil {
 		panic("undefined value")
 	}
-	x := ctx.manifest(v.v)
+	x := manifest(ctx, v.v)
 	return x.Value()
 }
 
-// func (v Value) evalFull(u value) (Value, adt.Value) {
-// 	ctx := v.ctx()
-// 	x := ctx.manifest(u)
-// }
-
 // TODO: change from Vertex to Vertex.
-func (c *context) manifest(v *adt.Vertex) *adt.Vertex {
-	v.Finalize(c.opCtx)
+func manifest(ctx *adt.OpContext, v *adt.Vertex) *adt.Vertex {
+	v.Finalize(ctx)
 	return v
 }
diff --git a/cue/errors.go b/cue/errors.go
index 9f3cacb..94c4b29 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -94,7 +94,7 @@
 	Err:  errors.Newf(token.NoPos, "undefined value"),
 }
 
-func (idx *index) mkErr(src adt.Node, args ...interface{}) *adt.Bottom {
+func mkErr(idx *index, src adt.Node, args ...interface{}) *adt.Bottom {
 	var e *adt.Bottom
 	var code adt.ErrorCode = -1
 outer:
diff --git a/cue/instance.go b/cue/instance.go
index 63b1d68..907e8f5 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -45,7 +45,7 @@
 	// complete bool // for cycle detection
 }
 
-func (x *index) addInst(p *Instance) *Instance {
+func addInst(x *index, p *Instance) *Instance {
 	if p.inst == nil {
 		p.inst = &build.Instance{
 			ImportPath: p.ImportPath,
@@ -128,7 +128,7 @@
 			st = &adt.Vertex{}
 			st.AddConjunct(adt.MakeRootConjunct(nil, x))
 		}
-		return v.ctx().index.addInst(&Instance{
+		return addInst(v.idx, &Instance{
 			root: st,
 		})
 	}
@@ -167,9 +167,9 @@
 	inst.Err = errors.Append(inst.Err, err)
 }
 
-func (inst *Instance) eval(ctx *context) adt.Value {
+func (inst *Instance) eval(ctx *adt.OpContext) adt.Value {
 	// TODO: remove manifest here?
-	v := ctx.manifest(inst.root)
+	v := manifest(ctx, inst.root)
 	return v
 }
 
@@ -178,7 +178,7 @@
 		v := value.(Value)
 		e := expr.(ast.Expr)
 		ctx := newContext(v.idx)
-		return newValueRoot(ctx, evalExpr(ctx, v.vertex(ctx), e))
+		return newValueRoot(v.idx, ctx, evalExpr(ctx, v.v, e))
 	}
 }
 
@@ -188,7 +188,7 @@
 }
 
 // evalExpr evaluates expr within scope.
-func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) adt.Value {
+func evalExpr(ctx *adt.OpContext, scope *adt.Vertex, expr ast.Expr) adt.Value {
 	cfg := &compile.Config{
 		Scope: scope,
 		Imports: func(x *ast.Ident) (pkgPath string) {
@@ -199,13 +199,13 @@
 		},
 	}
 
-	c, err := compile.Expr(cfg, ctx.opCtx, pkgID(), expr)
+	c, err := compile.Expr(cfg, ctx, pkgID(), expr)
 	if err != nil {
 		return &adt.Bottom{Err: err}
 	}
-	return adt.Resolve(ctx.opCtx, c)
+	return adt.Resolve(ctx, c)
 
-	// scope.Finalize(ctx.opCtx) // TODO: not appropriate here.
+	// scope.Finalize(ctx) // TODO: not appropriate here.
 	// switch s := scope.Value.(type) {
 	// case *bottom:
 	// 	return s
@@ -214,7 +214,7 @@
 	// 	return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind())
 	// }
 
-	// c := ctx.opCtx
+	// c := ctx
 
 	// x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr)
 	// if err != nil {
@@ -270,8 +270,8 @@
 // top-level values.
 func (inst *Instance) Value() Value {
 	ctx := newContext(inst.index)
-	inst.root.Finalize(ctx.opCtx)
-	return newVertexRoot(ctx, inst.root)
+	inst.root.Finalize(ctx)
+	return newVertexRoot(inst.index, ctx, inst.root)
 }
 
 // Eval evaluates an expression within an existing instance.
@@ -280,9 +280,9 @@
 func (inst *Instance) Eval(expr ast.Expr) Value {
 	ctx := newContext(inst.index)
 	v := inst.root
-	v.Finalize(ctx.opCtx)
+	v.Finalize(ctx)
 	result := evalExpr(ctx, v, expr)
-	return newValueRoot(ctx, result)
+	return newValueRoot(inst.index, ctx, result)
 }
 
 // DO NOT USE.
@@ -292,7 +292,7 @@
 	v := &adt.Vertex{}
 
 	i := inst[0]
-	ctx := newContext(i.index).opCtx
+	ctx := newContext(i.index)
 
 	// TODO: interesting test: use actual unification and then on K8s corpus.
 
@@ -302,7 +302,7 @@
 	}
 	v.Finalize(ctx)
 
-	p := i.index.addInst(&Instance{
+	p := addInst(i.index, &Instance{
 		root: v,
 		// complete: true,
 	})
@@ -343,7 +343,7 @@
 }
 
 func (inst *Instance) value() Value {
-	return newVertexRoot(newContext(inst.index), inst.root)
+	return newVertexRoot(inst.index, newContext(inst.index), inst.root)
 }
 
 // Lookup reports the value at a path starting from the top level struct. The
@@ -416,7 +416,7 @@
 		u.AddConjunct(adt.MakeRootConjunct(nil, expr))
 		u.Finalize(ctx)
 	}
-	inst = inst.index.addInst(&Instance{
+	inst = addInst(inst.index, &Instance{
 		root: u,
 		inst: nil,
 
diff --git a/cue/marshal.go b/cue/marshal.go
index 00e5720..47e319b 100644
--- a/cue/marshal.go
+++ b/cue/marshal.go
@@ -129,8 +129,6 @@
 // The stored instances are functionally the same, but preserving of file
 // information is only done on a best-effort basis.
 func (r *Runtime) Marshal(instances ...*Instance) (b []byte, err error) {
-	ctx := newContext(r.index())
-
 	staged := []instanceData{}
 	done := map[string]int{}
 
@@ -192,7 +190,7 @@
 		p := len(staged) - 1
 
 		for _, imp := range imports {
-			i := getImportFromPath(ctx.index, imp)
+			i := getImportFromPath(r.idx, imp)
 			if i == nil || !strings.Contains(imp, ".") {
 				continue // a builtin package.
 			}
diff --git a/cue/query.go b/cue/query.go
index 41ee6b9..d2829b6 100644
--- a/cue/query.go
+++ b/cue/query.go
@@ -41,16 +41,16 @@
 func errFn(pos token.Pos, msg string, args ...interface{}) {}
 
 // resolveExpr binds unresolved expressions to values in the expression or v.
-func resolveExpr(ctx *context, v *adt.Vertex, x ast.Expr) adt.Value {
+func resolveExpr(ctx *adt.OpContext, v *adt.Vertex, x ast.Expr) adt.Value {
 	cfg := &compile.Config{Scope: v}
 
 	astutil.ResolveExpr(x, errFn)
 
-	c, err := compile.Expr(cfg, ctx.opCtx, pkgID(), x)
+	c, err := compile.Expr(cfg, ctx, pkgID(), x)
 	if err != nil {
 		return &adt.Bottom{Err: err}
 	}
-	return adt.Resolve(ctx.opCtx, c)
+	return adt.Resolve(ctx, c)
 }
 
 // LookupPath reports the value for path p relative to v.
@@ -59,7 +59,7 @@
 		return Value{}
 	}
 	n := v.v
-	ctx := v.ctx().opCtx
+	ctx := v.ctx()
 
 outer:
 	for _, sel := range p.path {
@@ -88,7 +88,7 @@
 			x = &adt.Bottom{Err: err.Error}
 		} else {
 			// TODO: better message.
-			x = v.idx.mkErr(n, adt.NotExistError, "field %q not found", sel.sel)
+			x = mkErr(v.idx, n, adt.NotExistError, "field %q not found", sel.sel)
 		}
 		v := makeValue(v.idx, n)
 		return newErrValue(v, x)
diff --git a/cue/types.go b/cue/types.go
index 3df1a5d..e2a5b18 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -87,7 +87,7 @@
 //
 // TODO: remove
 type structValue struct {
-	ctx      *context
+	ctx      *adt.OpContext
 	v        Value
 	obj      *adt.Vertex
 	features []adt.Feature
@@ -104,7 +104,7 @@
 // At reports the key and value of the ith field, i < o.Len().
 func (o *structValue) At(i int) (key string, v Value) {
 	f := o.features[i]
-	return o.ctx.LabelStr(f), newChildValue(o, i)
+	return o.v.idx.LabelStr(f), newChildValue(o, i)
 }
 
 func (o *structValue) at(i int) (v *adt.Vertex, isOpt bool) {
@@ -115,8 +115,8 @@
 			Parent: o.v.v,
 			Label:  f,
 		}
-		o.obj.MatchAndInsert(o.ctx.opCtx, arc)
-		arc.Finalize(o.ctx.opCtx)
+		o.obj.MatchAndInsert(o.ctx, arc)
+		arc.Finalize(o.ctx)
 		isOpt = true
 	}
 	return arc, isOpt
@@ -125,7 +125,7 @@
 // Lookup reports the field for the given key. The returned Value is invalid
 // if it does not exist.
 func (o *structValue) Lookup(key string) Value {
-	f := o.ctx.StrLabel(key)
+	f := o.v.idx.StrLabel(key)
 	i := 0
 	len := o.Len()
 	for ; i < len; i++ {
@@ -135,8 +135,7 @@
 	}
 	if i == len {
 		// TODO: better message.
-		ctx := o.ctx
-		x := ctx.mkErr(o.obj, adt.NotExistError, "value %q not found", key)
+		x := mkErr(o.v.idx, o.obj, adt.NotExistError, "value %q not found", key)
 		return newErrValue(o.v, x)
 	}
 	return newChildValue(o, i)
@@ -181,7 +180,7 @@
 
 func marshalErrf(v Value, src adt.Node, code adt.ErrorCode, msg string, args ...interface{}) error {
 	arguments := append([]interface{}{code, msg}, args...)
-	b := v.idx.mkErr(src, arguments...)
+	b := mkErr(v.idx, src, arguments...)
 	return toMarshalErr(v, b)
 }
 
@@ -214,7 +213,8 @@
 //
 type Iterator struct {
 	val   Value
-	ctx   *context
+	idx   *index
+	ctx   *adt.OpContext
 	arcs  []field
 	p     int
 	cur   Value
@@ -235,7 +235,7 @@
 		return false
 	}
 	f := i.arcs[i.p]
-	f.arc.Finalize(i.ctx.opCtx)
+	f.arc.Finalize(i.ctx)
 	i.cur = makeValue(i.val.idx, f.arc)
 	i.f = f.arc.Label
 	i.isOpt = f.isOptional
@@ -259,7 +259,7 @@
 	if i.f == 0 {
 		return ""
 	}
-	return i.ctx.LabelStr(i.f)
+	return i.idx.LabelStr(i.f)
 }
 
 // IsHidden reports if a field is hidden from the data model.
@@ -575,24 +575,24 @@
 	return makeValue(v.idx, node)
 }
 
-func newVertexRoot(ctx *context, x *adt.Vertex) Value {
-	if ctx.opCtx != nil {
+func newVertexRoot(idx *index, ctx *adt.OpContext, x *adt.Vertex) Value {
+	if ctx != nil {
 		// This is indicative of an zero Value. In some cases this is called
 		// with an error value.
-		x.Finalize(ctx.opCtx)
+		x.Finalize(ctx)
 	} else {
 		x.UpdateStatus(adt.Finalized)
 	}
-	return makeValue(ctx.index, x)
+	return makeValue(idx, x)
 }
 
-func newValueRoot(ctx *context, x adt.Expr) Value {
+func newValueRoot(idx *index, ctx *adt.OpContext, x adt.Expr) Value {
 	if n, ok := x.(*adt.Vertex); ok {
-		return newVertexRoot(ctx, n)
+		return newVertexRoot(idx, ctx, n)
 	}
 	node := &adt.Vertex{}
 	node.AddConjunct(adt.MakeRootConjunct(nil, x))
-	return newVertexRoot(ctx, node)
+	return newVertexRoot(idx, ctx, node)
 }
 
 func newChildValue(o *structValue, i int) Value {
@@ -615,11 +615,11 @@
 	}
 
 	ctx := v.ctx()
-	n, b := ctx.opCtx.Resolve(c.Env, r)
+	n, b := ctx.Resolve(c.Env, r)
 	if b != nil {
 		return newErrValue(v, b)
 	}
-	n.Finalize(ctx.opCtx)
+	n.Finalize(ctx)
 	return makeValue(v.idx, n)
 }
 
@@ -632,7 +632,7 @@
 	runtime := ctx.Impl().(*runtime.Runtime)
 	index := runtime.Data.(*index)
 
-	return newValueRoot(newContext(index), v)
+	return newValueRoot(index, newContext(index), v)
 }
 
 func makeValue(idx *index, v *adt.Vertex) Value {
@@ -651,7 +651,7 @@
 	}
 	n := &adt.Vertex{Label: base.v.Label}
 	n.AddConjunct(adt.MakeRootConjunct(env, v))
-	n = base.ctx().manifest(n)
+	n = manifest(base.ctx(), n)
 	n.Parent = base.v.Parent
 	return makeValue(base.idx, n)
 }
@@ -662,11 +662,11 @@
 	return makeValue(base.idx, n)
 }
 
-func (v Value) ctx() *context {
+func (v Value) ctx() *adt.OpContext {
 	return newContext(v.idx)
 }
 
-func (v Value) makeChild(ctx *context, i uint32, a *adt.Vertex) Value {
+func (v Value) makeChild(ctx *adt.OpContext, i uint32, a *adt.Vertex) Value {
 	a.Parent = v.v
 	return makeValue(v.idx, a)
 }
@@ -679,7 +679,7 @@
 	}
 	x := v.v
 	// x = eval.FinalizeValue(v.idx.Runtime, v.v)
-	// x.Finalize(v.ctx().opCtx)
+	// x.Finalize(v.ctx())
 	x = x.ToDataSingle()
 	return makeValue(v.idx, x)
 	// return remakeValue(v, nil, ctx.value(x))
@@ -880,10 +880,10 @@
 	x := v.eval(ctx)
 
 	if _, ok := x.(adt.Resolver); ok {
-		return nil, marshalErrf(v, x, adt.IncompleteError, "value %q contains unresolved references", ctx.str(x))
+		return nil, marshalErrf(v, x, adt.IncompleteError, "value %q contains unresolved references", str(ctx, x))
 	}
 	if !adt.IsConcrete(x) {
-		return nil, marshalErrf(v, x, adt.IncompleteError, "cannot convert incomplete value %q to JSON", ctx.str(x))
+		return nil, marshalErrf(v, x, adt.IncompleteError, "cannot convert incomplete value %q to JSON", str(ctx, x))
 	}
 
 	// TODO: implement marshalles in value.
@@ -912,7 +912,7 @@
 	case adt.BottomKind:
 		return nil, toMarshalErr(v, x.(*adt.Bottom))
 	default:
-		return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
+		return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", str(ctx, x), x)
 	}
 }
 
@@ -1100,7 +1100,7 @@
 // Allows does not take into account validators like list.MaxItems(4). This may
 // change in the future.
 func (v Value) Allows(sel Selector) bool {
-	c := v.ctx().opCtx
+	c := v.ctx()
 	f := sel.sel.feature(c)
 	return v.v.Accept(c, f)
 }
@@ -1144,7 +1144,7 @@
 	return true
 }
 
-func (v Value) checkKind(ctx *context, want adt.Kind) *adt.Bottom {
+func (v Value) checkKind(ctx *adt.OpContext, want adt.Kind) *adt.Bottom {
 	if v.v == nil {
 		return errNotExists
 	}
@@ -1156,11 +1156,11 @@
 	k := x.Kind()
 	if want != adt.BottomKind {
 		if k&want == adt.BottomKind {
-			return ctx.mkErr(x, "cannot use value %v (type %s) as %s",
-				ctx.opCtx.Str(x), k, want)
+			return mkErr(v.idx, x, "cannot use value %v (type %s) as %s",
+				ctx.Str(x), k, want)
 		}
 		if !adt.IsConcrete(x) {
-			return ctx.mkErr(x, adt.IncompleteError, "non-concrete value %v", k)
+			return mkErr(v.idx, x, adt.IncompleteError, "non-concrete value %v", k)
 		}
 	}
 	return nil
@@ -1203,7 +1203,7 @@
 		}
 	}
 	const msg = "len not supported for type %v"
-	return remakeValue(v, nil, v.ctx().mkErr(v.v, msg, v.Kind()))
+	return remakeValue(v, nil, mkErr(v.idx, v.v, msg, v.Kind()))
 
 }
 
@@ -1225,7 +1225,7 @@
 	v, _ = v.Default()
 	ctx := v.ctx()
 	if err := v.checkKind(ctx, adt.ListKind); err != nil {
-		return Iterator{ctx: ctx}, v.toErr(err)
+		return Iterator{idx: v.idx, ctx: ctx}, v.toErr(err)
 	}
 	arcs := []field{}
 	for _, a := range v.v.Elems() {
@@ -1233,7 +1233,7 @@
 			arcs = append(arcs, field{arc: a})
 		}
 	}
-	return Iterator{ctx: ctx, val: v, arcs: arcs}, nil
+	return Iterator{idx: v.idx, ctx: ctx, val: v, arcs: arcs}, nil
 }
 
 // Null reports an error if v is not null.
@@ -1303,7 +1303,7 @@
 // a structVal.
 
 // structVal returns an structVal or an error if v is not a struct.
-func (v Value) structValData(ctx *context) (structValue, *adt.Bottom) {
+func (v Value) structValData(ctx *adt.OpContext) (structValue, *adt.Bottom) {
 	return v.structValOpts(ctx, options{
 		omitHidden:      true,
 		omitDefinitions: true,
@@ -1311,12 +1311,12 @@
 	})
 }
 
-func (v Value) structValFull(ctx *context) (structValue, *adt.Bottom) {
+func (v Value) structValFull(ctx *adt.OpContext) (structValue, *adt.Bottom) {
 	return v.structValOpts(ctx, options{allowScalar: true})
 }
 
 // structVal returns an structVal or an error if v is not a struct.
-func (v Value) structValOpts(ctx *context, o options) (s structValue, err *adt.Bottom) {
+func (v Value) structValOpts(ctx *adt.OpContext, o options) (s structValue, err *adt.Bottom) {
 	v, _ = v.Default()
 
 	obj := v.v
@@ -1347,7 +1347,7 @@
 				Parent: obj,
 				Label:  f,
 			}
-			obj.MatchAndInsert(ctx.opCtx, &v)
+			obj.MatchAndInsert(ctx, &v)
 			if len(v.Conjuncts) == 0 {
 				continue
 			}
@@ -1408,8 +1408,8 @@
 	ctx := s.v.ctx()
 
 	v := makeValue(s.v.idx, a)
-	name := ctx.LabelStr(a.Label)
-	str := a.Label.SelectorString(ctx.opCtx)
+	name := s.v.idx.LabelStr(a.Label)
+	str := a.Label.SelectorString(ctx)
 	return FieldInfo{str, name, i, v, a.Label.IsDef(), opt, a.Label.IsHidden()}
 }
 
@@ -1417,7 +1417,7 @@
 // look up a definition or hidden field (starting with `_` or `_#`). Otherwise
 // it interprets name as an arbitrary string for a regular field.
 func (s *Struct) FieldByName(name string, isIdent bool) (FieldInfo, error) {
-	f := s.v.ctx().Label(name, isIdent)
+	f := s.v.idx.Label(name, isIdent)
 	for i, a := range s.features {
 		if a == f {
 			return s.Field(i), nil
@@ -1440,7 +1440,7 @@
 	ctx := v.ctx()
 	obj, err := v.structValOpts(ctx, o)
 	if err != nil {
-		return &Iterator{ctx: ctx}, v.toErr(err)
+		return &Iterator{idx: v.idx, ctx: ctx}, v.toErr(err)
 	}
 
 	arcs := []field{}
@@ -1448,7 +1448,7 @@
 		arc, isOpt := obj.at(i)
 		arcs = append(arcs, field{arc: arc, isOptional: isOpt})
 	}
-	return &Iterator{ctx: ctx, val: v, arcs: arcs}, nil
+	return &Iterator{idx: v.idx, ctx: ctx, val: v, arcs: arcs}, nil
 }
 
 // Lookup reports the value at a path starting from v. The empty path returns v
@@ -1597,7 +1597,7 @@
 	}
 	ctx := v.ctx()
 	if err := p.Err(); err != nil {
-		return newErrValue(v, ctx.mkErr(nil, 0, "invalid path: %v", err))
+		return newErrValue(v, mkErr(v.idx, nil, 0, "invalid path: %v", err))
 	}
 	var expr adt.Expr
 	switch x := x.(type) {
@@ -1612,7 +1612,7 @@
 		n := getScopePrefix(v, p)
 		expr = resolveExpr(ctx, n, x)
 	default:
-		expr = convert.GoValueToValue(ctx.opCtx, x, true)
+		expr = convert.GoValueToValue(ctx, x, true)
 	}
 	for i := len(p.path) - 1; i >= 0; i-- {
 		expr = &adt.StructLit{Decls: []adt.Decl{
@@ -1624,7 +1624,7 @@
 	}
 	n := &adt.Vertex{}
 	n.AddConjunct(adt.MakeRootConjunct(nil, expr))
-	n.Finalize(ctx.opCtx)
+	n.Finalize(ctx)
 	w := makeValue(v.idx, n)
 	return v.Unify(w)
 }
@@ -1679,7 +1679,7 @@
 	if !o.raw {
 		p.Defaults = true
 	}
-	ctx := v.ctx().opCtx
+	ctx := v.ctx()
 	return p.Value(ctx, v.v, w.v)
 }
 
@@ -1694,7 +1694,7 @@
 // Value v and w must be obtained from the same build.
 // TODO: remove this requirement.
 func (v Value) Subsumes(w Value) bool {
-	ctx := v.ctx().opCtx
+	ctx := v.ctx()
 	p := subsume.Profile{Defaults: true}
 	return p.Check(ctx, v.v, w.v)
 }
@@ -1749,7 +1749,7 @@
 	addConjuncts(n, v.v)
 	addConjuncts(n, w.v)
 
-	ctx := newContext(v.idx).opCtx
+	ctx := newContext(v.idx)
 	n.Finalize(ctx)
 
 	n.Parent = v.v.Parent
@@ -1786,7 +1786,7 @@
 	n.AddConjunct(adt.MakeRootConjunct(nil, v.v))
 	n.AddConjunct(adt.MakeRootConjunct(nil, w.v))
 
-	ctx := newContext(v.idx).opCtx
+	ctx := newContext(v.idx)
 	n.Finalize(ctx)
 
 	n.Parent = v.v.Parent
@@ -1808,7 +1808,7 @@
 	if v.v == nil || other.v == nil {
 		return false
 	}
-	return adt.Equal(v.ctx().opCtx, v.v, other.v, 0)
+	return adt.Equal(v.ctx(), v.v, other.v, 0)
 }
 
 // Format prints a debug version of a value.
@@ -1820,9 +1820,9 @@
 	}
 	switch {
 	case state.Flag('#'):
-		_, _ = io.WriteString(state, ctx.str(v.v))
+		_, _ = io.WriteString(state, str(ctx, v.v))
 	case state.Flag('+'):
-		_, _ = io.WriteString(state, ctx.opCtx.Str(v.v))
+		_, _ = io.WriteString(state, ctx.Str(v.v))
 	default:
 		n, _ := export.Raw.Expr(v.idx.Runtime, v.instance().ID(), v.v)
 		b, _ := format.Node(n)
@@ -1849,11 +1849,11 @@
 	ctx := v.ctx()
 	c := v.v.Conjuncts[0]
 
-	return reference(ctx, c.Env, c.Expr())
+	return reference(v.idx, ctx, c.Env, c.Expr())
 }
 
-func reference(c *context, env *adt.Environment, r adt.Expr) (inst *Instance, path []string) {
-	ctx := c.opCtx
+func reference(rt *index, c *adt.OpContext, env *adt.Environment, r adt.Expr) (inst *Instance, path []string) {
+	ctx := c
 	defer ctx.PopState(ctx.PushState(env, r.Source()))
 
 	switch x := r.(type) {
@@ -1862,34 +1862,34 @@
 
 	case *adt.NodeLink:
 		// TODO: consider getting rid of NodeLink.
-		inst, path = mkPath(c, nil, x.Node)
+		inst, path = mkPath(rt, nil, x.Node)
 
 	case *adt.FieldReference:
 		env := ctx.Env(x.UpCount)
-		inst, path = mkPath(c, nil, env.Vertex)
-		path = append(path, x.Label.SelectorString(c.Runtime))
+		inst, path = mkPath(rt, nil, env.Vertex)
+		path = append(path, x.Label.SelectorString(c))
 
 	case *adt.LabelReference:
 		env := ctx.Env(x.UpCount)
-		return mkPath(c, nil, env.Vertex)
+		return mkPath(rt, nil, env.Vertex)
 
 	case *adt.DynamicReference:
 		env := ctx.Env(x.UpCount)
-		inst, path = mkPath(c, nil, env.Vertex)
+		inst, path = mkPath(rt, nil, env.Vertex)
 		v, _ := ctx.Evaluate(env, x.Label)
 		str := ctx.StringValue(v)
 		path = append(path, str)
 
 	case *adt.ImportReference:
 		imp := x.ImportPath.StringValue(ctx)
-		inst = getImportFromPath(c.index, imp)
+		inst = getImportFromPath(rt, imp)
 
 	case *adt.SelectorExpr:
-		inst, path = reference(c, env, x.X)
+		inst, path = reference(rt, c, env, x.X)
 		path = append(path, x.Sel.SelectorString(ctx))
 
 	case *adt.IndexExpr:
-		inst, path = reference(c, env, x.X)
+		inst, path = reference(rt, c, env, x.X)
 		v, _ := ctx.Evaluate(env, x.Index)
 		str := ctx.StringValue(v)
 		path = append(path, str)
@@ -1900,12 +1900,12 @@
 	return inst, path
 }
 
-func mkPath(ctx *context, a []string, v *adt.Vertex) (inst *Instance, path []string) {
+func mkPath(ctx *index, a []string, v *adt.Vertex) (inst *Instance, path []string) {
 	if v.Parent == nil {
-		return getImportFromNode(ctx.index, v), a
+		return getImportFromNode(ctx, v), a
 	}
 	inst, path = mkPath(ctx, a, v.Parent)
-	path = append(path, v.Label.SelectorString(ctx.opCtx))
+	path = append(path, v.Label.SelectorString(ctx))
 	return inst, path
 }
 
@@ -2062,7 +2062,7 @@
 		AllErrors:      true,
 	}
 
-	b := validate.Validate(v.ctx().opCtx, v.v, cfg)
+	b := validate.Validate(v.ctx(), v.v, cfg)
 	if b != nil {
 		return b.Err
 	}
@@ -2319,7 +2319,7 @@
 
 		default:
 			a := []Value{}
-			ctx := v.ctx().opCtx
+			ctx := v.ctx()
 			for _, c := range v.v.Conjuncts {
 				// Keep parent here. TODO: do we need remove the requirement
 				// from other conjuncts?
@@ -2364,7 +2364,7 @@
 		for i, disjunct := range x.Values {
 			if i < x.NumDefaults {
 				for _, n := range x.Values[x.NumDefaults:] {
-					if subsume.Value(v.ctx().opCtx, n, disjunct) == nil {
+					if subsume.Value(v.ctx(), n, disjunct) == nil {
 						continue outer
 					}
 				}
@@ -2422,7 +2422,7 @@
 
 	case *adt.FieldReference:
 		// TODO: allow hard link
-		ctx := v.ctx().opCtx
+		ctx := v.ctx()
 		f := ctx.PushState(env, x.Src)
 		env := ctx.Env(x.UpCount)
 		a = append(a, remakeValue(v, nil, &adt.NodeLink{Node: env.Vertex}))
@@ -2467,7 +2467,7 @@
 			Vertex: v.v,
 		}
 		fields := []adt.Decl{}
-		ctx := v.ctx().opCtx
+		ctx := v.ctx()
 		for _, d := range x.Decls {
 			switch x := d.(type) {
 			case adt.Expr:
diff --git a/cue/types_test.go b/cue/types_test.go
index afb3848..12bb243 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -1646,7 +1646,7 @@
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
 			v := getInstance(t, tc.value).Value()
-			v.v.Finalize(v.ctx().opCtx) // TODO: do in instance.
+			v.v.Finalize(v.ctx()) // TODO: do in instance.
 			for _, p := range tc.path {
 				if p == "" {
 					var ok bool
@@ -1659,7 +1659,7 @@
 				}
 			}
 			got := fmt.Sprint(v)
-			// got := debug.NodeString(v.ctx().opCtx, v.v, &debug.Config{Compact: true})
+			// got := debug.NodeString(v.ctx(), v.v, &debug.Config{Compact: true})
 			if got != tc.want {
 				t.Errorf("\n got: %q\nwant: %q", got, tc.want)
 			}
@@ -2309,7 +2309,7 @@
 				t.Errorf("exists: got %v; want %v", got, tc.notExists)
 			}
 
-			got := v.ctx().opCtx.Str(v.v)
+			got := v.ctx().Str(v.v)
 			if tc.str == "" {
 				t.Fatalf("str empty, got %q", got)
 			}
@@ -3650,5 +3650,5 @@
 func compactRawStr(v Value) string {
 	ctx := v.ctx()
 	cfg := &debug.Config{Compact: true, Raw: true}
-	return debug.NodeString(ctx.opCtx, v.v, cfg)
+	return debug.NodeString(ctx, v.v, cfg)
 }