reflect/prototype: initial commit

Add the prototype package which provides constructors for
protoreflect.{Enum,Message,Extension}Type.

Switch all usages of the internal/prototype equivalent to the new package.
Switch all custom implementions of {Enum,Message}Type to the new package.

Change-Id: Ia9dae6fed4f2b90e55c123627044a7faf098c4b1
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/178438
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/fileinit/desc_lazy.go b/internal/fileinit/desc_lazy.go
index 018b8e4..0e26e17 100644
--- a/internal/fileinit/desc_lazy.go
+++ b/internal/fileinit/desc_lazy.go
@@ -101,6 +101,8 @@
 }
 
 func (file *fileDesc) resolveExtensions() {
+	// TODO: Delete this and use reflect/prototype instead.
+
 	for i := range file.allExtensions {
 		xd := &file.allExtensions[i]
 
diff --git a/internal/impl/export.go b/internal/impl/export.go
index 9ea3900..8c7c121 100644
--- a/internal/impl/export.go
+++ b/internal/impl/export.go
@@ -10,6 +10,7 @@
 
 	"google.golang.org/protobuf/encoding/prototext"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 )
 
 // Export is a zero-length named type that exists only to export a set of
@@ -31,24 +32,16 @@
 // EnumTypeOf returns the protoreflect.EnumType for e.
 func (Export) EnumTypeOf(e enum) pref.EnumType {
 	if ev, ok := e.(pref.Enum); ok {
-		return &enumType{ev.Descriptor(), reflect.TypeOf(e)}
+		return &prototype.Enum{
+			EnumDescriptor: ev.Descriptor(),
+			NewEnum: func(n pref.EnumNumber) pref.Enum {
+				return reflect.ValueOf(n).Convert(reflect.TypeOf(e)).Interface().(pref.Enum)
+			},
+		}
 	}
 	return legacyWrapper.EnumTypeOf(e)
 }
 
-// TODO: This needs to be centralized in a package.
-type enumType struct {
-	// TODO: Remove me as an embedded field.
-	pref.EnumDescriptor
-	typ reflect.Type // must implement protoreflect.Enum
-}
-
-func (t *enumType) Descriptor() pref.EnumDescriptor { return t.EnumDescriptor }
-func (t *enumType) GoType() reflect.Type            { return t.typ }
-func (t *enumType) New(n pref.EnumNumber) pref.Enum {
-	return reflect.ValueOf(n).Convert(t.typ).Interface().(pref.Enum)
-}
-
 // EnumDescriptorOf returns the protoreflect.EnumDescriptor for e.
 func (Export) EnumDescriptorOf(e enum) pref.EnumDescriptor {
 	if ev, ok := e.(pref.Enum); ok {
@@ -82,24 +75,16 @@
 // MessageTypeOf returns the protoreflect.MessageType for m.
 func (Export) MessageTypeOf(m message) pref.MessageType {
 	if mv, ok := m.(pref.ProtoMessage); ok {
-		return &messageType{mv.ProtoReflect().Descriptor(), reflect.TypeOf(m)}
+		return &prototype.Message{
+			MessageDescriptor: mv.ProtoReflect().Descriptor(),
+			NewMessage: func() pref.Message {
+				return reflect.New(reflect.TypeOf(m).Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+			},
+		}
 	}
 	return legacyWrapper.MessageTypeOf(m)
 }
 
-// TODO: This needs to be centralized in a package.
-type messageType struct {
-	// TODO: Remove me as an embedded field.
-	pref.MessageDescriptor
-	typ reflect.Type // must implement protoreflect.ProtoMessage
-}
-
-func (t *messageType) Descriptor() pref.MessageDescriptor { return t.MessageDescriptor }
-func (t *messageType) GoType() reflect.Type               { return t.typ }
-func (t *messageType) New() pref.Message {
-	return reflect.New(t.typ.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
-}
-
 // MessageDescriptorOf returns the protoreflect.MessageDescriptor for m.
 func (Export) MessageDescriptorOf(m message) pref.MessageDescriptor {
 	if mv, ok := m.(pref.ProtoMessage); ok {
diff --git a/internal/impl/legacy_hook.go b/internal/impl/legacy_hook.go
index f3ffe63..98eaf2f 100644
--- a/internal/impl/legacy_hook.go
+++ b/internal/impl/legacy_hook.go
@@ -4,14 +4,39 @@
 
 package impl
 
-import pvalue "google.golang.org/protobuf/internal/value"
+import (
+	"reflect"
+
+	pvalue "google.golang.org/protobuf/internal/value"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	piface "google.golang.org/protobuf/runtime/protoiface"
+)
 
 // TODO: Add a default LegacyWrapper that panics with a more helpful message?
-var legacyWrapper pvalue.LegacyWrapper
+var legacyWrapper LegacyWrapper
 
 // RegisterLegacyWrapper registers a set of constructor functions that are
 // called when a legacy enum or message is encountered that does not natively
 // support the protobuf reflection APIs.
-func RegisterLegacyWrapper(w pvalue.LegacyWrapper) {
+func RegisterLegacyWrapper(w LegacyWrapper) {
 	legacyWrapper = w
 }
+
+// LegacyWrapper is a set of wrapper methods that wraps legacy v1 Go types
+// to implement the v2 reflection APIs.
+type LegacyWrapper interface {
+	NewConverter(reflect.Type, pref.Kind) pvalue.Converter
+
+	EnumOf(interface{}) pref.Enum
+	EnumTypeOf(interface{}) pref.EnumType
+	EnumDescriptorOf(interface{}) pref.EnumDescriptor
+
+	MessageOf(interface{}) pref.Message
+	MessageTypeOf(interface{}) pref.MessageType
+	MessageDescriptorOf(interface{}) pref.MessageDescriptor
+
+	// TODO: Remove these eventually.
+	// See the TODOs in internal/impl/legacy_extension.go.
+	ExtensionDescFromType(pref.ExtensionType) *piface.ExtensionDescV1
+	ExtensionTypeFromDesc(*piface.ExtensionDescV1) pref.ExtensionType
+}
diff --git a/internal/impl/message_field.go b/internal/impl/message_field.go
index 72f4523..a69d2c8 100644
--- a/internal/impl/message_field.go
+++ b/internal/impl/message_field.go
@@ -42,7 +42,7 @@
 	if !reflect.PtrTo(ot).Implements(ft) {
 		panic(fmt.Sprintf("invalid type: %v does not implement %v", ot, ft))
 	}
-	conv := pvalue.NewLegacyConverter(ot.Field(0).Type, fd.Kind(), legacyWrapper)
+	conv := newConverter(ot.Field(0).Type, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
 	return fieldInfo{
@@ -86,12 +86,9 @@
 			}
 			rv.Set(reflect.Zero(rv.Type()))
 		},
-		newMessage: func() pref.Message {
-			// This is only valid for messages and panics for other kinds.
-			return conv.MessageType.New()
-		},
-		offset:    fieldOffset,
-		isPointer: true,
+		newMessage: conv.NewMessage,
+		offset:     fieldOffset,
+		isPointer:  true,
 	}
 }
 
@@ -100,8 +97,8 @@
 	if ft.Kind() != reflect.Map {
 		panic(fmt.Sprintf("invalid type: got %v, want map kind", ft))
 	}
-	keyConv := pvalue.NewLegacyConverter(ft.Key(), fd.MapKey().Kind(), legacyWrapper)
-	valConv := pvalue.NewLegacyConverter(ft.Elem(), fd.MapValue().Kind(), legacyWrapper)
+	keyConv := newConverter(ft.Key(), fd.MapKey().Kind())
+	valConv := newConverter(ft.Elem(), fd.MapValue().Kind())
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
@@ -142,7 +139,7 @@
 	if ft.Kind() != reflect.Slice {
 		panic(fmt.Sprintf("invalid type: got %v, want slice kind", ft))
 	}
-	conv := pvalue.NewLegacyConverter(ft.Elem(), fd.Kind(), legacyWrapper)
+	conv := newConverter(ft.Elem(), fd.Kind())
 	var wiretag uint64
 	if !fd.IsPacked() {
 		wiretag = wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -197,7 +194,7 @@
 			ft = ft.Elem()
 		}
 	}
-	conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+	conv := newConverter(ft, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
 	// TODO: Implement unsafe fast path?
@@ -267,7 +264,7 @@
 
 func fieldInfoForMessage(fd pref.FieldDescriptor, fs reflect.StructField) fieldInfo {
 	ft := fs.Type
-	conv := pvalue.NewLegacyConverter(ft, fd.Kind(), legacyWrapper)
+	conv := newConverter(ft, fd.Kind())
 	fieldOffset := offsetOf(fs)
 	// TODO: Implement unsafe fast path?
 	wiretag := wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
@@ -300,14 +297,12 @@
 			rv := p.Apply(fieldOffset).AsValueOf(fs.Type).Elem()
 			rv.Set(reflect.Zero(rv.Type()))
 		},
-		newMessage: func() pref.Message {
-			return conv.MessageType.New()
-		},
-		funcs:     fieldCoder(fd, ft),
-		offset:    fieldOffset,
-		isPointer: true,
-		wiretag:   wiretag,
-		tagsize:   wire.SizeVarint(wiretag),
+		newMessage: conv.NewMessage,
+		funcs:      fieldCoder(fd, ft),
+		offset:     fieldOffset,
+		isPointer:  true,
+		wiretag:    wiretag,
+		tagsize:    wire.SizeVarint(wiretag),
 	}
 }
 
@@ -342,3 +337,10 @@
 		},
 	}
 }
+
+func newConverter(t reflect.Type, k pref.Kind) pvalue.Converter {
+	if legacyWrapper != nil {
+		return legacyWrapper.NewConverter(t, k)
+	}
+	return pvalue.NewConverter(t, k)
+}
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index 26f8db6..e1fc1ce 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -19,6 +19,7 @@
 	scalar "google.golang.org/protobuf/internal/scalar"
 	pvalue "google.golang.org/protobuf/internal/value"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 
 	proto2_20180125 "google.golang.org/protobuf/internal/testprotos/legacy/proto2.v1.0.0-20180125-92554152"
 	"google.golang.org/protobuf/types/descriptorpb"
@@ -189,8 +190,8 @@
 	MapBytes   map[MyString]MyBytes
 )
 
-var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto2Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto2)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "ScalarProto2",
 		Fields: []ptype.Field{
@@ -219,10 +220,10 @@
 			{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind, Default: V([]byte("22"))},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ScalarProto2)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ScalarProto2) Type() pref.MessageType { return scalarProto2Type.PBType }
@@ -304,8 +305,8 @@
 	MyBytesA  MyString  `protobuf:"22"`
 }
 
-var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var scalarProto3Type = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ScalarProto3)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto3,
 		FullName: "ScalarProto3",
 		Fields: []ptype.Field{
@@ -334,10 +335,10 @@
 			{Name: "f22", Number: 22, Cardinality: pref.Optional, Kind: pref.BytesKind},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ScalarProto3)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ScalarProto3) Type() pref.MessageType { return scalarProto3Type.PBType }
@@ -437,8 +438,8 @@
 	MyBytes4   ListStrings `protobuf:"19"`
 }
 
-var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var listScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(ListScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "ListScalars",
 		Fields: []ptype.Field{
@@ -465,10 +466,10 @@
 			{Name: "f19", Number: 19, Cardinality: pref.Repeated, Kind: pref.BytesKind},
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(ListScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *ListScalars) Type() pref.MessageType             { return listScalarsType.PBType }
@@ -628,8 +629,8 @@
 	}
 }
 
-var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var mapScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(MapScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "MapScalars",
 		Fields: []ptype.Field{
@@ -663,10 +664,10 @@
 			mustMakeMapEntry(25, pref.StringKind, pref.BytesKind),
 		},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(MapScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *MapScalars) Type() pref.MessageType             { return mapScalarsType.PBType }
@@ -807,8 +808,8 @@
 	Union isOneofScalars_Union `protobuf_oneof:"union"`
 }
 
-var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var oneofScalarsType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(OneofScalars)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "OneofScalars",
 		Fields: []ptype.Field{
@@ -828,10 +829,10 @@
 		},
 		Oneofs: []ptype.Oneof{{Name: "union"}},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(OneofScalars)
 	},
-)}
+}}
 
 // TODO: Remove this.
 func (m *OneofScalars) Type() pref.MessageType { return oneofScalarsType.PBType }
@@ -983,16 +984,16 @@
 
 type EnumProto2 int32
 
-var enumProto2Type = ptype.GoEnum(
-	mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto2Type = &prototype.Enum{
+	EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
 		Syntax:   pref.Proto2,
 		FullName: "EnumProto2",
 		Values:   []ptype.EnumValue{{Name: "DEAD", Number: 0xdead}, {Name: "BEEF", Number: 0xbeef}},
 	}),
-	func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+	NewEnum: func(n pref.EnumNumber) pref.Enum {
 		return EnumProto2(n)
 	},
-)
+}
 
 // TODO: Remove this.
 func (e EnumProto2) Type() pref.EnumType             { return enumProto2Type }
@@ -1002,16 +1003,16 @@
 
 type EnumProto3 int32
 
-var enumProto3Type = ptype.GoEnum(
-	mustMakeEnumDesc(ptype.StandaloneEnum{
+var enumProto3Type = &prototype.Enum{
+	EnumDescriptor: mustMakeEnumDesc(ptype.StandaloneEnum{
 		Syntax:   pref.Proto3,
 		FullName: "EnumProto3",
 		Values:   []ptype.EnumValue{{Name: "ALPHA", Number: 0}, {Name: "BRAVO", Number: 1}},
 	}),
-	func(_ pref.EnumType, n pref.EnumNumber) pref.Enum {
+	NewEnum: func(n pref.EnumNumber) pref.Enum {
 		return EnumProto3(n)
 	},
-)
+}
 
 // TODO: Remove this.
 func (e EnumProto3) Type() pref.EnumType             { return enumProto3Type }
@@ -1031,8 +1032,8 @@
 	Union         isEnumMessages_Union     `protobuf_oneof:"union"`
 }
 
-var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: ptype.GoMessage(
-	mustMakeMessageDesc(ptype.StandaloneMessage{
+var enumMessagesType = pimpl.MessageInfo{GoType: reflect.TypeOf(new(EnumMessages)), PBType: &prototype.Message{
+	MessageDescriptor: mustMakeMessageDesc(ptype.StandaloneMessage{
 		Syntax:   pref.Proto2,
 		FullName: "EnumMessages",
 		Fields: []ptype.Field{
@@ -1051,10 +1052,10 @@
 		},
 		Oneofs: []ptype.Oneof{{Name: "union"}},
 	}),
-	func(pref.MessageType) pref.Message {
+	NewMessage: func() pref.Message {
 		return new(EnumMessages)
 	},
-)}
+}}
 
 var enumMapDesc = mustMakeMessageDesc(ptype.StandaloneMessage{
 	Syntax:   pref.Proto2,
diff --git a/internal/legacy/enum.go b/internal/legacy/enum.go
index d750f1e..d38aa8c 100644
--- a/internal/legacy/enum.go
+++ b/internal/legacy/enum.go
@@ -13,6 +13,7 @@
 	ptype "google.golang.org/protobuf/internal/prototype"
 	pvalue "google.golang.org/protobuf/internal/value"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 )
 
 // wrapEnum wraps v as a protoreflect.Enum,
@@ -33,16 +34,20 @@
 	}
 
 	// Slow-path: derive enum descriptor and initialize EnumType.
+	var et pref.EnumType
 	var m sync.Map // map[protoreflect.EnumNumber]proto.Enum
 	ed := LoadEnumDesc(t)
-	et := ptype.GoEnum(ed, func(et pref.EnumType, n pref.EnumNumber) pref.Enum {
-		if e, ok := m.Load(n); ok {
-			return e.(pref.Enum)
-		}
-		e := &enumWrapper{num: n, pbTyp: et, goTyp: t}
-		m.Store(n, e)
-		return e
-	})
+	et = &prototype.Enum{
+		EnumDescriptor: ed,
+		NewEnum: func(n pref.EnumNumber) pref.Enum {
+			if e, ok := m.Load(n); ok {
+				return e.(pref.Enum)
+			}
+			e := &enumWrapper{num: n, pbTyp: et, goTyp: t}
+			m.Store(n, e)
+			return e
+		},
+	}
 	if et, ok := enumTypeCache.LoadOrStore(t, et); ok {
 		return et.(pref.EnumType)
 	}
diff --git a/internal/legacy/export.go b/internal/legacy/export.go
index ec87a68..701b578 100644
--- a/internal/legacy/export.go
+++ b/internal/legacy/export.go
@@ -5,6 +5,7 @@
 package legacy
 
 import (
+	"fmt"
 	"reflect"
 
 	pimpl "google.golang.org/protobuf/internal/impl"
@@ -17,8 +18,8 @@
 // functions that we do not want to appear in godoc.
 type Export struct{}
 
-func (Export) EnumOf(e interface{}) pvalue.LegacyEnum {
-	return wrapEnum(reflect.ValueOf(e)).(pvalue.LegacyEnum)
+func (Export) EnumOf(e interface{}) pref.Enum {
+	return wrapEnum(reflect.ValueOf(e))
 }
 
 func (Export) EnumTypeOf(e interface{}) pref.EnumType {
@@ -29,8 +30,8 @@
 	return LoadEnumDesc(reflect.TypeOf(e))
 }
 
-func (Export) MessageOf(m interface{}) pvalue.LegacyMessage {
-	return wrapMessage(reflect.ValueOf(m)).ProtoReflect().(pvalue.LegacyMessage)
+func (Export) MessageOf(m interface{}) pref.Message {
+	return wrapMessage(reflect.ValueOf(m)).ProtoReflect()
 }
 
 func (Export) MessageTypeOf(m interface{}) pref.MessageType {
@@ -49,6 +50,61 @@
 	return extensionTypeFromDesc(d)
 }
 
+var (
+	enumIfaceV2    = reflect.TypeOf((*pref.Enum)(nil)).Elem()
+	messageIfaceV1 = reflect.TypeOf((*piface.MessageV1)(nil)).Elem()
+	messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
+)
+
+func (Export) NewConverter(t reflect.Type, k pref.Kind) pvalue.Converter {
+	c, _ := newConverter(t, k)
+	return c
+}
+
+func newConverter(t reflect.Type, k pref.Kind) (pvalue.Converter, bool) {
+	switch k {
+	case pref.EnumKind:
+		if t.Kind() == reflect.Int32 && !t.Implements(enumIfaceV2) {
+			return pvalue.Converter{
+				PBValueOf: func(v reflect.Value) pref.Value {
+					if v.Type() != t {
+						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
+					}
+					return pref.ValueOf(pref.EnumNumber(v.Int()))
+				},
+				GoValueOf: func(v pref.Value) reflect.Value {
+					return reflect.ValueOf(v.Enum()).Convert(t)
+				},
+				NewEnum: func(n pref.EnumNumber) pref.Enum {
+					return wrapEnum(reflect.ValueOf(n).Convert(t))
+				},
+			}, true
+		}
+	case pref.MessageKind, pref.GroupKind:
+		if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) && !t.Implements(messageIfaceV2) {
+			return pvalue.Converter{
+				PBValueOf: func(v reflect.Value) pref.Value {
+					if v.Type() != t {
+						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
+					}
+					return pref.ValueOf(Export{}.MessageOf(v.Interface()))
+				},
+				GoValueOf: func(v pref.Value) reflect.Value {
+					rv := reflect.ValueOf(v.Message().(pvalue.Unwrapper).ProtoUnwrap())
+					if rv.Type() != t {
+						panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
+					}
+					return rv
+				},
+				NewMessage: func() pref.Message {
+					return wrapMessage(reflect.New(t.Elem())).ProtoReflect()
+				},
+			}, true
+		}
+	}
+	return pvalue.NewConverter(t, k), false
+}
+
 func init() {
 	pimpl.RegisterLegacyWrapper(Export{})
 }
diff --git a/internal/legacy/extension.go b/internal/legacy/extension.go
index 1f53f1b..c8cb3e9 100644
--- a/internal/legacy/extension.go
+++ b/internal/legacy/extension.go
@@ -16,6 +16,7 @@
 	pvalue "google.golang.org/protobuf/internal/value"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 	preg "google.golang.org/protobuf/reflect/protoregistry"
+	"google.golang.org/protobuf/reflect/prototype"
 	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
@@ -171,12 +172,19 @@
 	// Construct a v2 ExtensionType.
 	var ed pref.EnumDescriptor
 	var md pref.MessageDescriptor
-	conv := pvalue.NewLegacyConverter(t, f.Kind, Export{})
-	if conv.EnumType != nil {
-		ed = conv.EnumType.Descriptor()
-	}
-	if conv.MessageType != nil {
-		md = conv.MessageType.Descriptor()
+	switch f.Kind {
+	case pref.EnumKind:
+		if e, ok := reflect.Zero(t).Interface().(pref.Enum); ok {
+			ed = e.Descriptor()
+		} else {
+			ed = LoadEnumDesc(t)
+		}
+	case pref.MessageKind, pref.GroupKind:
+		if m, ok := reflect.Zero(t).Interface().(pref.ProtoMessage); ok {
+			md = m.ProtoReflect().Descriptor()
+		} else {
+			md = LoadMessageDesc(t)
+		}
 	}
 	xd, err := ptype.NewExtension(&ptype.StandaloneExtension{
 		FullName:     pref.FullName(d.Name),
@@ -207,17 +215,21 @@
 //
 // This is exported for testing purposes.
 func ExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
-	// Extension types for non-enums and non-messages are simple.
+	var conv pvalue.Converter
+	var isLegacy bool
+	xt := &prototype.Extension{ExtensionDescriptor: xd}
 	switch xd.Kind() {
-	case pref.EnumKind, pref.MessageKind, pref.GroupKind:
+	case pref.EnumKind:
+		conv, isLegacy = newConverter(t, xd.Kind())
+		xt.NewEnum = conv.NewEnum
+	case pref.MessageKind, pref.GroupKind:
+		conv, isLegacy = newConverter(t, xd.Kind())
+		xt.NewMessage = conv.NewMessage
 	default:
-		return ptype.GoExtension(xd, nil, nil)
+		// Extension types for non-enums and non-messages are simple.
+		return &prototype.Extension{ExtensionDescriptor: xd}
 	}
-
-	// Create an ExtensionType where GoType is the wrapper type.
-	conv := pvalue.NewLegacyConverter(t, xd.Kind(), Export{})
-	xt := ptype.GoExtension(xd, conv.EnumType, conv.MessageType)
-	if !conv.IsLegacy {
+	if !isLegacy {
 		return xt
 	}
 
diff --git a/internal/legacy/message.go b/internal/legacy/message.go
index 9919cdb..b17d705 100644
--- a/internal/legacy/message.go
+++ b/internal/legacy/message.go
@@ -15,6 +15,7 @@
 	pimpl "google.golang.org/protobuf/internal/impl"
 	ptype "google.golang.org/protobuf/internal/prototype"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/prototype"
 )
 
 // wrapMessage wraps v as a protoreflect.ProtoMessage,
@@ -38,10 +39,12 @@
 	md := LoadMessageDesc(t)
 	mt := new(pimpl.MessageInfo)
 	mt.GoType = t
-	mt.PBType = ptype.GoMessage(md, func(pref.MessageType) pref.Message {
-		p := reflect.New(t.Elem()).Interface()
-		return mt.MessageOf(p)
-	})
+	mt.PBType = &prototype.Message{
+		MessageDescriptor: md,
+		NewMessage: func() pref.Message {
+			return mt.MessageOf(reflect.New(t.Elem()).Interface())
+		},
+	}
 	if mt, ok := messageTypeCache.LoadOrStore(t, mt); ok {
 		return mt.(*pimpl.MessageInfo)
 	}
diff --git a/internal/prototype/go_type.go b/internal/prototype/go_type.go
deleted file mode 100644
index 16b49dc..0000000
--- a/internal/prototype/go_type.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2018 The Go 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 prototype
-
-import (
-	"fmt"
-	"reflect"
-	"sync"
-
-	"google.golang.org/protobuf/internal/descfmt"
-	"google.golang.org/protobuf/internal/value"
-	"google.golang.org/protobuf/reflect/protoreflect"
-)
-
-// GoEnum creates a new protoreflect.EnumType by combining the provided
-// protoreflect.EnumDescriptor with the provided constructor function.
-func GoEnum(ed protoreflect.EnumDescriptor, fn func(protoreflect.EnumType, protoreflect.EnumNumber) protoreflect.Enum) protoreflect.EnumType {
-	if ed.IsPlaceholder() {
-		panic("enum descriptor must not be a placeholder")
-	}
-	return &goEnum{EnumDescriptor: ed, new: fn}
-}
-
-type goEnum struct {
-	protoreflect.EnumDescriptor
-	new func(protoreflect.EnumType, protoreflect.EnumNumber) protoreflect.Enum
-
-	once sync.Once
-	typ  reflect.Type
-}
-
-func (t *goEnum) Descriptor() protoreflect.EnumDescriptor {
-	return t.EnumDescriptor
-}
-func (t *goEnum) GoType() reflect.Type {
-	t.New(0) // initialize t.typ
-	return t.typ
-}
-func (t *goEnum) New(n protoreflect.EnumNumber) protoreflect.Enum {
-	e := t.new(t, n)
-	t.once.Do(func() { t.typ = reflect.TypeOf(e) })
-	if t.typ != reflect.TypeOf(e) {
-		panic(fmt.Sprintf("mismatching types for enum: got %T, want %v", e, t.typ))
-	}
-	return e
-}
-func (t *goEnum) Format(s fmt.State, r rune) {
-	descfmt.FormatDesc(s, r, t)
-}
-
-// GoMessage creates a new protoreflect.MessageType by combining the provided
-// protoreflect.MessageDescriptor with the provided constructor function.
-func GoMessage(md protoreflect.MessageDescriptor, fn func(protoreflect.MessageType) protoreflect.Message) protoreflect.MessageType {
-	if md.IsPlaceholder() {
-		panic("message descriptor must not be a placeholder")
-	}
-	// NOTE: Avoid calling fn in the constructor since fn itself may depend on
-	// this function returning (for cyclic message dependencies).
-	return &goMessage{MessageDescriptor: md, new: fn}
-}
-
-type goMessage struct {
-	protoreflect.MessageDescriptor
-	new func(protoreflect.MessageType) protoreflect.Message
-
-	once sync.Once
-	typ  reflect.Type
-}
-
-func (t *goMessage) Descriptor() protoreflect.MessageDescriptor {
-	return t.MessageDescriptor
-}
-func (t *goMessage) GoType() reflect.Type {
-	t.New() // initialize t.typ
-	return t.typ
-}
-func (t *goMessage) New() protoreflect.Message {
-	m := t.new(t)
-	mi := m.Interface()
-	t.once.Do(func() { t.typ = reflect.TypeOf(mi) })
-	if t.typ != reflect.TypeOf(mi) {
-		panic(fmt.Sprintf("mismatching types for message: got %T, want %v", mi, t.typ))
-	}
-	return m
-}
-func (t *goMessage) Format(s fmt.State, r rune) {
-	descfmt.FormatDesc(s, r, t)
-}
-
-// GoExtension creates a new protoreflect.ExtensionType.
-//
-// An enum type must be provided for enum extension fields if
-// ExtensionDescriptor.EnumType does not implement protoreflect.EnumType,
-// in which case it replaces the original enum in ExtensionDescriptor.
-//
-// Similarly, a message type must be provided for message extension fields if
-// ExtensionDescriptor.MessageType does not implement protoreflect.MessageType,
-// in which case it replaces the original message in ExtensionDescriptor.
-//
-// The Go type is currently determined automatically.
-// The type is T for scalars and *[]T for lists (maps are not allowed).
-// The type T is determined as follows:
-//
-//	+------------+-------------------------------------+
-//	| Go type    | Protobuf kind                       |
-//	+------------+-------------------------------------+
-//	| bool       | BoolKind                            |
-//	| int32      | Int32Kind, Sint32Kind, Sfixed32Kind |
-//	| int64      | Int64Kind, Sint64Kind, Sfixed64Kind |
-//	| uint32     | Uint32Kind, Fixed32Kind             |
-//	| uint64     | Uint64Kind, Fixed64Kind             |
-//	| float32    | FloatKind                           |
-//	| float64    | DoubleKind                          |
-//	| string     | StringKind                          |
-//	| []byte     | BytesKind                           |
-//	| E          | EnumKind                            |
-//	| M          | MessageKind, GroupKind              |
-//	+------------+-------------------------------------+
-//
-// The type E is the concrete enum type returned by NewEnum,
-// which is often, but not required to be, a named int32 type.
-// The type M is the concrete message type returned by NewMessage,
-// which is often, but not required to be, a pointer to a named struct type.
-func GoExtension(xd protoreflect.ExtensionDescriptor, et protoreflect.EnumType, mt protoreflect.MessageType) protoreflect.ExtensionType {
-	if !xd.IsExtension() {
-		panic("field descriptor does not extend a message")
-	}
-	switch xd.Kind() {
-	case protoreflect.EnumKind:
-		if et2, ok := xd.Enum().(protoreflect.EnumType); ok && et == nil {
-			et = et2
-		}
-		if et == nil {
-			panic("enum type not provided for enum kind")
-		}
-		if mt != nil {
-			panic("message type provided for enum kind")
-		}
-	case protoreflect.MessageKind, protoreflect.GroupKind:
-		if mt2, ok := xd.Message().(protoreflect.MessageType); ok && mt == nil {
-			mt = mt2
-		}
-		if et != nil {
-			panic("enum type provided for message kind")
-		}
-		if mt == nil {
-			panic("message type not provided for message kind")
-		}
-	default:
-		if et != nil || mt != nil {
-			panic(fmt.Sprintf("enum or message type provided for %v kind", xd.Kind()))
-		}
-	}
-	return &goExtension{ExtensionDescriptor: xd, enumType: et, messageType: mt}
-}
-
-type goExtension struct {
-	protoreflect.ExtensionDescriptor
-	enumType    protoreflect.EnumType
-	messageType protoreflect.MessageType
-
-	once        sync.Once
-	typ         reflect.Type
-	new         func() protoreflect.Value
-	valueOf     func(v interface{}) protoreflect.Value
-	interfaceOf func(v protoreflect.Value) interface{}
-}
-
-func (t *goExtension) Descriptor() protoreflect.ExtensionDescriptor {
-	return t.ExtensionDescriptor
-}
-func (t *goExtension) EnumType() protoreflect.EnumDescriptor {
-	if t.enumType == nil {
-		return nil
-	}
-	return t.enumType.Descriptor()
-}
-func (t *goExtension) MessageType() protoreflect.MessageDescriptor {
-	if t.messageType == nil {
-		return nil
-	}
-	return t.messageType.Descriptor()
-}
-func (t *goExtension) GoType() reflect.Type {
-	t.lazyInit()
-	return t.typ
-}
-func (t *goExtension) New() protoreflect.Value {
-	t.lazyInit()
-	pv := t.new()
-	v := t.interfaceOf(pv)
-	if reflect.TypeOf(v) != t.typ {
-		panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.typ))
-	}
-	return pv
-}
-func (t *goExtension) ValueOf(v interface{}) protoreflect.Value {
-	t.lazyInit()
-	if reflect.TypeOf(v) != t.typ {
-		panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.typ))
-	}
-	return t.valueOf(v)
-}
-func (t *goExtension) InterfaceOf(pv protoreflect.Value) interface{} {
-	t.lazyInit()
-	v := t.interfaceOf(pv)
-	if reflect.TypeOf(v) != t.typ {
-		panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.typ))
-	}
-	return v
-}
-func (t *goExtension) Format(s fmt.State, r rune) {
-	descfmt.FormatDesc(s, r, t)
-}
-func (t *goExtension) lazyInit() {
-	t.once.Do(func() {
-		switch t.Cardinality() {
-		case protoreflect.Optional:
-			switch t.Kind() {
-			case protoreflect.EnumKind:
-				t.typ = t.enumType.GoType()
-				t.new = func() protoreflect.Value {
-					return t.Default()
-				}
-				t.valueOf = func(v interface{}) protoreflect.Value {
-					ev := v.(protoreflect.Enum)
-					return protoreflect.ValueOf(ev.Number())
-				}
-				t.interfaceOf = func(pv protoreflect.Value) interface{} {
-					return t.enumType.New(pv.Enum())
-				}
-			case protoreflect.MessageKind, protoreflect.GroupKind:
-				t.typ = t.messageType.GoType()
-				t.new = func() protoreflect.Value {
-					return protoreflect.ValueOf(t.messageType.New())
-				}
-				t.valueOf = func(v interface{}) protoreflect.Value {
-					mv := v.(protoreflect.ProtoMessage).ProtoReflect()
-					return protoreflect.ValueOf(mv)
-				}
-				t.interfaceOf = func(pv protoreflect.Value) interface{} {
-					return pv.Message().Interface()
-				}
-			default:
-				t.typ = goTypeForPBKind[t.Kind()]
-				t.new = func() protoreflect.Value {
-					return t.Default()
-				}
-				t.valueOf = func(v interface{}) protoreflect.Value {
-					return protoreflect.ValueOf(v)
-				}
-				t.interfaceOf = func(pv protoreflect.Value) interface{} {
-					return pv.Interface()
-				}
-			}
-		case protoreflect.Repeated:
-			var typ reflect.Type
-			var c value.Converter
-			switch t.Kind() {
-			case protoreflect.EnumKind:
-				typ = t.enumType.GoType()
-				c = value.NewEnumConverter(t.enumType)
-			case protoreflect.MessageKind, protoreflect.GroupKind:
-				typ = t.messageType.GoType()
-				c = value.NewMessageConverter(t.messageType)
-			default:
-				typ = goTypeForPBKind[t.Kind()]
-				c = value.NewConverter(typ, t.Kind())
-			}
-			t.typ = reflect.PtrTo(reflect.SliceOf(typ))
-			t.new = func() protoreflect.Value {
-				v := reflect.New(t.typ.Elem()).Interface()
-				return protoreflect.ValueOf(value.ListOf(v, c))
-			}
-			t.valueOf = func(v interface{}) protoreflect.Value {
-				return protoreflect.ValueOf(value.ListOf(v, c))
-			}
-			t.interfaceOf = func(pv protoreflect.Value) interface{} {
-				return pv.List().(value.Unwrapper).ProtoUnwrap()
-			}
-		default:
-			panic(fmt.Sprintf("invalid cardinality: %v", t.Cardinality()))
-		}
-	})
-}
-
-var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
-	protoreflect.BoolKind:     reflect.TypeOf(bool(false)),
-	protoreflect.Int32Kind:    reflect.TypeOf(int32(0)),
-	protoreflect.Sint32Kind:   reflect.TypeOf(int32(0)),
-	protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)),
-	protoreflect.Int64Kind:    reflect.TypeOf(int64(0)),
-	protoreflect.Sint64Kind:   reflect.TypeOf(int64(0)),
-	protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)),
-	protoreflect.Uint32Kind:   reflect.TypeOf(uint32(0)),
-	protoreflect.Fixed32Kind:  reflect.TypeOf(uint32(0)),
-	protoreflect.Uint64Kind:   reflect.TypeOf(uint64(0)),
-	protoreflect.Fixed64Kind:  reflect.TypeOf(uint64(0)),
-	protoreflect.FloatKind:    reflect.TypeOf(float32(0)),
-	protoreflect.DoubleKind:   reflect.TypeOf(float64(0)),
-	protoreflect.StringKind:   reflect.TypeOf(string("")),
-	protoreflect.BytesKind:    reflect.TypeOf([]byte(nil)),
-}
diff --git a/internal/prototype/go_type_test.go b/internal/prototype/go_type_test.go
deleted file mode 100644
index 7200afe..0000000
--- a/internal/prototype/go_type_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package prototype_test
-
-import (
-	"fmt"
-	"reflect"
-	"testing"
-
-	"google.golang.org/protobuf/internal/prototype"
-	"google.golang.org/protobuf/reflect/protoreflect"
-	"google.golang.org/protobuf/reflect/protoregistry"
-
-	testpb "google.golang.org/protobuf/internal/testprotos/test"
-)
-
-func TestGoEnum(t *testing.T) {
-	enumDescs := []protoreflect.EnumDescriptor{
-		testpb.ForeignEnum(0).Descriptor(),
-		testpb.TestAllTypes_NestedEnum(0).Descriptor(),
-	}
-	for _, ed := range enumDescs {
-		et := prototype.GoEnum(ed, newEnum)
-		if gotED := et.Descriptor(); gotED != ed {
-			fmt.Errorf("GoEnum(ed (%v), newEnum).Descriptor() != ed", ed.FullName())
-		}
-		e := et.New(0)
-		if gotED := e.Descriptor(); gotED != ed {
-			fmt.Errorf("GoEnum(ed (%v), newEnum).New(0).Descriptor() != ed", ed.FullName())
-		}
-		if n := e.Number(); n != 0 {
-			fmt.Errorf("GoEnum(ed (%v), newEnum).New(0).Number() = %v; want 0", ed.FullName(), n)
-		}
-		if _, ok := e.(fakeEnum); !ok {
-			fmt.Errorf("GoEnum(ed (%v), newEnum).New(0) type is %T; want fakeEnum", ed.FullName(), e)
-		}
-	}
-}
-
-func TestGoMessage(t *testing.T) {
-	msgDescs := []protoreflect.MessageDescriptor{
-		((*testpb.TestAllTypes)(nil)).ProtoReflect().Descriptor(),
-		((*testpb.TestAllTypes_NestedMessage)(nil)).ProtoReflect().Descriptor(),
-	}
-	for _, md := range msgDescs {
-		mt := prototype.GoMessage(md, newMessage)
-		if gotMD := mt.Descriptor(); gotMD != md {
-			fmt.Errorf("GoMessage(md (%v), newMessage).Descriptor() != md", md.FullName())
-		}
-		m := mt.New()
-		if gotMD := m.Descriptor(); gotMD != md {
-			fmt.Errorf("GoMessage(md (%v), newMessage).New().Descriptor() != md", md.FullName())
-		}
-		if _, ok := m.(*fakeMessage); !ok {
-			fmt.Errorf("GoMessage(md (%v), newMessage).New() type is %T; want *fakeMessage", md.FullName(), m)
-		}
-	}
-}
-
-func TestGoExtension(t *testing.T) {
-	testCases := []struct {
-		extName     protoreflect.FullName
-		wantNewType reflect.Type
-	}{{
-		extName:     "goproto.proto.test.optional_int32_extension",
-		wantNewType: reflect.TypeOf(int32(0)),
-	}, {
-		extName:     "goproto.proto.test.optional_string_extension",
-		wantNewType: reflect.TypeOf(""),
-	}, {
-		extName:     "goproto.proto.test.repeated_int32_extension",
-		wantNewType: reflect.TypeOf((*[]int32)(nil)),
-	}, {
-		extName:     "goproto.proto.test.repeated_string_extension",
-		wantNewType: reflect.TypeOf((*[]string)(nil)),
-	}, {
-		extName:     "goproto.proto.test.repeated_string_extension",
-		wantNewType: reflect.TypeOf((*[]string)(nil)),
-	}, {
-		extName:     "goproto.proto.test.optional_nested_enum_extension",
-		wantNewType: reflect.TypeOf((*fakeEnum)(nil)).Elem(),
-	}, {
-		extName:     "goproto.proto.test.optional_nested_message_extension",
-		wantNewType: reflect.TypeOf((*fakeMessageImpl)(nil)),
-	}, {
-		extName:     "goproto.proto.test.repeated_nested_enum_extension",
-		wantNewType: reflect.TypeOf((*[]fakeEnum)(nil)),
-	}, {
-		extName:     "goproto.proto.test.repeated_nested_message_extension",
-		wantNewType: reflect.TypeOf((*[]*fakeMessageImpl)(nil)),
-	}}
-	for _, tc := range testCases {
-		xd, err := protoregistry.GlobalFiles.FindExtensionByName(tc.extName)
-		if err != nil {
-			t.Errorf("GlobalFiles.FindExtensionByName(%q) = _, %v; want _, <nil>", tc.extName, err)
-			continue
-		}
-		var et protoreflect.EnumType
-		if ed := xd.Enum(); ed != nil {
-			et = prototype.GoEnum(ed, newEnum)
-		}
-		var mt protoreflect.MessageType
-		if md := xd.Message(); md != nil {
-			mt = prototype.GoMessage(md, newMessage)
-		}
-		xt := prototype.GoExtension(xd, et, mt)
-		v := xt.InterfaceOf(xt.New())
-		if typ := reflect.TypeOf(v); typ != tc.wantNewType {
-			t.Errorf("GoExtension(xd (%v), et, mt).New() type unwraps to %v; want %v", tc.extName, typ, tc.wantNewType)
-		}
-	}
-}
-
-type fakeMessage struct {
-	imp *fakeMessageImpl
-	protoreflect.Message
-}
-
-func (m *fakeMessage) Type() protoreflect.MessageType             { return m.imp.typ }
-func (m *fakeMessage) Descriptor() protoreflect.MessageDescriptor { return m.imp.typ.Descriptor() }
-func (m *fakeMessage) Interface() protoreflect.ProtoMessage       { return m.imp }
-
-type fakeMessageImpl struct{ typ protoreflect.MessageType }
-
-func (m *fakeMessageImpl) ProtoReflect() protoreflect.Message { return &fakeMessage{imp: m} }
-
-func newMessage(typ protoreflect.MessageType) protoreflect.Message {
-	return (&fakeMessageImpl{typ: typ}).ProtoReflect()
-}
-
-type fakeEnum struct {
-	typ protoreflect.EnumType
-	num protoreflect.EnumNumber
-}
-
-func (e fakeEnum) Descriptor() protoreflect.EnumDescriptor { return e.typ.Descriptor() }
-func (e fakeEnum) Type() protoreflect.EnumType             { return e.typ }
-func (e fakeEnum) Number() protoreflect.EnumNumber         { return e.num }
-
-func newEnum(typ protoreflect.EnumType, num protoreflect.EnumNumber) protoreflect.Enum {
-	return fakeEnum{
-		typ: typ,
-		num: num,
-	}
-}
diff --git a/internal/value/convert.go b/internal/value/convert.go
index 35de822..eb8d570 100644
--- a/internal/value/convert.go
+++ b/internal/value/convert.go
@@ -11,7 +11,6 @@
 	"reflect"
 
 	pref "google.golang.org/protobuf/reflect/protoreflect"
-	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
 // Unwrapper unwraps the value to the underlying value.
@@ -32,103 +31,60 @@
 	bytesType   = reflect.TypeOf([]byte(nil))
 
 	enumIfaceV2    = reflect.TypeOf((*pref.Enum)(nil)).Elem()
-	messageIfaceV1 = reflect.TypeOf((*piface.MessageV1)(nil)).Elem()
 	messageIfaceV2 = reflect.TypeOf((*pref.ProtoMessage)(nil)).Elem()
 
 	byteType = reflect.TypeOf(byte(0))
 )
 
 // NewConverter matches a Go type with a protobuf kind and returns a Converter
-// that converts between the two. NewConverter panics if it unable to provide a
-// conversion between the two. The Converter methods also panic when they are
-// called on incorrect Go types.
+// that converts between the two. Enums must be a named int32 kind that
+// implements protoreflect.Enum, and messages must be pointer to a named
+// struct type that implements protoreflect.ProtoMessage.
 //
 // This matcher deliberately supports a wider range of Go types than what
 // protoc-gen-go historically generated to be able to automatically wrap some
 // v1 messages generated by other forks of protoc-gen-go.
 func NewConverter(t reflect.Type, k pref.Kind) Converter {
-	return NewLegacyConverter(t, k, nil)
-}
-
-// LegacyWrapper is a set of wrapper methods that wraps legacy v1 Go types
-// to implement the v2 reflection APIs.
-type (
-	LegacyWrapper interface {
-		EnumOf(interface{}) LegacyEnum
-		EnumTypeOf(interface{}) pref.EnumType
-		EnumDescriptorOf(interface{}) pref.EnumDescriptor
-
-		MessageOf(interface{}) LegacyMessage
-		MessageTypeOf(interface{}) pref.MessageType
-		MessageDescriptorOf(interface{}) pref.MessageDescriptor
-
-		// TODO: Remove these eventually.
-		// See the TODOs in internal/impl/legacy_extension.go.
-		ExtensionDescFromType(pref.ExtensionType) *piface.ExtensionDescV1
-		ExtensionTypeFromDesc(*piface.ExtensionDescV1) pref.ExtensionType
-	}
-
-	LegacyEnum = interface {
-		pref.Enum
-		ProtoUnwrap() interface{}
-	}
-
-	LegacyMessage = interface {
-		pref.Message
-		ProtoUnwrap() interface{}
-	}
-)
-
-// NewLegacyConverter is identical to NewConverter,
-// but supports wrapping legacy v1 messages to implement the v2 message API
-// using the provided LegacyWrapper.
-func NewLegacyConverter(t reflect.Type, k pref.Kind, w LegacyWrapper) Converter {
 	switch k {
 	case pref.BoolKind:
 		if t.Kind() == reflect.Bool {
-			return makeScalarConverter(t, boolType)
+			return newScalarConverter(t, boolType)
 		}
 	case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
 		if t.Kind() == reflect.Int32 {
-			return makeScalarConverter(t, int32Type)
+			return newScalarConverter(t, int32Type)
 		}
 	case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
 		if t.Kind() == reflect.Int64 {
-			return makeScalarConverter(t, int64Type)
+			return newScalarConverter(t, int64Type)
 		}
 	case pref.Uint32Kind, pref.Fixed32Kind:
 		if t.Kind() == reflect.Uint32 {
-			return makeScalarConverter(t, uint32Type)
+			return newScalarConverter(t, uint32Type)
 		}
 	case pref.Uint64Kind, pref.Fixed64Kind:
 		if t.Kind() == reflect.Uint64 {
-			return makeScalarConverter(t, uint64Type)
+			return newScalarConverter(t, uint64Type)
 		}
 	case pref.FloatKind:
 		if t.Kind() == reflect.Float32 {
-			return makeScalarConverter(t, float32Type)
+			return newScalarConverter(t, float32Type)
 		}
 	case pref.DoubleKind:
 		if t.Kind() == reflect.Float64 {
-			return makeScalarConverter(t, float64Type)
+			return newScalarConverter(t, float64Type)
 		}
 	case pref.StringKind:
 		if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
-			return makeScalarConverter(t, stringType)
+			return newScalarConverter(t, stringType)
 		}
 	case pref.BytesKind:
 		if t.Kind() == reflect.String || (t.Kind() == reflect.Slice && t.Elem() == byteType) {
-			return makeScalarConverter(t, bytesType)
+			return newScalarConverter(t, bytesType)
 		}
 	case pref.EnumKind:
 		// Handle enums, which must be a named int32 type.
-		if t.PkgPath() != "" && t.Kind() == reflect.Int32 {
-			var et pref.EnumType
-			if t.Implements(enumIfaceV2) {
-				et = &enumType{reflect.Zero(t).Interface().(pref.Enum).Descriptor(), t}
-			} else {
-				et = w.EnumTypeOf(reflect.Zero(t).Interface())
-			}
+		if t.Implements(enumIfaceV2) && t.Kind() == reflect.Int32 {
 			return Converter{
 				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
@@ -139,44 +95,38 @@
 				GoValueOf: func(v pref.Value) reflect.Value {
 					return reflect.ValueOf(v.Enum()).Convert(t)
 				},
-				EnumType: et,
-				IsLegacy: !t.Implements(enumIfaceV2),
+				NewEnum: func(n pref.EnumNumber) pref.Enum {
+					return reflect.ValueOf(n).Convert(t).Interface().(pref.Enum)
+				},
 			}
 		}
 	case pref.MessageKind, pref.GroupKind:
 		// Handle v2 messages, which must satisfy the proto.Message interface.
-		if t.Kind() == reflect.Ptr && t.Implements(messageIfaceV2) {
-			md := reflect.Zero(t).Interface().(pref.ProtoMessage).ProtoReflect().Descriptor()
-			mt := &messageType{md, t}
-			return NewMessageConverter(mt)
-		}
-
-		// Handle v1 messages, which we need to wrap as a v2 message.
-		if w != nil && t.Kind() == reflect.Ptr && t.Implements(messageIfaceV1) {
-			mt := w.MessageTypeOf(reflect.Zero(t).Interface())
+		if t.Implements(messageIfaceV2) && t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
 			return Converter{
 				PBValueOf: func(v reflect.Value) pref.Value {
 					if v.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
 					}
-					return pref.ValueOf(w.MessageOf(v.Interface()))
+					return pref.ValueOf(v.Interface().(pref.ProtoMessage).ProtoReflect())
 				},
 				GoValueOf: func(v pref.Value) reflect.Value {
-					rv := reflect.ValueOf(v.Message().(Unwrapper).ProtoUnwrap())
+					rv := reflect.ValueOf(v.Message().Interface())
 					if rv.Type() != t {
 						panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
 					}
 					return rv
 				},
-				MessageType: mt,
-				IsLegacy:    true,
+				NewMessage: func() pref.Message {
+					return reflect.New(t.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+				},
 			}
 		}
 	}
 	panic(fmt.Sprintf("invalid Go type %v for protobuf kind %v", t, k))
 }
 
-func makeScalarConverter(goType, pbType reflect.Type) Converter {
+func newScalarConverter(goType, pbType reflect.Type) Converter {
 	return Converter{
 		PBValueOf: func(v reflect.Value) pref.Value {
 			if v.Type() != goType {
@@ -200,83 +150,11 @@
 	}
 }
 
-// NewEnumConverter returns a converter for an EnumType, whose GoType must implement protoreflect.Enum.
-func NewEnumConverter(et pref.EnumType) Converter {
-	t := et.GoType()
-	if !t.Implements(enumIfaceV2) {
-		panic(fmt.Sprintf("invalid type: %v does not implement %v", t, enumIfaceV2))
-	}
-	return Converter{
-		PBValueOf: func(v reflect.Value) pref.Value {
-			if v.Type() != t {
-				panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
-			}
-			e := v.Interface().(pref.Enum)
-			return pref.ValueOf(e.Number())
-		},
-		GoValueOf: func(v pref.Value) reflect.Value {
-			return reflect.ValueOf(et.New(v.Enum()))
-		},
-		EnumType: et,
-	}
-}
-
-// NewMessageConverter returns a converter for a MessageType, whose GoType must implement protoreflect.ProtoMessage.
-func NewMessageConverter(mt pref.MessageType) Converter {
-	t := mt.GoType()
-	if !t.Implements(messageIfaceV2) {
-		panic(fmt.Sprintf("invalid type: %v does not implement %v", t, messageIfaceV2))
-	}
-	return Converter{
-		PBValueOf: func(v reflect.Value) pref.Value {
-			if v.Type() != t {
-				panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), t))
-			}
-			return pref.ValueOf(v.Interface().(pref.ProtoMessage).ProtoReflect())
-		},
-		GoValueOf: func(v pref.Value) reflect.Value {
-			rv := reflect.ValueOf(v.Message().Interface())
-			if rv.Type() != t {
-				panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), t))
-			}
-			return rv
-		},
-		MessageType: mt,
-	}
-}
-
 // Converter provides functions for converting to/from Go reflect.Value types
 // and protobuf protoreflect.Value types.
 type Converter struct {
-	PBValueOf   func(reflect.Value) pref.Value
-	GoValueOf   func(pref.Value) reflect.Value
-	EnumType    pref.EnumType
-	MessageType pref.MessageType
-	IsLegacy    bool
-}
-
-// TODO: This needs to be centralized in a package.
-type enumType struct {
-	// TODO: Remove me as an embedded field.
-	pref.EnumDescriptor
-	typ reflect.Type // must implement protoreflect.Enum
-}
-
-func (t *enumType) Descriptor() pref.EnumDescriptor { return t.EnumDescriptor }
-func (t *enumType) GoType() reflect.Type            { return t.typ }
-func (t *enumType) New(n pref.EnumNumber) pref.Enum {
-	return reflect.ValueOf(n).Convert(t.typ).Interface().(pref.Enum)
-}
-
-// TODO: This needs to be centralized in a package.
-type messageType struct {
-	// TODO: Remove me as an embedded field.
-	pref.MessageDescriptor
-	typ reflect.Type // must implement protoreflect.ProtoMessage
-}
-
-func (t *messageType) Descriptor() pref.MessageDescriptor { return t.MessageDescriptor }
-func (t *messageType) GoType() reflect.Type               { return t.typ }
-func (t *messageType) New() pref.Message {
-	return reflect.New(t.typ.Elem()).Interface().(pref.ProtoMessage).ProtoReflect()
+	PBValueOf  func(reflect.Value) pref.Value
+	GoValueOf  func(pref.Value) reflect.Value
+	NewEnum    func(pref.EnumNumber) pref.Enum
+	NewMessage func() pref.Message
 }
diff --git a/internal/value/list.go b/internal/value/list.go
index f40a73f..5181f4f 100644
--- a/internal/value/list.go
+++ b/internal/value/list.go
@@ -43,7 +43,7 @@
 	ls.v.Elem().Set(ls.v.Elem().Slice(0, i))
 }
 func (ls listReflect) NewMessage() pref.Message {
-	return ls.conv.MessageType.New()
+	return ls.conv.NewMessage()
 }
 func (ls listReflect) ProtoUnwrap() interface{} {
 	return ls.v.Interface()
diff --git a/internal/value/map.go b/internal/value/map.go
index 3461d2e..9fc7358 100644
--- a/internal/value/map.go
+++ b/internal/value/map.go
@@ -77,7 +77,7 @@
 	}
 }
 func (ms mapReflect) NewMessage() pref.Message {
-	return ms.valConv.MessageType.New()
+	return ms.valConv.NewMessage()
 }
 func (ms mapReflect) ProtoUnwrap() interface{} {
 	return ms.v.Interface()
diff --git a/reflect/prototype/type.go b/reflect/prototype/type.go
new file mode 100644
index 0000000..0766b4b
--- /dev/null
+++ b/reflect/prototype/type.go
@@ -0,0 +1,358 @@
+// Copyright 2019 The Go 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 prototype provides constructors for protoreflect.EnumType,
+// protoreflect.MessageType, and protoreflect.ExtensionType.
+package prototype
+
+import (
+	"fmt"
+	"reflect"
+	"sync"
+
+	"google.golang.org/protobuf/internal/descfmt"
+	"google.golang.org/protobuf/internal/value"
+	"google.golang.org/protobuf/reflect/protoreflect"
+)
+
+// Enum is a protoreflect.EnumType which combines a
+// protoreflect.EnumDescriptor with a constructor function.
+//
+// Both EnumDescriptor and NewEnum must be populated.
+// Once constructed, the exported fields must not be modified.
+type Enum struct {
+	protoreflect.EnumDescriptor
+
+	// NewEnum constructs a new protoreflect.Enum representing the provided
+	// enum number. The returned Go type must be identical for every call.
+	NewEnum func(protoreflect.EnumNumber) protoreflect.Enum
+
+	once   sync.Once
+	goType reflect.Type
+}
+
+func (t *Enum) New(n protoreflect.EnumNumber) protoreflect.Enum {
+	e := t.NewEnum(n)
+	t.once.Do(func() {
+		t.goType = reflect.TypeOf(e)
+		if e.Descriptor() != t.Descriptor() {
+			panic(fmt.Sprintf("mismatching enum descriptor: got %v, want %v", e.Descriptor(), t.Descriptor()))
+		}
+		if e.Descriptor().IsPlaceholder() {
+			panic("enum descriptor must not be a placeholder")
+		}
+	})
+	if t.goType != reflect.TypeOf(e) {
+		panic(fmt.Sprintf("mismatching types for enum: got %T, want %v", e, t.goType))
+	}
+	return e
+}
+
+func (t *Enum) GoType() reflect.Type {
+	t.New(0) // initialize t.typ
+	return t.goType
+}
+
+func (t *Enum) Descriptor() protoreflect.EnumDescriptor {
+	return t.EnumDescriptor
+}
+
+func (t *Enum) Format(s fmt.State, r rune) {
+	descfmt.FormatDesc(s, r, t)
+}
+
+// Message is a protoreflect.MessageType which combines a
+// protoreflect.MessageDescriptor with a constructor function.
+//
+// Both MessageDescriptor and NewMessage must be populated.
+// Once constructed, the exported fields must not be modified.
+type Message struct {
+	protoreflect.MessageDescriptor
+
+	// NewMessage constructs an empty, newly allocated protoreflect.Message.
+	// The returned Go type must be identical for every call.
+	NewMessage func() protoreflect.Message
+
+	once   sync.Once
+	goType reflect.Type
+}
+
+func (t *Message) New() protoreflect.Message {
+	m := t.NewMessage()
+	mi := m.Interface()
+	t.once.Do(func() {
+		t.goType = reflect.TypeOf(mi)
+		if m.Descriptor() != t.Descriptor() {
+			panic(fmt.Sprintf("mismatching message descriptor: got %v, want %v", m.Descriptor(), t.Descriptor()))
+		}
+		if m.Descriptor().IsPlaceholder() {
+			panic("message descriptor must not be a placeholder")
+		}
+	})
+	if t.goType != reflect.TypeOf(mi) {
+		panic(fmt.Sprintf("mismatching types for message: got %T, want %v", mi, t.goType))
+	}
+	return m
+}
+
+func (t *Message) GoType() reflect.Type {
+	t.New() // initialize t.goType
+	return t.goType
+}
+
+func (t *Message) Descriptor() protoreflect.MessageDescriptor {
+	return t.MessageDescriptor
+}
+
+func (t *Message) Format(s fmt.State, r rune) {
+	descfmt.FormatDesc(s, r, t)
+}
+
+// Extension is a protoreflect.ExtensionType which combines a
+// protoreflect.ExtensionDescriptor with a constructor function.
+//
+// ExtensionDescriptor must be populated, while NewEnum or NewMessage must
+// populated depending on the kind of the extension field.
+// Once constructed, the exported fields must not be modified.
+type Extension struct {
+	protoreflect.ExtensionDescriptor
+
+	// NewEnum constructs a new enum (see Enum.NewEnum).
+	// This must be populated if and only if ExtensionDescriptor.Kind
+	// is a protoreflect.EnumKind.
+	NewEnum func(protoreflect.EnumNumber) protoreflect.Enum
+
+	// NewMessage constructs a new message (see Enum.NewMessage).
+	// This must be populated if and only if ExtensionDescriptor.Kind
+	// is a protoreflect.MessageKind or protoreflect.GroupKind.
+	NewMessage func() protoreflect.Message
+
+	// TODO: Allow users to manually set new, valueOf, and interfaceOf.
+	// This allows users to implement custom composite types (e.g., List) or
+	// custom Go types for primitives (e.g., int32).
+
+	once        sync.Once
+	goType      reflect.Type
+	new         func() protoreflect.Value
+	valueOf     func(v interface{}) protoreflect.Value
+	interfaceOf func(v protoreflect.Value) interface{}
+}
+
+func (t *Extension) New() protoreflect.Value {
+	t.lazyInit()
+	pv := t.new()
+	v := t.interfaceOf(pv)
+	if reflect.TypeOf(v) != t.goType {
+		panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
+	}
+	return pv
+}
+
+func (t *Extension) ValueOf(v interface{}) protoreflect.Value {
+	t.lazyInit()
+	if reflect.TypeOf(v) != t.goType {
+		panic(fmt.Sprintf("invalid type: got %T, want %v", v, t.goType))
+	}
+	return t.valueOf(v)
+}
+
+func (t *Extension) InterfaceOf(v protoreflect.Value) interface{} {
+	t.lazyInit()
+	vi := t.interfaceOf(v)
+	if reflect.TypeOf(vi) != t.goType {
+		panic(fmt.Sprintf("invalid type: got %T, want %v", vi, t.goType))
+	}
+	return vi
+}
+
+// GoType is the type of the extension field.
+// The type is T for scalars and *[]T for lists (maps are not allowed).
+// The type T is determined as follows:
+//
+//	+------------+-------------------------------------+
+//	| Go type    | Protobuf kind                       |
+//	+------------+-------------------------------------+
+//	| bool       | BoolKind                            |
+//	| int32      | Int32Kind, Sint32Kind, Sfixed32Kind |
+//	| int64      | Int64Kind, Sint64Kind, Sfixed64Kind |
+//	| uint32     | Uint32Kind, Fixed32Kind             |
+//	| uint64     | Uint64Kind, Fixed64Kind             |
+//	| float32    | FloatKind                           |
+//	| float64    | DoubleKind                          |
+//	| string     | StringKind                          |
+//	| []byte     | BytesKind                           |
+//	| E          | EnumKind                            |
+//	| M          | MessageKind, GroupKind              |
+//	+------------+-------------------------------------+
+//
+// The type E is the concrete enum type returned by NewEnum,
+// which is often, but not required to be, a named int32 type.
+// The type M is the concrete message type returned by NewMessage,
+// which is often, but not required to be, a pointer to a named struct type.
+func (t *Extension) GoType() reflect.Type {
+	t.lazyInit()
+	return t.goType
+}
+
+func (t *Extension) Descriptor() protoreflect.ExtensionDescriptor {
+	return t.ExtensionDescriptor
+}
+
+func (t *Extension) Format(s fmt.State, r rune) {
+	descfmt.FormatDesc(s, r, t)
+}
+
+func (t *Extension) lazyInit() {
+	t.once.Do(func() {
+		switch t.Kind() {
+		case protoreflect.EnumKind:
+			if t.NewEnum == nil || t.NewMessage != nil {
+				panic("NewEnum alone must be set")
+			}
+			e := t.NewEnum(0)
+			if e.Descriptor() != t.Enum() {
+				panic(fmt.Sprintf("mismatching enum descriptor: got %v, want %v", e.Descriptor(), t.Enum()))
+			}
+			t.goType = reflect.TypeOf(e)
+		case protoreflect.MessageKind, protoreflect.GroupKind:
+			if t.NewEnum != nil || t.NewMessage == nil {
+				panic("NewMessage alone must be set")
+			}
+			m := t.NewMessage()
+			if m.Descriptor() != t.Message() {
+				panic(fmt.Sprintf("mismatching message descriptor: got %v, want %v", m.Descriptor(), t.Message()))
+			}
+			t.goType = reflect.TypeOf(m.Interface())
+		default:
+			if t.NewEnum != nil || t.NewMessage != nil {
+				panic("neither NewEnum nor NewMessage may be set")
+			}
+			t.goType = goTypeForPBKind[t.Kind()]
+		}
+
+		switch t.Cardinality() {
+		case protoreflect.Optional:
+			switch t.Kind() {
+			case protoreflect.EnumKind:
+				t.new = func() protoreflect.Value {
+					return t.Default()
+				}
+				t.valueOf = func(v interface{}) protoreflect.Value {
+					ev := v.(protoreflect.Enum)
+					return protoreflect.ValueOf(ev.Number())
+				}
+				t.interfaceOf = func(v protoreflect.Value) interface{} {
+					return t.NewEnum(v.Enum())
+				}
+			case protoreflect.MessageKind, protoreflect.GroupKind:
+				t.new = func() protoreflect.Value {
+					return protoreflect.ValueOf(t.NewMessage())
+				}
+				t.valueOf = func(v interface{}) protoreflect.Value {
+					mv := v.(protoreflect.ProtoMessage).ProtoReflect()
+					return protoreflect.ValueOf(mv)
+				}
+				t.interfaceOf = func(v protoreflect.Value) interface{} {
+					return v.Message().Interface()
+				}
+			default:
+				t.new = func() protoreflect.Value {
+					v := t.Default()
+					if t.Kind() == protoreflect.BytesKind {
+						// Copy default bytes to avoid aliasing the original.
+						v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...))
+					}
+					return v
+				}
+				t.valueOf = func(v interface{}) protoreflect.Value {
+					return protoreflect.ValueOf(v)
+				}
+				t.interfaceOf = func(v protoreflect.Value) interface{} {
+					return v.Interface()
+				}
+			}
+		case protoreflect.Repeated:
+			var conv value.Converter
+			elemType := t.goType
+			switch t.Kind() {
+			case protoreflect.EnumKind:
+				conv = value.Converter{
+					PBValueOf: func(v reflect.Value) protoreflect.Value {
+						if v.Type() != elemType {
+							panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
+						}
+						e := v.Interface().(protoreflect.Enum)
+						return protoreflect.ValueOf(e.Number())
+					},
+					GoValueOf: func(v protoreflect.Value) reflect.Value {
+						rv := reflect.ValueOf(t.NewEnum(v.Enum()))
+						if rv.Type() != elemType {
+							panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
+						}
+						return rv
+					},
+					NewEnum: t.NewEnum,
+				}
+			case protoreflect.MessageKind, protoreflect.GroupKind:
+				conv = value.Converter{
+					PBValueOf: func(v reflect.Value) protoreflect.Value {
+						if v.Type() != elemType {
+							panic(fmt.Sprintf("invalid type: got %v, want %v", v.Type(), elemType))
+						}
+						m := v.Interface().(protoreflect.ProtoMessage).ProtoReflect()
+						return protoreflect.ValueOf(m)
+					},
+					GoValueOf: func(v protoreflect.Value) reflect.Value {
+						rv := reflect.ValueOf(v.Message().Interface())
+						if rv.Type() != elemType {
+							panic(fmt.Sprintf("invalid type: got %v, want %v", rv.Type(), elemType))
+						}
+						return rv
+					},
+					NewMessage: t.NewMessage,
+				}
+			default:
+				conv = value.NewConverter(elemType, t.Kind())
+			}
+
+			t.goType = reflect.PtrTo(reflect.SliceOf(elemType))
+			t.new = func() protoreflect.Value {
+				v := reflect.New(t.goType.Elem()).Interface()
+				return protoreflect.ValueOf(value.ListOf(v, conv))
+			}
+			t.valueOf = func(v interface{}) protoreflect.Value {
+				return protoreflect.ValueOf(value.ListOf(v, conv))
+			}
+			t.interfaceOf = func(v protoreflect.Value) interface{} {
+				return v.List().(value.Unwrapper).ProtoUnwrap()
+			}
+		default:
+			panic(fmt.Sprintf("invalid cardinality: %v", t.Cardinality()))
+		}
+	})
+}
+
+var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
+	protoreflect.BoolKind:     reflect.TypeOf(bool(false)),
+	protoreflect.Int32Kind:    reflect.TypeOf(int32(0)),
+	protoreflect.Sint32Kind:   reflect.TypeOf(int32(0)),
+	protoreflect.Sfixed32Kind: reflect.TypeOf(int32(0)),
+	protoreflect.Int64Kind:    reflect.TypeOf(int64(0)),
+	protoreflect.Sint64Kind:   reflect.TypeOf(int64(0)),
+	protoreflect.Sfixed64Kind: reflect.TypeOf(int64(0)),
+	protoreflect.Uint32Kind:   reflect.TypeOf(uint32(0)),
+	protoreflect.Fixed32Kind:  reflect.TypeOf(uint32(0)),
+	protoreflect.Uint64Kind:   reflect.TypeOf(uint64(0)),
+	protoreflect.Fixed64Kind:  reflect.TypeOf(uint64(0)),
+	protoreflect.FloatKind:    reflect.TypeOf(float32(0)),
+	protoreflect.DoubleKind:   reflect.TypeOf(float64(0)),
+	protoreflect.StringKind:   reflect.TypeOf(string("")),
+	protoreflect.BytesKind:    reflect.TypeOf([]byte(nil)),
+}
+
+var (
+	_ protoreflect.EnumType      = (*Enum)(nil)
+	_ protoreflect.MessageType   = (*Message)(nil)
+	_ protoreflect.ExtensionType = (*Extension)(nil)
+)