all: implement hidden identifiers scoped per-package

This has been part of the spec for a long time, but never implemented.

Note that all files with the same package name within a module
belong to the same package. A hidden identifier is thus uniqued by
module+name.

Closes #65
Fixes #503

Change-Id: I6d97ca1dbcf4ccc5730fde2cf8193c8e667787ad
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7361
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 5587650..257b52c 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -262,7 +262,7 @@
 		err := runtime.ResolveFiles(idx.Index, p, isBuiltin)
 		errs = errors.Append(errs, err)
 
-		v, err := compile.Files(nil, idx.Runtime, p.Files...)
+		v, err := compile.Files(nil, idx.Runtime, p.ID(), p.Files...)
 		errs = errors.Append(errs, err)
 
 		inst := newInstance(idx, p, v)
diff --git a/cue/build/instance.go b/cue/build/instance.go
index cd59a17..90eb64d 100644
--- a/cue/build/instance.go
+++ b/cue/build/instance.go
@@ -15,6 +15,7 @@
 package build
 
 import (
+	"fmt"
 	pathpkg "path"
 	"path/filepath"
 	"strings"
@@ -127,6 +128,15 @@
 	Match      []string
 }
 
+// ID returns the package ID unique for this module.
+func (inst *Instance) ID() string {
+	if inst.PkgName == "" {
+		return ""
+	}
+	s := fmt.Sprintf("%s:%s", inst.Module, inst.PkgName)
+	return s
+}
+
 // Dependencies reports all Instances on which this instance depends.
 func (inst *Instance) Dependencies() []*Instance {
 	// TODO: as cyclic dependencies are not allowed, we could just not check.
diff --git a/cue/instance.go b/cue/instance.go
index 493c2d4..e6c9446 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -127,6 +127,11 @@
 	}
 }
 
+// pkgID reports a package path that can never resolve to a valid package.
+func pkgID() string {
+	return "_"
+}
+
 // evalExpr evaluates expr within scope.
 func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) evaluated {
 	cfg := &compile.Config{
@@ -139,7 +144,7 @@
 		},
 	}
 
-	c, err := compile.Expr(cfg, ctx.opCtx, expr)
+	c, err := compile.Expr(cfg, ctx.opCtx, pkgID(), expr)
 	if err != nil {
 		return &adt.Bottom{Err: err}
 	}
@@ -182,6 +187,15 @@
 	// return c.NewErrf("could not evaluate %s", c.Str(x))
 }
 
+// ID returns the package identifier that uniquely qualifies module and
+// package name.
+func (inst *Instance) ID() string {
+	if inst == nil || inst.inst == nil {
+		return ""
+	}
+	return inst.inst.ID()
+}
+
 // Doc returns the package comments for this instance.
 func (inst *Instance) Doc() []*ast.CommentGroup {
 	var docs []*ast.CommentGroup
@@ -253,7 +267,8 @@
 
 	rErr := runtime.ResolveFiles(idx.Index, p, isBuiltin)
 
-	v, err := compile.Files(&compile.Config{Scope: inst.root}, r, p.Files...)
+	cfg := &compile.Config{Scope: inst.root}
+	v, err := compile.Files(cfg, r, p.ID(), p.Files...)
 
 	v.AddConjunct(adt.MakeRootConjunct(nil, inst.root))
 
diff --git a/cue/load/config.go b/cue/load/config.go
index 88b6a75..dd0cef3 100644
--- a/cue/load/config.go
+++ b/cue/load/config.go
@@ -528,7 +528,7 @@
 		}
 
 		r := runtime.New()
-		v, err := compile.Files(nil, r, file)
+		v, err := compile.Files(nil, r, "_", file)
 		if err != nil {
 			return nil, errors.Wrapf(err, token.NoPos, "invalid cue.mod file")
 		}
diff --git a/cue/marshal.go b/cue/marshal.go
index 98a3511..8ce44ea 100644
--- a/cue/marshal.go
+++ b/cue/marshal.go
@@ -142,7 +142,7 @@
 			return p
 		}
 		// TODO: support exporting instance
-		file, _ := export.Def(r.idx.Runtime, i.root)
+		file, _ := export.Def(r.idx.Runtime, i.inst.ID(), i.root)
 		imports := []string{}
 		file.VisitImports(func(i *ast.ImportDecl) {
 			for _, spec := range i.Specs {
diff --git a/cue/testdata/comprehensions/nested.txtar b/cue/testdata/comprehensions/nested.txtar
index 459a554..1d17742 100644
--- a/cue/testdata/comprehensions/nested.txtar
+++ b/cue/testdata/comprehensions/nested.txtar
@@ -88,7 +88,7 @@
                   0: (struct){
                     containerPort: (int){ 9100 }
                     name: (string){ "scrape" }
-                    _export: (bool){ |(*(bool){ true }, (bool){ false }) }
+                    _export(:kube): (bool){ |(*(bool){ true }, (bool){ false }) }
                   }
                 }
                 name: (string){ string }
@@ -97,11 +97,11 @@
           }
         }
       }
-      _name: (string){ string }
+      _name(:kube): (string){ string }
     }
   }
-  _spec: (struct){
-    _name: (string){ string }
+  _spec(:kube): (struct){
+    _name(:kube): (string){ string }
     spec: (struct){
       template: (struct){
         spec: (struct){
diff --git a/cue/testdata/definitions/hidden.txtar b/cue/testdata/definitions/hidden.txtar
index 5f900ee..ad442b1 100644
--- a/cue/testdata/definitions/hidden.txtar
+++ b/cue/testdata/definitions/hidden.txtar
@@ -12,29 +12,41 @@
   _name: d: int
 }
 
-d: pkg.#D & { _name: d: int }
+d: pkg.#D & { _name: d: int, _val: 4 }
 
 // TODO: this should fail, as the _name restricting it is in this
 // package.
-// e: pkg.#D & #def & { _name: e: int }
+e: pkg.#D & #def & { _name: e: int, _val: int }
+f: e._val
 
 -- pkg/bar.cue --
 package pkg
 
-#D: {}
+#D: { _val: 3 }
 
 -- out/eval --
 (struct){
   #def: (#struct){
-    _name: (#struct){
+    _name(:foo): (#struct){
       d: (int){ int }
     }
   }
   d: (#struct){
-    _name: (#struct){
+    _val(example.com:pkg): (int){ 3 }
+    _name(:foo): (#struct){
       d: (int){ int }
     }
+    _val(:foo): (int){ 4 }
   }
+  e: (#struct){
+    _val(example.com:pkg): (int){ 3 }
+    _name(:foo): (#struct){
+      d: (int){ int }
+      e: (int){ int }
+    }
+    _val(:foo): (int){ int }
+  }
+  f: (int){ int }
 }
 -- out/compile --
 --- in.cue
@@ -48,5 +60,13 @@
     _name: {
       d: int
     }
+    _val: 4
   })
+  e: ((〈import;"example.com/pkg"〉.#D & 〈0;#def〉) & {
+    _name: {
+      e: int
+    }
+    _val: int
+  })
+  f: 〈0;e〉._val
 }
diff --git a/cue/testdata/definitions/issue533.txtar b/cue/testdata/definitions/issue533.txtar
index 43b03f7..1daa2f7 100644
--- a/cue/testdata/definitions/issue533.txtar
+++ b/cue/testdata/definitions/issue533.txtar
@@ -35,7 +35,7 @@
   #x: (#struct){
     Name: (string){ string }
   }
-  _#x: (#struct){
+  _#x(:x): (#struct){
     Name: (string){ string }
   }
   x1: (_|_){
diff --git a/cue/testdata/fulleval/051_detectIncompleteYAML.txtar b/cue/testdata/fulleval/051_detectIncompleteYAML.txtar
index 67de9b0..b9f6f6e 100644
--- a/cue/testdata/fulleval/051_detectIncompleteYAML.txtar
+++ b/cue/testdata/fulleval/051_detectIncompleteYAML.txtar
@@ -69,7 +69,7 @@
 -- out/eval --
 (struct){
   #Spec: (#struct){
-    _vars: (#struct){
+    _vars(:foobar): (#struct){
       something: (string){ string }
     }
     data: (#struct){
@@ -87,7 +87,7 @@
     }
   }
   Val: (#struct){
-    _vars: (#struct){
+    _vars(:foobar): (#struct){
       something: (string){ "var-string" }
     }
     data: (#struct){
diff --git a/cue/testdata/fulleval/052_detectIncompleteJSON.txtar b/cue/testdata/fulleval/052_detectIncompleteJSON.txtar
index 8718c37..188a756 100644
--- a/cue/testdata/fulleval/052_detectIncompleteJSON.txtar
+++ b/cue/testdata/fulleval/052_detectIncompleteJSON.txtar
@@ -61,7 +61,7 @@
 -- out/eval --
 (struct){
   #Spec: (#struct){
-    _vars: (#struct){
+    _vars(:foobar): (#struct){
       something: (string){ string }
     }
     data: (#struct){
@@ -78,7 +78,7 @@
     }
   }
   Val: (#struct){
-    _vars: (#struct){
+    _vars(:foobar): (#struct){
       something: (string){ "var-string" }
     }
     data: (#struct){
diff --git a/cue/types.go b/cue/types.go
index 055d768..da8bc89 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -937,13 +937,15 @@
 		ShowDocs:        o.docs,
 	}
 
+	pkgID := v.instance().ID()
+
 	// var expr ast.Expr
 	var err error
 	var f *ast.File
 	if o.concrete || o.final {
 		// inst = v.instance()
 		var expr ast.Expr
-		expr, err = p.Value(v.idx.Runtime, v.v)
+		expr, err = p.Value(v.idx.Runtime, pkgID, v.v)
 		if err != nil {
 			return nil
 		}
@@ -955,7 +957,7 @@
 		}
 		// return expr
 	} else {
-		f, err = p.Def(v.idx.Runtime, v.v)
+		f, err = p.Def(v.idx.Runtime, pkgID, v.v)
 		if err != nil {
 			panic(err)
 		}
@@ -1718,7 +1720,7 @@
 	case state.Flag('+'):
 		_, _ = io.WriteString(state, ctx.opCtx.Str(v.v))
 	default:
-		n, _ := export.Raw.Expr(v.idx.Runtime, v.v)
+		n, _ := export.Raw.Expr(v.idx.Runtime, v.instance().ID(), v.v)
 		b, _ := format.Node(n)
 		_, _ = state.Write(b)
 	}
diff --git a/internal/core/adt/feature.go b/internal/core/adt/feature.go
index 6ee3b6a..0356e02 100644
--- a/internal/core/adt/feature.go
+++ b/internal/core/adt/feature.go
@@ -15,6 +15,7 @@
 package adt
 
 import (
+	"fmt"
 	"strconv"
 	"strings"
 
@@ -68,10 +69,36 @@
 		}
 		return literal.String.Quote(s)
 	default:
-		return index.IndexToString(int64(x))
+		return f.IdentString(index)
 	}
 }
 
+// IdentString reports the identifier of f. The result is undefined if f
+// is not an identifier label.
+func (f Feature) IdentString(index StringIndexer) string {
+	s := index.IndexToString(int64(f.Index()))
+	if f.IsHidden() {
+		if p := strings.IndexByte(s, '\x00'); p >= 0 {
+			s = s[:p]
+		}
+	}
+	return s
+}
+
+// PkgID returns the package identifier, composed of the module and package
+// name, associated with this identifier. It will return "" if this is not
+// a hidden label.
+func (f Feature) PkgID(index StringIndexer) string {
+	if !f.IsHidden() {
+		return ""
+	}
+	s := index.IndexToString(int64(f.Index()))
+	if p := strings.IndexByte(s, '\x00'); p >= 0 {
+		return s[p+1:]
+	}
+	return s
+}
+
 // StringValue reports the string value of f, which must be a string label.
 func (f Feature) StringValue(index StringIndexer) string {
 	if !f.IsString() {
@@ -113,17 +140,19 @@
 }
 
 // MakeIdentLabel creates a label for the given identifier.
-func MakeIdentLabel(r StringIndexer, s string) Feature {
-	i := r.StringToIndex(s)
+func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature {
 	t := StringLabel
 	switch {
 	case strings.HasPrefix(s, "_#"):
 		t = HiddenDefinitionLabel
+		s = fmt.Sprintf("%s\x00%s", s, pkgpath)
 	case strings.HasPrefix(s, "#"):
 		t = DefinitionLabel
 	case strings.HasPrefix(s, "_"):
+		s = fmt.Sprintf("%s\x00%s", s, pkgpath)
 		t = HiddenLabel
 	}
+	i := r.StringToIndex(s)
 	f, err := MakeLabel(nil, i, t)
 	if err != nil {
 		panic("out of free string slots")
diff --git a/internal/core/adt/feature_test.go b/internal/core/adt/feature_test.go
index 833b425..858cc7e 100644
--- a/internal/core/adt/feature_test.go
+++ b/internal/core/adt/feature_test.go
@@ -77,18 +77,18 @@
 		isRegular: true,
 		isInt:     true,
 	}, {
-		in:        adt.MakeIdentLabel(r, "foo"),
+		in:        adt.MakeIdentLabel(r, "foo", "main"),
 		isRegular: true,
 		isString:  true,
 	}, {
-		in:           adt.MakeIdentLabel(r, "#foo"),
+		in:           adt.MakeIdentLabel(r, "#foo", "main"),
 		isDefinition: true,
 	}, {
-		in:           adt.MakeIdentLabel(r, "_#foo"),
+		in:           adt.MakeIdentLabel(r, "_#foo", "main"),
 		isDefinition: true,
 		isHidden:     true,
 	}, {
-		in:       adt.MakeIdentLabel(r, "_foo"),
+		in:       adt.MakeIdentLabel(r, "_foo", "main"),
 		isHidden: true,
 	}}
 	for i, tc := range testCases {
diff --git a/internal/core/compile/compile.go b/internal/core/compile/compile.go
index e261193..740e904 100644
--- a/internal/core/compile/compile.go
+++ b/internal/core/compile/compile.go
@@ -39,18 +39,23 @@
 	// Imports allows unresolved identifiers to resolve to imports.
 	//
 	// Under normal circumstances, identifiers bind to import specifications,
-	// which get resolved to an ImportReference. Use this option to automaically
-	// resolve identifiers to imports.
+	// which get resolved to an ImportReference. Use this option to
+	// automatically resolve identifiers to imports.
 	Imports func(x *ast.Ident) (pkgPath string)
+
+	// pkgPath is used to qualify the scope of hidden fields. The default
+	// scope is "main".
+	pkgPath string
 }
 
 // Files compiles the given files as a single instance. It disregards
 // the package names and it is the responsibility of the user to verify that
-// the packages names are consistent.
+// the packages names are consistent. The pkgID must be a unique identifier
+// for a package in a module, for instance as obtained from build.Instance.ID.
 //
 // Files may return a completed parse even if it has errors.
-func Files(cfg *Config, r adt.Runtime, files ...*ast.File) (*adt.Vertex, errors.Error) {
-	c := newCompiler(cfg, r)
+func Files(cfg *Config, r adt.Runtime, pkgID string, files ...*ast.File) (*adt.Vertex, errors.Error) {
+	c := newCompiler(cfg, pkgID, r)
 
 	v := c.compileFiles(files)
 
@@ -60,14 +65,11 @@
 	return v, nil
 }
 
-func Expr(cfg *Config, r adt.Runtime, x ast.Expr) (adt.Conjunct, errors.Error) {
-	if cfg == nil {
-		cfg = &Config{}
-	}
-	c := &compiler{
-		Config: *cfg,
-		index:  r,
-	}
+// Expr compiles the given expression into a conjunct. The pkgID must be a
+// unique identifier for a package in a module, for instance as obtained from
+// build.Instance.ID.
+func Expr(cfg *Config, r adt.Runtime, pkgPath string, x ast.Expr) (adt.Conjunct, errors.Error) {
+	c := newCompiler(cfg, pkgPath, r)
 
 	v := c.compileExpr(x)
 
@@ -77,13 +79,17 @@
 	return v, nil
 }
 
-func newCompiler(cfg *Config, r adt.Runtime) *compiler {
+func newCompiler(cfg *Config, pkgPath string, r adt.Runtime) *compiler {
 	c := &compiler{
 		index: r,
 	}
 	if cfg != nil {
 		c.Config = *cfg
 	}
+	if pkgPath == "" {
+		pkgPath = "main"
+	}
+	c.Config.pkgPath = pkgPath
 	return c
 }
 
diff --git a/internal/core/compile/compile_test.go b/internal/core/compile/compile_test.go
index f5a73ac..4072ed9 100644
--- a/internal/core/compile/compile_test.go
+++ b/internal/core/compile/compile_test.go
@@ -53,7 +53,7 @@
 
 		a := t.ValidInstances()
 
-		v, err := compile.Files(nil, r, a[0].Files...)
+		v, err := compile.Files(nil, r, "main", a[0].Files...)
 
 		// Write the results.
 		t.WriteErrors(err)
@@ -113,7 +113,7 @@
 	}
 	r := runtime.New()
 
-	arc, err := compile.Files(nil, r, file)
+	arc, err := compile.Files(nil, r, "main", file)
 	if err != nil {
 		t.Error(errors.Details(err, nil))
 	}
diff --git a/internal/core/compile/label.go b/internal/core/compile/label.go
index 5fddde8..d35b7c0 100644
--- a/internal/core/compile/label.go
+++ b/internal/core/compile/label.go
@@ -29,7 +29,7 @@
 	index := c.index
 	switch x := n.(type) {
 	case *ast.Ident:
-		return adt.MakeIdentLabel(c.index, x.Name)
+		return adt.MakeIdentLabel(c.index, x.Name, c.pkgPath)
 
 	case *ast.BasicLit:
 		switch x.Kind {
diff --git a/internal/core/convert/go.go b/internal/core/convert/go.go
index 2a4173b..1997136 100644
--- a/internal/core/convert/go.go
+++ b/internal/core/convert/go.go
@@ -68,7 +68,7 @@
 }
 
 func compileExpr(ctx *adt.OpContext, expr ast.Expr) adt.Value {
-	c, err := compile.Expr(nil, ctx, expr)
+	c, err := compile.Expr(nil, ctx, pkgID(), expr)
 	if err != nil {
 		return &adt.Bottom{Err: errors.Promote(err, "compile")}
 	}
@@ -241,7 +241,7 @@
 		return &adt.Null{Src: ctx.Source()}
 
 	case *ast.File:
-		x, err := compile.Files(nil, ctx, v)
+		x, err := compile.Files(nil, ctx, pkgID(), v)
 		if err != nil {
 			return &adt.Bottom{Err: errors.Promote(err, "compile")}
 		}
@@ -737,7 +737,7 @@
 			ctx.AddErrf(msg, args...)
 		})
 		var x adt.Expr
-		c, err := compile.Expr(nil, ctx, e)
+		c, err := compile.Expr(nil, ctx, pkgID(), e)
 		if err != nil {
 			b := &adt.Bottom{Err: err}
 			ctx.AddBottom(b)
@@ -788,3 +788,8 @@
 	}
 	return ast.NewBinExpr(token.OR, null, e)
 }
+
+// pkgID returns a package path that can never resolve to an existing package.
+func pkgID() string {
+	return "_"
+}
diff --git a/internal/core/debug/debug.go b/internal/core/debug/debug.go
index a5d8e0b..b789a05 100644
--- a/internal/core/debug/debug.go
+++ b/internal/core/debug/debug.go
@@ -87,6 +87,13 @@
 
 // TODO: fold into label once :: is no longer supported.
 func (w *printer) labelString(f adt.Feature) string {
+	if f.IsHidden() {
+		ident := f.IdentString(w.index)
+		if pkgName := f.PkgID(w.index); pkgName != "main" {
+			ident = fmt.Sprintf("%s(%s)", ident, pkgName)
+		}
+		return ident
+	}
 	return f.SelectorString(w.index)
 }
 
diff --git a/internal/core/eval/optionals_test.go b/internal/core/eval/optionals_test.go
index 4d5c97c..1abadc4 100644
--- a/internal/core/eval/optionals_test.go
+++ b/internal/core/eval/optionals_test.go
@@ -60,7 +60,7 @@
 				t.Fatal(err)
 			}
 
-			v, errs := compile.Files(nil, ctx, f)
+			v, errs := compile.Files(nil, ctx, "", f)
 			if errs != nil {
 				t.Fatal(errs)
 			}
diff --git a/internal/core/export/adt.go b/internal/core/export/adt.go
index 8013a05..429d8e4 100644
--- a/internal/core/export/adt.go
+++ b/internal/core/export/adt.go
@@ -28,7 +28,7 @@
 )
 
 func (e *exporter) ident(x adt.Feature) *ast.Ident {
-	s := e.ctx.IndexToString(int64(x.Index()))
+	s := x.IdentString(e.ctx)
 	if !ast.IsValidIdent(s) {
 		panic(s + " is not a valid identifier")
 	}
diff --git a/internal/core/export/export.go b/internal/core/export/export.go
index 733c202..da23162 100644
--- a/internal/core/export/export.go
+++ b/internal/core/export/export.go
@@ -35,9 +35,13 @@
 	// IncludeDocs
 	ShowOptional    bool
 	ShowDefinitions bool
-	ShowHidden      bool
-	ShowDocs        bool
-	ShowAttributes  bool
+
+	// ShowHidden forces the inclusion of hidden fields when these would
+	// otherwise be omitted. Only hidden fields from the current package are
+	// included.
+	ShowHidden     bool
+	ShowDocs       bool
+	ShowAttributes bool
 
 	// AllowErrorType
 	// Use unevaluated conjuncts for these error types
@@ -77,13 +81,13 @@
 // Concrete
 
 // Def exports v as a definition.
-func Def(r adt.Runtime, v *adt.Vertex) (*ast.File, errors.Error) {
-	return All.Def(r, v)
+func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
+	return All.Def(r, pkgID, v)
 }
 
 // Def exports v as a definition.
-func (p *Profile) Def(r adt.Runtime, v *adt.Vertex) (*ast.File, errors.Error) {
-	e := newExporter(p, r, v)
+func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
+	e := newExporter(p, r, pkgID, v)
 	if v.Label.IsDef() {
 		e.inDefinition++
 	}
@@ -100,12 +104,12 @@
 	return e.toFile(v, expr)
 }
 
-func Expr(r adt.Runtime, n adt.Expr) (ast.Expr, errors.Error) {
-	return Simplified.Expr(r, n)
+func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
+	return Simplified.Expr(r, pkgID, n)
 }
 
-func (p *Profile) Expr(r adt.Runtime, n adt.Expr) (ast.Expr, errors.Error) {
-	e := newExporter(p, r, nil)
+func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
+	e := newExporter(p, r, pkgID, nil)
 	return e.expr(n), nil
 }
 
@@ -156,30 +160,32 @@
 
 // File
 
-func Vertex(r adt.Runtime, n *adt.Vertex) (*ast.File, errors.Error) {
-	return Simplified.Vertex(r, n)
+func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
+	return Simplified.Vertex(r, pkgID, n)
 }
 
-func (p *Profile) Vertex(r adt.Runtime, n *adt.Vertex) (*ast.File, errors.Error) {
+func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
 	e := exporter{
 		cfg:   p,
 		index: r,
+		pkgID: pkgID,
 	}
 	v := e.value(n, n.Conjuncts...)
 
 	return e.toFile(n, v)
 }
 
-func Value(r adt.Runtime, n adt.Value) (ast.Expr, errors.Error) {
-	return Simplified.Value(r, n)
+func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
+	return Simplified.Value(r, pkgID, n)
 }
 
 // Should take context.
-func (p *Profile) Value(r adt.Runtime, n adt.Value) (ast.Expr, errors.Error) {
+func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
 	e := exporter{
 		ctx:   eval.NewContext(r, nil),
 		cfg:   p,
 		index: r,
+		pkgID: pkgID,
 	}
 	v := e.value(n)
 	return v, e.errs
@@ -199,13 +205,20 @@
 	inDefinition int // for close() wrapping.
 
 	unique int
+
+	// hidden label handling
+	pkgID       string
+	hidden      map[string]adt.Feature // adt.InvalidFeatures means more than one.
+	usedFeature map[adt.Feature]string
+	usedHidden  map[string]bool
 }
 
-func newExporter(p *Profile, r adt.Runtime, v *adt.Vertex) *exporter {
+func newExporter(p *Profile, r adt.Runtime, pkgID string, v *adt.Vertex) *exporter {
 	return &exporter{
 		cfg:   p,
 		ctx:   eval.NewContext(r, v),
 		index: r,
+		pkgID: pkgID,
 	}
 }
 
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index d384681..7aa4652 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -49,7 +49,7 @@
 	test.Run(t, func(t *cuetxtar.Test) {
 		a := t.ValidInstances()
 
-		v, errs := compile.Files(nil, r, a[0].Files...)
+		v, errs := compile.Files(nil, r, "", a[0].Files...)
 		if errs != nil {
 			t.Fatal(errs)
 		}
@@ -58,7 +58,7 @@
 		// TODO: do we need to evaluate v? In principle not necessary.
 		// v.Finalize(eval.NewContext(r, v))
 
-		file, errs := export.Def(r, v)
+		file, errs := export.Def(r, "", v)
 		errors.Print(t, errs, nil)
 		_, _ = t.Write(formatNode(t.T, file))
 	})
@@ -117,7 +117,7 @@
 			if err != nil {
 				return nil, err
 			}
-			c, err := compile.Expr(nil, ctx, expr)
+			c, err := compile.Expr(nil, ctx, "_", expr)
 			if err != nil {
 				return nil, err
 			}
@@ -145,7 +145,7 @@
 			if err != nil {
 				t.Fatal("failed test case: ", err)
 			}
-			expr, err := export.Expr(ctx, v)
+			expr, err := export.Expr(ctx, "", v)
 			if err != nil {
 				t.Fatal("failed export: ", err)
 			}
@@ -223,13 +223,13 @@
 	// astutil.Sanitize(x)
 
 	r := runtime.New()
-	v, errs := compile.Files(nil, r, a[0].Files...)
+	v, errs := compile.Files(nil, r, "", a[0].Files...)
 	if errs != nil {
 		t.Fatal(errs)
 	}
 	v.Finalize(eval.NewContext(r, v))
 
-	file, errs := export.Def(r, v)
+	file, errs := export.Def(r, "main", v)
 	if errs != nil {
 		t.Fatal(errs)
 	}
diff --git a/internal/core/export/extract_test.go b/internal/core/export/extract_test.go
index 02a098c..a542975 100644
--- a/internal/core/export/extract_test.go
+++ b/internal/core/export/extract_test.go
@@ -38,7 +38,7 @@
 	test.Run(t, func(t *cuetxtar.Test) {
 		a := t.ValidInstances()
 
-		v, err := compile.Files(nil, r, a[0].Files...)
+		v, err := compile.Files(nil, r, "", a[0].Files...)
 		if err != nil {
 			t.Fatal(err)
 		}
diff --git a/internal/core/export/label.go b/internal/core/export/label.go
index b04cb93..50fca4d 100644
--- a/internal/core/export/label.go
+++ b/internal/core/export/label.go
@@ -33,7 +33,8 @@
 		return ast.NewLit(token.INT, strconv.Itoa(int(x)))
 
 	case adt.DefinitionLabel, adt.HiddenLabel, adt.HiddenDefinitionLabel:
-		return ast.NewIdent(e.ctx.IndexToString(int64(x)))
+		s := f.IdentString(e.ctx)
+		return ast.NewIdent(s)
 
 	case adt.StringLabel:
 		s := e.ctx.IndexToString(int64(x))
diff --git a/internal/core/export/testdata/hidden.txtar b/internal/core/export/testdata/hidden.txtar
new file mode 100644
index 0000000..334b20e
--- /dev/null
+++ b/internal/core/export/testdata/hidden.txtar
@@ -0,0 +1,57 @@
+-- in.cue --
+package foo
+
+import "acme.com/pkg"
+
+a: pkg.#A
+a: _val: 3
+a: _#val: 6
+-- cue.mod/module.cue --
+module: "acme.com"
+
+-- pkg/pkg.cue --
+package pkg
+
+#A: {
+    _val: 4
+	_#val: 5
+}
+
+-- out/definition --
+package foo
+
+import "acme.com/pkg"
+
+a: {
+	pkg.#A
+	_val:  3
+	_#val: 6
+}
+-- out/doc --
+[]
+[a]
+[a _val]
+[a _#val]
+-- out/value --
+== Simplified
+{
+	a: {}
+}
+== Raw
+{
+	a: {
+		_val:  3
+		_#val: 6
+	}
+}
+== Final
+{
+	a: {}
+}
+== All
+{
+	a: {
+		_val:  3
+		_#val: 6
+	}
+}
diff --git a/internal/core/export/value.go b/internal/core/export/value.go
index 4217425..eef9b21 100644
--- a/internal/core/export/value.go
+++ b/internal/core/export/value.go
@@ -310,8 +310,13 @@
 		if label.IsDef() && !p.ShowDefinitions {
 			continue
 		}
-		if label.IsHidden() && !p.ShowHidden {
-			continue
+		if label.IsHidden() {
+			if !p.ShowHidden {
+				continue
+			}
+			if label.PkgID(e.ctx) != e.pkgID {
+				continue
+			}
 		}
 
 		f := &ast.Field{Label: e.stringLabel(label)}
diff --git a/internal/core/export/value_test.go b/internal/core/export/value_test.go
index a75674d..e22c21f 100644
--- a/internal/core/export/value_test.go
+++ b/internal/core/export/value_test.go
@@ -47,9 +47,11 @@
 	test.Run(t, func(t *cuetxtar.Test) {
 		a := t.ValidInstances()
 
-		v, errs := compile.Files(nil, r, a[0].Files...)
-		if errs != nil {
-			t.Fatal(errs)
+		pkgID := a[0].ID()
+
+		v, err := r.Build(a[0])
+		if err != nil {
+			t.Fatal(err)
 		}
 
 		ctx := eval.NewContext(r, v)
@@ -57,7 +59,7 @@
 
 		for _, tc := range []struct {
 			name string
-			fn   func(r adt.Runtime, v adt.Value) (ast.Expr, errors.Error)
+			fn   func(r adt.Runtime, id string, v adt.Value) (ast.Expr, errors.Error)
 		}{
 			{"Simplified", export.Simplified.Value},
 			{"Raw", export.Raw.Value},
@@ -65,7 +67,7 @@
 			{"All", export.All.Value},
 		} {
 			fmt.Fprintln(t, "==", tc.name)
-			x, errs := tc.fn(r, v)
+			x, errs := tc.fn(r, pkgID, v)
 			errors.Print(t, errs, nil)
 			_, _ = t.Write(formatNode(t.T, x))
 			fmt.Fprintln(t)
@@ -89,7 +91,7 @@
 	a := cuetxtar.Load(archive, "/tmp/test")
 
 	r := runtime.New()
-	v, errs := compile.Files(nil, r, a[0].Files...)
+	v, errs := compile.Files(nil, r, "", a[0].Files...)
 	if errs != nil {
 		t.Fatal(errs)
 	}
@@ -97,7 +99,7 @@
 	ctx := eval.NewContext(r, v)
 	v.Finalize(ctx)
 
-	x, errs := export.Simplified.Value(r, v)
+	x, errs := export.Simplified.Value(r, "main", v)
 	if errs != nil {
 		t.Fatal(errs)
 	}
diff --git a/internal/core/runtime/index.go b/internal/core/runtime/index.go
index 81cb515..a8e381c 100644
--- a/internal/core/runtime/index.go
+++ b/internal/core/runtime/index.go
@@ -147,8 +147,7 @@
 }
 
 func (idx *Index) LabelStr(l adt.Feature) string {
-	index := int64(l.Index())
-	return idx.IndexToString(index)
+	return l.IdentString(idx)
 }
 
 func (x *Index) AddInst(path string, key, p interface{}) {
diff --git a/internal/core/runtime/runtime.go b/internal/core/runtime/runtime.go
index fd53f0c..6361e6b 100644
--- a/internal/core/runtime/runtime.go
+++ b/internal/core/runtime/runtime.go
@@ -80,7 +80,7 @@
 		return nil, errs
 	}
 
-	return compile.Files(nil, x, b.Files...)
+	return compile.Files(nil, x, b.ID(), b.Files...)
 }
 
 func (x *Runtime) buildSpec(b *build.Instance, spec *ast.ImportSpec) (errs errors.Error) {
diff --git a/internal/core/subsume/structural_test.go b/internal/core/subsume/structural_test.go
index b22ded7..2e1635e 100644
--- a/internal/core/subsume/structural_test.go
+++ b/internal/core/subsume/structural_test.go
@@ -465,7 +465,7 @@
 				t.Fatal(err)
 			}
 
-			root, errs := compile.Files(nil, r, file)
+			root, errs := compile.Files(nil, r, "", file)
 			if errs != nil {
 				t.Fatal(errs)
 			}
diff --git a/internal/core/subsume/subsume_test.go b/internal/core/subsume/subsume_test.go
index 757c901..7379689 100644
--- a/internal/core/subsume/subsume_test.go
+++ b/internal/core/subsume/subsume_test.go
@@ -50,7 +50,7 @@
 		t.Fatal(err)
 	}
 
-	root, errs := compile.Files(nil, ctx, file)
+	root, errs := compile.Files(nil, ctx, "", file)
 	if errs != nil {
 		t.Fatal(errs)
 	}
diff --git a/internal/core/subsume/value_test.go b/internal/core/subsume/value_test.go
index 80869c4..e5ed21d 100644
--- a/internal/core/subsume/value_test.go
+++ b/internal/core/subsume/value_test.go
@@ -387,7 +387,7 @@
 				t.Fatal(err)
 			}
 
-			root, errs := compile.Files(nil, r, file)
+			root, errs := compile.Files(nil, r, "", file)
 			if errs != nil {
 				t.Fatal(errs)
 			}
diff --git a/internal/core/validate/validate_test.go b/internal/core/validate/validate_test.go
index 496ad3b..fd88dd6 100644
--- a/internal/core/validate/validate_test.go
+++ b/internal/core/validate/validate_test.go
@@ -174,13 +174,13 @@
 			if err != nil {
 				t.Fatal(err)
 			}
-			v, err := compile.Files(nil, r, f)
+			v, err := compile.Files(nil, r, "", f)
 			if err != nil {
 				t.Fatal(err)
 			}
 			ctx.Unify(ctx, v, adt.Finalized)
 			if tc.lookup != "" {
-				v = v.Lookup(adt.MakeIdentLabel(r, tc.lookup))
+				v = v.Lookup(adt.MakeIdentLabel(r, tc.lookup, "main"))
 			}
 
 			b := Validate(ctx, v, tc.cfg)
diff --git a/pkg/internal/builtin.go b/pkg/internal/builtin.go
index ac69222..9523754 100644
--- a/pkg/internal/builtin.go
+++ b/pkg/internal/builtin.go
@@ -59,9 +59,9 @@
 	CUE    string
 }
 
-func (p *Package) MustCompile(ctx *adt.OpContext, pkgName string) *adt.Vertex {
+func (p *Package) MustCompile(ctx *adt.OpContext, importPath string) *adt.Vertex {
 	obj := &adt.Vertex{}
-	pkgLabel := ctx.StringLabel(pkgName)
+	pkgLabel := ctx.StringLabel(importPath)
 	st := &adt.StructLit{}
 	if len(p.Native) > 0 {
 		obj.AddConjunct(adt.MakeRootConjunct(nil, st))
@@ -83,11 +83,11 @@
 
 	// Parse builtin CUE
 	if p.CUE != "" {
-		expr, err := parser.ParseExpr(pkgName, p.CUE)
+		expr, err := parser.ParseExpr(importPath, p.CUE)
 		if err != nil {
 			panic(fmt.Errorf("could not parse %v: %v", p.CUE, err))
 		}
-		c, err := compile.Expr(nil, ctx.Runtime, expr)
+		c, err := compile.Expr(nil, ctx.Runtime, importPath, expr)
 		if err != nil {
 			panic(fmt.Errorf("could compile parse %v: %v", p.CUE, err))
 		}
@@ -147,7 +147,7 @@
 	if err != nil {
 		panic(err)
 	}
-	c, err := compile.Expr(nil, ctx, expr)
+	c, err := compile.Expr(nil, ctx, "_", expr)
 	if err != nil {
 		panic(err)
 	}