cue: interpret nil as _ or top

depending on the context.

Do not interpret as a disjuntion of both.

Fixes #220.

Change-Id: If935316e23d12825eda3fa75fea8f95a04d09da3
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4860
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cue/builtin.go b/cue/builtin.go
index 3fdf53c..a7479d7 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -310,7 +310,7 @@
 	case *valueError:
 		return v.err
 	}
-	return convert(ctx, x, false, call.ret)
+	return convert(ctx, x, true, call.ret)
 }
 
 // callCtxt is passed to builtin implementations.
diff --git a/cue/builtinutil.go b/cue/builtinutil.go
index d3c471e..dedf174 100644
--- a/cue/builtinutil.go
+++ b/cue/builtinutil.go
@@ -51,7 +51,7 @@
 	for i := len(path) - 1; i >= 0; i-- {
 		x = map[string]interface{}{path[i]: x}
 	}
-	value := convert(ctx, root, true, x)
+	value := convert(ctx, root, false, x)
 	eval := binOp(ctx, baseValue{}, opUnify, root, value)
 	return newValueRoot(ctx, eval)
 }
diff --git a/cue/go.go b/cue/go.go
index 22f45fb..ae21031 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -38,8 +38,8 @@
 // optimized.
 
 func init() {
-	internal.FromGoValue = func(runtime, x interface{}, allowDefault bool) interface{} {
-		return convertValue(runtime.(*Runtime), x, allowDefault)
+	internal.FromGoValue = func(runtime, x interface{}, nilIsTop bool) interface{} {
+		return convertValue(runtime.(*Runtime), x, nilIsTop)
 	}
 
 	internal.FromGoType = func(runtime, x interface{}) interface{} {
@@ -47,9 +47,9 @@
 	}
 }
 
-func convertValue(r *Runtime, x interface{}, allowDefault bool) Value {
+func convertValue(r *Runtime, x interface{}, nilIsTop bool) Value {
 	ctx := r.index().newContext()
-	v := convert(ctx, baseValue{}, allowDefault, x)
+	v := convert(ctx, baseValue{}, nilIsTop, x)
 	return newValueRoot(ctx, v)
 }
 
@@ -189,23 +189,30 @@
 	}
 }
 
-func convert(ctx *context, src source, allowDefault bool, x interface{}) evaluated {
-	v := convertRec(ctx, src, allowDefault, x)
+func convert(ctx *context, src source, nilIsTop bool, x interface{}) evaluated {
+	v := convertRec(ctx, src, nilIsTop, x)
 	if v == nil {
 		return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", v)
 	}
 	return v
 }
 
-func convertRec(ctx *context, src source, allowDefault bool, x interface{}) evaluated {
+func isNil(x reflect.Value) bool {
+	switch x.Kind() {
+	// Only check for supported types; ignore func and chan.
+	case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface:
+		return x.IsNil()
+	}
+	return false
+}
+
+func convertRec(ctx *context, src source, nilIsTop bool, x interface{}) evaluated {
 	switch v := x.(type) {
 	case nil:
-		if !allowDefault {
-			return &nullLit{src.base()}
+		if nilIsTop {
+			return &top{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)
+		return &nullLit{src.base()}
 
 	case ast.Expr:
 		x := newVisitorCtx(ctx, nil, nil, nil, false)
@@ -302,7 +309,7 @@
 
 	case reflect.Value:
 		if v.CanInterface() {
-			return convertRec(ctx, src, allowDefault, v.Interface())
+			return convertRec(ctx, src, nilIsTop, v.Interface())
 		}
 
 	default:
@@ -328,19 +335,16 @@
 			return toUint(ctx, src, value.Uint())
 
 		case reflect.Float32, reflect.Float64:
-			return convertRec(ctx, src, allowDefault, value.Float())
+			return convertRec(ctx, src, nilIsTop, value.Float())
 
 		case reflect.Ptr:
 			if value.IsNil() {
-				if !allowDefault {
-					return &nullLit{src.base()}
+				if nilIsTop {
+					return &top{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 &nullLit{src.base()}
 			}
-			return convertRec(ctx, src, allowDefault, value.Elem().Interface())
+			return convertRec(ctx, src, nilIsTop, value.Elem().Interface())
 
 		case reflect.Struct:
 			obj := newStruct(src)
@@ -351,10 +355,13 @@
 					continue
 				}
 				val := value.Field(i)
+				if !nilIsTop && isNil(val) {
+					continue
+				}
 				if isOmitEmpty(&t) && isZero(val) {
 					continue
 				}
-				sub := convertRec(ctx, src, allowDefault, val.Interface())
+				sub := convertRec(ctx, src, nilIsTop, val.Interface())
 				if sub == nil {
 					// mimic behavior of encoding/json: skip fields of unsupported types
 					continue
@@ -388,12 +395,12 @@
 				reflect.Uint, reflect.Uint8, reflect.Uint16,
 				reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 				for _, k := range value.MapKeys() {
-					s := fmt.Sprint(k)
-					keys = append(keys, s)
-					sorted = append(sorted, s)
+					val := value.MapIndex(k)
+					// if isNil(val) {
+					// 	continue
+					// }
 
-					val := value.MapIndex(k).Interface()
-					sub := convertRec(ctx, src, allowDefault, val)
+					sub := convertRec(ctx, src, nilIsTop, val.Interface())
 					// mimic behavior of encoding/json: report error of
 					// unsupported type.
 					if sub == nil {
@@ -403,6 +410,10 @@
 						return sub
 					}
 
+					s := fmt.Sprint(k)
+					keys = append(keys, s)
+					sorted = append(sorted, s)
+
 					// Set feature later.
 					obj.arcs = append(obj.arcs, arc{feature: 0, v: sub})
 				}
@@ -428,8 +439,8 @@
 			list := &list{baseValue: src.base()}
 			arcs := []arc{}
 			for i := 0; i < value.Len(); i++ {
-				val := value.Index(i).Interface()
-				x := convertRec(ctx, src, allowDefault, val)
+				val := value.Index(i)
+				x := convertRec(ctx, src, nilIsTop, val.Interface())
 				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 ed2db1b..d83d847 100644
--- a/cue/go_test.go
+++ b/cue/go_test.go
@@ -37,7 +37,7 @@
 		goVal interface{}
 		want  string
 	}{{
-		nil, "(null | _)",
+		nil, "_",
 	}, {
 		true, "true",
 	}, {
@@ -85,8 +85,18 @@
 	}, {
 		[]int{1, 2, 3, 4}, "[1,2,3,4]",
 	}, {
+		struct {
+			A int
+			B *int
+		}{3, nil},
+		"<0>{A: 3}",
+	}, {
 		[]interface{}{}, "[]",
 	}, {
+		[]interface{}{nil}, "[_]",
+	}, {
+		map[string]interface{}{"a": 1, "x": nil}, "<0>{x: _, a: 1}",
+	}, {
 		map[string][]int{
 			"a": []int{1},
 			"b": []int{3, 4},
@@ -124,7 +134,7 @@
 	}, {
 		&struct{ A int }{3}, "<0>{A: 3}",
 	}, {
-		(*struct{ A int })(nil), "(null | <0>{A: (int & >=-9223372036854775808 & int & <=9223372036854775807)})",
+		(*struct{ A int })(nil), "_",
 	}, {
 		reflect.ValueOf(3), "3",
 	}, {
diff --git a/cuego/cuego.go b/cuego/cuego.go
index 63647d9..f2460a3 100644
--- a/cuego/cuego.go
+++ b/cuego/cuego.go
@@ -171,12 +171,12 @@
 }
 
 // fromGoValue converts a Go value to CUE
-func fromGoValue(x interface{}, allowDefault bool) (v cue.Value, err error) {
+func fromGoValue(x interface{}, nilIsNull 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, allowDefault).(cue.Value)
+	v = internal.FromGoValue(runtime, x, nilIsNull).(cue.Value)
 	mutex.Unlock()
 	if err := v.Err(); err != nil {
 		return v, err
diff --git a/encoding/gocode/gocodec/codec_test.go b/encoding/gocode/gocodec/codec_test.go
index 25cc140..731747d 100644
--- a/encoding/gocode/gocodec/codec_test.go
+++ b/encoding/gocode/gocodec/codec_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/kr/pretty"
 
 	"cuelang.org/go/cue"
 )
@@ -219,7 +220,7 @@
 			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)
+				t.Error(pretty.Diff(tc.value, tc.result))
 			}
 		})
 	}