[fidl][go] Add support for recursive types in go. [1/2]

Next: Iacde292e443d2a0e2211a2cfc08d4d048134f3da

In order to do this, all marshalers are converted to references, so that
they may be included in recursive definitions (structs, xunions, etc),
and so that they can be stored in a single, global store to cut down on
memory usage (this store will be added in a future CL).

Additionally, the mStructUnsafeCopy marshaler is removed.  This is
because the marshaler type must now be finally decided before recursive
descent through the definition begins, otherwise nested recursive
definitions may end up pointing to different marshalers than their
parents.  Because mStructUnsafeCopy could only be allocated once all of
a structs fields have been visited, it will never be used in this new
setup.  Instead, the mStruct marshaler gets a new useUnsafeCopy field,
similar to mVector, which can be checked during calls to [un]marshal()
to confirm that an unsafe copy can be performed.

Bug: 38191
Test: fidldev test go (w/ next CL on fuchsia.git included)
Change-Id: I89863ed0f975d7c190a0281cfcf71e7de9834d9c
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/go/+/461074
Reviewed-by: Pascal Perez <pascallouis@google.com>
Commit-Queue: Alex Zaslavsky <azaslavsky@google.com>
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index bd63a71..c8c82da 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -108,7 +108,7 @@
 	// By construction, we know that message is a pointer to a struct since
 	// we only generate pointer receiver methods for top-level messages.
 	// Should one implement the interface differently, and call into this
-	// code, it would fail in an obsure way withing relfection. Just don't do
+	// code, it would fail in an obscure way withing reflection. Just don't do
 	// that.
 	var (
 		v = unsafevalue.ValueOf(message)
@@ -136,7 +136,7 @@
 	// By construction, we know that message is a pointer to a struct since
 	// we only generate pointer receiver methods for top-level messages.
 	// Should one implement the interface differently, and call into this
-	// code, it would fail in an obsure way withing relfection. Just don't do
+	// code, it would fail in an obscure way withing reflection. Just don't do
 	// that.
 	var (
 		v = unsafevalue.ValueOf(message)
@@ -241,34 +241,35 @@
 // Returns true if the go type has the same layout as the FIDL wire format.
 func matchesWireFormatLayout(marshaler Marshaler, typ reflect.Type) bool {
 	switch marshaler := marshaler.(type) {
-	case mBool:
+	case *mBool:
 		// Need to validate 0, 1.
 		return false
-	case mInt8, mInt16, mInt32, mInt64, mUint8, mUint16, mUint32, mUint64:
+	case *mInt8, *mInt16, *mInt32, *mInt64, *mUint8, *mUint16, *mUint32, *mUint64:
 		return true
-	case mEnumOfInt8, mEnumOfInt16, mEnumOfInt32, mEnumOfInt64:
+	case *mEnumOfInt8, *mEnumOfInt16, *mEnumOfInt32, *mEnumOfInt64:
 		// Need to validate.
 		return false
-	case mEnumOfUint8, mEnumOfUint16, mEnumOfUint32, mEnumOfUint64:
+	case *mEnumOfUint8, *mEnumOfUint16, *mEnumOfUint32, *mEnumOfUint64:
 		// Need to validate.
 		return false
-	case mBitsOfUint8, mBitsOfUint16, mBitsOfUint32, mBitsOfUint64:
+	case *mBitsOfUint8, *mBitsOfUint16, *mBitsOfUint32, *mBitsOfUint64:
 		// Need to validate.
 		return false
-	case mFloat32:
+	case *mFloat32:
 		// Need to validate.
 		return false
-	case mFloat64:
+	case *mFloat64:
 		// Need to validate.
 		return false
-	case mArray:
+	case *mArray:
 		return matchesWireFormatLayout(marshaler.Marshaler, typ.Elem())
-	case mStructUnsafeCopy:
-		return true
-	case mStruct:
+	case *mStruct:
 		// Note: In some cases, go structs may be different size than wire format
 		// structs but otherwise identical. It may be possible to change this
 		// logic to allow a partial copy in the future.
+		if marshaler.useUnsafeCopy {
+			return true
+		}
 		if marshaler.size != int(typ.Size()) {
 			return false
 		}
@@ -285,21 +286,36 @@
 			}
 		}
 		return true
-	case mEmptyStruct:
+	case *mEmptyStruct:
 		// Note: empty struct is 0 or 1 bytes at different times in go.
 		return false
-	case mHandle, mProtocol:
+	case *mHandle, *mProtocol:
 		// Note: In the future, we might instead consider treating handles
 		// like uint32 and decoding in a subsequent step.
 		return false
-	case mXUnion, mOptXUnion, mTable, mVector, mOptVector, mString, mOptString, mPointer:
+	case *mXUnion, *mOptXUnion, *mTable, *mVector, *mOptVector, *mString, *mOptString, *mPointer:
 		return false
 	default:
 		panic("unhandledType " + reflect.TypeOf(marshaler).Name() + " for " + typ.String())
 	}
 }
 
+type creator struct {
+	interns map[reflect.Type]Marshaler
+}
+
 func createMarshaler(typ reflect.Type) (Marshaler, error) {
+	c := &creator{
+		interns: map[reflect.Type]Marshaler{},
+	}
+	return c.createMarshalerForStruct(typ)
+}
+
+func (c *creator) createMarshalerForStruct(typ reflect.Type) (Marshaler, error) {
+	if seen, ok := c.interns[typ]; ok {
+		return seen, nil
+	}
+
 	// field 0 holds the tag
 	tagField := typ.Field(0)
 	marshalerKind, err := readKindTag(tagField)
@@ -315,9 +331,26 @@
 		presenceOffsets []uintptr
 	)
 
+	df := dataFields(typ)
+	var out Marshaler
+	switch kind {
+	case structTag:
+		if len(df) == 0 {
+			return &mEmptyStruct{}, nil
+		}
+		out = &mStruct{}
+	case xunionTag, strictXunionTag:
+		out = &mXUnion{}
+	case tableTag:
+		out = &mTable{}
+	default:
+		return nil, errors.New("unknown kind tag on " + nicefmt(typ))
+	}
+	c.interns[typ] = out
+
 	// - structs, unions, and xunions have fields one after the other;
 	// - tables have a field, followed by a bool presence indicator, etc.
-	for index, field := range dataFields(typ) {
+	for index, field := range df {
 		if (kind == xunionTag || kind == tableTag) && isUnknownDataField(field.Name) {
 			continue
 		}
@@ -348,7 +381,7 @@
 			ordinals = append(ordinals, uint64(ordinal))
 		default:
 		}
-		fieldMarshaler, err := createMarshalerForField(field.Type, fieldBounds, handleRights, handleSubtype)
+		fieldMarshaler, err := c.createMarshalerForField(field.Type, fieldBounds, handleRights, handleSubtype)
 		if err != nil {
 			return nil, err
 		}
@@ -364,12 +397,8 @@
 		return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error())
 	}
 
-	switch kind {
-	case structTag:
-		if len(fields) == 0 {
-			return mEmptyStruct{}, nil
-		}
-
+	switch m := out.(type) {
+	case *mStruct:
 		var structFields []mFieldWithWireOffset
 		for i := 0; i < len(fields); i++ {
 			structFields = append(structFields, mFieldWithWireOffset{
@@ -377,18 +406,15 @@
 				wireOffset: wireOffsets[i],
 			})
 		}
-		s := mStruct{
-			fields:    structFields,
-			size:      size,
-			alignment: alignment,
+		m.fields = structFields
+		m.size = size
+		m.alignment = alignment
+		m.useUnsafeCopy = matchesWireFormatLayout(m, typ)
+		if m.useUnsafeCopy {
+			m.fields = nil
 		}
-		if matchesWireFormatLayout(s, typ) {
-			return mStructUnsafeCopy{
-				size: size,
-			}, nil
-		}
-		return s, nil
-	case xunionTag, strictXunionTag:
+		return m, nil
+	case *mXUnion:
 		strictness := strictness(kind == strictXunionTag)
 		isResource, err := readBoolTag(tagField, tagIsResource)
 		if err != nil {
@@ -399,28 +425,27 @@
 		for i := 0; i < len(fields); i++ {
 			ordinalToFields[ordinals[i]] = fields[i]
 		}
-		return mXUnion{
-			typ:          typ,
-			fields:       ordinalToFields,
-			ordinals:     ordinals,
-			size:         size,
-			alignment:    alignment,
-			strictness:   strictness,
-			resourceness: resourceness(isResource),
-		}, nil
-	case tableTag:
+		m.typ = typ
+		m.fields = ordinalToFields
+		m.ordinals = ordinals
+		m.size = size
+		m.alignment = alignment
+		m.strictness = strictness
+		m.resourceness = resourceness(isResource)
+		return m, nil
+	case *mTable:
 		isResource, err := readBoolTag(tagField, tagIsResource)
 		if err != nil {
 			return nil, errors.New("error creating marshaller for " + typ.String() + ": " + err.Error())
 		}
-		return mTable{
-			fields:          fields,
-			presenceOffsets: presenceOffsets,
-			size:            size,
-			alignment:       alignment,
-			ordinals:        ordinals,
-			resourceness:    resourceness(isResource),
-		}, nil
+
+		m.fields = fields
+		m.presenceOffsets = presenceOffsets
+		m.size = size
+		m.alignment = alignment
+		m.ordinals = ordinals
+		m.resourceness = resourceness(isResource)
+		return m, nil
 	default:
 		return nil, errors.New("unknown kind tag on " + nicefmt(typ))
 	}
@@ -626,15 +651,15 @@
 	return m
 }
 
-func createMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
+func (c *creator) createMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
 	if isHandleType(typ) {
 		nullable, _ := bounds.pop()
-		return mHandle{nullable: nullable != 0, rights: handleRights, subtype: handleSubtype}, nil
+		return &mHandle{nullable: nullable != 0, rights: handleRights, subtype: handleSubtype}, nil
 	}
 
 	if typ.ConvertibleTo(proxyType) || typ.ConvertibleTo(interfaceRequestType) {
 		nullable, _ := bounds.pop()
-		return mProtocol{
+		return &mProtocol{
 			nullable: nullable != 0,
 			rights:   ProtocolRights,
 			subtype:  zx.ObjectTypeChannel}, nil
@@ -650,42 +675,42 @@
 		}
 		switch typ.Kind() {
 		case reflect.Int8:
-			return mEnumOfInt8{
+			return &mEnumOfInt8{
 				isStrict: isStrict,
 				values:   toInt8Map(values),
 			}, nil
 		case reflect.Int16:
-			return mEnumOfInt16{
+			return &mEnumOfInt16{
 				isStrict: isStrict,
 				values:   toInt16Map(values),
 			}, nil
 		case reflect.Int32:
-			return mEnumOfInt32{
+			return &mEnumOfInt32{
 				isStrict: isStrict,
 				values:   toInt32Map(values),
 			}, nil
 		case reflect.Int64:
-			return mEnumOfInt64{
+			return &mEnumOfInt64{
 				isStrict: isStrict,
 				values:   toInt64Map(values),
 			}, nil
 		case reflect.Uint8:
-			return mEnumOfUint8{
+			return &mEnumOfUint8{
 				isStrict: isStrict,
 				values:   toUint8Map(values),
 			}, nil
 		case reflect.Uint16:
-			return mEnumOfUint16{
+			return &mEnumOfUint16{
 				isStrict: isStrict,
 				values:   toUint16Map(values),
 			}, nil
 		case reflect.Uint32:
-			return mEnumOfUint32{
+			return &mEnumOfUint32{
 				isStrict: isStrict,
 				values:   toUint32Map(values),
 			}, nil
 		case reflect.Uint64:
-			return mEnumOfUint64{
+			return &mEnumOfUint64{
 				isStrict: isStrict,
 				values:   toUint64Map(values),
 			}, nil
@@ -704,22 +729,22 @@
 		}
 		switch typ.Kind() {
 		case reflect.Uint8:
-			return mBitsOfUint8{
+			return &mBitsOfUint8{
 				isStrict: isStrict,
 				mask:     uint8(mask.Uint()),
 			}, nil
 		case reflect.Uint16:
-			return mBitsOfUint16{
+			return &mBitsOfUint16{
 				isStrict: isStrict,
 				mask:     uint16(mask.Uint()),
 			}, nil
 		case reflect.Uint32:
-			return mBitsOfUint32{
+			return &mBitsOfUint32{
 				isStrict: isStrict,
 				mask:     uint32(mask.Uint()),
 			}, nil
 		case reflect.Uint64:
-			return mBitsOfUint64{
+			return &mBitsOfUint64{
 				isStrict: isStrict,
 				mask:     uint64(mask.Uint()),
 			}, nil
@@ -730,36 +755,37 @@
 
 	switch typ.Kind() {
 	case reflect.Bool:
-		return mBool{}, nil
+		return &mBool{}, nil
 	case reflect.Int8:
-		return mInt8{}, nil
+		return &mInt8{}, nil
 	case reflect.Int16:
-		return mInt16{}, nil
+		return &mInt16{}, nil
 	case reflect.Int32:
-		return mInt32{}, nil
+		return &mInt32{}, nil
 	case reflect.Int64:
-		return mInt64{}, nil
+		return &mInt64{}, nil
 	case reflect.Uint8:
-		return mUint8{}, nil
+		return &mUint8{}, nil
 	case reflect.Uint16:
-		return mUint16{}, nil
+		return &mUint16{}, nil
 	case reflect.Uint32:
-		return mUint32{}, nil
+		return &mUint32{}, nil
 	case reflect.Uint64:
-		return mUint64{}, nil
+		return &mUint64{}, nil
 	case reflect.Float32:
-		return mFloat32{}, nil
+		return &mFloat32{}, nil
 	case reflect.Float64:
-		return mFloat64{}, nil
+		return &mFloat64{}, nil
 	case reflect.String:
 		maxSize, _ := bounds.pop()
-		return mString(maxSize), nil
+		s := mString(maxSize)
+		return &s, nil
 	case reflect.Array:
-		elemMarshaler, err := createMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
+		elemMarshaler, err := c.createMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
 		if err != nil {
 			return nil, err
 		}
-		return mArray{
+		return &mArray{
 			Marshaler:  elemMarshaler,
 			size:       typ.Len(),
 			rtElemSize: typ.Elem().Size(),
@@ -767,50 +793,51 @@
 	case reflect.Slice:
 		maxSize, remainder := bounds.pop()
 		elemTyp := typ.Elem()
-		elemMarshaler, err := createMarshalerForField(elemTyp, remainder, handleRights, handleSubtype)
+		elemMarshaler, err := c.createMarshalerForField(elemTyp, remainder, handleRights, handleSubtype)
 		if err != nil {
 			return nil, err
 		}
-		return mVector{
+		return &mVector{
 			Marshaler:     elemMarshaler,
 			maxSize:       maxSize,
 			elemTyp:       elemTyp,
 			useUnsafeCopy: matchesWireFormatLayout(elemMarshaler, elemTyp),
 		}, nil
 	case reflect.Struct:
-		return createMarshaler(typ)
+		return c.createMarshalerForStruct(typ)
 	case reflect.Ptr:
-		return createOptMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
+		return c.createOptMarshalerForField(typ.Elem(), bounds, handleRights, handleSubtype)
 	default:
 		return nil, errors.New("unable to create field marshaler for " + nicefmt(typ))
 	}
 }
 
-func createOptMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
-	m, err := createMarshalerForField(typ, bounds, handleRights, handleSubtype)
+func (c *creator) createOptMarshalerForField(typ reflect.Type, bounds bounds, handleRights zx.Rights, handleSubtype zx.ObjectType) (Marshaler, error) {
+	m, err := c.createMarshalerForField(typ, bounds, handleRights, handleSubtype)
 	if err != nil {
 		return nil, err
 	}
 	switch m := m.(type) {
-	case mString:
-		return mOptString(m), nil
-	case mVector:
-		return mOptVector{
+	case *mString:
+		o := mOptString(*m)
+		return &o, nil
+	case *mEmptyStruct:
+		return &mPointer{
+			Marshaler: m,
+			elemTyp:   typ,
+		}, nil
+	case *mStruct:
+		return &mPointer{
+			Marshaler: m,
+			elemTyp:   typ,
+		}, nil
+	case *mVector:
+		return &mOptVector{
 			mVector:  m,
 			sliceTyp: typ,
 		}, nil
-	case mEmptyStruct:
-		return mPointer{
-			Marshaler: m,
-			elemTyp:   typ,
-		}, nil
-	case mStruct, mStructUnsafeCopy:
-		return mPointer{
-			Marshaler: m,
-			elemTyp:   typ,
-		}, nil
-	case mXUnion:
-		return mOptXUnion{
+	case *mXUnion:
+		return &mOptXUnion{
 			mXUnion: m,
 			typ:     typ,
 		}, nil
@@ -835,41 +862,40 @@
 
 // Assert various encoders implement the Marshaler interface.
 var _ = []Marshaler{
-	mStruct{},
-	mStructUnsafeCopy{},
-	mEmptyStruct{},
-	mXUnion{},
-	mOptXUnion{},
-	mTable{},
-	mPointer{},
-	mArray{},
-	mVector{},
-	mOptVector{},
-	mBool{},
-	mInt8{},
-	mInt16{},
-	mInt32{},
-	mInt64{},
-	mUint8{},
-	mUint16{},
-	mUint32{},
-	mUint64{},
-	mEnumOfInt8{},
-	mEnumOfInt16{},
-	mEnumOfInt32{},
-	mEnumOfInt64{},
-	mEnumOfUint8{},
-	mEnumOfUint16{},
-	mEnumOfUint32{},
-	mEnumOfUint64{},
-	mBitsOfUint8{},
-	mBitsOfUint16{},
-	mBitsOfUint32{},
-	mBitsOfUint64{},
-	mFloat32{},
-	mFloat64{},
-	mString(0),
-	mOptString(0),
+	(*mString)(nil),
+	(*mOptString)(nil),
+	(*mBool)(nil),
+	(*mInt8)(nil),
+	(*mInt16)(nil),
+	(*mInt32)(nil),
+	(*mInt64)(nil),
+	(*mUint8)(nil),
+	(*mUint16)(nil),
+	(*mUint32)(nil),
+	(*mUint64)(nil),
+	(*mFloat32)(nil),
+	(*mFloat64)(nil),
+	(*mEmptyStruct)(nil),
+	(*mStruct)(nil),
+	(*mXUnion)(nil),
+	(*mOptXUnion)(nil),
+	(*mTable)(nil),
+	(*mPointer)(nil),
+	(*mArray)(nil),
+	(*mVector)(nil),
+	(*mOptVector)(nil),
+	(*mEnumOfInt8)(nil),
+	(*mEnumOfInt16)(nil),
+	(*mEnumOfInt32)(nil),
+	(*mEnumOfInt64)(nil),
+	(*mEnumOfUint8)(nil),
+	(*mEnumOfUint16)(nil),
+	(*mEnumOfUint32)(nil),
+	(*mEnumOfUint64)(nil),
+	(*mBitsOfUint8)(nil),
+	(*mBitsOfUint16)(nil),
+	(*mBitsOfUint32)(nil),
+	(*mBitsOfUint64)(nil),
 }
 
 type mField struct {
@@ -886,17 +912,30 @@
 type mStruct struct {
 	fields          []mFieldWithWireOffset
 	size, alignment int
+	useUnsafeCopy   bool
 }
 
-func (m mStruct) getMarshalSize(ctx MarshalerContext) int {
+func (m *mStruct) getMarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
-func (m mStruct) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mStruct) getUnmarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
-func (m mStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+	if m.useUnsafeCopy {
+		// Directly copy the object's memory to the buffer.
+		sh := reflect.SliceHeader{
+			Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
+			Len:  m.size,
+			Cap:  m.size,
+		}
+		s := *(*[]uint8)(unsafe.Pointer(&sh))
+		copy(out.buffer[offset:], s)
+		return nil
+	}
+
 	for _, field := range m.fields {
 		fieldOffset := offset + field.wireOffset
 		if err := field.Marshaler.marshal(ctx, v.StructFieldOffset(field.offset), out, fieldOffset); err != nil {
@@ -906,7 +945,22 @@
 	return nil
 }
 
-func (m mStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+	if m.useUnsafeCopy {
+		// Directly copy from the buffer to the object's memory.
+		if len(in.buffer) < offset+m.size {
+			return ErrPayloadTooSmall
+		}
+		sh := reflect.SliceHeader{
+			Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
+			Len:  m.size,
+			Cap:  m.size,
+		}
+		s := *(*[]uint8)(unsafe.Pointer(&sh))
+		copy(s, in.buffer[offset:])
+		return nil
+	}
+
 	for _, field := range m.fields {
 		fieldOffset := offset + field.wireOffset
 		if err := field.Marshaler.unmarshal(ctx, in, fieldOffset, v.StructFieldOffset(field.offset)); err != nil {
@@ -916,61 +970,22 @@
 	return nil
 }
 
-type mStructUnsafeCopy struct {
-	size int
-}
-
-func (m mStructUnsafeCopy) getMarshalSize(ctx MarshalerContext) int {
-	return m.size
-}
-
-func (m mStructUnsafeCopy) getUnmarshalSize(ctx MarshalerContext) int {
-	return m.size
-}
-
-func (m mStructUnsafeCopy) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
-	// Directly copy the object's memory to the buffer.
-	sh := reflect.SliceHeader{
-		Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
-		Len:  m.size,
-		Cap:  m.size,
-	}
-	s := *(*[]uint8)(unsafe.Pointer(&sh))
-	copy(out.buffer[offset:], s)
-	return nil
-}
-
-func (m mStructUnsafeCopy) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
-	// Directly copy from the buffer to the object's memory.
-	if len(in.buffer) < offset+m.size {
-		return ErrPayloadTooSmall
-	}
-	sh := reflect.SliceHeader{
-		Data: uintptr(unsafe.Pointer(v.UnsafeAddr())),
-		Len:  m.size,
-		Cap:  m.size,
-	}
-	s := *(*[]uint8)(unsafe.Pointer(&sh))
-	copy(s, in.buffer[offset:])
-	return nil
-}
-
 type mEmptyStruct struct{}
 
-func (_ mEmptyStruct) getMarshalSize(ctx MarshalerContext) int {
+func (_ *mEmptyStruct) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (_ mEmptyStruct) getUnmarshalSize(ctx MarshalerContext) int {
+func (_ *mEmptyStruct) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (_ mEmptyStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (_ *mEmptyStruct) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint8(offset, 0)
 	return nil
 }
 
-func (_ mEmptyStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (_ *mEmptyStruct) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	if structByte := in.readUint8(offset); structByte != 0 {
 		return newValueError(ErrInvalidEmptyStruct, structByte)
 	}
@@ -995,15 +1010,15 @@
 	resourceness
 }
 
-func (m mXUnion) getMarshalSize(ctx MarshalerContext) int {
+func (m *mXUnion) getMarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
-func (m mXUnion) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mXUnion) getUnmarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
-func (m mXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	var ordinal uint64
 	ordinal = v.StructFieldOffset(0).Uint64()
 	field, ok := m.fields[ordinal]
@@ -1043,11 +1058,11 @@
 	return nil
 }
 
-func (m mXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	return m.unmarshalWithOptSpecified(ctx, in, offset, v, nil)
 }
 
-func (m mXUnion) unmarshalWithOptSpecified(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value, typ reflect.Type) error {
+func (m *mXUnion) unmarshalWithOptSpecified(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value, typ reflect.Type) error {
 	// Can this envelope be optional? If it can, the type must be provided
 	// in order to reflectively create a pointer container.
 	optAllowed := typ != nil
@@ -1128,19 +1143,19 @@
 }
 
 type mOptXUnion struct {
-	mXUnion
+	*mXUnion
 	typ reflect.Type
 }
 
-func (m mOptXUnion) getMarshalSize(ctx MarshalerContext) int {
+func (m *mOptXUnion) getMarshalSize(ctx MarshalerContext) int {
 	return m.mXUnion.getMarshalSize(ctx)
 }
 
-func (m mOptXUnion) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mOptXUnion) getUnmarshalSize(ctx MarshalerContext) int {
 	return m.mXUnion.getUnmarshalSize(ctx)
 }
 
-func (m mOptXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mOptXUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if v.PointerIsNil() {
 		out.writeUint64(offset, 0) // ordinal + padding
 		marshalEnvelopeAbsent(out, offset+8)
@@ -1150,7 +1165,7 @@
 	}
 }
 
-func (m mOptXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mOptXUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	return m.unmarshalWithOptSpecified(ctx, in, offset, v, m.typ)
 }
 
@@ -1167,11 +1182,11 @@
 	resourceness
 }
 
-func (m mTable) getMarshalSize(ctx MarshalerContext) int {
+func (m *mTable) getMarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
-func (m mTable) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mTable) getUnmarshalSize(ctx MarshalerContext) int {
 	return m.size
 }
 
@@ -1335,7 +1350,7 @@
 	}
 }
 
-func (m mTable) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mTable) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	var (
 		maxOrdinal   uint64
 		numKnown     = len(m.ordinals)
@@ -1428,7 +1443,7 @@
 	return nil
 }
 
-func (m mTable) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mTable) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	mou := in.readUint64(offset)
 	// uints/ints are only guaranteed to be 32 bit longs.
 	// we use maxOrdinal as an int, so we must make sure that it fits.
@@ -1510,15 +1525,15 @@
 	elemTyp reflect.Type
 }
 
-func (m mPointer) getMarshalSize(ctx MarshalerContext) int {
+func (m *mPointer) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mPointer) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mPointer) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mPointer) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mPointer) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	// Nil?
 	if v.PointerIsNil() {
 		out.writeUint64(offset, noAlloc)
@@ -1539,7 +1554,7 @@
 	return nil
 }
 
-func (m mPointer) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mPointer) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	// Nil?
 	switch ptr := in.readUint64(offset); ptr {
 	case noAlloc:
@@ -1573,19 +1588,19 @@
 	mOptXUnion
 }
 
-func (m mOptUnion) getMarshalSize(ctx MarshalerContext) int {
+func (m *mOptUnion) getMarshalSize(ctx MarshalerContext) int {
 	return 24
 }
 
-func (m mOptUnion) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mOptUnion) getUnmarshalSize(ctx MarshalerContext) int {
 	return 24
 }
 
-func (m mOptUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mOptUnion) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	return m.mOptXUnion.marshal(ctx, v, out, offset)
 }
 
-func (m mOptUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mOptUnion) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	return m.mOptXUnion.unmarshal(ctx, in, offset, v)
 }
 
@@ -1595,14 +1610,14 @@
 	size       int
 }
 
-func (m mArray) getMarshalSize(ctx MarshalerContext) int {
+func (m *mArray) getMarshalSize(ctx MarshalerContext) int {
 	return m.size * m.Marshaler.getMarshalSize(ctx)
 }
-func (m mArray) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mArray) getUnmarshalSize(ctx MarshalerContext) int {
 	return m.size * m.Marshaler.getUnmarshalSize(ctx)
 }
 
-func (m mArray) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mArray) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	elemSize := m.Marshaler.getMarshalSize(ctx)
 	for i, len := 0, m.size; i < len; i++ {
 		if err := m.Marshaler.marshal(ctx, v.ArrayIndex(m.rtElemSize, i), out, offset+i*elemSize); err != nil {
@@ -1612,7 +1627,7 @@
 	return nil
 }
 
-func (m mArray) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mArray) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	elemSize := m.Marshaler.getUnmarshalSize(ctx)
 	for i, len := 0, m.size; i < len; i++ {
 		if err := m.Marshaler.unmarshal(ctx, in, offset+i*elemSize, v.ArrayIndex(m.rtElemSize, i)); err != nil {
@@ -1629,14 +1644,14 @@
 	useUnsafeCopy bool
 }
 
-func (m mVector) getMarshalSize(ctx MarshalerContext) int {
+func (m *mVector) getMarshalSize(ctx MarshalerContext) int {
 	return 16
 }
-func (m mVector) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mVector) getUnmarshalSize(ctx MarshalerContext) int {
 	return 16
 }
 
-func (m mVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	// Bounds check.
 	vLen := v.SliceLen()
 	if m.maxSize < vLen {
@@ -1676,7 +1691,7 @@
 	return nil
 }
 
-func (m mVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	size := in.readUint64(offset)
 	ptr := in.readUint64(offset + 8)
 	switch ptr {
@@ -1689,7 +1704,7 @@
 	}
 }
 
-func (m mVector) unmarshalWithUncheckedSize(ctx MarshalerContext, in *decoder, v unsafevalue.Value, size int) error {
+func (m *mVector) unmarshalWithUncheckedSize(ctx MarshalerContext, in *decoder, v unsafevalue.Value, size int) error {
 	if size < 0 || m.maxSize < size {
 		return newExpectError(ErrVectorTooLong, m.maxSize, size)
 	}
@@ -1725,18 +1740,18 @@
 }
 
 type mOptVector struct {
-	mVector
+	*mVector
 	sliceTyp reflect.Type
 }
 
-func (m mOptVector) getMarshalSize(ctx MarshalerContext) int {
+func (m *mOptVector) getMarshalSize(ctx MarshalerContext) int {
 	return 16
 }
-func (m mOptVector) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mOptVector) getUnmarshalSize(ctx MarshalerContext) int {
 	return 16
 }
 
-func (m mOptVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mOptVector) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if v.PointerIsNil() {
 		out.writeUint64(offset, 0)
 		out.writeUint64(offset+8, noAlloc)
@@ -1746,7 +1761,7 @@
 	return m.mVector.marshal(ctx, v.PointerElem(), out, offset)
 }
 
-func (m mOptVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mOptVector) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	size := in.readUint64(offset)
 	ptr := in.readUint64(offset + 8)
 	switch ptr {
@@ -1763,14 +1778,14 @@
 
 type mBool struct{}
 
-func (m mBool) getMarshalSize(ctx MarshalerContext) int {
+func (m *mBool) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mBool) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mBool) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mBool) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mBool) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if v.Bool() {
 		out.writeUint8(offset, 1)
 	} else {
@@ -1779,7 +1794,7 @@
 	return nil
 }
 
-func (m mBool) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mBool) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	b := in.readUint8(offset)
 	switch b {
 	case 0, 1:
@@ -1792,152 +1807,152 @@
 
 type mInt8 struct{}
 
-func (m mInt8) getMarshalSize(ctx MarshalerContext) int {
+func (m *mInt8) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mInt8) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mInt8) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint8(offset, uint8(v.Int8()))
 	return nil
 }
 
-func (m mInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetInt8(int8(in.readUint8(offset)))
 	return nil
 }
 
 type mInt16 struct{}
 
-func (m mInt16) getMarshalSize(ctx MarshalerContext) int {
+func (m *mInt16) getMarshalSize(ctx MarshalerContext) int {
 	return 2
 }
-func (m mInt16) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mInt16) getUnmarshalSize(ctx MarshalerContext) int {
 	return 2
 }
 
-func (m mInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint16(offset, uint16(v.Int16()))
 	return nil
 }
 
-func (m mInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetInt16(int16(in.readUint16(offset)))
 	return nil
 }
 
 type mInt32 struct{}
 
-func (m mInt32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mInt32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mInt32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mInt32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint32(offset, uint32(v.Int32()))
 	return nil
 }
 
-func (m mInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetInt32(int32(in.readUint32(offset)))
 	return nil
 }
 
 type mInt64 struct{}
 
-func (m mInt64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mInt64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mInt64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mInt64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint64(offset, uint64(v.Int64()))
 	return nil
 }
 
-func (m mInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetInt64(int64(in.readUint64(offset)))
 	return nil
 }
 
 type mUint8 struct{}
 
-func (m mUint8) getMarshalSize(ctx MarshalerContext) int {
+func (m *mUint8) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mUint8) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mUint8) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint8(offset, v.Uint8())
 	return nil
 }
 
-func (m mUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetUint8(in.readUint8(offset))
 	return nil
 }
 
 type mUint16 struct{}
 
-func (m mUint16) getMarshalSize(ctx MarshalerContext) int {
+func (m *mUint16) getMarshalSize(ctx MarshalerContext) int {
 	return 2
 }
-func (m mUint16) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mUint16) getUnmarshalSize(ctx MarshalerContext) int {
 	return 2
 }
 
-func (m mUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint16(offset, v.Uint16())
 	return nil
 }
 
-func (m mUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetUint16(in.readUint16(offset))
 	return nil
 }
 
 type mUint32 struct{}
 
-func (m mUint32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mUint32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mUint32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mUint32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint32(offset, v.Uint32())
 	return nil
 }
 
-func (m mUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetUint32(in.readUint32(offset))
 	return nil
 }
 
 type mUint64 struct{}
 
-func (m mUint64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mUint64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mUint64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mUint64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint64(offset, v.Uint64())
 	return nil
 }
 
-func (m mUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetUint64(in.readUint64(offset))
 	return nil
 }
@@ -1947,14 +1962,14 @@
 	values   map[int8]struct{}
 }
 
-func (m mEnumOfInt8) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt8) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mEnumOfInt8) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt8) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mEnumOfInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfInt8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Int8()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Int8())
@@ -1964,7 +1979,7 @@
 	return nil
 }
 
-func (m mEnumOfInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfInt8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := int8(in.readUint8(offset))
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -1980,14 +1995,14 @@
 	values   map[int16]struct{}
 }
 
-func (m mEnumOfInt16) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt16) getMarshalSize(ctx MarshalerContext) int {
 	return 2
 }
-func (m mEnumOfInt16) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt16) getUnmarshalSize(ctx MarshalerContext) int {
 	return 2
 }
 
-func (m mEnumOfInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfInt16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Int16()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Int16())
@@ -1997,7 +2012,7 @@
 	return nil
 }
 
-func (m mEnumOfInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfInt16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := int16(in.readUint16(offset))
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2013,14 +2028,14 @@
 	values   map[int32]struct{}
 }
 
-func (m mEnumOfInt32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mEnumOfInt32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mEnumOfInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfInt32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Int32()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Int32())
@@ -2030,7 +2045,7 @@
 	return nil
 }
 
-func (m mEnumOfInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfInt32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := int32(in.readUint32(offset))
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2046,14 +2061,14 @@
 	values   map[int64]struct{}
 }
 
-func (m mEnumOfInt64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mEnumOfInt64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfInt64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mEnumOfInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfInt64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Int64()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Int64())
@@ -2063,7 +2078,7 @@
 	return nil
 }
 
-func (m mEnumOfInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfInt64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := int64(in.readUint64(offset))
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2079,14 +2094,14 @@
 	values   map[uint8]struct{}
 }
 
-func (m mEnumOfUint8) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint8) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mEnumOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mEnumOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Uint8()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Uint8())
@@ -2096,7 +2111,7 @@
 	return nil
 }
 
-func (m mEnumOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint8(offset)
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2112,14 +2127,14 @@
 	values   map[uint16]struct{}
 }
 
-func (m mEnumOfUint16) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint16) getMarshalSize(ctx MarshalerContext) int {
 	return 2
 }
-func (m mEnumOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
 	return 2
 }
 
-func (m mEnumOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Uint16()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Uint16())
@@ -2129,7 +2144,7 @@
 	return nil
 }
 
-func (m mEnumOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint16(offset)
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2145,14 +2160,14 @@
 	values   map[uint32]struct{}
 }
 
-func (m mEnumOfUint32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mEnumOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mEnumOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Uint32()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Uint32())
@@ -2162,7 +2177,7 @@
 	return nil
 }
 
-func (m mEnumOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint32(offset)
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2178,14 +2193,14 @@
 	values   map[uint64]struct{}
 }
 
-func (m mEnumOfUint64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mEnumOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mEnumOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mEnumOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mEnumOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if _, ok := m.values[v.Uint64()]; !ok {
 			return newValueError(ErrInvalidEnumValue, v.Uint64())
@@ -2195,7 +2210,7 @@
 	return nil
 }
 
-func (m mEnumOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mEnumOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint64(offset)
 	if m.isStrict {
 		if _, ok := m.values[i]; !ok {
@@ -2211,14 +2226,14 @@
 	mask     uint8
 }
 
-func (m mBitsOfUint8) getMarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint8) getMarshalSize(ctx MarshalerContext) int {
 	return 1
 }
-func (m mBitsOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint8) getUnmarshalSize(ctx MarshalerContext) int {
 	return 1
 }
 
-func (m mBitsOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mBitsOfUint8) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if (v.Uint8() & m.mask) != v.Uint8() {
 			return newValueError(ErrInvalidBitsValue, v.Uint8())
@@ -2228,7 +2243,7 @@
 	return nil
 }
 
-func (m mBitsOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mBitsOfUint8) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint8(offset)
 	if m.isStrict {
 		if (i & m.mask) != i {
@@ -2244,14 +2259,14 @@
 	mask     uint16
 }
 
-func (m mBitsOfUint16) getMarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint16) getMarshalSize(ctx MarshalerContext) int {
 	return 2
 }
-func (m mBitsOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint16) getUnmarshalSize(ctx MarshalerContext) int {
 	return 2
 }
 
-func (m mBitsOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mBitsOfUint16) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if (v.Uint16() & m.mask) != v.Uint16() {
 			return newValueError(ErrInvalidBitsValue, v.Uint16())
@@ -2261,7 +2276,7 @@
 	return nil
 }
 
-func (m mBitsOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mBitsOfUint16) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint16(offset)
 	if m.isStrict {
 		if (i & m.mask) != i {
@@ -2277,14 +2292,14 @@
 	mask     uint32
 }
 
-func (m mBitsOfUint32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mBitsOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mBitsOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mBitsOfUint32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if (v.Uint32() & m.mask) != v.Uint32() {
 			return newValueError(ErrInvalidBitsValue, v.Uint32())
@@ -2294,7 +2309,7 @@
 	return nil
 }
 
-func (m mBitsOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mBitsOfUint32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint32(offset)
 	if m.isStrict {
 		if (i & m.mask) != i {
@@ -2310,14 +2325,14 @@
 	mask     uint64
 }
 
-func (m mBitsOfUint64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mBitsOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mBitsOfUint64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mBitsOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mBitsOfUint64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if m.isStrict {
 		if (v.Uint64() & m.mask) != v.Uint64() {
 			return newValueError(ErrInvalidBitsValue, v.Uint64())
@@ -2327,7 +2342,7 @@
 	return nil
 }
 
-func (m mBitsOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mBitsOfUint64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	i := in.readUint64(offset)
 	if m.isStrict {
 		if (i & m.mask) != i {
@@ -2340,54 +2355,54 @@
 
 type mFloat32 struct{}
 
-func (m mFloat32) getMarshalSize(ctx MarshalerContext) int {
+func (m *mFloat32) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mFloat32) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mFloat32) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mFloat32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mFloat32) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint32(offset, math.Float32bits(v.Float32()))
 	return nil
 }
 
-func (m mFloat32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mFloat32) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetFloat32(math.Float32frombits(in.readUint32(offset)))
 	return nil
 }
 
 type mFloat64 struct{}
 
-func (m mFloat64) getMarshalSize(ctx MarshalerContext) int {
+func (m *mFloat64) getMarshalSize(ctx MarshalerContext) int {
 	return 8
 }
-func (m mFloat64) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mFloat64) getUnmarshalSize(ctx MarshalerContext) int {
 	return 8
 }
 
-func (m mFloat64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mFloat64) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	out.writeUint64(offset, math.Float64bits(v.Float64()))
 	return nil
 }
 
-func (m mFloat64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mFloat64) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	v.SetFloat64(math.Float64frombits(in.readUint64(offset)))
 	return nil
 }
 
 type mString int
 
-func (m mString) getMarshalSize(ctx MarshalerContext) int {
+func (m *mString) getMarshalSize(ctx MarshalerContext) int {
 	return 16
 }
-func (m mString) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mString) getUnmarshalSize(ctx MarshalerContext) int {
 	return 16
 }
 
-func (m mString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	var (
-		maxSize = int(m)
+		maxSize = int(*m)
 		s       = v.String()
 		length  = len(s)
 	)
@@ -2410,7 +2425,7 @@
 	return nil
 }
 
-func (m mString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	size := in.readUint64(offset)
 	ptr := in.readUint64(offset + 8)
 	switch ptr {
@@ -2423,8 +2438,8 @@
 	}
 }
 
-func (m mString) unmarshalWithUncheckedSize(in *decoder, v unsafevalue.Value, size int) error {
-	if maxSize := int(m); size < 0 || maxSize < size {
+func (m *mString) unmarshalWithUncheckedSize(in *decoder, v unsafevalue.Value, size int) error {
+	if maxSize := int(*m); size < 0 || maxSize < size {
 		return newExpectError(ErrStringTooLong, maxSize, size)
 	}
 	start, err := in.newObject(size)
@@ -2441,28 +2456,29 @@
 
 type mOptString uint64
 
-func (m mOptString) getMarshalSize(ctx MarshalerContext) int {
+func (m *mOptString) getMarshalSize(ctx MarshalerContext) int {
 	return 16
 }
-func (m mOptString) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mOptString) getUnmarshalSize(ctx MarshalerContext) int {
 	return 16
 }
 
-func (m mOptString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mOptString) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	if v.PointerIsNil() {
 		out.writeUint64(offset, 0)
 		out.writeUint64(offset+8, noAlloc)
 		return nil
 	}
 
-	return mString(m).marshal(ctx, v.PointerElem(), out, offset)
+	s := mString(*m)
+	return (&s).marshal(ctx, v.PointerElem(), out, offset)
 }
 
 var (
 	typString = reflect.TypeOf("")
 )
 
-func (m mOptString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mOptString) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	size := in.readUint64(offset)
 	ptr := in.readUint64(offset + 8)
 	switch ptr {
@@ -2471,7 +2487,8 @@
 		return nil
 	case allocPresent:
 		v.PointerSetNew(typString)
-		return mString(m).unmarshalWithUncheckedSize(in, v.PointerElem(), int(size))
+		s := mString(*m)
+		return (&s).unmarshalWithUncheckedSize(in, v.PointerElem(), int(size))
 	default:
 		return newValueError(ErrBadRefEncoding, v)
 	}
@@ -2483,18 +2500,18 @@
 	subtype  zx.ObjectType
 }
 
-func (m mHandle) getMarshalSize(ctx MarshalerContext) int {
+func (m *mHandle) getMarshalSize(ctx MarshalerContext) int {
 	return 4
 }
-func (m mHandle) getUnmarshalSize(ctx MarshalerContext) int {
+func (m *mHandle) getUnmarshalSize(ctx MarshalerContext) int {
 	return 4
 }
 
-func (m mHandle) isOpt() bool {
+func (m *mHandle) isOpt() bool {
 	return m.nullable
 }
 
-func (m mHandle) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+func (m *mHandle) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
 	// The underlying type of all the handles is a uint32, so we're
 	// safe calling Uint(). This will panic if that is no longer true.
 	raw := zx.Handle(v.Uint32())
@@ -2516,21 +2533,21 @@
 	return nil
 }
 
-func (m mHandle) requiredRightsArePresent(actual zx.Rights) bool {
+func (m *mHandle) requiredRightsArePresent(actual zx.Rights) bool {
 	if m.rights == zx.RightSameRights {
 		return true
 	}
 	return actual.SupersetOf(m.rights)
 }
 
-func (m mHandle) filterOutUnspecifiedRights(actual zx.Rights) zx.Rights {
+func (m *mHandle) filterOutUnspecifiedRights(actual zx.Rights) zx.Rights {
 	if m.rights == zx.RightSameRights {
 		return actual
 	}
 	return actual & m.rights
 }
 
-func (m mHandle) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+func (m *mHandle) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
 	h := in.readUint32(offset)
 	switch uint32(h) {
 	case noHandle:
@@ -2576,17 +2593,22 @@
 // represented by an `InterfaceRequest` whose first field is a `zx.Channel`.
 type mProtocol mHandle
 
-func (m mProtocol) getMarshalSize(ctx MarshalerContext) int {
-	return mHandle(m).getMarshalSize(ctx)
-}
-func (m mProtocol) getUnmarshalSize(ctx MarshalerContext) int {
-	return mHandle(m).getUnmarshalSize(ctx)
+func (m *mProtocol) asHandle() *mHandle {
+	h := mHandle(*m)
+	return &h
 }
 
-func (m mProtocol) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
-	return mHandle(m).marshal(ctx, v.StructFieldOffset(0), out, offset)
+func (m *mProtocol) getMarshalSize(ctx MarshalerContext) int {
+	return m.asHandle().getMarshalSize(ctx)
+}
+func (m *mProtocol) getUnmarshalSize(ctx MarshalerContext) int {
+	return m.asHandle().getUnmarshalSize(ctx)
 }
 
-func (m mProtocol) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
-	return mHandle(m).unmarshal(ctx, in, offset, v.StructFieldOffset(0))
+func (m *mProtocol) marshal(ctx MarshalerContext, v unsafevalue.Value, out *encoder, offset int) error {
+	return m.asHandle().marshal(ctx, v.StructFieldOffset(0), out, offset)
+}
+
+func (m *mProtocol) unmarshal(ctx MarshalerContext, in *decoder, offset int, v unsafevalue.Value) error {
+	return m.asHandle().unmarshal(ctx, in, offset, v.StructFieldOffset(0))
 }
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_fuchsia_test.go b/src/syscall/zx/fidl/fidl_test/encoding_fuchsia_test.go
index d7ded09..d1e2788 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_fuchsia_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_fuchsia_test.go
@@ -181,6 +181,12 @@
 				Channel: zx.Channel(zx.HandleInvalid),
 			}),
 		}, 16},
+
+		{"recursive-struct", &bindingstest.TestRecursiveStruct{
+			S: &bindingstest.TestRecursiveStruct{
+				S: &bindingstest.TestRecursiveStruct{},
+			},
+		}, 24},
 	}
 }
 
@@ -815,8 +821,8 @@
 		t.Errorf("Expected modified-in-place union modified from unknown variant and constructed union to be equal")
 	}
 
-	union.SetA([3]int8{0,1,2})
-	if union != bindingstest.XUnion1WithA([3]int8{0,1,2}) {
+	union.SetA([3]int8{0, 1, 2})
+	if union != bindingstest.XUnion1WithA([3]int8{0, 1, 2}) {
 		t.Errorf("Expected modified-in-place union modified from known variant and constructed union to be equal")
 	}
 }
@@ -864,3 +870,47 @@
 		t.Errorf("Expected empty unknown handles")
 	}
 }
+
+func TestRecursiveVectors(t *testing.T) {
+	successCase{
+		name:    "recursive-vector-struct",
+		context: testCtx,
+		input: &bindingstest.TestRecursiveVectorStruct{
+			V: []*bindingstest.TestRecursiveVectorStruct{
+				{
+					V: []*bindingstest.TestRecursiveVectorStruct{},
+				},
+			},
+		},
+		bytes: []byte{
+			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		},
+	}.check(t)
+
+	a := bindingstest.TestRecursiveVectorUnionWithA(1.0)
+	b := bindingstest.TestRecursiveVectorUnionWithB([]*bindingstest.TestRecursiveVectorUnion{
+		&a,
+	})
+	successCase{
+		name:    "recursive-vector-union",
+		context: testCtx,
+		input: &bindingstest.TestRecursiveUnionStruct{
+			U: &b,
+		},
+		bytes: []byte{
+			0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+			0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00,
+		},
+	}.check(t)
+}
diff --git a/src/syscall/zx/fidl/internal/bindingstest/impl.go b/src/syscall/zx/fidl/internal/bindingstest/impl.go
index f076c21..b78e49f 100644
--- a/src/syscall/zx/fidl/internal/bindingstest/impl.go
+++ b/src/syscall/zx/fidl/internal/bindingstest/impl.go
@@ -413,6 +413,39 @@
 	return _mEmptyStruct
 }
 
+type TestRecursiveStruct struct {
+	_ struct{}             `fidl:"s" fidl_size_v1:"8" fidl_alignment_v1:"8"`
+	S *TestRecursiveStruct `fidl_offset_v1:"0"`
+}
+
+var _mTestRecursiveStruct = _bindings.CreateLazyMarshaler(TestRecursiveStruct{})
+
+func (msg *TestRecursiveStruct) Marshaler() _bindings.Marshaler {
+	return _mTestRecursiveStruct
+}
+
+type TestRecursiveVectorStruct struct {
+	_ struct{}                     `fidl:"s" fidl_size_v1:"16" fidl_alignment_v1:"8"`
+	V []*TestRecursiveVectorStruct `fidl_offset_v1:"0" fidl_bounds:""`
+}
+
+var _mTestRecursiveVectorStruct = _bindings.CreateLazyMarshaler(TestRecursiveVectorStruct{})
+
+func (msg *TestRecursiveVectorStruct) Marshaler() _bindings.Marshaler {
+	return _mTestRecursiveVectorStruct
+}
+
+type TestRecursiveUnionStruct struct {
+	_ struct{}                  `fidl:"s" fidl_size_v1:"24" fidl_alignment_v1:"8"`
+	U *TestRecursiveVectorUnion `fidl_offset_v1:"0"`
+}
+
+var _mTestRecursiveUnionStruct = _bindings.CreateLazyMarshaler(TestRecursiveUnionStruct{})
+
+func (msg *TestRecursiveUnionStruct) Marshaler() _bindings.Marshaler {
+	return _mTestRecursiveUnionStruct
+}
+
 type TestUnion1 struct {
 	_ struct{} `fidl:"s" fidl_size_v1:"48" fidl_alignment_v1:"8"`
 	A Union1   `fidl_offset_v1:"0"`
@@ -835,6 +868,77 @@
 	return _mtest1WithCtxEchoHandleRightsResponse
 }
 
+type I_testRecursiveVectorUnionTag uint64
+
+const (
+	TestRecursiveVectorUnion_unknownData = 0 // 0x00000000
+	TestRecursiveVectorUnionA            = 1 // 0x00000001
+	TestRecursiveVectorUnionB            = 2 // 0x00000002
+)
+
+type TestRecursiveVectorUnion struct {
+	I_testRecursiveVectorUnionTag `fidl:"x" fidl_size_v1:"24" fidl_alignment_v1:"8" fidl_resource:"false"`
+	I_unknownData                 interface{}
+	A                             float32                     `fidl_ordinal:"1"`
+	B                             []*TestRecursiveVectorUnion `fidl_bounds:"" fidl_ordinal:"2"`
+}
+
+func (_m *TestRecursiveVectorUnion) reset() {
+	switch _m.I_testRecursiveVectorUnionTag {
+	case 1:
+		var _zeroed float32
+		_m.A = _zeroed
+	case 2:
+		var _zeroed []*TestRecursiveVectorUnion
+		_m.B = _zeroed
+	default:
+		var _zeroed interface{}
+		_m.I_unknownData = _zeroed
+	}
+}
+
+func (_m *TestRecursiveVectorUnion) Which() I_testRecursiveVectorUnionTag {
+	switch _m.I_testRecursiveVectorUnionTag {
+	case 1:
+		return TestRecursiveVectorUnionA
+	case 2:
+		return TestRecursiveVectorUnionB
+	default:
+		return TestRecursiveVectorUnion_unknownData
+	}
+}
+
+func (_m *TestRecursiveVectorUnion) Ordinal() uint64 {
+	return uint64(_m.I_testRecursiveVectorUnionTag)
+}
+
+func (_m *TestRecursiveVectorUnion) SetA(a float32) {
+	_m.reset()
+	_m.I_testRecursiveVectorUnionTag = TestRecursiveVectorUnionA
+	_m.A = a
+}
+
+func TestRecursiveVectorUnionWithA(a float32) TestRecursiveVectorUnion {
+	var _u TestRecursiveVectorUnion
+	_u.SetA(a)
+	return _u
+}
+
+func (_m *TestRecursiveVectorUnion) SetB(b []*TestRecursiveVectorUnion) {
+	_m.reset()
+	_m.I_testRecursiveVectorUnionTag = TestRecursiveVectorUnionB
+	_m.B = b
+}
+
+func TestRecursiveVectorUnionWithB(b []*TestRecursiveVectorUnion) TestRecursiveVectorUnion {
+	var _u TestRecursiveVectorUnion
+	_u.SetB(b)
+	return _u
+}
+func (_m *TestRecursiveVectorUnion) GetUnknownData() _bindings.UnknownData {
+	return _m.I_unknownData.(_bindings.UnknownData)
+}
+
 type I_union1Tag uint64
 
 const (
diff --git a/src/syscall/zx/fidl/internal/bindingstest/test.test.fidl b/src/syscall/zx/fidl/internal/bindingstest/test.test.fidl
index f41a109..54c92d3 100644
--- a/src/syscall/zx/fidl/internal/bindingstest/test.test.fidl
+++ b/src/syscall/zx/fidl/internal/bindingstest/test.test.fidl
@@ -116,6 +116,23 @@
 struct EmptyStruct {
 };
 
+struct TestRecursiveStruct {
+  TestRecursiveStruct? s;
+};
+
+struct TestRecursiveVectorStruct {
+  vector<TestRecursiveVectorStruct?> v;
+};
+
+flexible union TestRecursiveVectorUnion {
+    1: float32 a;
+    2: vector<TestRecursiveVectorUnion?> b;
+};
+
+struct TestRecursiveUnionStruct {
+  TestRecursiveVectorUnion? u;
+};
+
 union Union1 {
     1: array<int8>:3 a;
     2: TestSimple b;