encoding/gocode: add Go code generator

Also:
- add gocodec used by generator
- added ability to create temporary Runtime (for concurrency)
- removed index freezing

The latter was only for precondition checking of
internal code.  At the same time, freezing could
lead to spurious races if Runtime is used
concurrently.

Note:
- gocodec is a separate package as it is useful
  on its own and that way it can be used without
  pulling all the dependencies of gocode.

Change-Id: Ib59b65084038b616c9fb725cbe152a56b8869416
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2742
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/build.go b/cue/build.go
index 4ac5214..8487a2b 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -39,13 +39,22 @@
 	internal.GetRuntime = func(instance interface{}) interface{} {
 		return &Runtime{idx: instance.(*Instance).index}
 	}
+
+	internal.CheckAndForkRuntime = func(runtime, value interface{}) interface{} {
+		r := runtime.(*Runtime)
+		idx := value.(Value).ctx().index
+		if idx != r.idx {
+			panic("value not from same runtime")
+		}
+		return &Runtime{idx: newIndex(idx)}
+	}
 }
 
 func dummyLoad(token.Pos, string) *build.Instance { return nil }
 
 func (r *Runtime) index() *index {
 	if r.idx == nil {
-		r.idx = newIndex()
+		r.idx = newIndex(sharedIndex)
 	}
 	return r.idx
 }
@@ -197,12 +206,19 @@
 
 	offset label
 	parent *index
-	freeze bool
 
 	mutex     sync.Mutex
 	typeCache sync.Map // map[reflect.Type]evaluated
 }
 
+// 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
@@ -225,8 +241,7 @@
 }
 
 // newIndex creates a new index.
-func newIndex() *index {
-	parent := sharedIndex
+func newIndex(parent *index) *index {
 	i := &index{
 		labelMap:      map[string]label{},
 		loaded:        map[*build.Instance]*Instance{},
@@ -235,7 +250,6 @@
 		offset:        label(len(parent.labels)) + parent.offset,
 		parent:        parent,
 	}
-	parent.freeze = true
 	return i
 }
 
@@ -267,9 +281,6 @@
 func (idx *index) label(s string, isIdent bool) label {
 	f, ok := idx.findLabel(s)
 	if !ok {
-		if idx.freeze {
-			panic("adding label to frozen index")
-		}
 		f = label(len(idx.labelMap)) + idx.offset
 		idx.labelMap[s] = f
 		idx.labels = append(idx.labels, s)
diff --git a/cue/builtin.go b/cue/builtin.go
index af15465..dc73586 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -257,7 +257,7 @@
 	if e, ok := call.ret.(value); ok {
 		return e
 	}
-	return convert(ctx, x, call.ret)
+	return convert(ctx, x, false, call.ret)
 }
 
 // callCtxt is passed to builtin implementations.
diff --git a/cue/go.go b/cue/go.go
index 7012edf..b61026d 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -36,8 +36,8 @@
 // optimized.
 
 func init() {
-	internal.FromGoValue = func(runtime, x interface{}) interface{} {
-		return convertValue(runtime.(*Runtime), x)
+	internal.FromGoValue = func(runtime, x interface{}, allowDefault bool) interface{} {
+		return convertValue(runtime.(*Runtime), x, allowDefault)
 	}
 
 	internal.FromGoType = func(runtime, x interface{}) interface{} {
@@ -45,9 +45,9 @@
 	}
 }
 
-func convertValue(r *Runtime, x interface{}) Value {
+func convertValue(r *Runtime, x interface{}, allowDefault bool) Value {
 	ctx := r.index().newContext()
-	v := convert(ctx, baseValue{}, x)
+	v := convert(ctx, baseValue{}, allowDefault, x)
 	return newValueRoot(ctx, v)
 }
 
@@ -187,17 +187,20 @@
 	}
 }
 
-func convert(ctx *context, src source, x interface{}) evaluated {
-	v := convertRec(ctx, src, x)
+func convert(ctx *context, src source, allowDefault bool, x interface{}) evaluated {
+	v := convertRec(ctx, src, allowDefault, x)
 	if v == nil {
 		return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", v)
 	}
 	return v
 }
 
-func convertRec(ctx *context, src source, x interface{}) evaluated {
+func convertRec(ctx *context, src source, allowDefault bool, x interface{}) evaluated {
 	switch v := x.(type) {
 	case nil:
+		if !allowDefault {
+			return &nullLit{src.base()}
+		}
 		// Interpret a nil pointer as an undefined value that is only
 		// null by default, but may still be set: *null | _.
 		return makeNullable(&top{src.base()}, false).(evaluated)
@@ -291,16 +294,16 @@
 		return toUint(ctx, src, uint64(v))
 	case float64:
 		r := newNum(src, floatKind)
-		r.v.SetString(fmt.Sprintf("%g", v))
+		_, _, _ = r.v.SetString(fmt.Sprintf("%g", v))
 		return r
 	case float32:
 		r := newNum(src, floatKind)
-		r.v.SetString(fmt.Sprintf("%g", v))
+		_, _, _ = r.v.SetString(fmt.Sprintf("%g", v))
 		return r
 
 	case reflect.Value:
 		if v.CanInterface() {
-			return convertRec(ctx, src, v.Interface())
+			return convertRec(ctx, src, allowDefault, v.Interface())
 		}
 
 	default:
@@ -321,16 +324,19 @@
 			return toUint(ctx, src, value.Uint())
 
 		case reflect.Float32, reflect.Float64:
-			return convertRec(ctx, src, value.Float())
+			return convertRec(ctx, src, allowDefault, value.Float())
 
 		case reflect.Ptr:
 			if value.IsNil() {
+				if !allowDefault {
+					return &nullLit{src.base()}
+				}
 				// Interpret a nil pointer as an undefined value that is only
 				// null by default, but may still be set: *null | _.
 				elem := goTypeToValue(ctx, false, reflect.TypeOf(v).Elem())
 				return makeNullable(elem, false).(evaluated)
 			}
-			return convertRec(ctx, src, value.Elem().Interface())
+			return convertRec(ctx, src, allowDefault, value.Elem().Interface())
 
 		case reflect.Struct:
 			obj := newStruct(src)
@@ -344,7 +350,7 @@
 				if isOmitEmpty(&t) && isZero(val) {
 					continue
 				}
-				sub := convertRec(ctx, src, val.Interface())
+				sub := convertRec(ctx, src, allowDefault, val.Interface())
 				if sub == nil {
 					// mimic behavior of encoding/json: skip fields of unsupported types
 					continue
@@ -383,7 +389,7 @@
 					sorted = append(sorted, s)
 
 					val := value.MapIndex(k).Interface()
-					sub := convertRec(ctx, src, val)
+					sub := convertRec(ctx, src, allowDefault, val)
 					// mimic behavior of encoding/json: report error of
 					// unsupported type.
 					if sub == nil {
@@ -419,7 +425,7 @@
 			arcs := []arc{}
 			for i := 0; i < value.Len(); i++ {
 				val := value.Index(i).Interface()
-				x := convertRec(ctx, src, val)
+				x := convertRec(ctx, src, allowDefault, val)
 				if x == nil {
 					return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", val)
 				}
diff --git a/cue/go_test.go b/cue/go_test.go
index cedfbe8..fe8133b 100644
--- a/cue/go_test.go
+++ b/cue/go_test.go
@@ -130,7 +130,7 @@
 	for _, tc := range testCases {
 		ctx := inst.newContext()
 		t.Run("", func(t *testing.T) {
-			v := convert(ctx, newNode(b), tc.goVal)
+			v := convert(ctx, newNode(b), true, tc.goVal)
 			got := debugStr(ctx, v)
 			if got != tc.want {
 				t.Errorf("got %q; want %q", got, tc.want)
diff --git a/cue/instance.go b/cue/instance.go
index 1083cd0..2b1d551 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -279,7 +279,7 @@
 	for i := len(path) - 1; i >= 0; i-- {
 		x = map[string]interface{}{path[i]: x}
 	}
-	value := convert(ctx, root, x)
+	value := convert(ctx, root, true, x)
 	eval := binOp(ctx, baseValue{}, opUnify, root, value)
 	// TODO: validate recursively?
 	err := inst.Err
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index eaf637f..34798b2 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -43,7 +43,7 @@
 	inst, err := r.Parse("test", body)
 
 	if err != nil {
-		x := newIndex().newInstance(nil)
+		x := newIndex(sharedIndex).newInstance(nil)
 		ctx := x.newContext()
 		return ctx, x, err
 	}
diff --git a/cuego/cuego.go b/cuego/cuego.go
index 6f8e35c..63647d9 100644
--- a/cuego/cuego.go
+++ b/cuego/cuego.go
@@ -75,7 +75,7 @@
 // function.
 func (c *Context) Validate(x interface{}) error {
 	a := c.load(x)
-	v, err := fromGoValue(x)
+	v, err := fromGoValue(x, false)
 	if err != nil {
 		return err
 	}
@@ -99,7 +99,7 @@
 // successful update.
 func (c *Context) Complete(x interface{}) error {
 	a := c.load(x)
-	v, err := fromGoValue(x)
+	v, err := fromGoValue(x, true)
 	if err != nil {
 		return err
 	}
@@ -171,12 +171,12 @@
 }
 
 // fromGoValue converts a Go value to CUE
-func fromGoValue(x interface{}) (v cue.Value, err error) {
+func fromGoValue(x interface{}, allowDefault bool) (v cue.Value, err error) {
 	// TODO: remove the need to have a lock here. We could use a new index (new
 	// Instance) here as any previously unrecognized field can never match an
 	// existing one and can only be merged.
 	mutex.Lock()
-	v = internal.FromGoValue(runtime, x).(cue.Value)
+	v = internal.FromGoValue(runtime, x, allowDefault).(cue.Value)
 	mutex.Unlock()
 	if err := v.Err(); err != nil {
 		return v, err
diff --git a/encoding/gocode/doc.go b/encoding/gocode/doc.go
new file mode 100644
index 0000000..7bf0af6
--- /dev/null
+++ b/encoding/gocode/doc.go
@@ -0,0 +1,20 @@
+// Copyright 2019 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 gocode defines functions for extracting CUE definitions from Go code
+// and generating Go code from CUE values.
+//
+// This package is used for offline processing. For converting Go values to and
+// from CUE at runtime, use the gocodec package.
+package gocode
diff --git a/encoding/gocode/gen_test.go b/encoding/gocode/gen_test.go
new file mode 100644
index 0000000..6f01c2b
--- /dev/null
+++ b/encoding/gocode/gen_test.go
@@ -0,0 +1,89 @@
+// Copyright 2019 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.
+
+// +build !gen
+
+package gocode
+
+import (
+	"strings"
+	"testing"
+
+	"cuelang.org/go/encoding/gocode/testdata/pkg1"
+	"cuelang.org/go/encoding/gocode/testdata/pkg2"
+)
+
+func TestPackages(t *testing.T) {
+	testCases := []struct {
+		name string
+		got  error
+		want string
+	}{{
+		name: "failing int",
+		got: func() error {
+			v := pkg2.PickMe(4)
+			return v.Validate()
+		}(),
+		want: "invalid value 4 (out of bound >5):\n    pkg2/instance.cue:x:x",
+	}, {
+		name: "failing field with validator",
+		got: func() error {
+			v := &pkg1.OtherStruct{A: "car"}
+			return v.Validate()
+		}(),
+		want: "A: invalid value \"car\" (does not satisfy strings.ContainsAny(\"X\")):\n    pkg1/instance.cue:x:x",
+	}, {
+		name: "failing field of type int",
+		got: func() error {
+			v := &pkg1.MyStruct{A: 11, B: "dog"}
+			return v.Validate()
+		}(),
+		want: "A: invalid value 11 (out of bound <=10):\n    pkg1/instance.cue:x:x",
+	}, {
+		name: "failing nested struct ",
+		got: func() error {
+			v := &pkg1.MyStruct{A: 5, B: "dog", O: &pkg1.OtherStruct{A: "car"}}
+			return v.Validate()
+		}(),
+		want: "O.A: invalid value \"car\" (does not satisfy strings.ContainsAny(\"X\")):\n    pkg1/instance.cue:x:x",
+	}, {
+		name: "fail nested struct of different package",
+		got: func() error {
+			v := &pkg1.MyStruct{A: 5, B: "dog", O: &pkg1.OtherStruct{A: "X", P: 4}}
+			return v.Validate()
+		}(),
+		want: "O.P: invalid value 4 (out of bound >5):\n    pkg2/instance.cue:x:x",
+	}, {
+		name: "all good",
+		got: func() error {
+			v := &pkg1.MyStruct{
+				A: 5,
+				B: "dog",
+				I: &pkg2.ImportMe{A: 1000, B: "a"},
+			}
+			return v.Validate()
+		}(),
+		want: "nil",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			got := strings.TrimSpace(errStr(tc.got))
+			want := tc.want
+			if got != want {
+				t.Errorf("got:\n%q\nwant:\n%q", got, want)
+			}
+
+		})
+	}
+}
diff --git a/encoding/gocode/generator.go b/encoding/gocode/generator.go
new file mode 100644
index 0000000..494edf1
--- /dev/null
+++ b/encoding/gocode/generator.go
@@ -0,0 +1,277 @@
+// Copyright 2019 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 gocode
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/format"
+	"go/types"
+	"text/template"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal"
+	"golang.org/x/tools/go/packages"
+)
+
+// Config defines options for generation Go code.
+type Config struct {
+	// Prefix is used as a prefix to all generated variables. It defaults to
+	// cuegen.
+	Prefix string
+
+	// ValidateName defines the default name for validation methods or prefix
+	// for validation functions. The default is "Validate". Set to "-" to
+	// disable generating validators.
+	ValidateName string
+
+	// CompleteName defines the default name for complete methods or prefix
+	// for complete functions. The default is "-" (disabled).
+	CompleteName string
+
+	// The cue.Runtime variable name to use for initializing Codecs.
+	// A new Runtime is created by default.
+	RuntimeVar string
+}
+
+const defaultPrefix = "cuegen"
+
+// Generate generates Go code for the given instance in the directory of the
+// given package.
+//
+// Generate converts top-level declarations to corresponding Go code. By default,
+// it will only generate validation functions of methods for exported top-level
+// declarations. The behavior can be altered with the @go attribute.
+//
+// The go attribute has the following form @go(<name>{,<option>}), where option
+// is either a key-value pair or a flag. The name maps the CUE name to an
+// alternative Go name. The special value '-' is used to indicate the field
+// should be ignored for any Go generation.
+//
+// The following options are supported:
+//
+//     type=<gotype>    The Go type as which this value should be interpreted.
+//                      This defaults to the type with the (possibly overridden)
+//                      name of the field.
+//     validate=<name>  Alternative name for the validation function or method
+//                      Setting this to the empty string disables generation.
+//     complete=<name>  Alternative name for the validation function or method.
+//                      Setting this to the empty string disables generation.
+//     func             Generate as a function instead of a method.
+//
+//
+// Selection and Naming
+//
+// Generate will not generate any code for fields that have no go attribute
+// and that are not exported or for which there is no namesake Go type.
+// If the go attribute has the special value '-' as its name it wil be dropped
+// as well. In all other cases Generate will generate Go code, even if the
+// resulting code will not compile. For instance, Generate will generate Go
+// code even if the user defines a Go type in the attribute that does not
+// exist.
+//
+// If a field selected for generation and the go name matches that the name of
+// the Go type, the corresponding validate and complete code are generated as
+// methods by default. If not, it will be generated as a function. The default
+// function name is the default operation name with the Go name as a suffix.
+//
+//
+// Caveats
+// Currently not supported:
+//   - option to generate Go structs (or automatically generate if undefined)
+//   - for type option to refer to types outside the package.
+//
+func Generate(pkgPath string, inst *cue.Instance, c *Config) (b []byte, err error) {
+	// TODO: if inst is nil, the instance is loaded from CUE files in the same
+	// package directory with the same package name.
+	if err = inst.Value().Validate(); err != nil {
+		return nil, err
+	}
+	if c == nil {
+		c = &Config{}
+	}
+	g := &generator{
+		Config: *c,
+
+		typeMap: map[string]types.Type{},
+	}
+
+	pkgName := inst.Name
+
+	if pkgPath != "" {
+		loadCfg := &packages.Config{
+			Mode: packages.LoadAllSyntax,
+		}
+		pkgs, err := packages.Load(loadCfg, pkgPath)
+		if err != nil {
+			return nil, fmt.Errorf("generating failed: %v", err)
+		}
+
+		if len(pkgs) != 1 {
+			return nil, fmt.Errorf(
+				"generate only allowed for one package at a time, found %d",
+				len(pkgs))
+		}
+
+		g.pkg = pkgs[0]
+		if len(g.pkg.Errors) > 0 {
+			for _, err := range g.pkg.Errors {
+				g.addErr(err)
+			}
+			return nil, g.err
+		}
+
+		pkgName = g.pkg.Name
+
+		for _, obj := range g.pkg.TypesInfo.Defs {
+			if obj == nil || obj.Pkg() != g.pkg.Types {
+				continue
+			}
+			g.typeMap[obj.Name()] = obj.Type()
+		}
+	}
+
+	// TODO: add package doc if there is no existing Go package or if it doesn't
+	// have package documentation already.
+	g.exec(headerCode, map[string]string{
+		"pkgName": pkgName,
+	})
+
+	iter, err := inst.Value().Fields()
+	g.addErr(err)
+
+	for iter.Next() {
+		g.decl(iter.Label(), iter.Value())
+	}
+
+	r := internal.GetRuntime(inst).(*cue.Runtime)
+	b, err = r.Marshal(inst)
+	g.addErr(err)
+
+	g.exec(loadCode, map[string]string{
+		"runtime": g.RuntimeVar,
+		"prefix":  strValue(g.Prefix, defaultPrefix),
+		"data":    string(b),
+	})
+
+	if g.err != nil {
+		return nil, g.err
+	}
+
+	b, err = format.Source(g.w.Bytes())
+	if err != nil {
+		// Return bytes as well to allow analysis of the failed Go code.
+		return g.w.Bytes(), err
+	}
+
+	return b, err
+}
+
+type generator struct {
+	Config
+	pkg     *packages.Package
+	typeMap map[string]types.Type
+
+	w   bytes.Buffer
+	err errors.Error
+}
+
+func (g *generator) addErr(err error) {
+	if err != nil {
+		g.err = errors.Append(g.err, errors.Promote(err, "generate failed"))
+	}
+}
+
+func (g *generator) exec(t *template.Template, data interface{}) {
+	g.addErr(t.Execute(&g.w, data))
+}
+
+func (g *generator) decl(name string, v cue.Value) {
+	attr := v.Attribute("go")
+
+	if !ast.IsExported(name) && attr.Err() != nil {
+		return
+	}
+
+	goName := name
+	switch s, _ := attr.String(0); s {
+	case "":
+	case "-":
+		return
+	default:
+		goName = s
+	}
+
+	goTypeName := goName
+	goType := ""
+	if str, ok, _ := attr.Lookup(1, "type"); ok {
+		goType = str
+		goTypeName = str
+	}
+
+	isFunc, _ := attr.Flag(1, "func")
+	if goTypeName != goName {
+		isFunc = true
+	}
+
+	zero := "nil"
+
+	typ := g.typeMap[goTypeName]
+	if goType == "" {
+		goType = goTypeName
+		if typ != nil {
+			switch typ.Underlying().(type) {
+			case *types.Struct, *types.Array:
+				goType = "*" + goTypeName
+				zero = fmt.Sprintf("&%s{}", goTypeName)
+			case *types.Pointer:
+				zero = fmt.Sprintf("%s(nil)", goTypeName)
+				isFunc = true
+			}
+		}
+	}
+
+	g.exec(stubCode, map[string]interface{}{
+		"prefix":  strValue(g.Prefix, defaultPrefix),
+		"cueName": name,   // the field name of the CUE type
+		"goType":  goType, // the receiver or argument type
+		"zero":    zero,   // the zero value of the underlying type
+
+		// @go attribute options
+		"func":     isFunc,
+		"validate": lookupName(attr, "validate", strValue(g.ValidateName, "Validate")),
+		"complete": lookupName(attr, "complete", g.CompleteName),
+	})
+}
+
+func lookupName(attr cue.Attribute, option, config string) string {
+	name, ok, _ := attr.Lookup(1, option)
+	if !ok {
+		name = config
+	}
+	if name == "-" {
+		return ""
+	}
+	return name
+}
+
+func strValue(have, fallback string) string {
+	if have == "" {
+		return fallback
+	}
+	return have
+}
diff --git a/encoding/gocode/generator_test.go b/encoding/gocode/generator_test.go
new file mode 100644
index 0000000..70e0aa8
--- /dev/null
+++ b/encoding/gocode/generator_test.go
@@ -0,0 +1,84 @@
+// Copyright 2019 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 gocode
+
+import (
+	"bytes"
+	"flag"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"testing"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/load"
+)
+
+var update = flag.Bool("update", false, "update test files")
+
+func TestGenerate(t *testing.T) {
+	dirs, err := ioutil.ReadDir("testdata")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cwd, err := os.Getwd()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, d := range dirs {
+		if !d.IsDir() {
+			continue
+		}
+		t.Run(d.Name(), func(t *testing.T) {
+			dir := filepath.Join(cwd, "testdata")
+			pkg := "." + string(filepath.Separator) + d.Name()
+			inst := cue.Build(load.Instances([]string{pkg}, &load.Config{
+				Dir:        dir,
+				ModuleRoot: dir,
+				Module:     "cuelang.org/go/encoding/gocode/testdata",
+			}))[0]
+			if err := inst.Err; err != nil {
+				t.Fatal(err)
+			}
+
+			goPkg := "./testdata/" + d.Name()
+			b, err := Generate(goPkg, inst, nil)
+			if err != nil {
+				t.Fatal(errStr(err))
+			}
+			// t.Log(string(b))
+
+			goFile := filepath.Join("testdata", d.Name(), "cue_gen.go")
+			if *update {
+				_ = ioutil.WriteFile(goFile, b, 0644)
+				return
+			}
+		})
+	}
+}
+
+func errStr(err error) string {
+	if err == nil {
+		return "nil"
+	}
+	buf := &bytes.Buffer{}
+	errors.Print(buf, err, nil)
+	r := regexp.MustCompile(`.cue:\d+:\d+`)
+	return r.ReplaceAllString(buf.String(), ".cue:x:x")
+}
diff --git a/encoding/gocode/gocodec/codec.go b/encoding/gocode/gocodec/codec.go
new file mode 100644
index 0000000..0037d2a
--- /dev/null
+++ b/encoding/gocode/gocodec/codec.go
@@ -0,0 +1,157 @@
+// Copyright 2019 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 codec converts Go to and from CUE and validates Go values based on
+// CUE constraints.
+//
+// CUE constraints can be used to validate Go types as well as fill out
+// missing struct fields that are implied from the constraints and the values
+// already defined by the struct value.
+package gocodec
+
+import (
+	"sync"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/internal"
+)
+
+// Config has no options yet, but is defined for future extensibility.
+type Config struct {
+}
+
+// A Codec decodes and encodes CUE from and to Go values and validates and
+// completes Go values based on CUE templates.
+type Codec struct {
+	runtime *cue.Runtime
+	mutex   sync.RWMutex
+}
+
+// New creates a new Codec for the given instance.
+//
+// It is safe to use the methods of Codec concurrently as long as the given
+// Runtime is not used elsewhere while using Codec. However, only the concurrent
+// use of Decode, Validate, and Complete is efficient.
+func New(r *cue.Runtime, c *Config) *Codec {
+	return &Codec{runtime: r}
+}
+
+// ExtractType extracts a CUE value from a Go type.
+//
+// The type represented by x is converted as the underlying type. Specific
+// values, such as map or slice elements or field values of structs are ignored.
+// If x is of type reflect.Type, the type represented by x is extracted.
+//
+// Fields of structs can be annoted using additional constrains using the 'cue'
+// field tag. The value of the tag is a CUE expression, which may contain
+// references to the JSON name of other fields in a struct.
+//
+//     type Sum struct {
+//         A int `cue:"c-b" json:"a,omitempty"`
+//         B int `cue:"c-a" json:"b,omitempty"`
+//         C int `cue:"a+b" json:"c,omitempty"`
+//     }
+//
+func (c *Codec) ExtractType(x interface{}) (cue.Value, error) {
+	// ExtractType cannot introduce new fields on repeated calls. We could
+	// consider optimizing the lock usage based on this property.
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	return fromGoType(c.runtime, x)
+}
+
+// TODO: allow extracting constraints and type info separately?
+
+// Decode converts x to a CUE value.
+//
+// If x is of type reflect.Value it will convert the value represented by x.
+func (c *Codec) Decode(x interface{}) (cue.Value, error) {
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+
+	// Depending on the type, can introduce new labels on repeated calls.
+	return fromGoValue(c.runtime, x, false)
+}
+
+// Encode converts v to a Go value.
+func (c *Codec) Encode(v cue.Value, x interface{}) error {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+
+	return v.Decode(x)
+}
+
+// Validate checks whether x satisfies the constraints defined by v.
+//
+// The given value must be created using the same Runtime with which c was
+// initialized.
+func (c *Codec) Validate(v cue.Value, x interface{}) error {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+
+	r := checkAndForkRuntime(c.runtime, v)
+	w, err := fromGoValue(r, x, false)
+	if err != nil {
+		return err
+	}
+	return w.Unify(v).Err()
+}
+
+// Complete sets previously undefined values in x that can be uniquely
+// determined form the constraints defined by v if validation passes, or returns
+// an error, without modifying anything, otherwise.
+//
+// Only undefined values are modified. A value is considered undefined if it is
+// pointer type and is nil or if it is a field with a zero value that has a json
+// tag with the omitempty flag.
+//
+// The given value must be created using the same Runtime with which c was
+// initialized.
+//
+// Complete does a JSON round trip. This means that data not preserved in such a
+// round trip, such as the location name of a time.Time, is lost after a
+// successful update.
+func (c *Codec) Complete(v cue.Value, x interface{}) error {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+
+	r := checkAndForkRuntime(c.runtime, v)
+	w, err := fromGoValue(r, x, true)
+	if err != nil {
+		return err
+	}
+
+	return w.Unify(v).Decode(x)
+}
+
+func fromGoValue(r *cue.Runtime, x interface{}, allowDefault bool) (cue.Value, error) {
+	v := internal.FromGoValue(r, x, allowDefault).(cue.Value)
+	if err := v.Err(); err != nil {
+		return v, err
+	}
+	return v, nil
+}
+
+func fromGoType(r *cue.Runtime, x interface{}) (cue.Value, error) {
+	v := internal.FromGoType(r, x).(cue.Value)
+	if err := v.Err(); err != nil {
+		return v, err
+	}
+	return v, nil
+}
+
+func checkAndForkRuntime(r *cue.Runtime, v cue.Value) *cue.Runtime {
+	return internal.CheckAndForkRuntime(r, v).(*cue.Runtime)
+}
diff --git a/encoding/gocode/gocodec/codec_test.go b/encoding/gocode/gocodec/codec_test.go
new file mode 100644
index 0000000..cb38865
--- /dev/null
+++ b/encoding/gocode/gocodec/codec_test.go
@@ -0,0 +1,286 @@
+// Copyright 2019 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 gocodec
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+
+	"cuelang.org/go/cue"
+	"github.com/google/go-cmp/cmp"
+)
+
+type Sum struct {
+	A int `cue:"C-B" json:",omitempty"`
+	B int `cue:"C-A" json:",omitempty"`
+	C int `cue:"A+B & >=5" json:",omitempty"`
+}
+
+func checkErr(t *testing.T, got error, want string) {
+	t.Helper()
+	if (got == nil) != (want == "") {
+		t.Errorf("error: got %v; want %v", got, want)
+	}
+}
+func TestValidate(t *testing.T) {
+	fail := "some error"
+	testCases := []struct {
+		name        string
+		value       interface{}
+		constraints string
+		err         string
+	}{{
+		name:        "*Sum: nil disallowed by constraint",
+		value:       (*Sum)(nil),
+		constraints: "!=null",
+		err:         fail,
+	}, {
+		name:  "Sum",
+		value: Sum{A: 1, B: 4, C: 5},
+	}, {
+		name:  "*Sum",
+		value: &Sum{A: 1, B: 4, C: 5},
+	}, {
+		name:  "*Sum: incorrect sum",
+		value: &Sum{A: 1, B: 4, C: 6},
+		err:   fail,
+	}, {
+		name:  "*Sum: field C is too low",
+		value: &Sum{A: 1, B: 3, C: 4},
+		err:   fail,
+	}, {
+		name:  "*Sum: nil value",
+		value: (*Sum)(nil),
+	}, {
+		// Not a typical constraint, but it is possible.
+		name:        "string list",
+		value:       []string{"a", "b", "c"},
+		constraints: `[_, "b", ...]`,
+	}, {
+		// Not a typical constraint, but it is possible.
+		name:        "string list incompatible lengths",
+		value:       []string{"a", "b", "c"},
+		constraints: `4*[string]`,
+		err:         fail,
+	}}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			r := &cue.Runtime{}
+			codec := New(r, nil)
+
+			v, err := codec.ExtractType(tc.value)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if tc.constraints != "" {
+				inst, err := r.Compile(tc.name, tc.constraints)
+				if err != nil {
+					t.Fatal(err)
+				}
+				v = v.Unify(inst.Value())
+				fmt.Println("XXX", v)
+				fmt.Println("XXX", inst.Value())
+				fmt.Println("UUU", v)
+			}
+
+			err = codec.Validate(v, tc.value)
+			checkErr(t, err, tc.err)
+		})
+	}
+}
+
+func TestComplete(t *testing.T) {
+	type updated struct {
+		A []*int `cue:"[...int|*1]"` // arbitrary length slice with values 1
+		B []int  `cue:"3*[int|*1]"`  // slice of length 3, defaults to [1,1,1]
+
+		// TODO: better errors if the user forgets to quote.
+		M map[string]int `cue:",opt"`
+	}
+	type sump struct {
+		A *int `cue:"C-B"`
+		B *int `cue:"C-A"`
+		C *int `cue:"A+B"`
+	}
+	one := 1
+	two := 2
+	fail := "some error"
+	_ = fail
+	_ = one
+	testCases := []struct {
+		name        string
+		value       interface{}
+		result      interface{}
+		constraints string
+		err         string
+	}{{
+		name:   "*Sum",
+		value:  &Sum{A: 1, B: 4, C: 5},
+		result: &Sum{A: 1, B: 4, C: 5},
+	}, {
+		name:   "*Sum",
+		value:  &Sum{A: 1, B: 4},
+		result: &Sum{A: 1, B: 4, C: 5},
+	}, {
+		name:   "*sump",
+		value:  &sump{A: &one, B: &one},
+		result: &sump{A: &one, B: &one, C: &two},
+	}, {
+		name:   "*Sum: backwards",
+		value:  &Sum{B: 4, C: 8},
+		result: &Sum{A: 4, B: 4, C: 8},
+	}, {
+		name:   "*Sum: sum too low",
+		value:  &Sum{A: 1, B: 3},
+		result: &Sum{A: 1, B: 3}, // Value should not be updated
+		err:    fail,
+	}, {
+		name:   "*Sum: sum underspecified",
+		value:  &Sum{A: 1},
+		result: &Sum{A: 1}, // Value should not be updated
+		err:    fail,
+	}, {
+		name:   "Sum: cannot modify",
+		value:  Sum{A: 3, B: 4, C: 7},
+		result: Sum{A: 3, B: 4, C: 7},
+		err:    fail,
+	}, {
+		name:   "*Sum: cannot update nil value",
+		value:  (*Sum)(nil),
+		result: (*Sum)(nil),
+		err:    fail,
+	}, {
+		name:   "cannot modify slice",
+		value:  []string{"a", "b", "c"},
+		result: []string{"a", "b", "c"},
+		err:    fail,
+	}, {
+		name: "composite values update",
+		// allocate a slice with uninitialized values and let Update fill
+		// out default values.
+		value: &updated{A: make([]*int, 3)},
+		result: &updated{
+			A: []*int{&one, &one, &one},
+			B: []int{1, 1, 1},
+			M: map[string]int(nil),
+		},
+	}, {
+		name:   "composite values update with unsatisfied map constraints",
+		value:  &updated{},
+		result: &updated{},
+
+		constraints: ` { M: {foo: bar, bar: foo} } `,
+		err:         fail, // incomplete values
+	}, {
+		name:        "composite values update with map constraints",
+		value:       &updated{M: map[string]int{"foo": 1}},
+		constraints: ` { M: {foo: bar, bar: foo} } `,
+		result: &updated{
+			// TODO: would be better if this is nil, but will not matter for
+			// JSON output: if omitempty is false, an empty list will be
+			// printed regardless, and if it is true, it will be omitted
+			// regardless.
+			A: []*int{},
+			B: []int{1, 1, 1},
+			M: map[string]int{"bar": 1, "foo": 1},
+		},
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			r := &cue.Runtime{}
+			codec := New(r, nil)
+
+			v, err := codec.ExtractType(tc.value)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if tc.constraints != "" {
+				inst, err := r.Compile(tc.name, tc.constraints)
+				if err != nil {
+					t.Fatal(err)
+				}
+				v = v.Unify(inst.Value())
+			}
+
+			err = codec.Complete(v, tc.value)
+			checkErr(t, err, tc.err)
+			if !reflect.DeepEqual(tc.value, tc.result) {
+				t.Errorf("value:\n got: %#v;\nwant: %#v", tc.value, tc.result)
+			}
+		})
+	}
+}
+
+func TestEncode(t *testing.T) {
+	testCases := []struct {
+		in   string
+		dst  interface{}
+		want interface{}
+	}{{
+		in:   "4",
+		dst:  new(int),
+		want: 4,
+	}}
+	r := &cue.Runtime{}
+	c := New(r, nil)
+
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			inst, err := r.Compile("test", tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			err = c.Encode(inst.Value(), tc.dst)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			got := reflect.ValueOf(tc.dst).Elem().Interface()
+			if !cmp.Equal(got, tc.want) {
+				t.Error(cmp.Diff(got, tc.want))
+			}
+		})
+	}
+}
+
+func TestDecode(t *testing.T) {
+	testCases := []struct {
+		in   interface{}
+		want string
+	}{{
+		in:   "str",
+		want: `"str"`,
+	}}
+	c := New(&cue.Runtime{}, nil)
+
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			v, err := c.Decode(tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			got := fmt.Sprint(v)
+			if got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/encoding/gocode/templates.go b/encoding/gocode/templates.go
new file mode 100644
index 0000000..db1b906
--- /dev/null
+++ b/encoding/gocode/templates.go
@@ -0,0 +1,99 @@
+// Copyright 2019 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 gocode
+
+import "text/template"
+
+// Inputs:
+// .pkgName  the Go package name
+var headerCode = template.Must(template.New("header").Parse(
+	`// Code generated by gogen.Generate; DO NOT EDIT.
+
+package {{.pkgName}}
+
+import (
+	"fmt"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/encoding/gocode/gocodec"
+)
+
+`))
+
+// Inputs:
+// .prefix 	  prefix to all generated variable names
+// .cueName   name of the top-level CUE value
+// .goType    Go type of the receiver or argument
+// .zero      zero value of the Go type; nil indicates no value
+// .validate  name of the validate function; "" means no validate
+// .complete  name of the complete function; "" means no complete
+var stubCode = template.Must(template.New("type").Parse(`
+var {{.prefix}}val{{.cueName}} = {{.prefix}}Make("{{.cueName}}", {{.zero}})
+
+{{ $sig := .goType | printf "(x %s)" -}}
+{{if .validate}}
+// {{.validate}}{{if .func}}{{.cueName}}{{end}} validates x.
+func {{if .func}}{{.validate}}{{.cueName}}{{$sig}}
+     {{- else -}}{{$sig}} {{.validate}}(){{end}} error {
+	return {{.prefix}}Codec.Validate({{.prefix}}val{{.cueName}}, x)
+}
+{{end}}
+{{if .complete}}
+// {{.complete}}{{if .func}}{{.cueName}}{{end}} completes x.
+func {{if .func}}{{.complete}}{{.cueName}}{{$sig}}
+     {{- else -}}{{$sig}} {{.complete}}(){{end}} error {
+	return {{.prefix}}Codec.Complete({{.prefix}}val{{.cueName}}, x)
+}
+{{end}}
+`))
+
+// Inputs:
+// .prefix 	  prefix to all generated variable names
+// .runtime   the variable name of a user-supplied runtime, if any
+// .data      bytes obtained from Instance.MarshalBinary
+var loadCode = template.Must(template.New("load").Parse(`
+var {{.prefix}}Codec, {{.prefix}}Instance = func() (*gocodec.Codec, *cue.Instance) {
+	var r *cue.Runtime
+	r = {{if .runtime}}{{.runtime}}{{else}}&cue.Runtime{}{{end}}
+	instances, err := r.Unmarshal({{.prefix}}InstanceData)
+	if err != nil {
+		panic(err)
+	}
+	if len(instances) != 1 {
+		panic("expected encoding of exactly one instance")
+	}
+	return gocodec.New(r, nil), instances[0]
+}()
+
+// {{.prefix}}Make is called in the init phase to initialize CUE values for
+// validation functions.
+func {{.prefix}}Make(name string, x interface{}) cue.Value {
+	v := {{.prefix}}Instance.Lookup(name)
+	if !v.Exists() {
+		panic(fmt.Errorf("could not find type %q in instance", name))
+	}
+	if x != nil {
+		w, err := {{.prefix}}Codec.ExtractType(x)
+		if err != nil {
+			panic(err)
+		}
+		v = v.Unify(w)
+	}
+	return v
+}
+
+// Data size: {{len .data}} bytes.
+var {{.prefix}}InstanceData = []byte({{printf "%+q" .data}})
+`))
diff --git a/encoding/gocode/test.cue b/encoding/gocode/test.cue
new file mode 100644
index 0000000..1d62a37
--- /dev/null
+++ b/encoding/gocode/test.cue
@@ -0,0 +1,3 @@
+import "strings"
+
+a: strings.ContainsAny("X") & "car"
diff --git a/encoding/gocode/testdata/cue.mod b/encoding/gocode/testdata/cue.mod
new file mode 100644
index 0000000..eaf98ca
--- /dev/null
+++ b/encoding/gocode/testdata/cue.mod
@@ -0,0 +1 @@
+module: "cuelang.org/go/encoding/gocode/testdata"
diff --git a/encoding/gocode/testdata/pkg1/code.go b/encoding/gocode/testdata/pkg1/code.go
new file mode 100644
index 0000000..4f5fc3b
--- /dev/null
+++ b/encoding/gocode/testdata/pkg1/code.go
@@ -0,0 +1,43 @@
+// Copyright 2019 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 pkg1
+
+import (
+	"time"
+
+	"cuelang.org/go/encoding/gocode/testdata/pkg2"
+)
+
+type MyStruct struct {
+	A int
+	B string
+	T time.Time // maps to builtin
+	O *OtherStruct
+	I *pkg2.ImportMe
+}
+
+type OtherStruct struct {
+	A string
+	// D time.Duration // maps to builtin
+	P pkg2.PickMe
+}
+
+type String string
+
+type Omit int
+
+type Ptr *struct {
+	A int
+}
diff --git a/encoding/gocode/testdata/pkg1/cue_gen.go b/encoding/gocode/testdata/pkg1/cue_gen.go
new file mode 100644
index 0000000..1fe592f
--- /dev/null
+++ b/encoding/gocode/testdata/pkg1/cue_gen.go
@@ -0,0 +1,83 @@
+// Code generated by gogen.Generate; DO NOT EDIT.
+
+package pkg1
+
+import (
+	"fmt"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/encoding/gocode/gocodec"
+)
+
+var cuegenvalMyStruct = cuegenMake("MyStruct", &MyStruct{})
+
+// Validate validates x.
+func (x *MyStruct) Validate() error {
+	return cuegenCodec.Validate(cuegenvalMyStruct, x)
+}
+
+// Complete completes x.
+func (x *MyStruct) Complete() error {
+	return cuegenCodec.Complete(cuegenvalMyStruct, x)
+}
+
+var cuegenvalOtherStruct = cuegenMake("OtherStruct", &OtherStruct{})
+
+// Validate validates x.
+func (x *OtherStruct) Validate() error {
+	return cuegenCodec.Validate(cuegenvalOtherStruct, x)
+}
+
+var cuegenvalString = cuegenMake("String", nil)
+
+// ValidateCUE validates x.
+func (x String) ValidateCUE() error {
+	return cuegenCodec.Validate(cuegenvalString, x)
+}
+
+var cuegenvalSpecialString = cuegenMake("SpecialString", nil)
+
+// ValidateSpecialString validates x.
+func ValidateSpecialString(x string) error {
+	return cuegenCodec.Validate(cuegenvalSpecialString, x)
+}
+
+var cuegenvalPtr = cuegenMake("Ptr", Ptr(nil))
+
+// ValidatePtr validates x.
+func ValidatePtr(x Ptr) error {
+	return cuegenCodec.Validate(cuegenvalPtr, x)
+}
+
+var cuegenCodec, cuegenInstance = func() (*gocodec.Codec, *cue.Instance) {
+	var r *cue.Runtime
+	r = &cue.Runtime{}
+	instances, err := r.Unmarshal(cuegenInstanceData)
+	if err != nil {
+		panic(err)
+	}
+	if len(instances) != 1 {
+		panic("expected encoding of exactly one instance")
+	}
+	return gocodec.New(r, nil), instances[0]
+}()
+
+// cuegenMake is called in the init phase to initialize CUE values for
+// validation functions.
+func cuegenMake(name string, x interface{}) cue.Value {
+	v := cuegenInstance.Lookup(name)
+	if !v.Exists() {
+		panic(fmt.Errorf("could not find type %q in instance", name))
+	}
+	if x != nil {
+		w, err := cuegenCodec.ExtractType(x)
+		if err != nil {
+			panic(err)
+		}
+		v = v.Unify(w)
+	}
+	return v
+}
+
+// Data size: 518 bytes.
+var cuegenInstanceData = []byte("\x01\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\x94R]k\xd4@\x14\x9d\x9b\xae`.U\xf0\a\b\xe3<m\xa4\xcd~\x80\b\xa1Qk\xad\u0407v\x17\x8b\"\x88\x0f\xe3\xeclv\xd8l&$w\u0165\xb6\xa0\xd6\xda_\xe7\xab\xff\xa6\x91|\xb5\xabo\xcd\xcb\\n\xe6\x9c{\xe6\xdcs\xaf\xf8\xe5\x80S\\2(\xbe3\xf6\xb4\xf8\xb6\x01\xb0i\x92\x9cd\xa2\xf4+I\xb2\xec\xc3\x06t\xdeXK\xe00\xe8\x8c%\xcd`\x93\xc1\x9d\xd7&\xd69\x14\x17\x8c\xb1\x87\xc5O\a\xe0\xfe\x87\x8fj\xa9\xfd\xa9\x89\x1b\xe4\x05\x83\u2731n\xf1c\x03\xe0\xeeM\xff\x9c\x81\x03\x9d#\xb9\xd0%Q\xa7j\"c\xec\xca\xf9]\\2\a\x00\xb6\xd4R\xc72\x89|\x9bE\xbd\xc8\xf6t\xa2\xec\xc4$e\xad\xecD\xf7H\xe74\x91${\xe9<\x1a\x00\xc0\x83\xf2\ucd7a}\xb5\xd4p\x05\u007fR\xa9\xe62\u04bc\xfc\x89h\x16\xa9\u0348w\xd1\x15\xb7`\x1f\nt\xc5B\u04ac<s\xcaL\x12\xe5\x02=\xc4\xc3\xd51eKE\x01?Aw7\xe0|'\x1c\xf4\xd1}\x19p\x1e\x9e\t%I\xf0\xaf\xfc\xb1\x98\xd8H\xa0;z\x1e\xf0\x11\xcdtVc\xd0=\bx)k\xe8\x1fT\xaa\x0e5\x9e\xf2\x17\x91\xedn)\xbbHcM:\xdck\n\x0f\u05c0\xed\xb0F\x88\xbfg\x13\x92&\xc9w\x93UW\xbc\x17\x1e\xba\xe3\xa0\xe6\x1d\x1b5/Y\xf1\xb8\xba\x1a\xf0\xe6{\x14\n\xd1\xd6\xd5\xc0\xcf26\x13I:|\xd7\x14{o\xf7=<N\xb522n\xc1\xe1\x99\xc8\ub3a8Q\xb4JuX\xab\xf0p\xb40t=\x80s\x93\x10_\x1f\xb1\xed\xe1\x91M\xf6\xbf\x98\x9c*\xb2\x93\u02a5\x1a\xdc<{\xdb\u00e9\xb5A\t\xc51e\xed3K\xdf\xfd\xc3eL&\x8d\xf5h\xda\x1d\xf4=<E\u019c\xdb\xe4c\xd8\xe4c\xf8o>\xe4Z:\x86\xd7\xe9\xb8Y1\xb6\x9bi\xc5\xec\f\xfa\xfd5\xe5\xff\x99/?)Q\x8a\xab}\x0f\xf8\xb3'\xc8\xd8\xdf\x00\x00\x00\xff\xff\xa9a\xee\x8d^\x03\x00\x00")
diff --git a/encoding/gocode/testdata/pkg1/instance.cue b/encoding/gocode/testdata/pkg1/instance.cue
new file mode 100644
index 0000000..3358cbe
--- /dev/null
+++ b/encoding/gocode/testdata/pkg1/instance.cue
@@ -0,0 +1,37 @@
+package pkg1
+
+import (
+	"math"
+	"strings"
+	"cuelang.org/go/encoding/gocode/testdata/pkg2"
+)
+
+MyStruct: {
+	A:  <=10
+	B:  =~"cat" | *"dog"
+	O?: OtherStruct
+	I:  pkg2.ImportMe
+} @go(,complete=Complete)
+
+OtherStruct: {
+	A: strings.ContainsAny("X")
+	P: pkg2.PickMe
+}
+
+String: !="" @go(,validate=ValidateCUE)
+
+SpecialString: =~"special" @go(,type=string)
+
+Omit: int @go(-)
+
+// NonExisting will be omitted as there is no equivalent Go type.
+NonExisting: {
+	B: string
+} @go(-)
+
+// ignore unexported unless explicitly enabled.
+foo: int
+
+Ptr: {
+	A: math.MultipleOf(10)
+}
diff --git a/encoding/gocode/testdata/pkg2/code.go b/encoding/gocode/testdata/pkg2/code.go
new file mode 100644
index 0000000..3620911
--- /dev/null
+++ b/encoding/gocode/testdata/pkg2/code.go
@@ -0,0 +1,22 @@
+// Copyright 2019 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 pkg2
+
+type ImportMe struct {
+	A int    `json:"a"`
+	B string `json:"b"`
+}
+
+type PickMe int
diff --git a/encoding/gocode/testdata/pkg2/cue_gen.go b/encoding/gocode/testdata/pkg2/cue_gen.go
new file mode 100644
index 0000000..74917b3
--- /dev/null
+++ b/encoding/gocode/testdata/pkg2/cue_gen.go
@@ -0,0 +1,57 @@
+// Code generated by gogen.Generate; DO NOT EDIT.
+
+package pkg2
+
+import (
+	"fmt"
+
+	"cuelang.org/go/cue"
+	"cuelang.org/go/encoding/gocode/gocodec"
+)
+
+var cuegenvalImportMe = cuegenMake("ImportMe", &ImportMe{})
+
+// Validate validates x.
+func (x *ImportMe) Validate() error {
+	return cuegenCodec.Validate(cuegenvalImportMe, x)
+}
+
+var cuegenvalPickMe = cuegenMake("PickMe", nil)
+
+// Validate validates x.
+func (x PickMe) Validate() error {
+	return cuegenCodec.Validate(cuegenvalPickMe, x)
+}
+
+var cuegenCodec, cuegenInstance = func() (*gocodec.Codec, *cue.Instance) {
+	var r *cue.Runtime
+	r = &cue.Runtime{}
+	instances, err := r.Unmarshal(cuegenInstanceData)
+	if err != nil {
+		panic(err)
+	}
+	if len(instances) != 1 {
+		panic("expected encoding of exactly one instance")
+	}
+	return gocodec.New(r, nil), instances[0]
+}()
+
+// cuegenMake is called in the init phase to initialize CUE values for
+// validation functions.
+func cuegenMake(name string, x interface{}) cue.Value {
+	v := cuegenInstance.Lookup(name)
+	if !v.Exists() {
+		panic(fmt.Errorf("could not find type %q in instance", name))
+	}
+	if x != nil {
+		w, err := cuegenCodec.ExtractType(x)
+		if err != nil {
+			panic(err)
+		}
+		v = v.Unify(w)
+	}
+	return v
+}
+
+// Data size: 276 bytes.
+var cuegenInstanceData = []byte("\x01\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xffD\x8e\xc1J\x031\x10\x86\xe7\u07ee`C\x15|\x00a\xd9S\x05\u066d\x82\bE\x84\xaa\b\x1e\x94\xe2U<\x8ci\x8c\xa1mR\x9a\xecA\u0103Z\xabO\xe3+FR*\x9ef\xf8f\xfe\x9fo+~e\xc8\xe27!\xbe\x13\x1d\u01f7\x16\xd01\xd6\a\xb6R]p\xe0\xc4\xd1B~\xeb\\@F\u0207\x1c\x9e\xd0!l\\\x9a\x89\xf2\x88K\"\u068d\x9f\x19\xb0}w/\x1bU=\x9a\xc9:\xb9$\xc4\x05Q7~\xb4\x80\xcd\u007f\xbe d\xc8ox\xaaRQ\xbe\x82\x82\x88\xe2O\x12\x01\xb0/\x1b5a\xab+7\u05f5v\xb5\xb2\u048d\x8cM\xbbt#U\a\xe5\u00c8\x03\u05f3\xb1>\x04\xb0\x93f\xfd\xa7]\xc9F\x81g,\u01ecU\x91NB\x98\xe9\xcc\xcdCQ\xfa07V\xfbR\x88\xab\x15\xb9V\xfd\xe2E\xb4\a\xfd\xe2\xe4\xa0\xd7\x13\xed\xb3~\xb1~\xa9\u039d\rl\xac\x1f\xd8\xe7n\xc9\x0f\xb2\xdc\x13\xafbh\xe48eN\x8f\x04\xd1o\x00\x00\x00\xff\xff\ue135\t=\x01\x00\x00")
diff --git a/encoding/gocode/testdata/pkg2/instance.cue b/encoding/gocode/testdata/pkg2/instance.cue
new file mode 100644
index 0000000..5a6e606
--- /dev/null
+++ b/encoding/gocode/testdata/pkg2/instance.cue
@@ -0,0 +1,10 @@
+package pkg2
+
+import "strings"
+
+ImportMe: {
+	A: <100
+	B: strings.ContainsAny("abc")
+}
+
+PickMe: >5
diff --git a/internal/internal.go b/internal/internal.go
index d9b82a7..36cacc8 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Package internal exposes some cue internals to other packages.
+//
+// A better name for this package would be technicaldebt.
 package internal // import "cuelang.org/go/internal"
 
 // TODO: refactor packages as to make this package unnecessary.
@@ -40,7 +43,7 @@
 // FromGoValue converts an arbitrary Go value to the corresponding CUE value.
 // instance must be of type *cue.Instance.
 // The returned value is a cue.Value, which the caller must cast to.
-var FromGoValue func(instance, x interface{}) interface{}
+var FromGoValue func(instance, x interface{}, allowDefault bool) interface{}
 
 // FromGoType converts an arbitrary Go type to the corresponding CUE value.
 // instance must be of type *cue.Instance.
@@ -56,3 +59,8 @@
 
 // GetRuntime reports the runtime for an Instance.
 var GetRuntime func(instance interface{}) interface{}
+
+// CheckAndForkRuntime checks that value is created using runtime, panicking
+// if it does not, and returns a forked runtime that will discard additional
+// keys.
+var CheckAndForkRuntime func(runtime, value interface{}) interface{}