cue: align import with top-level package for -e

This allows refering to hidden fields in the top-level
package from -e expressions.

Defines the ImportPath encoding option.

Fixes #904

Change-Id: Ic6c80ca97d50e64b93c054a43c0f91865f7139fb
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9661
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index 9f6fbbf..9ad8a7c 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -172,7 +172,11 @@
 			i:    -1,
 		}
 	default:
-		i = &instanceIterator{a: []*cue.Instance{b.instance}, i: -1}
+		i = &instanceIterator{
+			inst: b.instance,
+			a:    []*cue.Instance{b.instance},
+			i:    -1,
+		}
 		b.instance = nil
 	}
 	if len(b.expressions) > 0 {
@@ -217,7 +221,7 @@
 	return v
 }
 func (i *instanceIterator) instance() *cue.Instance {
-	if i.inst != nil {
+	if i.i >= len(i.a) {
 		return nil
 	}
 	return i.a[i.i]
@@ -352,9 +356,14 @@
 		return i.iter.value()
 	}
 	v := i.iter.value()
+	path := ""
+	if inst := i.iter.instance(); inst != nil {
+		path = inst.ID()
+	}
 	return v.Context().BuildExpr(i.expr[i.i],
 		cue.Scope(v),
 		cue.InferBuiltins(true),
+		cue.ImportPath(path),
 	)
 }
 
diff --git a/cmd/cue/cmd/testdata/script/eval_e_hidden.txt b/cmd/cue/cmd/testdata/script/eval_e_hidden.txt
new file mode 100644
index 0000000..0148499
--- /dev/null
+++ b/cmd/cue/cmd/testdata/script/eval_e_hidden.txt
@@ -0,0 +1,15 @@
+# Issue #904
+cue eval -e _a
+stdout '34'
+
+cue eval -e _a dep.cue
+stdout '34'
+
+cue eval -e _a tst.cue
+stdout '34'
+-- dep.cue --
+package dep
+
+_a: 34
+-- tst.cue --
+_a: 34
\ No newline at end of file
diff --git a/cue/build/instance.go b/cue/build/instance.go
index 11c6b5d..8f3f3d4 100644
--- a/cue/build/instance.go
+++ b/cue/build/instance.go
@@ -125,12 +125,12 @@
 
 // ID returns the package ID unique for this module.
 func (inst *Instance) ID() string {
-	if inst.PkgName == "" {
-		return "_"
-	}
 	if s := inst.ImportPath; s != "" {
 		return s
 	}
+	if inst.PkgName == "" {
+		return "_"
+	}
 	s := fmt.Sprintf("%s:%s", inst.Module, inst.PkgName)
 	return s
 }
diff --git a/cue/context.go b/cue/context.go
index 26cb73f..e3831a4 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -81,6 +81,16 @@
 	return func(o *runtime.Config) { o.Filename = filename }
 }
 
+// ImportPath defines the import path to use for building CUE. The import path
+// influences the scope in which identifiers occurring in the input CUE are
+// defined. Passing the empty string is equal to not specifying this option.
+//
+// This option is typically not necessary when building using a build.Instance,
+// but takes precedence otherwise.
+func ImportPath(path string) BuildOption {
+	return func(o *runtime.Config) { o.ImportPath = path }
+}
+
 // InferBuiltins allows unresolved references to bind to builtin packages with a
 // unique package name.
 //
@@ -168,8 +178,16 @@
 
 	ctx := c.ctx()
 
+	// TODO: move to runtime?: it probably does not make sense to treat BuildExpr
+	// and the expression resulting from CompileString differently.
 	astutil.ResolveExpr(x, errFn)
-	conjunct, err := compile.Expr(&cfg.Config, r, anonymousPkg, x)
+
+	pkgPath := cfg.ImportPath
+	if pkgPath == "" {
+		pkgPath = anonymousPkg
+	}
+
+	conjunct, err := compile.Expr(&cfg.Config, r, pkgPath, x)
 	if err != nil {
 		return c.makeError(err)
 	}
diff --git a/cue/types.go b/cue/types.go
index cd003a3..9360ae1 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1654,6 +1654,7 @@
 		expr = x.v
 	case ast.Expr:
 		n := getScopePrefix(v, p)
+		// TODO: inject import path of current package?
 		expr = resolveExpr(ctx, n, x)
 	default:
 		expr = convert.GoValueToValue(ctx, x, true)
diff --git a/internal/core/runtime/build.go b/internal/core/runtime/build.go
index 1dcb820..e3fb7ab 100644
--- a/internal/core/runtime/build.go
+++ b/internal/core/runtime/build.go
@@ -28,8 +28,9 @@
 )
 
 type Config struct {
-	Runtime  *Runtime
-	Filename string
+	Runtime    *Runtime
+	Filename   string
+	ImportPath string
 
 	compile.Config
 }
@@ -69,6 +70,10 @@
 	if cfg != nil {
 		cc = &cfg.Config
 	}
+	if cfg != nil && cfg.ImportPath != "" {
+		b.ImportPath = cfg.ImportPath
+		b.PkgName = astutil.ImportPathName(b.ImportPath)
+	}
 	v, err = compile.Files(cc, x, b.ID(), b.Files...)
 	errs = errors.Append(errs, err)