cue: hoist functionality to cue/internal/runtime

This will allow this code to be shared between new and old
implementation.

This makes cue use the new internal representation for labels.

Change-Id: I574f1401f9c55c0e151445d1f89312d06be49818
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6508
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 634cdb1..846c48f 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -15,8 +15,6 @@
 package cue
 
 import (
-	"path"
-	"strconv"
 	"sync"
 
 	"cuelang.org/go/cue/ast"
@@ -25,6 +23,7 @@
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/runtime"
 )
 
 // A Runtime is used for creating CUE interpretations.
@@ -193,121 +192,25 @@
 //
 // All instances belonging to the same package should share this index.
 type index struct {
-	labelMap map[string]label
-	labels   []string
+	*runtime.Index
 
-	loaded        map[*build.Instance]*Instance
-	imports       map[value]*Instance // key is always a *structLit
-	importsByPath map[string]*Instance
-
-	offset label
-	parent *index
-
-	mutex     sync.Mutex
-	typeCache sync.Map // map[reflect.Type]evaluated
+	loaded map[*build.Instance]*Instance
+	mutex  sync.Mutex
 }
 
-// work around golang-ci linter bug: fields are used.
-func init() {
-	var i index
-	i.mutex.Lock()
-	i.mutex.Unlock()
-	i.typeCache.Load(1)
-}
-
-const sharedOffset = 0x40000000
-
 // sharedIndex is used for indexing builtins and any other labels common to
 // all instances.
-var sharedIndex = newSharedIndex()
-
-func newSharedIndex() *index {
-	// TODO: nasty hack to indicate FileSet of shared index. Remove the whole
-	// FileSet idea from the API. Just take the hit of the extra pointers for
-	// positions in the ast, and then optimize the storage in an abstract
-	// machine implementation for storing graphs.
-	i := &index{
-		labelMap:      map[string]label{"": 0},
-		labels:        []string{""},
-		imports:       map[value]*Instance{},
-		importsByPath: map[string]*Instance{},
-	}
-	return i
+var sharedIndex = &index{
+	Index:  runtime.SharedIndex,
+	loaded: map[*build.Instance]*Instance{},
 }
 
 // newIndex creates a new index.
 func newIndex(parent *index) *index {
-	i := &index{
-		labelMap:      map[string]label{},
-		loaded:        map[*build.Instance]*Instance{},
-		imports:       map[value]*Instance{},
-		importsByPath: map[string]*Instance{},
-		offset:        label(len(parent.labels)) + parent.offset,
-		parent:        parent,
+	return &index{
+		Index:  runtime.NewIndex(parent.Index),
+		loaded: map[*build.Instance]*Instance{},
 	}
-	return i
-}
-
-func (idx *index) StrLabel(str string) label {
-	return idx.Label(str, false)
-}
-
-func (idx *index) NodeLabel(n ast.Node) (f label, ok bool) {
-	switch x := n.(type) {
-	case *ast.BasicLit:
-		name, _, err := ast.LabelName(x)
-		return idx.Label(name, false), err == nil
-	case *ast.Ident:
-		name, err := ast.ParseIdent(x)
-		return idx.Label(name, true), err == nil
-	}
-	return 0, false
-}
-
-func (idx *index) HasLabel(s string) (ok bool) {
-	for x := idx; x != nil; x = x.parent {
-		_, ok = x.labelMap[s]
-		if ok {
-			break
-		}
-	}
-	return ok
-}
-
-func (idx *index) findLabel(s string) (f label, ok bool) {
-	for x := idx; x != nil; x = x.parent {
-		f, ok = x.labelMap[s]
-		if ok {
-			break
-		}
-	}
-	return f, ok
-}
-
-func (idx *index) Label(s string, isIdent bool) label {
-	f, ok := idx.findLabel(s)
-	if !ok {
-		f = label(len(idx.labelMap)) + idx.offset
-		idx.labelMap[s] = f
-		idx.labels = append(idx.labels, s)
-	}
-	f <<= labelShift
-	if isIdent {
-		if internal.IsDef(s) {
-			f |= definition
-		}
-		if internal.IsHidden(s) {
-			f |= hidden
-		}
-	}
-	return f
-}
-
-func (idx *index) LabelStr(l label) string {
-	l >>= labelShift
-	for ; l < idx.offset; idx = idx.parent {
-	}
-	return idx.labels[l-idx.offset]
 }
 
 func isBuiltin(s string) bool {
@@ -331,7 +234,7 @@
 	if inst.Err == nil {
 		// inst.instance.index.state = s
 		// inst.instance.inst = p
-		inst.Err = resolveFiles(idx, p, isBuiltin)
+		inst.Err = runtime.ResolveFiles(idx.Index, p, isBuiltin)
 		for _, f := range files {
 			err := inst.insertFile(f)
 			inst.Err = errors.Append(inst.Err, err)
@@ -342,144 +245,3 @@
 	inst.complete = true
 	return inst
 }
-
-func lineStr(idx *index, n ast.Node) string {
-	return n.Pos().String()
-}
-
-func resolveFiles(
-	idx *index,
-	p *build.Instance,
-	isBuiltin func(s string) bool,
-) errors.Error {
-	// Link top-level declarations. As top-level entries get unified, an entry
-	// may be linked to any top-level entry of any of the files.
-	allFields := map[string]ast.Node{}
-	for _, file := range p.Files {
-		for _, d := range file.Decls {
-			if f, ok := d.(*ast.Field); ok && f.Value != nil {
-				if ident, ok := f.Label.(*ast.Ident); ok {
-					allFields[ident.Name] = f.Value
-				}
-			}
-		}
-	}
-	for _, f := range p.Files {
-		if err := resolveFile(idx, f, p, allFields, isBuiltin); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func resolveFile(
-	idx *index,
-	f *ast.File,
-	p *build.Instance,
-	allFields map[string]ast.Node,
-	isBuiltin func(s string) bool,
-) errors.Error {
-	unresolved := map[string][]*ast.Ident{}
-	for _, u := range f.Unresolved {
-		unresolved[u.Name] = append(unresolved[u.Name], u)
-	}
-	fields := map[string]ast.Node{}
-	for _, d := range f.Decls {
-		if f, ok := d.(*ast.Field); ok && f.Value != nil {
-			if ident, ok := f.Label.(*ast.Ident); ok {
-				fields[ident.Name] = d
-			}
-		}
-	}
-	var errs errors.Error
-
-	specs := []*ast.ImportSpec{}
-
-	for _, spec := range f.Imports {
-		id, err := strconv.Unquote(spec.Path.Value)
-		if err != nil {
-			continue // quietly ignore the error
-		}
-		name := path.Base(id)
-		if imp := p.LookupImport(id); imp != nil {
-			name = imp.PkgName
-		} else if !isBuiltin(id) {
-			errs = errors.Append(errs,
-				nodeErrorf(spec, "package %q not found", id))
-			continue
-		}
-		if spec.Name != nil {
-			name = spec.Name.Name
-		}
-		if n, ok := fields[name]; ok {
-			errs = errors.Append(errs, nodeErrorf(spec,
-				"%s redeclared as imported package name\n"+
-					"\tprevious declaration at %v", name, lineStr(idx, n)))
-			continue
-		}
-		fields[name] = spec
-		used := false
-		for _, u := range unresolved[name] {
-			used = true
-			u.Node = spec
-		}
-		if !used {
-			specs = append(specs, spec)
-		}
-	}
-
-	// Verify each import is used.
-	if len(specs) > 0 {
-		// Find references to imports. This assumes that identifiers in labels
-		// are not resolved or that such errors are caught elsewhere.
-		ast.Walk(f, nil, func(n ast.Node) {
-			if x, ok := n.(*ast.Ident); ok {
-				// As we also visit labels, most nodes will be nil.
-				if x.Node == nil {
-					return
-				}
-				for i, s := range specs {
-					if s == x.Node {
-						specs[i] = nil
-						return
-					}
-				}
-			}
-		})
-
-		// Add errors for unused imports.
-		for _, spec := range specs {
-			if spec == nil {
-				continue
-			}
-			if spec.Name == nil {
-				errs = errors.Append(errs, nodeErrorf(spec,
-					"imported and not used: %s", spec.Path.Value))
-			} else {
-				errs = errors.Append(errs, nodeErrorf(spec,
-					"imported and not used: %s as %s", spec.Path.Value, spec.Name))
-			}
-		}
-	}
-
-	k := 0
-	for _, u := range f.Unresolved {
-		if u.Node != nil {
-			continue
-		}
-		if n, ok := allFields[u.Name]; ok {
-			u.Node = n
-			u.Scope = f
-			continue
-		}
-		f.Unresolved[k] = u
-		k++
-	}
-	f.Unresolved = f.Unresolved[:k]
-	// TODO: also need to resolve types.
-	// if len(f.Unresolved) > 0 {
-	// 	n := f.Unresolved[0]
-	// 	return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name)
-	// }
-	return errs
-}
diff --git a/cue/build_test.go b/cue/build_test.go
index 51309a5..7817ec7 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -53,45 +53,6 @@
 	}
 }
 
-// TestPartiallyResolved tests that the resolve will detect the usage of
-// imports that are referenced by previously resolved nodes.
-func TestPartiallyResolved(t *testing.T) {
-	const importPath = "acme.com/foo"
-	spec1 := &ast.ImportSpec{
-		Path: ast.NewString(importPath),
-	}
-	spec2 := &ast.ImportSpec{
-		Name: ast.NewIdent("bar"),
-		Path: ast.NewString(importPath),
-	}
-
-	f := &ast.File{
-		Decls: []ast.Decl{
-			&ast.ImportDecl{Specs: []*ast.ImportSpec{spec1, spec2}},
-			&ast.Field{
-				Label: ast.NewIdent("X"),
-				Value: &ast.Ident{Name: "foo", Node: spec1},
-			},
-			&ast.Alias{
-				Ident: ast.NewIdent("Y"),
-				Expr:  &ast.Ident{Name: "bar", Node: spec2},
-			},
-		},
-		Imports: []*ast.ImportSpec{spec1, spec2},
-	}
-
-	err := resolveFile(nil, f, &build.Instance{
-		Imports: []*build.Instance{{
-			ImportPath: importPath,
-			PkgName:    "foo",
-		}},
-	}, map[string]ast.Node{}, isBuiltin)
-
-	if err != nil {
-		t.Errorf("exected no error, found %v", err)
-	}
-}
-
 func TestBuild(t *testing.T) {
 	files := func(s ...string) []string { return s }
 	insts := func(i ...*bimport) []*bimport { return i }
diff --git a/cue/debug.go b/cue/debug.go
index 3de2dcf..12fbe73 100644
--- a/cue/debug.go
+++ b/cue/debug.go
@@ -143,8 +143,8 @@
 	}
 
 	str := p.ctx.LabelStr(f)
-	if strings.HasPrefix(str, "#") && f&definition == 0 ||
-		strings.HasPrefix(str, "_") && f&hidden == 0 ||
+	if strings.HasPrefix(str, "#") && !f.IsDef() ||
+		strings.HasPrefix(str, "_") && !f.IsHidden() ||
 		!ast.IsValidIdent(str) {
 		return strconv.Quote(str)
 	}
@@ -381,7 +381,7 @@
 		if x.optional {
 			p.write("?")
 		}
-		if x.definition && x.feature&definition == 0 {
+		if x.definition && !x.feature.IsDef() {
 			p.write(" :: ")
 		} else {
 			p.write(": ")
diff --git a/cue/export.go b/cue/export.go
index ea5e9d0..f86592d 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -146,8 +146,8 @@
 
 func (p *exporter) label(f label) ast.Label {
 	str := p.ctx.LabelStr(f)
-	if strings.HasPrefix(str, "#") && f&definition == 0 ||
-		strings.HasPrefix(str, "_") && f&hidden == 0 ||
+	if strings.HasPrefix(str, "#") && !f.IsDef() ||
+		strings.HasPrefix(str, "_") && !f.IsHidden() ||
 		!ast.IsValidIdent(str) {
 		return ast.NewLit(token.STRING, strconv.Quote(str))
 	}
@@ -878,7 +878,7 @@
 				f.Token = token.ISA
 			}
 		}
-		if a.feature&hidden != 0 && p.mode.concrete && p.mode.omitHidden {
+		if a.feature.IsHidden() && p.mode.concrete && p.mode.omitHidden {
 			continue
 		}
 		oldInDef := p.inDef
diff --git a/cue/go.go b/cue/go.go
index 20dee9a..2be1ae0 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -508,7 +508,7 @@
 }
 
 func goTypeToValueRec(ctx *context, allowNullDefault bool, t reflect.Type) (e value) {
-	if e, ok := ctx.typeCache.Load(t); ok {
+	if e, ok := ctx.LoadType(t); ok {
 		return e.(value)
 	}
 
@@ -578,7 +578,7 @@
 		// resolve field tags to allow field tags to refer to the struct fields.
 		tags := map[label]string{}
 		obj := newStruct(baseValue{})
-		ctx.typeCache.Store(t, obj)
+		ctx.StoreType(t, obj)
 
 		for i := 0; i < t.NumField(); i++ {
 			f := t.Field(i)
@@ -674,7 +674,7 @@
 store:
 	// TODO: store error if not nil?
 	if e != nil {
-		ctx.typeCache.Store(t, e)
+		ctx.StoreType(t, e)
 	}
 	return e
 }
diff --git a/cue/instance.go b/cue/instance.go
index 0ec3dee..4258436 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -20,6 +20,7 @@
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/token"
 	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/runtime"
 )
 
 // An Instance defines a single configuration based on a collection of
@@ -48,23 +49,25 @@
 }
 
 func (x *index) addInst(p *Instance) *Instance {
-	if p.rootStruct == nil {
-		panic("struct must not be nil")
-	}
+	x.Index.AddInst(p.ImportPath, p.rootStruct, p)
 	p.index = x
-	x.imports[p.rootValue] = p
-	if p.ImportPath != "" {
-		x.importsByPath[p.ImportPath] = p
-	}
 	return p
 }
 
 func (x *index) getImportFromNode(v value) *Instance {
-	imp := x.imports[v]
-	if imp == nil && x.parent != nil {
-		return x.parent.getImportFromNode(v)
+	p := x.Index.GetImportFromNode(v)
+	if p == nil {
+		return nil
 	}
-	return imp
+	return p.(*Instance)
+}
+
+func (x *index) getImportFromPath(id string) *Instance {
+	node := x.Index.GetImportFromPath(id)
+	if node == nil {
+		return nil
+	}
+	return x.Index.GetImportFromNode(node).(*Instance)
 }
 
 func init() {
@@ -251,7 +254,7 @@
 	}
 	i.scope = v.obj
 
-	if err := resolveFiles(idx, p, isBuiltin); err != nil {
+	if err := runtime.ResolveFiles(idx.Index, p, isBuiltin); err != nil {
 		i.setError(err)
 		return i
 	}
diff --git a/cue/marshal.go b/cue/marshal.go
index a1e3509..4ae5cb7 100644
--- a/cue/marshal.go
+++ b/cue/marshal.go
@@ -19,6 +19,7 @@
 	"compress/gzip"
 	"encoding/gob"
 	"path/filepath"
+	"strings"
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/build"
@@ -183,8 +184,8 @@
 		p := len(staged) - 1
 
 		for _, imp := range imports {
-			i := ctx.importsByPath[imp]
-			if i == nil {
+			i := ctx.getImportFromPath(imp)
+			if i == nil || !strings.Contains(imp, ".") {
 				continue // a builtin package.
 			}
 			stageInstance(i)
diff --git a/cue/types.go b/cue/types.go
index e1f2ce2..bdbcbe0 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -286,7 +286,7 @@
 
 // IsHidden reports if a field is hidden from the data model.
 func (i *Iterator) IsHidden() bool {
-	return i.f&hidden != 0
+	return i.f.IsHidden()
 }
 
 // IsOptional reports if a field is optional.
@@ -564,7 +564,7 @@
 		a = append(a, strconv.FormatInt(int64(v.index), 10))
 	case structKind:
 		f := idx.LabelStr(v.arc.feature)
-		if v.arc.feature&(hidden|definition) == 0 {
+		if l := v.arc.feature; !l.IsDef() && !l.IsHidden() {
 			if !isIdent(f) && !isNumber(f) {
 				f = quote(f, '"')
 			}
@@ -1341,7 +1341,7 @@
 				break
 			}
 		}
-		needFilter = needFilter || f&hidden != 0
+		needFilter = needFilter || f.IsHidden()
 	}
 
 	if needFilter {
@@ -1351,7 +1351,7 @@
 			if a.definition && (o.omitDefinitions || o.concrete) {
 				continue
 			}
-			if a.feature&hidden != 0 && o.omitHidden {
+			if a.feature.IsHidden() && o.omitHidden {
 				continue
 			}
 			if o.omitOptional && a.optional {
@@ -1441,7 +1441,7 @@
 
 	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}
+	return FieldInfo{str, i, v, a.definition, a.optional, a.feature.IsHidden()}
 }
 
 // FieldByName looks up a field for the given name. If isIdent is true, it will
@@ -1518,7 +1518,7 @@
 	f := v.ctx().Label(name, true)
 	for i, a := range o.arcs {
 		if a.feature == f {
-			if f&hidden != 0 || !a.definition || a.optional {
+			if f.IsHidden() || !a.definition || a.optional {
 				break
 			}
 			return newChildValue(&o, i)
diff --git a/cue/value.go b/cue/value.go
index fd5ada1..14ebb70 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -801,8 +801,7 @@
 	if o == nil {
 		return false
 	}
-
-	if f&(hidden|definition) != 0 {
+	if f.IsDef() || f.IsHidden() {
 		return false
 	}
 
@@ -975,7 +974,7 @@
 
 func (x *structLit) allows(ctx *context, f label) bool {
 	return !x.closeStatus.isClosed() ||
-		f&hidden != 0 ||
+		f.IsHidden() ||
 		x.optionals.allows(ctx, f)
 }
 
@@ -1206,7 +1205,7 @@
 		return v, nil
 	}
 
-	if x.arcs[i].feature&(hidden|definition) == 0 {
+	if f := x.arcs[i].feature; !f.IsHidden() && !f.IsDef() {
 		name := ctx.LabelStr(x.arcs[i].feature)
 		arg := &stringLit{x.baseValue, name, nil}
 
@@ -1226,13 +1225,6 @@
 // A label is a canonicalized feature name.
 type label = adt.Feature
 
-const (
-	hidden     label = 0x01 // only set iff identifier starting with _ or #_
-	definition label = 0x02 // only set iff identifier starting with #
-
-	labelShift = 2
-)
-
 // An arc holds the label-value pair.
 //
 // A fully evaluated arc has either a node or a value. An unevaluated arc,
@@ -1917,7 +1909,7 @@
 				ctx.LabelStr(a.feature),
 				nil,
 			}
-			if a.definition || a.optional || a.feature&hidden != 0 {
+			if a.definition || a.optional || a.feature.IsHidden() {
 				continue
 			}
 			val := src.at(ctx, i)
diff --git a/internal/core/runtime/errors.go b/internal/core/runtime/errors.go
new file mode 100644
index 0000000..e98cdda
--- /dev/null
+++ b/internal/core/runtime/errors.go
@@ -0,0 +1,52 @@
+// 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 runtime
+
+import (
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+)
+
+var _ errors.Error = &nodeError{}
+
+// A nodeError is an error associated with processing an AST node.
+type nodeError struct {
+	path []string // optional
+	n    ast.Node
+
+	errors.Message
+}
+
+func (n *nodeError) Error() string {
+	return errors.String(n)
+}
+
+func nodeErrorf(n ast.Node, format string, args ...interface{}) *nodeError {
+	return &nodeError{
+		n:       n,
+		Message: errors.NewMessage(format, args),
+	}
+}
+
+func (e *nodeError) Position() token.Pos {
+	return e.n.Pos()
+}
+
+func (e *nodeError) InputPositions() []token.Pos { return nil }
+
+func (e *nodeError) Path() []string {
+	return e.path
+}
diff --git a/internal/core/runtime/index.go b/internal/core/runtime/index.go
index 8af0f41..11c342c 100644
--- a/internal/core/runtime/index.go
+++ b/internal/core/runtime/index.go
@@ -19,81 +19,158 @@
 	"sync"
 
 	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/core/adt"
 )
 
-// index maps conversions from label names to internal codes.
+// Index maps conversions from label names to internal codes.
 //
-// All instances belonging to the same package should share this index.
-type index struct {
-	labelMap map[string]int
+// All instances belonging to the same package should share this Index.
+//
+// INDEX IS A TRANSITIONAL TYPE TO BRIDGE THE OLD AND NEW
+// IMPLEMENTATIONS. USE RUNTIME.
+type Index struct {
+	labelMap map[string]int64
 	labels   []string
 
-	offset int
-	parent *index
+	// Change this to Instance at some point.
+	// From *structLit/*Vertex -> Instance
+	imports       map[interface{}]interface{}
+	importsByPath map[string]interface{}
+	// imports map[string]*adt.Vertex
 
-	mutex     sync.Mutex
+	offset int64
+	parent *Index
+
+	// mutex     sync.Mutex
 	typeCache sync.Map // map[reflect.Type]evaluated
 }
 
-// sharedIndex is used for indexing builtins and any other labels common to
+// SharedIndex is used for indexing builtins and any other labels common to
 // all instances.
-var sharedIndex = newSharedIndex()
+var SharedIndex = newSharedIndex()
 
-func newSharedIndex() *index {
-	i := &index{
-		labelMap: map[string]int{"": 0},
-		labels:   []string{"_"},
+var SharedIndexNew = newSharedIndex()
+
+func newSharedIndex() *Index {
+	i := &Index{
+		labelMap:      map[string]int64{"": 0},
+		labels:        []string{""},
+		imports:       map[interface{}]interface{}{},
+		importsByPath: map[string]interface{}{},
 	}
 	return i
 }
 
-// newIndex creates a new index.
-func newIndex(parent *index) *index {
-	i := &index{
-		labelMap: map[string]int{},
-		offset:   len(parent.labels) + parent.offset,
-		parent:   parent,
+// NewIndex creates a new index.
+func NewIndex(parent *Index) *Index {
+	i := &Index{
+		labelMap:      map[string]int64{},
+		imports:       map[interface{}]interface{}{},
+		importsByPath: map[string]interface{}{},
+		offset:        int64(len(parent.labels)) + parent.offset,
+		parent:        parent,
 	}
 	return i
 }
 
-func (x *index) IndexToString(i int64) string {
-	for ; int(i) < x.offset; x = x.parent {
+func (x *Index) IndexToString(i int64) string {
+	for ; i < x.offset; x = x.parent {
 	}
-	return x.labels[int(i)-x.offset]
+	return x.labels[i-x.offset]
 }
 
-func (x *index) StringToIndex(s string) int64 {
+func (x *Index) StringToIndex(s string) int64 {
 	for p := x; p != nil; p = p.parent {
 		if f, ok := p.labelMap[s]; ok {
 			return int64(f)
 		}
 	}
-	index := len(x.labelMap) + x.offset
+	index := int64(len(x.labelMap)) + x.offset
 	x.labelMap[s] = index
 	x.labels = append(x.labels, s)
 	return int64(index)
 }
 
-func (x *index) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
-	if expr == nil {
-		x.typeCache.Store(t, src)
-	} else {
-		x.typeCache.Store(t, expr)
+func (x *Index) HasLabel(s string) (ok bool) {
+	for c := x; c != nil; c = c.parent {
+		_, ok = c.labelMap[s]
+		if ok {
+			break
+		}
+	}
+	return ok
+}
+
+func (x *Index) StoreType(t reflect.Type, v interface{}) {
+	x.typeCache.Store(t, v)
+}
+
+func (x *Index) LoadType(t reflect.Type) (v interface{}, ok bool) {
+	v, ok = x.typeCache.Load(t)
+	return v, ok
+}
+
+func (x *Index) StrLabel(str string) adt.Feature {
+	return x.Label(str, false)
+}
+
+func (x *Index) NodeLabel(n ast.Node) (f adt.Feature, ok bool) {
+	switch label := n.(type) {
+	case *ast.BasicLit:
+		name, _, err := ast.LabelName(label)
+		return x.Label(name, false), err == nil
+	case *ast.Ident:
+		name, err := ast.ParseIdent(label)
+		return x.Label(name, true), err == nil
+	}
+	return 0, false
+}
+
+func (x *Index) Label(s string, isIdent bool) adt.Feature {
+	index := x.StringToIndex(s)
+	typ := adt.StringLabel
+	if isIdent {
+		switch {
+		case internal.IsDef(s) && internal.IsHidden(s):
+			typ = adt.HiddenDefinitionLabel
+		case internal.IsDef(s):
+			typ = adt.DefinitionLabel
+		case internal.IsHidden(s):
+			typ = adt.HiddenLabel
+		}
+	}
+	f, _ := adt.MakeLabel(nil, index, typ)
+	return f
+}
+
+func (idx *Index) LabelStr(l adt.Feature) string {
+	index := int64(l.Index())
+	return idx.IndexToString(index)
+}
+
+func (x *Index) AddInst(path string, key, p interface{}) {
+	if key == nil {
+		panic("key must not be nil")
+	}
+	x.imports[key] = p
+	if path != "" {
+		x.importsByPath[path] = key
 	}
 }
 
-func (x *index) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
-	v, ok := x.typeCache.Load(t)
-	if ok {
-		switch x := v.(type) {
-		case ast.Expr:
-			return x, nil, true
-		case adt.Expr:
-			src, _ = x.Source().(ast.Expr)
-			return src, x, true
-		}
+func (x *Index) GetImportFromNode(key interface{}) interface{} {
+	imp := x.imports[key]
+	if imp == nil && x.parent != nil {
+		return x.parent.GetImportFromNode(key)
 	}
-	return nil, nil, false
+	return imp
+}
+
+func (x *Index) GetImportFromPath(id string) interface{} {
+	key := x.importsByPath[id]
+	if key == nil && x.parent != nil {
+		return x.parent.GetImportFromPath(id)
+	}
+	return key
 }
diff --git a/internal/core/runtime/resolve.go b/internal/core/runtime/resolve.go
new file mode 100644
index 0000000..80ba789
--- /dev/null
+++ b/internal/core/runtime/resolve.go
@@ -0,0 +1,165 @@
+// 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 runtime
+
+import (
+	"path"
+	"strconv"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/errors"
+)
+
+func lineStr(idx *Index, n ast.Node) string {
+	return n.Pos().String()
+}
+
+func ResolveFiles(
+	idx *Index,
+	p *build.Instance,
+	isBuiltin func(s string) bool,
+) errors.Error {
+	// Link top-level declarations. As top-level entries get unified, an entry
+	// may be linked to any top-level entry of any of the files.
+	allFields := map[string]ast.Node{}
+	for _, file := range p.Files {
+		for _, d := range file.Decls {
+			if f, ok := d.(*ast.Field); ok && f.Value != nil {
+				if ident, ok := f.Label.(*ast.Ident); ok {
+					allFields[ident.Name] = f.Value
+				}
+			}
+		}
+	}
+	for _, f := range p.Files {
+		if err := ResolveFile(idx, f, p, allFields, isBuiltin); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func ResolveFile(
+	idx *Index,
+	f *ast.File,
+	p *build.Instance,
+	allFields map[string]ast.Node,
+	isBuiltin func(s string) bool,
+) errors.Error {
+	unresolved := map[string][]*ast.Ident{}
+	for _, u := range f.Unresolved {
+		unresolved[u.Name] = append(unresolved[u.Name], u)
+	}
+	fields := map[string]ast.Node{}
+	for _, d := range f.Decls {
+		if f, ok := d.(*ast.Field); ok && f.Value != nil {
+			if ident, ok := f.Label.(*ast.Ident); ok {
+				fields[ident.Name] = d
+			}
+		}
+	}
+	var errs errors.Error
+
+	specs := []*ast.ImportSpec{}
+
+	for _, spec := range f.Imports {
+		id, err := strconv.Unquote(spec.Path.Value)
+		if err != nil {
+			continue // quietly ignore the error
+		}
+		name := path.Base(id)
+		if imp := p.LookupImport(id); imp != nil {
+			name = imp.PkgName
+		} else if !isBuiltin(id) {
+			errs = errors.Append(errs,
+				nodeErrorf(spec, "package %q not found", id))
+			continue
+		}
+		if spec.Name != nil {
+			name = spec.Name.Name
+		}
+		if n, ok := fields[name]; ok {
+			errs = errors.Append(errs, nodeErrorf(spec,
+				"%s redeclared as imported package name\n"+
+					"\tprevious declaration at %v", name, lineStr(idx, n)))
+			continue
+		}
+		fields[name] = spec
+		used := false
+		for _, u := range unresolved[name] {
+			used = true
+			u.Node = spec
+		}
+		if !used {
+			specs = append(specs, spec)
+		}
+	}
+
+	// Verify each import is used.
+	if len(specs) > 0 {
+		// Find references to imports. This assumes that identifiers in labels
+		// are not resolved or that such errors are caught elsewhere.
+		ast.Walk(f, nil, func(n ast.Node) {
+			if x, ok := n.(*ast.Ident); ok {
+				// As we also visit labels, most nodes will be nil.
+				if x.Node == nil {
+					return
+				}
+				for i, s := range specs {
+					if s == x.Node {
+						specs[i] = nil
+						return
+					}
+				}
+			}
+		})
+
+		// Add errors for unused imports.
+		for _, spec := range specs {
+			if spec == nil {
+				continue
+			}
+			if spec.Name == nil {
+				errs = errors.Append(errs, nodeErrorf(spec,
+					"imported and not used: %s", spec.Path.Value))
+			} else {
+				errs = errors.Append(errs, nodeErrorf(spec,
+					"imported and not used: %s as %s", spec.Path.Value, spec.Name))
+			}
+		}
+	}
+
+	k := 0
+	for _, u := range f.Unresolved {
+		if u.Node != nil {
+			continue
+		}
+		if n, ok := allFields[u.Name]; ok {
+			u.Node = n
+			u.Scope = f
+			continue
+		}
+		f.Unresolved[k] = u
+		k++
+	}
+	f.Unresolved = f.Unresolved[:k]
+	// TODO: also need to resolve types.
+	// if len(f.Unresolved) > 0 {
+	// 	n := f.Unresolved[0]
+	// 	return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name)
+	// }
+	return errs
+}
diff --git a/internal/core/runtime/resolve_test.go b/internal/core/runtime/resolve_test.go
new file mode 100644
index 0000000..42ec1dd
--- /dev/null
+++ b/internal/core/runtime/resolve_test.go
@@ -0,0 +1,63 @@
+// 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 runtime
+
+import (
+	"testing"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+)
+
+// TestPartiallyResolved tests that the resolve will detect the usage of
+// imports that are referenced by previously resolved nodes.
+func TestPartiallyResolved(t *testing.T) {
+	const importPath = "acme.com/foo"
+	spec1 := &ast.ImportSpec{
+		Path: ast.NewString(importPath),
+	}
+	spec2 := &ast.ImportSpec{
+		Name: ast.NewIdent("bar"),
+		Path: ast.NewString(importPath),
+	}
+
+	isBuiltin := func(s string) bool { return false }
+
+	f := &ast.File{
+		Decls: []ast.Decl{
+			&ast.ImportDecl{Specs: []*ast.ImportSpec{spec1, spec2}},
+			&ast.Field{
+				Label: ast.NewIdent("X"),
+				Value: &ast.Ident{Name: "foo", Node: spec1},
+			},
+			&ast.Alias{
+				Ident: ast.NewIdent("Y"),
+				Expr:  &ast.Ident{Name: "bar", Node: spec2},
+			},
+		},
+		Imports: []*ast.ImportSpec{spec1, spec2},
+	}
+
+	err := ResolveFile(nil, f, &build.Instance{
+		Imports: []*build.Instance{{
+			ImportPath: importPath,
+			PkgName:    "foo",
+		}},
+	}, map[string]ast.Node{}, isBuiltin)
+
+	if err != nil {
+		t.Errorf("exected no error, found %v", err)
+	}
+}
diff --git a/internal/core/runtime/runtime.go b/internal/core/runtime/runtime.go
index c2f7d83..61ae9a1 100644
--- a/internal/core/runtime/runtime.go
+++ b/internal/core/runtime/runtime.go
@@ -15,22 +15,59 @@
 package runtime
 
 import (
+	"reflect"
+
+	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/internal/core/adt"
 )
 
 // A Runtime maintains data structures for indexing and resuse for evaluation.
 type Runtime struct {
-	*index
+	index *Index
 }
 
 // New creates a new Runtime.
 func New() *Runtime {
 	return &Runtime{
-		index: newIndex(sharedIndex),
+		index: NewIndex(SharedIndexNew),
 	}
 }
 
+func (x *Runtime) IndexToString(i int64) string {
+	return x.index.IndexToString(i)
+}
+
+func (x *Runtime) StringToIndex(s string) int64 {
+	return x.index.StringToIndex(s)
+}
+
 func (x *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
-	return nil, nil
+	v := x.index.GetImportFromPath(importPath)
+	if v == nil {
+		return nil, nil
+	}
+	return v.(*adt.Vertex), nil
+}
+
+func (x *Runtime) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
+	if expr == nil {
+		x.index.StoreType(t, src)
+	} else {
+		x.index.StoreType(t, expr)
+	}
+}
+
+func (x *Runtime) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
+	v, ok := x.index.LoadType(t)
+	if ok {
+		switch x := v.(type) {
+		case ast.Expr:
+			return x, nil, true
+		case adt.Expr:
+			src, _ = x.Source().(ast.Expr)
+			return src, x, true
+		}
+	}
+	return nil, nil, false
 }