v23: ValueRead and ValueDecoder - vdl.Value impl of Decoder interface

Change-Id: Ie57bbe88f884dd839e06df086abc8dc9ff359de5
diff --git a/vdl/.api b/vdl/.api
index 78bed34..5a28db8 100644
--- a/vdl/.api
+++ b/vdl/.api
@@ -421,6 +421,7 @@
 pkg vdl, method (*Value) Bytes() []byte
 pkg vdl, method (*Value) ContainsKey(*Value) bool
 pkg vdl, method (*Value) CopyBytes([]byte) *Value
+pkg vdl, method (*Value) Decoder() Decoder
 pkg vdl, method (*Value) DeleteSetKey(*Value) *Value
 pkg vdl, method (*Value) Elem() *Value
 pkg vdl, method (*Value) EnumIndex() int
@@ -443,6 +444,7 @@
 pkg vdl, method (*Value) TypeObject() *Type
 pkg vdl, method (*Value) Uint() uint64
 pkg vdl, method (*Value) UnionField() (int, *Value)
+pkg vdl, method (*Value) VDLRead(Decoder) error
 pkg vdl, method (*WireError) FillVDLTarget(Target, *Type) error
 pkg vdl, method (*WireError) MakeVDLTarget() Target
 pkg vdl, method (*WireError) VDLRead(Decoder) error
diff --git a/vdl/value_decoder.go b/vdl/value_decoder.go
new file mode 100644
index 0000000..0506c0d
--- /dev/null
+++ b/vdl/value_decoder.go
@@ -0,0 +1,356 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vdl
+
+import (
+	"errors"
+	"fmt"
+)
+
+var (
+	errEmptyDecoderStack = errors.New("vdl: empty decoder stack")
+)
+
+// Decoder returns a decoder that traverses vv.
+func (vv *Value) Decoder() Decoder {
+	return &valueDecoder{initial: vv}
+}
+
+// ValueDecoder is an implementation of Decoder for vdl Value.
+type valueDecoder struct {
+	initial    *Value
+	ignoreNext bool
+	stack      []vdStackEntry
+}
+
+type vdStackEntry struct {
+	Value      *Value   // the vdl Value
+	Index      int      // next index or field (index into vdStackEntry.keys for map/set)
+	NumStarted int      // hold state for multiple StartValue() calls
+	IsAny      bool     // true iff this value is within an any value
+	IsOptional bool     // true iff this value is within an optional value
+	Keys       []*Value // keys for set/map
+}
+
+func (d *valueDecoder) StartValue() error {
+	if d.ignoreNext {
+		d.ignoreNext = false
+		return nil
+	}
+	var vv *Value
+	if top := d.top(); top == nil {
+		vv = d.initial
+	} else {
+		switch top.Value.Kind() {
+		case Array, List:
+			vv = top.Value.Index(top.Index)
+		case Set:
+			vv = top.Keys[top.Index]
+		case Map:
+			switch top.NumStarted % 2 {
+			case 0:
+				vv = top.Keys[top.Index]
+			case 1:
+				vv = top.Value.MapIndex(top.Keys[top.Index])
+			}
+		case Struct:
+			vv = top.Value.StructField(top.Index)
+		case Union:
+			_, vv = top.Value.UnionField()
+		default:
+			return fmt.Errorf("unknown composite kind: %v", top.Value.Kind())
+		}
+		top.NumStarted++
+	}
+	var isAny, isOptional bool
+	if vv.Kind() == Any {
+		isAny = true
+		if !vv.IsNil() {
+			vv = vv.Elem()
+		}
+	}
+	if vv.Kind() == Optional {
+		isOptional = true
+		if !vv.IsNil() {
+			vv = vv.Elem()
+		}
+	}
+	entry := vdStackEntry{
+		Value:      vv,
+		IsAny:      isAny,
+		IsOptional: isOptional,
+		Index:      -1,
+	}
+	if vv.Kind() == Map || vv.Kind() == Set {
+		entry.Keys = vv.Keys()
+	}
+	d.stack = append(d.stack, entry)
+	return nil
+}
+
+func (d *valueDecoder) IgnoreNextStartValue() {
+	d.ignoreNext = true
+}
+
+func (d *valueDecoder) FinishValue() error {
+	if len(d.stack) == 0 {
+		return errEmptyDecoderStack
+	}
+	d.stack = d.stack[:len(d.stack)-1]
+	d.ignoreNext = false
+	return nil
+}
+
+func (d *valueDecoder) SkipValue() error {
+	return nil
+}
+
+func (d *valueDecoder) NextEntry() (bool, error) {
+	top := d.top()
+	if top == nil {
+		return false, errEmptyDecoderStack
+	}
+	top.Index++
+	switch top.Value.Kind() {
+	case Array, List, Set, Map:
+		switch {
+		case top.Index == top.Value.Len():
+			return true, nil
+		case top.Index > top.Value.Len():
+			return false, fmt.Errorf("vdl: NextEntry called after done, stack: %+v", d.stack)
+		}
+	}
+	return false, nil
+}
+
+func (d *valueDecoder) NextField() (string, error) {
+	top := d.top()
+	if top == nil {
+		return "", errEmptyDecoderStack
+	}
+	top.Index++
+	index, max := top.Index, top.Value.Type().NumField()
+	if top.Value.Kind() == Union {
+		max = 1
+		index, _ = top.Value.UnionField()
+	}
+	if top.Index == max {
+		return "", nil
+	} else if top.Index > max {
+		return "", fmt.Errorf("vdl: NextField called after done, stack: %+v", d.stack)
+	}
+	return top.Value.Type().Field(index).Name, nil
+}
+
+func (d *valueDecoder) Type() *Type {
+	if top := d.top(); top != nil {
+		return top.Value.Type()
+	}
+	return nil
+}
+
+func (d *valueDecoder) IsAny() bool {
+	if top := d.top(); top != nil {
+		return top.IsAny
+	}
+	return false
+}
+
+func (d *valueDecoder) IsOptional() bool {
+	if top := d.top(); top != nil {
+		return top.IsOptional
+	}
+	return false
+}
+
+func (d *valueDecoder) IsNil() bool {
+	if top := d.top(); top != nil {
+		return top.Value.IsNil()
+	}
+	return false
+}
+
+func (d *valueDecoder) Index() int {
+	if top := d.top(); top != nil {
+		return top.Index
+	}
+	return -1
+}
+
+func (d *valueDecoder) LenHint() int {
+	if top := d.top(); top != nil {
+		switch top.Value.Kind() {
+		case List, Map, Set, Array:
+			return top.Value.Len()
+		}
+	}
+	return -1
+}
+
+func (d *valueDecoder) DecodeBool() (bool, error) {
+	top := d.top()
+	if top == nil {
+		return false, errEmptyDecoderStack
+	}
+	if top.Value.Kind() != Bool {
+		return false, fmt.Errorf("vdl: type mismatch, got %v, want bool", top.Value.Type())
+	}
+	return top.Value.Bool(), nil
+}
+
+func (d *valueDecoder) DecodeUint(bitlen uint) (uint64, error) {
+	const errFmt = "vdl: %v conversion to uint%d loses precision: %v"
+	top := d.top()
+	if top == nil {
+		return 0, errEmptyDecoderStack
+	}
+	switch top.Value.Kind() {
+	case Byte, Uint16, Uint32, Uint64:
+		x := top.Value.Uint()
+		if shift := 64 - bitlen; x != (x<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return x, nil
+	case Int8, Int16, Int32, Int64:
+		x := top.Value.Int()
+		ux := uint64(x)
+		if shift := 64 - bitlen; x < 0 || ux != (ux<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return ux, nil
+	case Float32, Float64:
+		x := top.Value.Float()
+		ux := uint64(x)
+		if shift := 64 - bitlen; x != float64(ux) || ux != (ux<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return ux, nil
+	default:
+		return 0, fmt.Errorf("vdl: type mismatch, got %v, want uint%d", top.Value.Type(), bitlen)
+	}
+}
+
+func (d *valueDecoder) DecodeInt(bitlen uint) (int64, error) {
+	const errFmt = "vdl: %v conversion to int%d loses precision: %v"
+	top := d.top()
+	if top == nil {
+		return 0, errEmptyDecoderStack
+	}
+	switch top.Value.Kind() {
+	case Byte, Uint16, Uint32, Uint64:
+		x := top.Value.Uint()
+		ix := int64(x)
+		if shift := 64 - bitlen; ix < 0 || x != (x<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return ix, nil
+	case Int8, Int16, Int32, Int64:
+		x := top.Value.Int()
+		if shift := 64 - bitlen; x != (x<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return x, nil
+	case Float32, Float64:
+		x := top.Value.Float()
+		ix := int64(x)
+		if shift := 64 - bitlen; x != float64(ix) || ix != (ix<<shift)>>shift {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return ix, nil
+	default:
+		return 0, fmt.Errorf("vdl: type mismatch, got %v, want int%d", top.Value.Type(), bitlen)
+	}
+}
+
+func (d *valueDecoder) DecodeFloat(bitlen uint) (float64, error) {
+	const errFmt = "vdl: %v conversion to float%d loses precision: %v"
+	top := d.top()
+	if top == nil {
+		return 0, errEmptyDecoderStack
+	}
+	switch top.Value.Kind() {
+	case Byte, Uint16, Uint32, Uint64:
+		x := top.Value.Uint()
+		var max uint64
+		if bitlen > 32 {
+			max = float64MaxInt
+		} else {
+			max = float32MaxInt
+		}
+		if x > max {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return float64(x), nil
+	case Int8, Int16, Int32, Int64:
+		x := top.Value.Int()
+		var min, max int64
+		if bitlen > 32 {
+			min, max = float64MinInt, float64MaxInt
+		} else {
+			min, max = float32MinInt, float32MaxInt
+		}
+		if x < min || x > max {
+			return 0, fmt.Errorf(errFmt, top.Value, bitlen, x)
+		}
+		return float64(x), nil
+	case Float32, Float64:
+		return top.Value.Float(), nil
+	default:
+		return 0, fmt.Errorf("vdl: type mismatch, got %v, want float%d", top.Value.Type(), bitlen)
+	}
+}
+
+func (d *valueDecoder) DecodeBytes(fixedlen int, v *[]byte) error {
+	top := d.top()
+	if top == nil {
+		return errEmptyDecoderStack
+	}
+	if !top.Value.Type().IsBytes() {
+		return fmt.Errorf("vdl: type mismatch, got %v, want bytes", top.Value.Type())
+	}
+	if fixedlen >= 0 && top.Value.Len() != fixedlen {
+		return fmt.Errorf("vdl: %v got %v bytes, want fixed len %v", top.Value.Type(), top.Value.Len(), fixedlen)
+	}
+	if cap(*v) < top.Value.Len() {
+		*v = make([]byte, top.Value.Len())
+	} else {
+		*v = (*v)[:top.Value.Len()]
+	}
+	copy(*v, top.Value.Bytes())
+	return nil
+}
+
+func (d *valueDecoder) DecodeString() (string, error) {
+	top := d.top()
+	if top == nil {
+		return "", errEmptyDecoderStack
+	}
+	switch top.Value.Kind() {
+	case String:
+		return top.Value.RawString(), nil
+	case Enum:
+		return top.Value.EnumLabel(), nil
+	default:
+		return "", fmt.Errorf("vdl: type mismatch, got %v, want string", top.Value.Type())
+	}
+}
+
+func (d *valueDecoder) DecodeTypeObject() (*Type, error) {
+	top := d.top()
+	if top == nil {
+		return nil, errEmptyDecoderStack
+	}
+	if top.Value.Type() != TypeObjectType {
+		return nil, fmt.Errorf("vdl: type mismatch, got %v, want typeobject", top.Value.Type())
+	}
+	return top.Value.TypeObject(), nil
+}
+
+func (d *valueDecoder) top() *vdStackEntry {
+	if len(d.stack) > 0 {
+		return &d.stack[len(d.stack)-1]
+	}
+	return nil
+}
diff --git a/vdl/value_decoder_test.go b/vdl/value_decoder_test.go
new file mode 100644
index 0000000..922bbb0
--- /dev/null
+++ b/vdl/value_decoder_test.go
@@ -0,0 +1,615 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vdl
+
+import (
+	"bytes"
+	"reflect"
+	"sort"
+	"testing"
+)
+
+func TestValueDecoderDecodeBool(t *testing.T) {
+	expected := true
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), BoolType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeBool(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != expected:
+		t.Errorf("got %d, want %d", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeUint(t *testing.T) {
+	expected := uint32(5)
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), Uint32Type; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeUint(32); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != 5:
+		t.Errorf("got %d, want %d", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeInt(t *testing.T) {
+	expected := int64(5)
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), Int64Type; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeInt(64); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != 5:
+		t.Errorf("got %d, want %d", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeFloat(t *testing.T) {
+	expected := float32(5)
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), Float32Type; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeFloat(32); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != 5:
+		t.Errorf("got %d, want %d", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeString(t *testing.T) {
+	expected := "abc"
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeString(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != expected:
+		t.Errorf("got %v, want %v", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeEnum(t *testing.T) {
+	expectedType := EnumType("A", "B")
+	expected := ZeroValue(expectedType)
+	vd := expected.Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), expectedType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeString(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != expected.EnumLabel():
+		t.Errorf("got %v, want %v", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeByteList(t *testing.T) {
+	expected := []byte("abc")
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	var out []byte
+	if err := vd.DecodeBytes(-1, &out); err != nil {
+		t.Errorf("error decoding value: %v", err)
+	}
+	if !bytes.Equal(expected, out) {
+		t.Errorf("got %v, want %v", out, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeByteArray(t *testing.T) {
+	expected := [3]byte{byte('a'), byte('b'), byte('c')}
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	var out []byte
+	if err := vd.DecodeBytes(3, &out); err != nil {
+		t.Errorf("error decoding value: %v", err)
+	}
+	if !bytes.Equal(expected[:], out) {
+		t.Errorf("got %v, want %v", out, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+
+	vd = ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if err := vd.DecodeBytes(2, &out); err == nil {
+		t.Errorf("expected error decoding with mismatched length")
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeTypeObject(t *testing.T) {
+	expected := TypeOf("abc")
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeObjectType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeTypeObject(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != expected:
+		t.Errorf("got %v, want %v", val, expected)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func testValueDecoderDecodeSequence(t *testing.T, expected interface{}) {
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeString(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != "a":
+		t.Errorf("got %v, want %v", val, "a")
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeString(); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != "b":
+		t.Errorf("got %v, want %v", val, "b")
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case !done:
+		t.Fatalf("expected end marker")
+	}
+
+	if _, err := vd.NextEntry(); err == nil {
+		t.Errorf("expected error in final call to NextEntry()")
+	}
+
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeList(t *testing.T) {
+	testValueDecoderDecodeSequence(t, []string{"a", "b"})
+}
+
+func TestValueDecoderDecodeArray(t *testing.T) {
+	testValueDecoderDecodeSequence(t, [2]string{"a", "b"})
+}
+
+func TestValueDecoderDecodeSet(t *testing.T) {
+	expected := map[string]struct{}{"a": struct{}{}, "b": struct{}{}}
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	key, err := vd.DecodeString()
+	switch {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case key != "a" && key != "b":
+		t.Errorf(`got %v, expected "a" or "b"`, key)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	key, err = vd.DecodeString()
+	switch {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case key != "a" && key != "b":
+		t.Errorf(`got %v, expected "a" or "b"`, key)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case !done:
+		t.Fatalf("expected end marker")
+	}
+
+	if _, err := vd.NextEntry(); err == nil {
+		t.Errorf("expected error in final call to NextEntry()")
+	}
+
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeMap(t *testing.T) {
+	expected := map[string]uint32{"a": 3, "b": 7}
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	key, err := vd.DecodeString()
+	switch {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case key != "a" && key != "b":
+		t.Errorf(`got %v, expected "a" or "b"`, key)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), Uint32Type; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeUint(32); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != uint64(expected[key]):
+		t.Errorf("got %v, want %v", expected[key], 3)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case done:
+		t.Fatalf("ended prematurely")
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), StringType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	key, err = vd.DecodeString()
+	switch {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case key != "a" && key != "b":
+		t.Errorf(`got %v, expected "a" or "b"`, key)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Fatalf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), Uint32Type; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+	switch val, err := vd.DecodeUint(32); {
+	case err != nil:
+		t.Errorf("error decoding value: %v", err)
+	case val != uint64(expected[key]):
+		t.Errorf("got %v, want %v", val, expected[key])
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+
+	switch done, err := vd.NextEntry(); {
+	case err != nil:
+		t.Fatalf("error in call to NextEntry(): %v", err)
+	case !done:
+		t.Fatalf("expected end marker")
+	}
+
+	if _, err := vd.NextEntry(); err == nil {
+		t.Errorf("expected error in final call to NextEntry()")
+	}
+
+	if err := vd.FinishValue(); err != nil {
+		t.Fatalf("error in FinishValue: %v", err)
+	}
+}
+
+type decoderTestStruct struct {
+	A int32
+	B []bool
+	C string
+}
+
+func TestValueDecoderDecodeStruct(t *testing.T) {
+	expected := decoderTestStruct{1, []bool{true, false}, "abc"}
+	vd := ValueOf(expected).Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), TypeOf(expected); got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	var seen []string
+loop:
+	for {
+		name, err := vd.NextField()
+		switch {
+		case err != nil:
+			t.Fatalf("error in NextField: %v", err)
+		case name == "A":
+			seen = append(seen, name)
+			if err := vd.StartValue(); err != nil {
+				t.Errorf("error in StartValue: %v", err)
+			}
+			switch val, err := vd.DecodeInt(32); {
+			case err != nil:
+				t.Errorf("error during decode: %v", err)
+			case val != 1:
+				t.Errorf("got %v, want %v", val, 1)
+			}
+			if err := vd.FinishValue(); err != nil {
+				t.Errorf("error in FinishValue: %v", err)
+			}
+		case name == "B":
+			seen = append(seen, name)
+			if err := vd.StartValue(); err != nil {
+				t.Errorf("error in StartValue: %v", err)
+			}
+
+			switch done, err := vd.NextEntry(); {
+			case err != nil:
+				t.Errorf("error in NextEntry: %v", err)
+			case done:
+				t.Errorf("unexpected end marker")
+			}
+			if err := vd.SkipValue(); err != nil {
+				t.Errorf("error in IgnoreValue: %v", err)
+			}
+
+			switch done, err := vd.NextEntry(); {
+			case err != nil:
+				t.Errorf("error in NextEntry: %v", err)
+			case done:
+				t.Errorf("unexpected end marker")
+			}
+			if err := vd.StartValue(); err != nil {
+				t.Errorf("error in StartValue: %v", err)
+			}
+			switch val, err := vd.DecodeBool(); {
+			case err != nil:
+				t.Errorf("error during decode: %v", err)
+			case val != false:
+				t.Errorf("got %v, want %v", val, false)
+			}
+			if err := vd.FinishValue(); err != nil {
+				t.Errorf("error in FinishValue: %v", err)
+			}
+
+			if err := vd.FinishValue(); err != nil {
+				t.Errorf("error in FinishValue: %v", err)
+			}
+		case name == "C":
+			seen = append(seen, name)
+			if err := vd.StartValue(); err != nil {
+				t.Errorf("error in StartValue: %v", err)
+			}
+			switch val, err := vd.DecodeString(); {
+			case err != nil:
+				t.Errorf("error during decode: %v", err)
+			case val != "abc":
+				t.Errorf("got %v, want %v", val, "abc")
+			}
+			if err := vd.FinishValue(); err != nil {
+				t.Errorf("error in FinishValue: %v", err)
+			}
+		case name == "":
+			sort.Strings(seen)
+			if !reflect.DeepEqual(seen, []string{"A", "B", "C"}) {
+				t.Errorf("unexpected field names received: %v", seen)
+			}
+			break loop
+		default:
+			t.Fatalf("received unknown field")
+		}
+	}
+
+	if _, err := vd.NextField(); err == nil {
+		t.Errorf("expected error in call to NextField()")
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
+
+func TestValueDecoderDecodeUnion(t *testing.T) {
+	b := TypeBuilder{}
+	union := b.Union()
+	union.AppendField("A", BoolType).AppendField("B", StringType)
+	b.Build()
+	expectedType, err := union.Built()
+	if err != nil {
+		t.Fatalf("error building union type: %v", err)
+	}
+	expected := ZeroValue(expectedType)
+	vd := expected.Decoder()
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	if got, want := vd.Type(), expectedType; got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+
+	switch name, err := vd.NextField(); {
+	case err != nil:
+		t.Errorf("error in NextField(): %v", err)
+	case name != "A":
+		t.Errorf("unexpected field name: %v", name)
+	}
+	if err := vd.StartValue(); err != nil {
+		t.Errorf("error in StartValue: %v", err)
+	}
+	switch val, err := vd.DecodeBool(); {
+	case err != nil:
+		t.Errorf("error during decode: %v", err)
+	case val != false:
+		t.Errorf("got %v, want %v", val, false)
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+	switch name, err := vd.NextField(); {
+	case err != nil:
+		t.Errorf("error in NextField(): %v", err)
+	case name != "":
+		t.Errorf("unexpected field after end of fields: %v", name)
+	}
+	if _, err := vd.NextField(); err == nil {
+		t.Errorf("expected error in call to NextField()")
+	}
+	if err := vd.FinishValue(); err != nil {
+		t.Errorf("error in FinishValue: %v", err)
+	}
+}
diff --git a/vdl/value_reader.go b/vdl/value_reader.go
new file mode 100644
index 0000000..901f7d6
--- /dev/null
+++ b/vdl/value_reader.go
@@ -0,0 +1,307 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vdl
+
+import (
+	"fmt"
+)
+
+// VDLRead reads from a decoder into this vdl Value.
+func (vv *Value) VDLRead(dec Decoder) error {
+	if vv == nil || vv.t == nil {
+		return fmt.Errorf("cannot decode into nil vdl value")
+	}
+	if err := dec.StartValue(); err != nil {
+		return err
+	}
+	if dec.IsNil() {
+		return vv.readHandleNil(dec)
+	}
+	fillvvAny := vv
+	if vv.Kind() == Any {
+		innerType := dec.Type()
+		if dec.IsOptional() {
+			innerType = OptionalType(innerType)
+		}
+		fillvvAny = ZeroValue(innerType)
+	}
+	fillvv := fillvvAny
+	if fillvvAny.Kind() == Optional {
+		fillvv = ZeroValue(fillvvAny.Type().Elem())
+	}
+
+	if err := fillvv.readFillValue(dec); err != nil {
+		return err
+	}
+
+	if fillvvAny.Kind() == Optional {
+		fillvvAny.Assign(OptionalValue(fillvv))
+	}
+	if vv.Kind() == Any {
+		vv.Assign(fillvvAny)
+	}
+	return dec.FinishValue()
+}
+
+// readHandleNil handles the case that dec.IsNil() is true
+func (vv *Value) readHandleNil(dec Decoder) error {
+	switch {
+	case dec.IsOptional():
+		// handles optional inside-any and optional on-its-own cases
+		if dec.Type().Kind() != Optional {
+			return fmt.Errorf("invalid optional value returned from decoder of type %v", dec.Type())
+		}
+		vv.Assign(ZeroValue(dec.Type()))
+	case dec.IsAny():
+		vv.Assign(nil)
+	default:
+		return fmt.Errorf("invalid non-any, non-optional nil value of type %v", dec.Type())
+	}
+	return dec.FinishValue()
+}
+
+func (vv *Value) readFillValue(dec Decoder) error {
+	// Fill in the value.
+	if vv.Type().IsBytes() {
+		fixedLength := -1
+		if vv.Kind() == Array {
+			fixedLength = vv.Type().Len()
+		}
+		var val []byte
+		if err := dec.DecodeBytes(fixedLength, &val); err != nil {
+			return err
+		}
+		vv.AssignBytes(val)
+		return nil
+	}
+	switch vv.Kind() {
+	case Bool:
+		val, err := dec.DecodeBool()
+		if err != nil {
+			return err
+		}
+		vv.AssignBool(val)
+	case Byte, Uint16, Uint32, Uint64:
+		val, err := dec.DecodeUint(uint(bitlenV(vv.Kind())))
+		if err != nil {
+			return err
+		}
+		vv.AssignUint(val)
+	case Int8, Int16, Int32, Int64:
+		val, err := dec.DecodeInt(uint(bitlenV(vv.Kind())))
+		if err != nil {
+			return err
+		}
+		vv.AssignInt(val)
+	case Float32, Float64:
+		val, err := dec.DecodeFloat(uint(bitlenV(vv.Kind())))
+		if err != nil {
+			return err
+		}
+		vv.AssignFloat(val)
+	case String:
+		val, err := dec.DecodeString()
+		if err != nil {
+			return err
+		}
+		vv.AssignString(val)
+	case TypeObject:
+		val, err := dec.DecodeTypeObject()
+		if err != nil {
+			return err
+		}
+		vv.AssignTypeObject(val)
+	case Enum:
+		val, err := dec.DecodeString()
+		if err != nil {
+			return err
+		}
+
+		index := vv.Type().EnumIndex(val)
+		if index == -1 {
+			return fmt.Errorf("vdl: %v invalid enum label %q", vv.Type(), val)
+		}
+		vv.AssignEnumIndex(index)
+	case Array:
+		if err := vv.readArray(dec); err != nil {
+			return err
+		}
+	case List:
+		if err := vv.readList(dec); err != nil {
+			return err
+		}
+	case Set:
+		if err := vv.readSet(dec); err != nil {
+			return err
+		}
+	case Map:
+		if err := vv.readMap(dec); err != nil {
+			return err
+		}
+	case Struct:
+		if err := vv.readStruct(dec); err != nil {
+			return err
+		}
+	case Union:
+		if err := vv.readUnion(dec); err != nil {
+			return err
+		}
+	default:
+		panic(fmt.Sprintf("unhandled type: %v", dec.Type()))
+	}
+	return nil
+}
+
+func (vv *Value) readSet(dec Decoder) error {
+	if dec.Type().Kind() != Set {
+		return fmt.Errorf("cannot decode into set from %v", dec.Type())
+	}
+	for {
+		switch done, err := dec.NextEntry(); {
+		case err != nil:
+			return err
+		case done:
+			return nil
+		}
+		key := ZeroValue(vv.Type().Key())
+		if err := key.VDLRead(dec); err != nil {
+			return err
+		}
+		vv.AssignSetKey(key)
+	}
+}
+
+func (vv *Value) readArray(dec Decoder) error {
+	switch dec.Type().Kind() {
+	case Array, List:
+	default:
+		return fmt.Errorf("cannot decode into array from %v", dec.Type())
+	}
+	index := 0
+	for {
+		switch done, err := dec.NextEntry(); {
+		case err != nil:
+			return err
+		case done != (index >= vv.Type().Len()):
+			return fmt.Errorf("array len mismatch, got %d, want %v", index, vv.Type())
+		case done:
+			return nil
+		}
+		if err := vv.Index(index).VDLRead(dec); err != nil {
+			return err
+		}
+		index++
+	}
+	return nil
+}
+
+func (vv *Value) readList(dec Decoder) error {
+	switch dec.Type().Kind() {
+	case Array, List:
+	default:
+		return fmt.Errorf("cannot decode into list from %v", dec.Type())
+	}
+	if len := dec.LenHint(); len >= 0 {
+		vv.AssignLen(len)
+	}
+	index := 0
+	for {
+		switch done, err := dec.NextEntry(); {
+		case err != nil:
+			return err
+		case done:
+			return nil
+		}
+		if index >= vv.Len() {
+			vv.AssignLen(index + 1)
+		}
+		if err := vv.Index(index).VDLRead(dec); err != nil {
+			return err
+		}
+		index++
+	}
+	return nil
+}
+
+func (vv *Value) readMap(dec Decoder) error {
+	if dec.Type().Kind() != Map {
+		return fmt.Errorf("cannot decode into map from %v", dec.Type())
+	}
+	for {
+		switch done, err := dec.NextEntry(); {
+		case err != nil:
+			return err
+		case done:
+			return nil
+		}
+		key := ZeroValue(vv.Type().Key())
+		elem := ZeroValue(vv.Type().Elem())
+		if err := key.VDLRead(dec); err != nil {
+			return err
+		}
+		if err := elem.VDLRead(dec); err != nil {
+			return err
+		}
+		vv.AssignMapIndex(key, elem)
+	}
+	return nil
+}
+
+func (vv *Value) readStruct(dec Decoder) error {
+	if dec.Type().Kind() != Struct {
+		return fmt.Errorf("cannot decode into struct from %v", dec.Type())
+	}
+	var matches int
+	for {
+		name, err := dec.NextField()
+		if err != nil {
+			return err
+		}
+		if name == "" {
+			if matches == 0 && dec.Type().NumField() > 0 && vv.Type().NumField() > 0 {
+				return fmt.Errorf("no matching fields found when decoding into struct of type %v", vv.Type())
+			}
+			return nil
+		}
+
+		field := vv.StructFieldByName(name)
+		if field == nil {
+			if err := dec.SkipValue(); err != nil {
+				return err
+			}
+		} else {
+			matches++
+			if err := field.VDLRead(dec); err != nil {
+				return err
+			}
+		}
+	}
+}
+
+func (vv *Value) readUnion(dec Decoder) error {
+	if dec.Type().Kind() != Union {
+		return fmt.Errorf("cannot decode into union from %v", dec.Type())
+	}
+	name, err := dec.NextField()
+	if err != nil {
+		return err
+	}
+	fld, index := vv.Type().FieldByName(name)
+	if index < 0 {
+		return fmt.Errorf("invalid field name %s when decoding into union %v", name, vv.Type())
+	}
+	unionElem := ZeroValue(fld.Type)
+	if err := unionElem.VDLRead(dec); err != nil {
+		return err
+	}
+	vv.AssignUnionField(index, unionElem)
+	switch name, err := dec.NextField(); {
+	case err != nil:
+		return err
+	case name != "":
+		return fmt.Errorf("multiple fields illegally specified for union")
+	}
+	return nil
+}
diff --git a/vdl/value_reader_test.go b/vdl/value_reader_test.go
new file mode 100644
index 0000000..03bee7d
--- /dev/null
+++ b/vdl/value_reader_test.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vdl_test
+
+import (
+	"testing"
+
+	"v.io/v23/vdl"
+	"v.io/v23/vom/testdata/data81"
+)
+
+func TestValueRead(t *testing.T) {
+	for _, test := range data81.Tests {
+		out := vdl.ZeroValue(test.Value.Type())
+		if err := out.VDLRead(test.Value.Decoder()); err != nil {
+			t.Errorf("%s: error in ValueRead: %v", test.Name, err)
+			continue
+		}
+		if !vdl.EqualValue(test.Value, out) {
+			t.Errorf("%s: got %v, want %v", test.Name, out, test.Value)
+		}
+	}
+}
diff --git a/vom/encoder.go b/vom/encoder.go
index f55cf48..8d0a30b 100644
--- a/vom/encoder.go
+++ b/vom/encoder.go
@@ -314,7 +314,7 @@
 // prepareTypeHelper encodes any unsent types, and manages the type stack. If
 // fromNil is true, we skip encoding the typeid for any type, since we'll be
 // encoding a nil instead.
-func (e *encoder) prepareTypeHelper(tt *vdl.Type, fromNil bool) error {
+func (e *encoder) prepareTypeHelper(tt *vdl.Type, preventAnyWrap bool) error {
 	var tid typeId
 	// Check the bootstrap wire types first to avoid recursive calls to the type
 	// encoder for wire types.
@@ -332,7 +332,7 @@
 		// Encoding the top-level. We postpone encoding of the tid until writeMsg
 		// is called, to handle positive and negative ids, and the message length.
 		e.pushType(tt)
-	case !fromNil && e.topType().Kind() == vdl.Any:
+	case !preventAnyWrap && e.topType().Kind() == vdl.Any:
 		if e.version == Version80 {
 			binaryEncodeUint(e.buf, uint64(tid))
 		} else {
@@ -579,7 +579,9 @@
 	if !tt.CanBeNil() {
 		return errTypeMismatch(tt, nilAllowed...)
 	}
-	if err := e.prepareTypeHelper(tt, true); err != nil {
+	// Emit any wrapper iff this is a nil optional within an any.
+	preventAnyWrap := len(e.typeStack) == 0 || e.topType() != vdl.AnyType || tt.Kind() != vdl.Optional
+	if err := e.prepareTypeHelper(tt, preventAnyWrap); err != nil {
 		return err
 	}
 	e.buf.WriteOneByte(WireCtrlNil)
diff --git a/vom/testdata/data80/data80.vdl.go b/vom/testdata/data80/data80.vdl.go
index 9cde28a..c408ad3 100644
--- a/vom/testdata/data80/data80.vdl.go
+++ b/vom/testdata/data80/data80.vdl.go
@@ -2278,6 +2278,28 @@
 		HexValue:   "5205002a0000e1",
 	},
 	{
+		Name: "types.StructAny{Any: ?types.NStruct(nil)}",
+		Value: vdl.ValueOf(types.StructAny{
+			Any: vdl.ValueOf((*types.NStruct)(nil)),
+		}),
+		TypeString: "v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		Hex:        "805133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae0e1",
+		HexVersion: "80",
+		HexType:    "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1",
+		HexValue:   "5204002ae0e1",
+	},
+	{
+		Name: "types.StructAny{Any: ?types.NStruct{}}",
+		Value: vdl.ValueOf(types.StructAny{
+			Any: vdl.ValueOf(&types.NStruct{}),
+		}),
+		TypeString: "v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		Hex:        "805133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae1e1",
+		HexVersion: "80",
+		HexType:    "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1",
+		HexValue:   "5204002ae1e1",
+	},
+	{
 		Name: "types.StructAny{Any: types.StructMap{}}",
 		Value: vdl.ValueOf(types.StructAny{
 			Any: vdl.ValueOf(types.StructMap{}),
diff --git a/vom/testdata/data80/vomdata.vdl b/vom/testdata/data80/vomdata.vdl
index ba7420b..660296a 100644
--- a/vom/testdata/data80/vomdata.vdl
+++ b/vom/testdata/data80/vomdata.vdl
@@ -6171,6 +6171,148 @@
 		"80", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1533a070022762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e556e696f6e01030001410101e10001420103e10001430109e1e1", "5205002a0000e1",
 	},
 	// 80                   Version                          128 [vom version 80]
+	// DumpStatus{MsgId: 0, MsgN: 1, Buf(126): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae0e1"}
+	// 51                   MsgId                            -41
+	//                      TypeMsg                           41
+	// 33                   MsgLen                            51
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 25                   ByteLen                           37 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e79 PrimValue       "v.io/v23/vom/testdata/types.StructAny" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 01                   ValueLen                           1 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 03                   ByteLen                            3 [string len]
+	// 416e79               PrimValue                      "Any" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 0f                   PrimValue                         15 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -41, MsgLen: 51, MsgN: 51, Buf(73): "553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae0e1"}
+	// 55                   MsgId                            -43
+	//                      TypeMsg                           43
+	// 3b                   MsgLen                            59
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 23                   ByteLen                           35 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e537472756374 PrimValue       "v.io/v23/vom/testdata/types.NStruct" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 03                   ValueLen                           3 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 41                   PrimValue                        "A" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 01                   PrimValue                          1 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 42                   PrimValue                        "B" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 03                   PrimValue                          3 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 43                   PrimValue                        "C" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 09                   PrimValue                          9 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -43, MsgLen: 59, MsgN: 59, Buf(12): "530408012be15204002ae0e1"}
+	// 53                   MsgId                            -42
+	//                      TypeMsg                           42
+	// 04                   MsgLen                             4
+	// 08                   WireTypeIndex                      8 [v.io/v23/vom.wireOptional]
+	// 01                   Index                              1 [v.io/v23/vom.wireOptional.Elem]
+	// 2b                   PrimValue                         43 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireOptional END]
+	// DumpStatus{MsgId: -42, MsgLen: 4, MsgN: 4, Buf(6): "5204002ae0e1"}
+	// 52                   MsgId                             41
+	//                      ValueMsg                          41 [v.io/v23/vom/testdata/types.StructAny struct{Any any}]
+	// 04                   MsgLen                             4
+	// 00                   Index                              0 [v.io/v23/vom/testdata/types.StructAny.Any]
+	// 2a                   TypeId                            42 [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}]
+	// e0                   Control                          Nil [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64} is nil]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.StructAny END]
+	// DumpStatus{MsgId: 41, MsgLen: 4, MsgN: 4, Value: v.io/v23/vom/testdata/types.StructAny struct{Any any}{Any: ?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}(nil)}}
+	{
+		`types.StructAny{Any: ?types.NStruct(nil)}`,
+		types.StructAny{Any: ?types.NStruct(nil)},
+		"v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		"805133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae0e1",
+		"80", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1", "5204002ae0e1",
+	},
+	// 80                   Version                          128 [vom version 80]
+	// DumpStatus{MsgId: 0, MsgN: 1, Buf(126): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae1e1"}
+	// 51                   MsgId                            -41
+	//                      TypeMsg                           41
+	// 33                   MsgLen                            51
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 25                   ByteLen                           37 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e79 PrimValue       "v.io/v23/vom/testdata/types.StructAny" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 01                   ValueLen                           1 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 03                   ByteLen                            3 [string len]
+	// 416e79               PrimValue                      "Any" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 0f                   PrimValue                         15 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -41, MsgLen: 51, MsgN: 51, Buf(73): "553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae1e1"}
+	// 55                   MsgId                            -43
+	//                      TypeMsg                           43
+	// 3b                   MsgLen                            59
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 23                   ByteLen                           35 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e537472756374 PrimValue       "v.io/v23/vom/testdata/types.NStruct" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 03                   ValueLen                           3 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 41                   PrimValue                        "A" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 01                   PrimValue                          1 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 42                   PrimValue                        "B" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 03                   PrimValue                          3 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 43                   PrimValue                        "C" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 09                   PrimValue                          9 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -43, MsgLen: 59, MsgN: 59, Buf(12): "530408012be15204002ae1e1"}
+	// 53                   MsgId                            -42
+	//                      TypeMsg                           42
+	// 04                   MsgLen                             4
+	// 08                   WireTypeIndex                      8 [v.io/v23/vom.wireOptional]
+	// 01                   Index                              1 [v.io/v23/vom.wireOptional.Elem]
+	// 2b                   PrimValue                         43 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireOptional END]
+	// DumpStatus{MsgId: -42, MsgLen: 4, MsgN: 4, Buf(6): "5204002ae1e1"}
+	// 52                   MsgId                             41
+	//                      ValueMsg                          41 [v.io/v23/vom/testdata/types.StructAny struct{Any any}]
+	// 04                   MsgLen                             4
+	// 00                   Index                              0 [v.io/v23/vom/testdata/types.StructAny.Any]
+	// 2a                   TypeId                            42 [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.NStruct END]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.StructAny END]
+	// DumpStatus{MsgId: 41, MsgLen: 4, MsgN: 4, Value: v.io/v23/vom/testdata/types.StructAny struct{Any any}{Any: ?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}({A: false, B: "", C: 0})}}
+	{
+		`types.StructAny{Any: ?types.NStruct{}}`,
+		types.StructAny{Any: ?types.NStruct{}},
+		"v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		"805133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be15204002ae1e1",
+		"80", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1", "5204002ae1e1",
+	},
+	// 80                   Version                          128 [vom version 80]
 	// DumpStatus{MsgId: 0, MsgN: 1, Buf(120): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e155060501090209e15333060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e5374727563744d6170010100034d6170012be1e15204002ae1e1"}
 	// 51                   MsgId                            -41
 	//                      TypeMsg                           41
diff --git a/vom/testdata/data81/data81.vdl.go b/vom/testdata/data81/data81.vdl.go
index 17bf3eb..85a581c 100644
--- a/vom/testdata/data81/data81.vdl.go
+++ b/vom/testdata/data81/data81.vdl.go
@@ -2278,6 +2278,28 @@
 		HexValue:   "52012a0102060000000000e1",
 	},
 	{
+		Name: "types.StructAny{Any: ?types.NStruct(nil)}",
+		Value: vdl.ValueOf(types.StructAny{
+			Any: vdl.ValueOf((*types.NStruct)(nil)),
+		}),
+		TypeString: "v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		Hex:        "815133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e0e1",
+		HexVersion: "81",
+		HexType:    "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1",
+		HexValue:   "52012a010105000000e0e1",
+	},
+	{
+		Name: "types.StructAny{Any: ?types.NStruct{}}",
+		Value: vdl.ValueOf(types.StructAny{
+			Any: vdl.ValueOf(&types.NStruct{}),
+		}),
+		TypeString: "v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		Hex:        "815133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e1e1",
+		HexVersion: "81",
+		HexType:    "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1",
+		HexValue:   "52012a010105000000e1e1",
+	},
+	{
 		Name: "types.StructAny{Any: types.StructMap{}}",
 		Value: vdl.ValueOf(types.StructAny{
 			Any: vdl.ValueOf(types.StructMap{}),
diff --git a/vom/testdata/data81/vomdata.vdl b/vom/testdata/data81/vomdata.vdl
index 68769dd..aa4fd5a 100644
--- a/vom/testdata/data81/vomdata.vdl
+++ b/vom/testdata/data81/vomdata.vdl
@@ -6369,6 +6369,158 @@
 		"81", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1533a070022762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e556e696f6e01030001410101e10001420103e10001430109e1e1", "52012a0102060000000000e1",
 	},
 	// 81                   Version                          129 [vom version 81]
+	// DumpStatus{MsgId: 0, MsgN: 1, Buf(131): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e0e1"}
+	// 51                   MsgId                            -41
+	//                      TypeMsg                           41
+	// 33                   MsgLen                            51
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 25                   ByteLen                           37 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e79 PrimValue       "v.io/v23/vom/testdata/types.StructAny" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 01                   ValueLen                           1 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 03                   ByteLen                            3 [string len]
+	// 416e79               PrimValue                      "Any" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 0f                   PrimValue                         15 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -41, MsgLen: 51, MsgN: 51, Buf(78): "553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e0e1"}
+	// 55                   MsgId                            -43
+	//                      TypeMsg                           43
+	// 3b                   MsgLen                            59
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 23                   ByteLen                           35 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e537472756374 PrimValue       "v.io/v23/vom/testdata/types.NStruct" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 03                   ValueLen                           3 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 41                   PrimValue                        "A" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 01                   PrimValue                          1 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 42                   PrimValue                        "B" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 03                   PrimValue                          3 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 43                   PrimValue                        "C" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 09                   PrimValue                          9 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -43, MsgLen: 59, MsgN: 59, Buf(17): "530408012be152012a010105000000e0e1"}
+	// 53                   MsgId                            -42
+	//                      TypeMsg                           42
+	// 04                   MsgLen                             4
+	// 08                   WireTypeIndex                      8 [v.io/v23/vom.wireOptional]
+	// 01                   Index                              1 [v.io/v23/vom.wireOptional.Elem]
+	// 2b                   PrimValue                         43 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireOptional END]
+	// DumpStatus{MsgId: -42, MsgLen: 4, MsgN: 4, Buf(11): "52012a010105000000e0e1"}
+	// 52                   MsgId                             41
+	//                      ValueMsg                          41 [v.io/v23/vom/testdata/types.StructAny struct{Any any}]
+	// 01                   TypeIdsLen                         1
+	// 2a                   TypeId                            42
+	// 01                   AnyLensLen                         1
+	// 01                   AnyMsgLen                          1
+	// 05                   MsgLen                             5
+	// 00                   Index                              0 [v.io/v23/vom/testdata/types.StructAny.Any]
+	// 00                   TypeId                             0 [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}]
+	// 00                   AnyMsgLen                          0 [len 1]
+	// e0                   Control                          Nil [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64} is nil]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.StructAny END]
+	// DumpStatus{MsgId: 41, MsgLen: 5, MsgN: 5, Value: v.io/v23/vom/testdata/types.StructAny struct{Any any}{Any: ?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}(nil)}}
+	{
+		`types.StructAny{Any: ?types.NStruct(nil)}`,
+		types.StructAny{Any: ?types.NStruct(nil)},
+		"v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		"815133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e0e1",
+		"81", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1", "52012a010105000000e0e1",
+	},
+	// 81                   Version                          129 [vom version 81]
+	// DumpStatus{MsgId: 0, MsgN: 1, Buf(131): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e1e1"}
+	// 51                   MsgId                            -41
+	//                      TypeMsg                           41
+	// 33                   MsgLen                            51
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 25                   ByteLen                           37 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e79 PrimValue       "v.io/v23/vom/testdata/types.StructAny" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 01                   ValueLen                           1 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 03                   ByteLen                            3 [string len]
+	// 416e79               PrimValue                      "Any" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 0f                   PrimValue                         15 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -41, MsgLen: 51, MsgN: 51, Buf(78): "553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e1e1"}
+	// 55                   MsgId                            -43
+	//                      TypeMsg                           43
+	// 3b                   MsgLen                            59
+	// 06                   WireTypeIndex                      6 [v.io/v23/vom.wireStruct]
+	// 00                   Index                              0 [v.io/v23/vom.wireStruct.Name]
+	// 23                   ByteLen                           35 [string len]
+	// 762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e537472756374 PrimValue       "v.io/v23/vom/testdata/types.NStruct" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireStruct.Fields]
+	// 03                   ValueLen                           3 [list len]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 41                   PrimValue                        "A" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 01                   PrimValue                          1 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 42                   PrimValue                        "B" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 03                   PrimValue                          3 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// 00                   Index                              0 [v.io/v23/vom.wireField.Name]
+	// 01                   ByteLen                            1 [string len]
+	// 43                   PrimValue                        "C" [string]
+	// 01                   Index                              1 [v.io/v23/vom.wireField.Type]
+	// 09                   PrimValue                          9 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireField END]
+	// e1                   Control                          End [v.io/v23/vom.wireStruct END]
+	// DumpStatus{MsgId: -43, MsgLen: 59, MsgN: 59, Buf(17): "530408012be152012a010105000000e1e1"}
+	// 53                   MsgId                            -42
+	//                      TypeMsg                           42
+	// 04                   MsgLen                             4
+	// 08                   WireTypeIndex                      8 [v.io/v23/vom.wireOptional]
+	// 01                   Index                              1 [v.io/v23/vom.wireOptional.Elem]
+	// 2b                   PrimValue                         43 [uint]
+	// e1                   Control                          End [v.io/v23/vom.wireOptional END]
+	// DumpStatus{MsgId: -42, MsgLen: 4, MsgN: 4, Buf(11): "52012a010105000000e1e1"}
+	// 52                   MsgId                             41
+	//                      ValueMsg                          41 [v.io/v23/vom/testdata/types.StructAny struct{Any any}]
+	// 01                   TypeIdsLen                         1
+	// 2a                   TypeId                            42
+	// 01                   AnyLensLen                         1
+	// 01                   AnyMsgLen                          1
+	// 05                   MsgLen                             5
+	// 00                   Index                              0 [v.io/v23/vom/testdata/types.StructAny.Any]
+	// 00                   TypeId                             0 [?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}]
+	// 00                   AnyMsgLen                          0 [len 1]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.NStruct END]
+	// e1                   Control                          End [v.io/v23/vom/testdata/types.StructAny END]
+	// DumpStatus{MsgId: 41, MsgLen: 5, MsgN: 5, Value: v.io/v23/vom/testdata/types.StructAny struct{Any any}{Any: ?v.io/v23/vom/testdata/types.NStruct struct{A bool;B string;C int64}({A: false, B: "", C: 0})}}
+	{
+		`types.StructAny{Any: ?types.NStruct{}}`,
+		types.StructAny{Any: ?types.NStruct{}},
+		"v.io/v23/vom/testdata/types.StructAny struct{Any any}",
+		"815133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be152012a010105000000e1e1",
+		"81", "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e1553b060023762e696f2f7632332f766f6d2f74657374646174612f74797065732e4e53747275637401030001410101e10001420103e10001430109e1e1530408012be1", "52012a010105000000e1e1",
+	},
+	// 81                   Version                          129 [vom version 81]
 	// DumpStatus{MsgId: 0, MsgN: 1, Buf(125): "5133060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e537472756374416e7901010003416e79010fe1e155060501090209e15333060025762e696f2f7632332f766f6d2f74657374646174612f74797065732e5374727563744d6170010100034d6170012be1e152012a010105000000e1e1"}
 	// 51                   MsgId                            -41
 	//                      TypeMsg                           41
diff --git a/vom/testdata/vomdata.vdl.config b/vom/testdata/vomdata.vdl.config
index 873b4b3..209e8f0 100644
--- a/vom/testdata/vomdata.vdl.config
+++ b/vom/testdata/vomdata.vdl.config
@@ -198,6 +198,7 @@
 	t.StructAny{t.NListUint64{}}, t.StructAny{t.NByteArray{}}, t.StructAny{t.NArray2Uint64{}},
     t.StructAny{t.NSetUint64{}}, t.StructAny{t.NMapUint64String{}},
     t.StructAny{t.NStruct{}}, t.StructAny{t.NUnion{A: false}},
+    t.StructAny{?t.NStruct(nil)}, t.StructAny{?t.NStruct{}},
 	t.StructAny{t.StructMap{}}, t.StructAny{t.StructMap{{0: 0}}},
 	t.StructAny{typeobject(any)},
 	?t.StructAny(nil), ?t.StructAny{nil}, ?t.StructAny{false},