internal/impl: allow reflection on typed nil pointers

Similar to how generated messages allow you to call Get methods on a
nil pointer, we permit similar functionality when protobuf reflection
is used on a nil pointer.

Change-Id: Ie2f596d39105c191073b42d7d689525c3b715240
Reviewed-on: https://go-review.googlesource.com/c/152021
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go
index df92f02..baa13fd 100644
--- a/internal/impl/legacy_extension.go
+++ b/internal/impl/legacy_extension.go
@@ -17,6 +17,9 @@
 		return nil
 	}
 	return func(p *messageDataType) pref.KnownFields {
+		if p.p.IsNil() {
+			return emptyExtensionFields{}
+		}
 		return legacyExtensionFields{p.mi, f(p)}
 	}
 }
@@ -33,13 +36,13 @@
 	case fx1.Type == extTypeA:
 		fieldOffset := offsetOf(fx1)
 		return func(p *messageDataType) papi.ExtensionFields {
-			v := p.p.apply(fieldOffset).asType(fx1.Type).Interface()
+			v := p.p.Apply(fieldOffset).AsValueOf(fx1.Type).Interface()
 			return papi.ExtensionFieldsOf(v)
 		}
 	case fx2.Type == extTypeB:
 		fieldOffset := offsetOf(fx2)
 		return func(p *messageDataType) papi.ExtensionFields {
-			v := p.p.apply(fieldOffset).asType(fx2.Type).Interface()
+			v := p.p.Apply(fieldOffset).AsValueOf(fx2.Type).Interface()
 			return papi.ExtensionFieldsOf(v)
 		}
 	default:
diff --git a/internal/impl/legacy_unknown.go b/internal/impl/legacy_unknown.go
index 0492eca..b4f57da 100644
--- a/internal/impl/legacy_unknown.go
+++ b/internal/impl/legacy_unknown.go
@@ -23,12 +23,18 @@
 	}
 	fieldOffset := offsetOf(fu)
 	unkFunc := func(p *messageDataType) pref.UnknownFields {
-		rv := p.p.apply(fieldOffset).asType(bytesType)
+		if p.p.IsNil() {
+			return emptyUnknownFields{}
+		}
+		rv := p.p.Apply(fieldOffset).AsValueOf(bytesType)
 		return (*legacyUnknownBytes)(rv.Interface().(*[]byte))
 	}
 	extFunc := makeLegacyExtensionMapFunc(t)
 	if extFunc != nil {
 		return func(p *messageDataType) pref.UnknownFields {
+			if p.p.IsNil() {
+				return emptyUnknownFields{}
+			}
 			return &legacyUnknownBytesAndExtensionMap{
 				unkFunc(p), extFunc(p), p.mi.Type.ExtensionRanges(),
 			}
diff --git a/internal/impl/message.go b/internal/impl/message.go
index 089c6e7..328103c 100644
--- a/internal/impl/message.go
+++ b/internal/impl/message.go
@@ -169,7 +169,7 @@
 
 func (mi *MessageType) dataTypeOf(p interface{}) *messageDataType {
 	mi.init(p)
-	return &messageDataType{pointerOfIface(&p), mi}
+	return &messageDataType{pointerOfIface(p), mi}
 }
 
 // messageDataType is a tuple of a pointer to the message data and
@@ -216,7 +216,7 @@
 	return m
 }
 func (m *messageWrapper) ProtoUnwrap() interface{} {
-	return m.p.asType(m.mi.goType.Elem()).Interface()
+	return m.p.AsIfaceOf(m.mi.goType.Elem())
 }
 func (m *messageWrapper) ProtoMutable() {}
 
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index 681c918..c55cbc8 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -51,25 +51,28 @@
 		// typed nil pointer to one of the wrapper structs.
 
 		has: func(p pointer) bool {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return false
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
 				return false
 			}
 			return true
 		},
 		get: func(p pointer) pref.Value {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return defaultValueOf(fd)
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
-				if fd.Kind() == pref.MessageKind || fd.Kind() == pref.GroupKind {
-					return pref.Value{}
-				}
-				return fd.Default()
+				return defaultValueOf(fd)
 			}
 			rv = rv.Elem().Elem().Field(0)
 			return conv.PBValueOf(rv)
 		},
 		set: func(p pointer, v pref.Value) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
 				rv.Set(reflect.New(ot))
 			}
@@ -77,7 +80,7 @@
 			rv.Set(conv.GoValueOf(v))
 		},
 		clear: func(p pointer) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
 				return
 			}
@@ -85,7 +88,7 @@
 		},
 		mutable: func(p pointer) pref.Mutable {
 			// Mutable is only valid for messages and panics for other kinds.
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() || rv.Elem().Type().Elem() != ot {
 				rv.Set(reflect.New(ot))
 			}
@@ -110,23 +113,30 @@
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
 		has: func(p pointer) bool {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return false
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			return rv.Len() > 0
 		},
 		get: func(p pointer) pref.Value {
-			v := p.apply(fieldOffset).asType(fs.Type).Interface()
+			if p.IsNil() {
+				v := reflect.Zero(reflect.PtrTo(fs.Type)).Interface()
+				return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv))
+			}
+			v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
 			return pref.ValueOf(pvalue.MapOf(v, keyConv, valConv))
 		},
 		set: func(p pointer, v pref.Value) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.ValueOf(v.Map().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
 		},
 		clear: func(p pointer) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
 		mutable: func(p pointer) pref.Mutable {
-			v := p.apply(fieldOffset).asType(fs.Type).Interface()
+			v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
 			return pvalue.MapOf(v, keyConv, valConv)
 		},
 	}
@@ -142,23 +152,30 @@
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
 		has: func(p pointer) bool {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return false
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			return rv.Len() > 0
 		},
 		get: func(p pointer) pref.Value {
-			v := p.apply(fieldOffset).asType(fs.Type).Interface()
+			if p.IsNil() {
+				v := reflect.Zero(reflect.PtrTo(fs.Type)).Interface()
+				return pref.ValueOf(pvalue.ListOf(v, conv))
+			}
+			v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
 			return pref.ValueOf(pvalue.ListOf(v, conv))
 		},
 		set: func(p pointer, v pref.Value) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.ValueOf(v.List().(pvalue.Unwrapper).ProtoUnwrap()).Elem())
 		},
 		clear: func(p pointer) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
 		mutable: func(p pointer) pref.Mutable {
-			v := p.apply(fieldOffset).asType(fs.Type).Interface()
+			v := p.Apply(fieldOffset).AsIfaceOf(fs.Type)
 			return pvalue.ListOf(v, conv)
 		},
 	}
@@ -182,7 +199,10 @@
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
 		has: func(p pointer) bool {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return false
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if nullable {
 				return !rv.IsNil()
 			}
@@ -202,14 +222,13 @@
 			}
 		},
 		get: func(p pointer) pref.Value {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return defaultValueOf(fd)
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if nullable {
 				if rv.IsNil() {
-					pv := fd.Default()
-					if fd.Kind() == pref.BytesKind && len(pv.Bytes()) > 0 {
-						return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety
-					}
-					return pv
+					return defaultValueOf(fd)
 				}
 				if rv.Kind() == reflect.Ptr {
 					rv = rv.Elem()
@@ -218,7 +237,7 @@
 			return conv.PBValueOf(rv)
 		},
 		set: func(p pointer, v pref.Value) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if nullable && rv.Kind() == reflect.Ptr {
 				if rv.IsNil() {
 					rv.Set(reflect.New(ft))
@@ -231,7 +250,7 @@
 			}
 		},
 		clear: func(p pointer) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
 		mutable: func(p pointer) pref.Mutable {
@@ -247,30 +266,36 @@
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
 		has: func(p pointer) bool {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return false
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			return !rv.IsNil()
 		},
 		get: func(p pointer) pref.Value {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			if p.IsNil() {
+				return pref.Value{}
+			}
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() {
 				return pref.Value{}
 			}
 			return conv.PBValueOf(rv)
 		},
 		set: func(p pointer, v pref.Value) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(conv.GoValueOf(v))
 			if rv.IsNil() {
 				panic("invalid nil pointer")
 			}
 		},
 		clear: func(p pointer) {
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
 		mutable: func(p pointer) pref.Mutable {
 			// Mutable is only valid for messages and panics for other kinds.
-			rv := p.apply(fieldOffset).asType(fs.Type).Elem()
+			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			if rv.IsNil() {
 				pv := pref.ValueOf(conv.MessageType.New().ProtoReflect())
 				rv.Set(conv.GoValueOf(pv))
@@ -279,3 +304,15 @@
 		},
 	}
 }
+
+// defaultValueOf returns the default value for the field.
+func defaultValueOf(fd pref.FieldDescriptor) pref.Value {
+	if fd == nil {
+		return pref.Value{}
+	}
+	pv := fd.Default() // invalid Value for messages and repeated fields
+	if fd.Kind() == pref.BytesKind && pv.IsValid() && len(pv.Bytes()) > 0 {
+		return pref.ValueOf(append([]byte(nil), pv.Bytes()...)) // copy default bytes for safety
+	}
+	return pv
+}
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index d388494..1f97430 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -11,7 +11,6 @@
 	"testing"
 
 	protoV1 "github.com/golang/protobuf/proto"
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	pimpl "github.com/golang/protobuf/v2/internal/impl"
 	scalar "github.com/golang/protobuf/v2/internal/scalar"
 	pvalue "github.com/golang/protobuf/v2/internal/value"
@@ -24,6 +23,7 @@
 	// TODO: Remove this when protoV1 registers these hooks for you.
 	_ "github.com/golang/protobuf/v2/internal/legacy"
 
+	descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	proto2_20180125 "github.com/golang/protobuf/v2/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
 )
 
@@ -256,6 +256,18 @@
 		clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
 		equalMessage{&ScalarProto2{}},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*ScalarProto2)(nil), messageOps{
+		hasFields{
+			1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
+			12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
+		},
+		getFields{
+			1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V([]byte("10")), 11: V([]byte("11")),
+			12: V(bool(true)), 13: V(int32(13)), 14: V(int64(14)), 15: V(uint32(15)), 16: V(uint64(16)), 17: V(float32(17)), 18: V(float64(18)), 19: V(string("19")), 20: V(string("20")), 21: V([]byte("21")), 22: V([]byte("22")),
+		},
+	})
 }
 
 type ScalarProto3 struct {
@@ -366,6 +378,18 @@
 		clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
 		equalMessage{&ScalarProto3{}},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*ScalarProto3)(nil), messageOps{
+		hasFields{
+			1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false,
+			12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false,
+		},
+		getFields{
+			1: V(bool(false)), 2: V(int32(0)), 3: V(int64(0)), 4: V(uint32(0)), 5: V(uint64(0)), 6: V(float32(0)), 7: V(float64(0)), 8: V(string("")), 9: V(string("")), 10: V([]byte(nil)), 11: V([]byte(nil)),
+			12: V(bool(false)), 13: V(int32(0)), 14: V(int64(0)), 15: V(uint32(0)), 16: V(uint64(0)), 17: V(float32(0)), 18: V(float64(0)), 19: V(string("")), 20: V(string("")), 21: V([]byte(nil)), 22: V([]byte(nil)),
+		},
+	})
 }
 
 type ListScalars struct {
@@ -519,6 +543,12 @@
 		clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
 		equalMessage{empty},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*ListScalars)(nil), messageOps{
+		hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false},
+		listFields{2: {lenList(0)}, 4: {lenList(0)}, 6: {lenList(0)}, 8: {lenList(0)}, 10: {lenList(0)}, 12: {lenList(0)}, 14: {lenList(0)}, 16: {lenList(0)}, 18: {lenList(0)}},
+	})
 }
 
 type MapScalars struct {
@@ -565,7 +595,7 @@
 				{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: keyKind},
 				{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: valKind},
 			},
-			Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+			Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
 		}),
 	}
 }
@@ -731,6 +761,12 @@
 		clearFields{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
 		equalMessage{empty},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*MapScalars)(nil), messageOps{
+		hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false, 23: false, 24: false, 25: false},
+		mapFields{2: {lenMap(0)}, 4: {lenMap(0)}, 6: {lenMap(0)}, 8: {lenMap(0)}, 10: {lenMap(0)}, 12: {lenMap(0)}, 14: {lenMap(0)}, 16: {lenMap(0)}, 18: {lenMap(0)}, 20: {lenMap(0)}, 22: {lenMap(0)}, 24: {lenMap(0)}},
+	})
 }
 
 type OneofScalars struct {
@@ -888,6 +924,12 @@
 		clearFields{13},
 		equalMessage{empty},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*OneofScalars)(nil), messageOps{
+		hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false, 13: false},
+		getFields{1: V(bool(true)), 2: V(int32(2)), 3: V(int64(3)), 4: V(uint32(4)), 5: V(uint64(5)), 6: V(float32(6)), 7: V(float64(7)), 8: V(string("8")), 9: V(string("9")), 10: V(string("10")), 11: V([]byte("11")), 12: V([]byte("12")), 13: V([]byte("13"))},
+	})
 }
 
 type EnumProto2 int32
@@ -970,7 +1012,7 @@
 		{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: pref.StringKind},
 		{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: pref.EnumKind, EnumType: enumProto3Type},
 	},
-	Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+	Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
 })
 
 var messageMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
@@ -980,7 +1022,7 @@
 		{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: pref.StringKind},
 		{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: scalarProto3Type.Type},
 	},
-	Options: &descriptorV1.MessageOptions{MapEntry: scalar.Bool(true)},
+	Options: &descriptorpb.MessageOptions{MapEntry: scalar.Bool(true)},
 })
 
 func (m *EnumMessages) Type() pref.MessageType            { return enumMessagesType.Type }
@@ -1119,6 +1161,14 @@
 		clearFields{1, 2, 3, 4, 6, 7, 12},
 		equalMessage{&EnumMessages{}},
 	})
+
+	// Test read-only operations on nil message.
+	testMessage(t, nil, (*EnumMessages)(nil), messageOps{
+		hasFields{1: false, 2: false, 3: false, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false, 11: false, 12: false},
+		getFields{1: VE(0xbeef), 2: VE(1), 3: V(nil), 4: V(nil), 9: VE(0xbeef), 10: VE(1), 11: V(nil), 12: V(nil)},
+		listFields{5: {lenList(0)}, 6: {lenList(0)}},
+		mapFields{7: {lenMap(0)}, 8: {lenMap(0)}},
+	})
 }
 
 var cmpOpts = cmp.Options{
@@ -1179,13 +1229,13 @@
 		case listFields:
 			for n, tt := range op {
 				p.Push(int(n))
-				testLists(t, p, fs.Mutable(n).(pref.List), tt)
+				testLists(t, p, fs.Get(n).List(), tt)
 				p.Pop()
 			}
 		case mapFields:
 			for n, tt := range op {
 				p.Push(int(n))
-				testMaps(t, p, fs.Mutable(n).(pref.Map), tt)
+				testMaps(t, p, fs.Get(n).Map(), tt)
 				p.Pop()
 			}
 		case rangeFields:
diff --git a/internal/impl/pointer_reflect.go b/internal/impl/pointer_reflect.go
index 6732824..0b62f6c 100644
--- a/internal/impl/pointer_reflect.go
+++ b/internal/impl/pointer_reflect.go
@@ -32,21 +32,33 @@
 }
 
 // pointerOfIface returns the pointer portion of an interface.
-func pointerOfIface(v *interface{}) pointer {
-	return pointer{v: reflect.ValueOf(*v)}
+func pointerOfIface(v interface{}) pointer {
+	return pointer{v: reflect.ValueOf(v)}
 }
 
-// apply adds an offset to the pointer to derive a new pointer
+// IsNil reports whether the pointer is nil.
+func (p pointer) IsNil() bool {
+	return p.v.IsNil()
+}
+
+// Apply adds an offset to the pointer to derive a new pointer
 // to a specified field. The current pointer must be pointing at a struct.
-func (p pointer) apply(f offset) pointer {
+func (p pointer) Apply(f offset) pointer {
 	// TODO: Handle unexported fields in an API that hides XXX fields?
 	return pointer{v: p.v.Elem().FieldByIndex(f).Addr()}
 }
 
-// asType treats p as a pointer to an object of type t and returns the value.
-func (p pointer) asType(t reflect.Type) reflect.Value {
+// AsValueOf treats p as a pointer to an object of type t and returns the value.
+// It is equivalent to reflect.ValueOf(p.AsIfaceOf(t))
+func (p pointer) AsValueOf(t reflect.Type) reflect.Value {
 	if p.v.Type().Elem() != t {
 		panic(fmt.Sprintf("invalid type: got %v, want %v", p.v.Type(), t))
 	}
 	return p.v
 }
+
+// AsIfaceOf treats p as a pointer to an object of type t and returns the value.
+// It is equivalent to p.AsValueOf(t).Interface()
+func (p pointer) AsIfaceOf(t reflect.Type) interface{} {
+	return p.AsValueOf(t).Interface()
+}
diff --git a/internal/impl/pointer_unsafe.go b/internal/impl/pointer_unsafe.go
index c2b9d21..fc77cfc 100644
--- a/internal/impl/pointer_unsafe.go
+++ b/internal/impl/pointer_unsafe.go
@@ -29,21 +29,37 @@
 }
 
 // pointerOfIface returns the pointer portion of an interface.
-func pointerOfIface(v *interface{}) pointer {
+func pointerOfIface(v interface{}) pointer {
 	type ifaceHeader struct {
 		Type unsafe.Pointer
 		Data unsafe.Pointer
 	}
-	return pointer{p: (*ifaceHeader)(unsafe.Pointer(v)).Data}
+	return pointer{p: (*ifaceHeader)(unsafe.Pointer(&v)).Data}
 }
 
-// apply adds an offset to the pointer to derive a new pointer
-// to a specified field. The current pointer must be pointing at a struct.
-func (p pointer) apply(f offset) pointer {
+// IsNil reports whether the pointer is nil.
+func (p pointer) IsNil() bool {
+	return p.p == nil
+}
+
+// Apply adds an offset to the pointer to derive a new pointer
+// to a specified field. The pointer must be valid and pointing at a struct.
+func (p pointer) Apply(f offset) pointer {
+	if p.IsNil() {
+		panic("invalid nil pointer")
+	}
 	return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))}
 }
 
-// asType treats p as a pointer to an object of type t and returns the value.
-func (p pointer) asType(t reflect.Type) reflect.Value {
+// AsValueOf treats p as a pointer to an object of type t and returns the value.
+// It is equivalent to reflect.ValueOf(p.AsIfaceOf(t))
+func (p pointer) AsValueOf(t reflect.Type) reflect.Value {
 	return reflect.NewAt(t, p.p)
 }
+
+// AsIfaceOf treats p as a pointer to an object of type t and returns the value.
+// It is equivalent to p.AsValueOf(t).Interface()
+func (p pointer) AsIfaceOf(t reflect.Type) interface{} {
+	// TODO: Use tricky unsafe magic to directly create ifaceHeader.
+	return p.AsValueOf(t).Interface()
+}
diff --git a/internal/value/list.go b/internal/value/list.go
index dadb05f..bcac98b 100644
--- a/internal/value/list.go
+++ b/internal/value/list.go
@@ -15,41 +15,44 @@
 	Unwrapper
 } {
 	// TODO: Validate that p is a *[]T?
-	rv := reflect.ValueOf(p).Elem()
+	rv := reflect.ValueOf(p)
 	return listReflect{rv, c}
 }
 
 type listReflect struct {
-	v    reflect.Value // addressable []T
+	v    reflect.Value // *[]T
 	conv Converter
 }
 
 func (ls listReflect) Len() int {
-	return ls.v.Len()
+	if ls.v.IsNil() {
+		return 0
+	}
+	return ls.v.Elem().Len()
 }
 func (ls listReflect) Get(i int) pref.Value {
-	return ls.conv.PBValueOf(ls.v.Index(i))
+	return ls.conv.PBValueOf(ls.v.Elem().Index(i))
 }
 func (ls listReflect) Set(i int, v pref.Value) {
-	ls.v.Index(i).Set(ls.conv.GoValueOf(v))
+	ls.v.Elem().Index(i).Set(ls.conv.GoValueOf(v))
 }
 func (ls listReflect) Append(v pref.Value) {
-	ls.v.Set(reflect.Append(ls.v, ls.conv.GoValueOf(v)))
+	ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(v)))
 }
 func (ls listReflect) Mutable(i int) pref.Mutable {
 	// Mutable is only valid for messages and panics for other kinds.
-	return ls.conv.PBValueOf(ls.v.Index(i)).Message()
+	return ls.conv.PBValueOf(ls.v.Elem().Index(i)).Message()
 }
 func (ls listReflect) MutableAppend() pref.Mutable {
 	// MutableAppend is only valid for messages and panics for other kinds.
 	pv := pref.ValueOf(ls.conv.MessageType.New().ProtoReflect())
-	ls.v.Set(reflect.Append(ls.v, ls.conv.GoValueOf(pv)))
+	ls.v.Elem().Set(reflect.Append(ls.v.Elem(), ls.conv.GoValueOf(pv)))
 	return pv.Message()
 }
 func (ls listReflect) Truncate(i int) {
-	ls.v.Set(ls.v.Slice(0, i))
+	ls.v.Elem().Set(ls.v.Elem().Slice(0, i))
 }
 func (ls listReflect) ProtoUnwrap() interface{} {
-	return ls.v.Addr().Interface()
+	return ls.v.Interface()
 }
 func (ls listReflect) ProtoMutable() {}
diff --git a/internal/value/map.go b/internal/value/map.go
index 8505798..28b901b 100644
--- a/internal/value/map.go
+++ b/internal/value/map.go
@@ -15,61 +15,73 @@
 	Unwrapper
 } {
 	// TODO: Validate that p is a *map[K]V?
-	rv := reflect.ValueOf(p).Elem()
+	rv := reflect.ValueOf(p)
 	return mapReflect{rv, kc, kv}
 }
 
 type mapReflect struct {
-	v       reflect.Value // addressable map[K]V
+	v       reflect.Value // *map[K]V
 	keyConv Converter
 	valConv Converter
 }
 
 func (ms mapReflect) Len() int {
-	return ms.v.Len()
+	if ms.v.IsNil() {
+		return 0
+	}
+	return ms.v.Elem().Len()
 }
 func (ms mapReflect) Has(k pref.MapKey) bool {
+	if ms.v.IsNil() {
+		return false
+	}
 	rk := ms.keyConv.GoValueOf(k.Value())
-	rv := ms.v.MapIndex(rk)
+	rv := ms.v.Elem().MapIndex(rk)
 	return rv.IsValid()
 }
 func (ms mapReflect) Get(k pref.MapKey) pref.Value {
+	if ms.v.IsNil() {
+		return pref.Value{}
+	}
 	rk := ms.keyConv.GoValueOf(k.Value())
-	rv := ms.v.MapIndex(rk)
+	rv := ms.v.Elem().MapIndex(rk)
 	if !rv.IsValid() {
 		return pref.Value{}
 	}
 	return ms.valConv.PBValueOf(rv)
 }
 func (ms mapReflect) Set(k pref.MapKey, v pref.Value) {
-	if ms.v.IsNil() {
-		ms.v.Set(reflect.MakeMap(ms.v.Type()))
+	if ms.v.Elem().IsNil() {
+		ms.v.Elem().Set(reflect.MakeMap(ms.v.Elem().Type()))
 	}
 	rk := ms.keyConv.GoValueOf(k.Value())
 	rv := ms.valConv.GoValueOf(v)
-	ms.v.SetMapIndex(rk, rv)
+	ms.v.Elem().SetMapIndex(rk, rv)
 }
 func (ms mapReflect) Clear(k pref.MapKey) {
 	rk := ms.keyConv.GoValueOf(k.Value())
-	ms.v.SetMapIndex(rk, reflect.Value{})
+	ms.v.Elem().SetMapIndex(rk, reflect.Value{})
 }
 func (ms mapReflect) Mutable(k pref.MapKey) pref.Mutable {
 	// Mutable is only valid for messages and panics for other kinds.
-	if ms.v.IsNil() {
-		ms.v.Set(reflect.MakeMap(ms.v.Type()))
+	if ms.v.Elem().IsNil() {
+		ms.v.Elem().Set(reflect.MakeMap(ms.v.Elem().Type()))
 	}
 	rk := ms.keyConv.GoValueOf(k.Value())
-	rv := ms.v.MapIndex(rk)
+	rv := ms.v.Elem().MapIndex(rk)
 	if !rv.IsValid() {
 		pv := pref.ValueOf(ms.valConv.MessageType.New().ProtoReflect())
 		rv = ms.valConv.GoValueOf(pv)
-		ms.v.SetMapIndex(rk, rv)
+		ms.v.Elem().SetMapIndex(rk, rv)
 	}
 	return ms.valConv.PBValueOf(rv).Message()
 }
 func (ms mapReflect) Range(f func(pref.MapKey, pref.Value) bool) {
-	for _, k := range ms.v.MapKeys() {
-		if v := ms.v.MapIndex(k); v.IsValid() {
+	if ms.v.IsNil() {
+		return
+	}
+	for _, k := range ms.v.Elem().MapKeys() {
+		if v := ms.v.Elem().MapIndex(k); v.IsValid() {
 			pk := ms.keyConv.PBValueOf(k).MapKey()
 			pv := ms.valConv.PBValueOf(v)
 			if !f(pk, pv) {
@@ -79,6 +91,6 @@
 	}
 }
 func (ms mapReflect) ProtoUnwrap() interface{} {
-	return ms.v.Addr().Interface()
+	return ms.v.Interface()
 }
 func (ms mapReflect) ProtoMutable() {}