cue: fix import shortname mapping

- resolution was broken for builtin packages
- allow exporting variables in addition to builtins

Change-Id: I5ae4381f66a9a5b6f24ba98656e79cfbd8ef6fa0
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2715
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index df566c4..6e394d3 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -155,7 +155,7 @@
 		}
 		if v.inSelector > 0 {
 			if p := getBuiltinShorthandPkg(ctx, n.Name); p != nil {
-				return &nodeRef{baseValue: newExpr(n), node: p}
+				return &nodeRef{newExpr(n), p, label}
 			}
 		}
 	}
@@ -454,7 +454,11 @@
 			ret = &selectorExpr{newExpr(n), ret, f}
 		} else {
 			n2 := v.mapScope(n.Node)
-			ret = &nodeRef{baseValue: newExpr(n), node: n2}
+			ref := &nodeRef{baseValue: newExpr(n), node: n2}
+			ret = ref
+			if inst := v.ctx().getImportFromNode(n2); inst != nil {
+				ref.short = f
+			}
 		}
 
 	case *ast.BottomLit:
diff --git a/cue/build.go b/cue/build.go
index b00fd86..a76be73 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -419,13 +419,13 @@
 		name := path.Base(id)
 		if imp := p.LookupImport(id); imp != nil {
 			name = imp.PkgName
-			if spec.Name != nil {
-				name = spec.Name.Name
-			}
 		} else if _, ok := builtins[id]; !ok {
 			// continue
 			return nodeErrorf(spec, "package %q not found", id)
 		}
+		if spec.Name != nil {
+			name = spec.Name.Name
+		}
 		if n, ok := fields[name]; ok {
 			return nodeErrorf(spec,
 				"%s redeclared as imported package name\n"+
diff --git a/cue/build_test.go b/cue/build_test.go
index 8377e8a..772ba9a 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -147,15 +147,24 @@
 	}{{
 		insts(&bimport{"", files(`test: "ok"`)}),
 		`{test: "ok"}`,
-		// }, {
-		// 	insts(pkg1, &bimport{"",
-		// 		files(
-		// 			`package test
+	}, {
+		insts(&bimport{"",
+			files(
+				`package test
 
-		// 			import "math"
+				import "math"
 
-		// 			"Pi: \(math.Pi)!"`)}),
-		// 	`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
+				"Pi: \(math.Pi)!"`)}),
+		`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
+	}, {
+		insts(&bimport{"",
+			files(
+				`package test
+
+				import math2 "math"
+
+				"Pi: \(math2.Pi)!"`)}),
+		`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
 	}, {
 		insts(pkg1, &bimport{"",
 			files(
@@ -172,12 +181,21 @@
 				`package test
 
 				import "pkg1"
-				pkg1: 1
 
 				"Hello \(pkg1.Object)!"`),
 		}),
-		`pkg1 redeclared as imported package name
-	previous declaration at file0.cue:4:5`,
+		`"Hello World!"`,
+	}, {
+		insts(pkg1, &bimport{"",
+			files(
+				`package test
+
+				import pkg2 "pkg1"
+				pkg1: pkg2.Object
+
+				"Hello \(pkg1)!"`),
+		}),
+		`"Hello World!"`,
 	}, {
 		insts(pkg1, &bimport{"",
 			files(
diff --git a/cue/copy.go b/cue/copy.go
index f4f3f28..5a6594b 100644
--- a/cue/copy.go
+++ b/cue/copy.go
@@ -26,7 +26,7 @@
 		if node == x.node {
 			return x, false
 		}
-		return &nodeRef{x.baseValue, node}, false
+		return &nodeRef{x.baseValue, node, x.short}, false
 
 	case *structLit:
 		arcs := make(arcs, len(x.arcs))
diff --git a/cue/export.go b/cue/export.go
index 61454d3..5454c68 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -164,6 +164,43 @@
 	panic(fmt.Sprintf("unsupported clause type %T", v))
 }
 
+func (p *exporter) shortName(preferred, pkg string) string {
+	info, ok := p.imports[pkg]
+	short := info.short
+	if !ok {
+		short = pkg
+		if i := strings.LastIndexByte(pkg, '.'); i >= 0 {
+			short = pkg[i+1:]
+		}
+		if _, ok := p.top[p.ctx.label(short, true)]; ok && preferred != "" {
+			short = preferred
+			info.name = short
+		}
+		for {
+			if _, ok := p.top[p.ctx.label(short, true)]; !ok {
+				break
+			}
+			short += "x"
+			info.name = short
+		}
+		info.short = short
+		p.top[p.ctx.label(short, true)] = true
+		p.imports[pkg] = info
+	}
+	f := p.ctx.label(short, true)
+	for _, e := range p.stack {
+		if e.from == f {
+			if info.alias == "" {
+				info.alias = p.unique(short)
+				p.imports[pkg] = info
+			}
+			short = info.alias
+			break
+		}
+	}
+	return short
+}
+
 func (p *exporter) expr(v value) ast.Expr {
 	if doEval(p.mode) {
 		e := v.evalPartial(p.ctx)
@@ -189,41 +226,19 @@
 		if x.pkg == 0 {
 			return name
 		}
-		pkg := p.ctx.labelStr(x.pkg)
-		info, ok := p.imports[pkg]
-		short := info.short
-		if !ok {
-			info.short = ""
-			short = pkg
-			if i := strings.LastIndexByte(pkg, '.'); i >= 0 {
-				short = pkg[i+1:]
-			}
-			for {
-				if _, ok := p.top[p.ctx.label(short, true)]; !ok {
-					break
-				}
-				short += "x"
-				info.name = short
-			}
-			info.short = short
-			p.top[p.ctx.label(short, true)] = true
-			p.imports[pkg] = info
-		}
-		f := p.ctx.label(short, true)
-		for _, e := range p.stack {
-			if e.from == f {
-				if info.alias == "" {
-					info.alias = p.unique(short)
-					p.imports[pkg] = info
-				}
-				short = info.alias
-				break
-			}
-		}
+		short := p.shortName("", p.ctx.labelStr(x.pkg))
 		return &ast.SelectorExpr{X: ast.NewIdent(short), Sel: name}
 
 	case *nodeRef:
-		return nil
+		if x.short == 0 {
+			return nil
+		}
+		inst := p.ctx.getImportFromNode(x.node)
+		if inst == nil {
+			return nil // should not happen!
+		}
+		short := p.ctx.labelStr(x.short)
+		return ast.NewIdent(p.shortName(short, inst.ImportPath))
 
 	case *selectorExpr:
 		n := p.expr(x.x)
diff --git a/cue/export_test.go b/cue/export_test.go
index 1646c5c..ef18af0 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -349,6 +349,50 @@
 		a: strings.ContainsAny("c")`),
 	}, {
 		in: `
+		import "time"
+
+		a: time.Time
+		`,
+		out: unindent(`
+		import "time"
+
+		a: time.Time`),
+	}, {
+		in: `
+		import "time"
+
+		{
+			a: time.Time
+		} & {
+			time: int
+		}		`,
+		out: unindent(`
+		import timex "time"
+
+		time: int
+		a:    timex.Time`),
+	}, {
+		in: `
+		import time2 "time"
+
+		a:    time2.Time`,
+		out: unindent(`
+		import "time"
+
+		a: time.Time`),
+	}, {
+		in: `
+		import time2 "time"
+
+		time: int
+		a:    time2.Time`,
+		out: unindent(`
+		import time2 "time"
+
+		time: int
+		a:    time2.Time`),
+	}, {
+		in: `
 		import "strings"
 
 		a: strings.TrimSpace("  c  ")
@@ -406,7 +450,7 @@
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
 			var r Runtime
-			inst, err := r.Parse("test", tc.in)
+			inst, err := r.Compile("test", tc.in)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/cue/value.go b/cue/value.go
index a459740..f8b6479 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -920,7 +920,8 @@
 // A nodeRef is a reference to a node.
 type nodeRef struct {
 	baseValue
-	node scope
+	node  scope
+	short label // only for packages, otherwise 0
 }
 
 func (x *nodeRef) kind() kind {