Merge "v23: Remove "UNION TRICK" from vdl"
diff --git a/vdl/convert.go b/vdl/convert.go
index b7136f2..283a0d7 100644
--- a/vdl/convert.go
+++ b/vdl/convert.go
@@ -188,21 +188,6 @@
 //
 // If fin is already a concrete type it's used directly as the fill target.
 //
-// There is one particularly weird trick that we need to use when the conversion
-// target (i.e. fin) is a reflect union interface.  The problem is that
-// createFillTarget is called before we know which union field is set, so
-// there's no way to create a concrete union struct representing the appropriate
-// field.  So we return a vdl.Value as the fill target, to capture the value.
-// We still want the concrete union struct to be created at the end of the
-// conversion, so we call makeReflectUnion in finishConvert to convert the
-// vdl.Value back into a concrete union struct, which needs to work even if the
-// type hasn't been registered.
-//
-// The comments below refer to this trick as the UNION TRICK.
-//
-// TODO(toddw): The UNION TRICK is probably very slow.  We should benchmark and
-// probably re-design our conversion strategy.
-//
 // TODO(toddw): The conversion logic is way too complicated, with many similar
 // branches; rewrite this entire file..
 func createFillTarget(fin convTarget, ttFrom *Type) (convTarget, error) {
@@ -222,11 +207,6 @@
 			if err != nil {
 				return convTarget{}, err
 			}
-			if tt.Kind() == Union {
-				// The wire type is a union.  Apply the UNION TRICK (see above), and
-				// fill *Value, since we don't know which field is set.
-				return valueConv(ZeroValue(tt)), nil
-			}
 			return reflectConv(reflect.New(ni.WireType).Elem(), tt)
 		}
 		if fin.rv.Kind() == reflect.Interface {
@@ -240,9 +220,7 @@
 				// Create the standard WireError struct to fill in, without the pointer.
 				return reflectConv(reflect.New(rtWireError).Elem(), ErrorType.Elem())
 			case fin.tt.Kind() == Union:
-				// The fin target is a union interface.  Apply the UNION TRICK (see
-				// above), and fill *Value, since we don't know which field is set.
-				return valueConv(ZeroValue(fin.tt)), nil
+				return reflectConv(reflect.New(fin.rv.Type()).Elem(), fin.tt)
 			case fin.tt.Kind() != Any:
 				return convTarget{}, fmt.Errorf("internal error - cannot convert to Go type %v vdl type %v from %v", fin.rv.Type(), fin.tt, ttFrom)
 			}
@@ -261,18 +239,8 @@
 				// fill target, and rely on finishConvert to perform the final step of
 				// converting from the wire type to the native type.
 				if ni := nativeInfoFromNative(rt); ni != nil {
-					if ttFrom.Kind() == Union {
-						// The wire type is a union interface.  Apply the UNION TRICK (see
-						// above), and fill *Value, since we don't know which field is set.
-						return valueConv(ZeroValue(ttFrom)), nil
-					}
 					return reflectConv(reflect.New(ni.WireType).Elem(), ttFrom)
 				}
-				if rt.Kind() == reflect.Interface && ttFrom.Kind() == Union {
-					// The wire type is a union interface.  Apply the UNION TRICK (see
-					// above), and fill *Value, since we don't know which field is set.
-					return valueConv(ZeroValue(ttFrom)), nil
-				}
 				rv := reflect.New(rt).Elem()
 				for rv.Kind() == reflect.Ptr {
 					rv.Set(reflect.New(rv.Type().Elem()))
@@ -318,18 +286,7 @@
 		// Handle mirrored case in createFillTarget where fin.rv is a native type;
 		// fill.rv is the wire type that has been filled in.
 		if ni := nativeInfoFromNative(fin.rv.Type()); ni != nil {
-			tt, err := TypeFromReflect(ni.WireType)
-			if err != nil {
-				return err
-			}
 			rvFill := fill.rv
-			if tt.Kind() == Union {
-				// If the fill target is a union type, finish conversion for the UNION
-				// TRICK (see above), to turn it into a concrete field struct.
-				if rvFill, err = makeReflectUnion(ni.WireType, fill.vv); err != nil {
-					return err
-				}
-			}
 			return ni.ToNative(rvFill, fin.rv.Addr())
 		}
 		if fin.rv.Kind() == reflect.Interface {
@@ -337,7 +294,10 @@
 			var rvFill reflect.Value
 			if fill.vv == nil {
 				rvFill = fill.rv
-				if fill.rv.Type() == rtWireError {
+				// Note: this if statement and the one in the else if block do not correctly
+				// support the case of optional wiretypes.
+				// TODO(bprosnitz) Fix this
+				if fill.rv.Type() == rtWireError && fin.rv.Type() != rtWireError {
 					// Handle case where fill.rv has type WireError; if error conversions
 					// have been registered with the vdl package, we need to convert to
 					// the standard error interface.
@@ -348,7 +308,7 @@
 						}
 						rvFill = newNative.Elem()
 					}
-				} else if ni := nativeInfoFromWire(fill.rv.Type()); ni != nil {
+				} else if ni := nativeInfoFromWire(fill.rv.Type()); ni != nil && fin.rv.Type() != ni.WireType {
 					// Handle case where fill.rv is a wire type with a native type; set
 					// rvFill to a new native type and call ToNative to fill it in.
 					newNative := reflect.New(ni.NativeType)
@@ -368,36 +328,11 @@
 					}
 				}
 			} else {
-				// If the fill target is a union type, finish conversion for the UNION
-				// TRICK (see above), to turn it into a concrete field struct.
-				if fill.tt.Kind() == Union {
-					var err error
-					rvFill, err = makeReflectUnion(fin.rv.Type(), fill.vv)
-					if err != nil {
-						// Handle case where fin.rv is a native type; fill.vv is the union
-						// wire type that has been filled in.
-						if rt := TypeToReflect(fill.tt); rt != nil {
-							if ni := nativeInfoFromNative(rt); ni != nil {
-								rvWireUnion, err := makeReflectUnion(ni.WireType, fill.vv)
-								if err != nil {
-									return err
-								}
-								newNative := reflect.New(ni.NativeType)
-								if err := ni.ToNative(rvWireUnion, newNative); err != nil {
-									return err
-								}
-								rvFill = newNative.Elem()
-							}
-						}
-					}
-				}
-				if !rvFill.IsValid() {
-					switch fill.tt.Kind() {
-					case Optional:
-						rvFill = reflect.ValueOf(OptionalValue(fill.vv))
-					default:
-						rvFill = reflect.ValueOf(fill.vv)
-					}
+				switch fill.tt.Kind() {
+				case Optional:
+					rvFill = reflect.ValueOf(OptionalValue(fill.vv))
+				default:
+					rvFill = reflect.ValueOf(fill.vv)
 				}
 			}
 			if to, from := fin.rv.Type(), rvFill.Type(); !from.AssignableTo(to) {
@@ -485,45 +420,6 @@
 	panic(fmt.Errorf("vdl: rvSettableZeroValue unhandled rt: %v tt: %v", rt, tt))
 }
 
-// makeReflectUnion returns the union concrete struct based on the union
-// interface type rt, corresponding to the field that is set in vv.  The point
-// of this machinery is to ensure union can always be created, without any type
-// registration.
-func makeReflectUnion(rt reflect.Type, vv *Value) (reflect.Value, error) {
-	// TODO(toddw): Cache the field types for faster access.
-	ri, _, err := deriveReflectInfo(rt)
-	if err != nil || len(ri.UnionFields) == 0 {
-		// If rt isn't a union interface type, it still might be registered, so we
-		// can get the reflect type from vv.
-		if rt2 := TypeToReflect(vv.Type()); rt2 != nil {
-			rt = rt2
-			ri, _, err = deriveReflectInfo(rt)
-		}
-	}
-	switch {
-	case err != nil:
-		return reflect.Value{}, err
-	case len(ri.UnionFields) == 0 || vv.Kind() != Union:
-		return reflect.Value{}, fmt.Errorf("vdl: makeReflectUnion(%v, %v) must only be called on Union", rt, vv.Type())
-	}
-	index, vvField := vv.UnionField()
-	if index >= len(ri.UnionFields) {
-		return reflect.Value{}, fmt.Errorf("vdl: makeReflectUnion(%v, %v) field index %d out of range, len=%d", rt, vv.Type(), index, len(ri.UnionFields))
-	}
-	// Run our regular conversion from vvField to rvField.  Keep in mind that
-	// rvField is the concrete union struct, so we convert into Field(0), which
-	// corresponds to the "Value" field.
-	rvField := reflect.New(ri.UnionFields[index].RepType).Elem()
-	target, err := ReflectTarget(rvField.Field(0))
-	if err != nil {
-		return reflect.Value{}, err
-	}
-	if err := FromValue(target, vvField); err != nil {
-		return reflect.Value{}, err
-	}
-	return rvField, nil
-}
-
 func removeOptional(tt *Type) *Type {
 	if tt.Kind() == Optional {
 		tt = tt.Elem()
@@ -1292,8 +1188,8 @@
 		switch c.rv.Kind() {
 		case reflect.Map:
 			return reflectConv(rvSettableZeroValue(c.rv.Type().Key(), tt.Key()), tt.Key())
-		case reflect.Struct:
-			// The key for struct is the field name, which is a string.
+		case reflect.Struct, reflect.Interface:
+			// The key for struct and union is the field name, which is a string.
 			return reflectConv(reflect.New(rtString).Elem(), StringType)
 		}
 	} else {
@@ -1362,6 +1258,20 @@
 				rvField.SetBool(true)
 			}
 			return reflectConv(rvField, ttField.Type)
+		case reflect.Interface:
+			if tt.Kind() == Union {
+				ri, _, err := deriveReflectInfo(c.rv.Type())
+				if err != nil {
+					return convTarget{}, err
+				}
+				ttField, index := tt.FieldByName(key.rv.String())
+				fld, found := ri.UnionFields[index].RepType.FieldByName("Value")
+				if !found {
+					return convTarget{}, fmt.Errorf("concrete union type %q missing required Value field", ri.UnionFields[index].RepType)
+				}
+				rvValue := reflect.New(fld.Type).Elem()
+				return reflectConv(rvValue, ttField.Type)
+			}
 		}
 	} else {
 		switch c.vv.Kind() {
@@ -1418,6 +1328,18 @@
 			return nil
 		case reflect.Struct:
 			return nil
+		case reflect.Interface:
+			if tt.Kind() == Union {
+				ri, _, err := deriveReflectInfo(c.rv.Type())
+				if err != nil {
+					return err
+				}
+				_, index := c.tt.FieldByName(key.rv.String())
+				rvField := reflect.New(ri.UnionFields[index].RepType).Elem()
+				rvField.FieldByName("Value").Set(field.rv)
+				c.rv.Set(rvField)
+				return nil
+			}
 		}
 	} else {
 		switch c.vv.Kind() {
diff --git a/vdl/convert_test.go b/vdl/convert_test.go
index b1b7a80..180104c 100644
--- a/vdl/convert_test.go
+++ b/vdl/convert_test.go
@@ -1353,3 +1353,24 @@
 		}
 	}
 }
+
+// Note: There are no other tests which cover these cases outside of a test in
+// v.io/v23/security.
+// These tests also can't be moved to TestConverter because it involves
+// conversion to an interface which will resolve in a non-wiretype output.
+func TestConverterToWiretype(t *testing.T) {
+	var nWire vdl.NWire
+	if err := vdl.Convert(&nWire, vdl.NWire{"100"}); err != nil {
+		t.Fatalf("unexpected error converting to wire type")
+	}
+	if got, want := nWire, (vdl.NWire{"100"}); got != want {
+		t.Errorf("got %#v, want %#v", got, want)
+	}
+	var nUnionWire vdl.NUnionWire
+	if err := vdl.Convert(&nUnionWire, vdl.NUnionNative("A=false")); err != nil {
+		t.Fatalf("unexpected error converting to wire type")
+	}
+	if got, want := nUnionWire, (vdl.NUnionWireA{false}); got != want {
+		t.Errorf("got %#v, want %#v", got, want)
+	}
+}
diff --git a/vdl/target.go b/vdl/target.go
index e4eae29..1446a0b 100644
--- a/vdl/target.go
+++ b/vdl/target.go
@@ -128,14 +128,14 @@
 	FinishField(key, field Target) error
 }
 
-// Convert converts from src to target - it is a helper for calling
-// ReflectTarget(reflect.ValueOf(target)).FromReflect(reflect.ValueOf(src)).
-func Convert(target, src interface{}) error {
-	rtarget, err := ReflectTarget(reflect.ValueOf(target))
+// Convert converts from src to dst - it is a helper for calling
+// ReflectTarget(reflect.ValueOf(dst)).FromReflect(reflect.ValueOf(src)).
+func Convert(dst, src interface{}) error {
+	target, err := ReflectTarget(reflect.ValueOf(dst))
 	if err != nil {
 		return err
 	}
-	return FromReflect(rtarget, reflect.ValueOf(src))
+	return FromReflect(target, reflect.ValueOf(src))
 }
 
 // ValueOf returns the value corresponding to v.  It's a helper for calling