reflect/protodesc: split descriptor related functionality from prototype

In order to generate descriptor.proto, the generated code would want to depend
on the prototype package to construct the reflection data structures.
However, this is a problem since descriptor itself is one of the dependencies
for prototype. To break this dependency, we do the following:
* Avoid using concrete *descriptorpb.XOptions messages in the public API, and
instead just use protoreflect.ProtoMessage. We do lose some type safety here
as a result.
* Use protobuf reflection to interpret the Options message.
* Split out NewFileFromDescriptorProto into a separate protodesc package since
constructing protobuf reflection from the descriptor proto obviously depends
on the descriptor protos themselves.

As part of this CL, we check in a pre-generated version of descriptor and plugin
that supports protobuf reflection natively and switchover all usages of those
protos to the new definitions. These files were generated by protoc-gen-go
from CL/150074, but hand-modified to remove dependencies on the v1 proto runtime.

Change-Id: I81e03c42eeab480b03764e2fcbe1aae0e058fc57
Reviewed-on: https://go-review.googlesource.com/c/152020
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/reflect/prototype/protofile_desc.go b/reflect/protodesc/protodesc.go
similarity index 85%
rename from reflect/prototype/protofile_desc.go
rename to reflect/protodesc/protodesc.go
index b4ea5c3..6ab1ce8 100644
--- a/reflect/prototype/protofile_desc.go
+++ b/reflect/protodesc/protodesc.go
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package prototype
+// Package protodesc provides for converting descriptorpb.FileDescriptorProto
+// to/from the reflective protoreflect.FileDescriptor.
+package protodesc
 
 import (
 	"fmt"
@@ -10,12 +12,13 @@
 	"strconv"
 	"strings"
 
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-
 	"github.com/golang/protobuf/v2/internal/encoding/text"
 	"github.com/golang/protobuf/v2/internal/errors"
 	"github.com/golang/protobuf/v2/reflect/protoreflect"
 	"github.com/golang/protobuf/v2/reflect/protoregistry"
+	"github.com/golang/protobuf/v2/reflect/prototype"
+
+	descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
 )
 
 // TODO: Should we be responsible for validating other parts of the descriptor
@@ -41,9 +44,9 @@
 // However, this will complicate future work for validation since File may now
 // diverge from the stored descriptor proto (see above TODO).
 
-// NewFileFromDescriptorProto creates a new protoreflect.FileDescriptor from
-// the provided descriptor message. The file must represent a valid proto file
-// according to protobuf semantics.
+// NewFile creates a new protoreflect.FileDescriptor from the provided
+// file descriptor message. The file must represent a valid proto file according
+// to protobuf semantics.
 //
 // Any import files, enum types, or message types referenced in the file are
 // resolved using the provided registry. When looking up an import file path,
@@ -52,8 +55,8 @@
 //
 // The caller must relinquish full ownership of the input fd and must not
 // access or mutate any fields.
-func NewFileFromDescriptorProto(fd *descriptorV1.FileDescriptorProto, r *protoregistry.Files) (protoreflect.FileDescriptor, error) {
-	var f File
+func NewFile(fd *descriptorpb.FileDescriptorProto, r *protoregistry.Files) (protoreflect.FileDescriptor, error) {
+	var f prototype.File
 	switch fd.GetSyntax() {
 	case "", "proto2":
 		f.Syntax = protoreflect.Proto2
@@ -91,7 +94,7 @@
 			return nil, errors.New("duplicate files for import %q", path)
 		}
 		if imp.IsWeak || imp.FileDescriptor == nil {
-			imp.FileDescriptor = PlaceholderFile(path, "")
+			imp.FileDescriptor = prototype.PlaceholderFile(path, "")
 		}
 	}
 
@@ -113,16 +116,16 @@
 		return nil, err
 	}
 
-	return NewFile(&f)
+	return prototype.NewFile(&f)
 }
 
-func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, syntax protoreflect.Syntax, r *protoregistry.Files) (ms []Message, err error) {
+func messagesFromDescriptorProto(mds []*descriptorpb.DescriptorProto, syntax protoreflect.Syntax, r *protoregistry.Files) (ms []prototype.Message, err error) {
 	for _, md := range mds {
-		var m Message
+		var m prototype.Message
 		m.Name = protoreflect.Name(md.GetName())
 		m.Options = md.GetOptions()
 		for _, fd := range md.GetField() {
-			var f Field
+			var f prototype.Field
 			f.Name = protoreflect.Name(fd.GetName())
 			f.Number = protoreflect.FieldNumber(fd.GetNumber())
 			f.Cardinality = protoreflect.Cardinality(fd.GetLabel())
@@ -142,28 +145,29 @@
 				}
 				f.OneofName = protoreflect.Name(md.GetOneofDecl()[i].GetName())
 			}
+			opts, _ := f.Options.(*descriptorpb.FieldOptions)
 			switch f.Kind {
 			case protoreflect.EnumKind:
 				f.EnumType, err = findEnumDescriptor(fd.GetTypeName(), r)
 				if err != nil {
 					return nil, err
 				}
-				if f.Options.GetWeak() && !f.EnumType.IsPlaceholder() {
-					f.EnumType = PlaceholderEnum(f.EnumType.FullName())
+				if opts.GetWeak() && !f.EnumType.IsPlaceholder() {
+					f.EnumType = prototype.PlaceholderEnum(f.EnumType.FullName())
 				}
 			case protoreflect.MessageKind, protoreflect.GroupKind:
 				f.MessageType, err = findMessageDescriptor(fd.GetTypeName(), r)
 				if err != nil {
 					return nil, err
 				}
-				if f.Options.GetWeak() && !f.MessageType.IsPlaceholder() {
-					f.MessageType = PlaceholderMessage(f.MessageType.FullName())
+				if opts.GetWeak() && !f.MessageType.IsPlaceholder() {
+					f.MessageType = prototype.PlaceholderMessage(f.MessageType.FullName())
 				}
 			}
 			m.Fields = append(m.Fields, f)
 		}
 		for _, od := range md.GetOneofDecl() {
-			m.Oneofs = append(m.Oneofs, Oneof{
+			m.Oneofs = append(m.Oneofs, prototype.Oneof{
 				Name:    protoreflect.Name(od.GetName()),
 				Options: od.Options,
 			})
@@ -194,13 +198,13 @@
 	return ms, nil
 }
 
-func enumsFromDescriptorProto(eds []*descriptorV1.EnumDescriptorProto, r *protoregistry.Files) (es []Enum, err error) {
+func enumsFromDescriptorProto(eds []*descriptorpb.EnumDescriptorProto, r *protoregistry.Files) (es []prototype.Enum, err error) {
 	for _, ed := range eds {
-		var e Enum
+		var e prototype.Enum
 		e.Name = protoreflect.Name(ed.GetName())
 		e.Options = ed.GetOptions()
 		for _, vd := range ed.GetValue() {
-			e.Values = append(e.Values, EnumValue{
+			e.Values = append(e.Values, prototype.EnumValue{
 				Name:    protoreflect.Name(vd.GetName()),
 				Number:  protoreflect.EnumNumber(vd.GetNumber()),
 				Options: vd.Options,
@@ -211,9 +215,9 @@
 	return es, nil
 }
 
-func extensionsFromDescriptorProto(xds []*descriptorV1.FieldDescriptorProto, r *protoregistry.Files) (xs []Extension, err error) {
+func extensionsFromDescriptorProto(xds []*descriptorpb.FieldDescriptorProto, r *protoregistry.Files) (xs []prototype.Extension, err error) {
 	for _, xd := range xds {
-		var x Extension
+		var x prototype.Extension
 		x.Name = protoreflect.Name(xd.GetName())
 		x.Number = protoreflect.FieldNumber(xd.GetNumber())
 		x.Cardinality = protoreflect.Cardinality(xd.GetLabel())
@@ -246,13 +250,13 @@
 	return xs, nil
 }
 
-func servicesFromDescriptorProto(sds []*descriptorV1.ServiceDescriptorProto, r *protoregistry.Files) (ss []Service, err error) {
+func servicesFromDescriptorProto(sds []*descriptorpb.ServiceDescriptorProto, r *protoregistry.Files) (ss []prototype.Service, err error) {
 	for _, sd := range sds {
-		var s Service
+		var s prototype.Service
 		s.Name = protoreflect.Name(sd.GetName())
 		s.Options = sd.GetOptions()
 		for _, md := range sd.GetMethod() {
-			var m Method
+			var m prototype.Method
 			m.Name = protoreflect.Name(md.GetName())
 			m.Options = md.GetOptions()
 			m.InputType, err = findMessageDescriptor(md.GetInputType(), r)
@@ -290,7 +294,7 @@
 		}
 		return m, nil
 	case err == protoregistry.NotFound:
-		return PlaceholderMessage(name), nil
+		return prototype.PlaceholderMessage(name), nil
 	default:
 		return nil, err
 	}
@@ -309,7 +313,7 @@
 		}
 		return e, nil
 	case err == protoregistry.NotFound:
-		return PlaceholderEnum(name), nil
+		return prototype.PlaceholderEnum(name), nil
 	default:
 		return nil, err
 	}
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index 5a12921..b2c4813 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -102,6 +102,10 @@
 	// Support for this functionality is optional and may return (nil, false).
 	DescriptorProto() (Message, bool)
 
+	// TODO: Should DescriptorProto exist if prototype does not depend on
+	// the descriptor package? Should this instead be a function in the
+	// protodesc package?
+
 	// Options returns the descriptor options. The caller must not modify
 	// the returned value.
 	//
@@ -120,12 +124,16 @@
 	//	| MethodDescriptor    | google.protobuf.MethodOptions            |
 	//	+---------------------+------------------------------------------+
 	//
-	// This method will never return a nil interface value, although the
-	// concrete value contained in the interface may be nil (e.g.,
-	// (*descpb.FileOptions)(nil)).
+	// This method may return a nil interface value if no options are present.
+	Options() ProtoMessage
+
+	// TODO: If no options are set, can Options return a typed nil-pointer
+	// using a form of dependency injection where the descriptor proto
+	// registers the option types with the prototype package?
 	//
-	// TODO: Return ProtoMessage instead of interface{}.
-	Options() interface{}
+	// However, what happens if the descriptor proto is never linked in?
+	// Then we cannot provide this guarantee.
+	// Perhaps this should return a bool as well?
 
 	doNotImplement
 }
@@ -304,6 +312,11 @@
 	// If true, then it implies Cardinality is Repeated.
 	IsPacked() bool
 
+	// IsWeak reports whether this is a weak field, which does not impose a
+	// direct dependency on the target type.
+	// If true, then MessageDescriptor returns a placeholder type.
+	IsWeak() bool
+
 	// IsMap reports whether this field represents a map.
 	// The value type for the associated field is a Map instead of a List.
 	//
@@ -311,11 +324,6 @@
 	// and MessageDescriptor.IsMapEntry is true.
 	IsMap() bool
 
-	// IsWeak reports whether this is a weak field, which does not impose a
-	// direct dependency on the target type.
-	// If true, then MessageDescriptor returns a placeholder type.
-	IsWeak() bool
-
 	// Default returns the default value for scalar fields.
 	// For proto2, it is the default value as specified in the proto file,
 	// or the zero value if unspecified.
diff --git a/reflect/prototype/desc_test.go b/reflect/prototype/desc_test.go
new file mode 100644
index 0000000..50f1a6d
--- /dev/null
+++ b/reflect/prototype/desc_test.go
@@ -0,0 +1,100 @@
+// 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 (
+	"reflect"
+	"testing"
+
+	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+)
+
+// TestDescriptors tests that the implementations do not declare additional
+// methods that do not exist on the interface types.
+func TestDescriptors(t *testing.T) {
+	tests := []interface{}{
+		[]pref.FileDescriptor{placeholderFile{}, fileDesc{}},
+		[]pref.MessageDescriptor{placeholderMessage{}, standaloneMessage{}, messageDesc{}},
+		[]pref.FieldDescriptor{standaloneExtension{}, fieldDesc{}, extensionDesc{}},
+		[]pref.OneofDescriptor{oneofDesc{}},
+		[]pref.EnumDescriptor{placeholderEnum{}, standaloneEnum{}, enumDesc{}},
+		[]pref.EnumValueDescriptor{enumValueDesc{}},
+		[]pref.ServiceDescriptor{serviceDesc{}},
+		[]pref.MethodDescriptor{methodDesc{}},
+
+		[]pref.FileImports{(*fileImports)(nil)},
+		[]pref.MessageDescriptors{(*messages)(nil)},
+		[]pref.FieldNumbers{(*numbers)(nil)},
+		[]pref.FieldRanges{(*ranges)(nil)},
+		[]pref.FieldDescriptors{(*fields)(nil), (*oneofFields)(nil)},
+		[]pref.OneofDescriptors{(*oneofs)(nil)},
+		[]pref.ExtensionDescriptors{(*extensions)(nil)},
+		[]pref.EnumDescriptors{(*enums)(nil)},
+		[]pref.EnumValueDescriptors{(*enumValues)(nil)},
+		[]pref.ServiceDescriptors{(*services)(nil)},
+		[]pref.MethodDescriptors{(*methods)(nil)},
+	}
+
+	for _, tt := range tests {
+		v := reflect.ValueOf(tt) // []T where T is an interface
+		ifaceType := v.Type().Elem()
+		for i := 0; i < v.Len(); i++ {
+			implType := v.Index(i).Elem().Type()
+
+			var hasName bool
+			for j := 0; j < implType.NumMethod(); j++ {
+				if name := implType.Method(j).Name; name == "Format" {
+					hasName = true
+				} else if _, ok := ifaceType.MethodByName(name); !ok {
+					t.Errorf("spurious method: %v.%v", implType, name)
+				}
+			}
+			if !hasName {
+				t.Errorf("missing method: %v.Format", implType)
+			}
+		}
+	}
+}
+
+// TestDescriptorAccessors tests that descriptorAccessors is up-to-date.
+func TestDescriptorAccessors(t *testing.T) {
+	ignore := map[string]bool{
+		"DefaultEnumValue": true,
+		"DescriptorByName": true,
+		"ProtoType":        true,
+	}
+	rt := reflect.TypeOf((*pref.Descriptor)(nil)).Elem()
+	for i := 0; i < rt.NumMethod(); i++ {
+		ignore[rt.Method(i).Name] = true
+	}
+
+	for rt, m := range descriptorAccessors {
+		got := map[string]bool{}
+		for _, s := range m {
+			got[s] = true
+		}
+		want := map[string]bool{}
+		for i := 0; i < rt.NumMethod(); i++ {
+			want[rt.Method(i).Name] = true
+		}
+
+		// Check if descriptorAccessors contains a non-existent accessor.
+		// If this test fails, remove the accessor from descriptorAccessors.
+		for s := range got {
+			if !want[s] && !ignore[s] {
+				t.Errorf("%v.%v does not exist", rt, s)
+			}
+		}
+
+		// Check if there are new protoreflect interface methods that are not
+		// handled by the formatter. If this fails, either add the method to
+		// ignore or add them to descriptorAccessors.
+		for s := range want {
+			if !got[s] && !ignore[s] {
+				t.Errorf("%v.%v is not called by formatter", rt, s)
+			}
+		}
+	}
+}
diff --git a/reflect/prototype/placeholder_type.go b/reflect/prototype/placeholder_type.go
index 559dbf6..746f388 100644
--- a/reflect/prototype/placeholder_type.go
+++ b/reflect/prototype/placeholder_type.go
@@ -7,8 +7,7 @@
 import (
 	"fmt"
 
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/internal/pragma"
+	pragma "github.com/golang/protobuf/v2/internal/pragma"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
@@ -41,7 +40,7 @@
 	placeholderName
 }
 
-func (t placeholderFile) Options() interface{}                           { return (*descriptorV1.FileOptions)(nil) }
+func (t placeholderFile) Options() pref.ProtoMessage                     { return nil }
 func (t placeholderFile) Path() string                                   { return t.path }
 func (t placeholderFile) Package() pref.FullName                         { return t.FullName() }
 func (t placeholderFile) Imports() pref.FileImports                      { return &emptyFiles }
@@ -57,7 +56,7 @@
 	placeholderName
 }
 
-func (t placeholderMessage) Options() interface{}                  { return (*descriptorV1.MessageOptions)(nil) }
+func (t placeholderMessage) Options() pref.ProtoMessage            { return nil }
 func (t placeholderMessage) IsMapEntry() bool                      { return false }
 func (t placeholderMessage) Fields() pref.FieldDescriptors         { return &emptyFields }
 func (t placeholderMessage) Oneofs() pref.OneofDescriptors         { return &emptyOneofs }
@@ -73,7 +72,7 @@
 	placeholderName
 }
 
-func (t placeholderEnum) Options() interface{}              { return (*descriptorV1.EnumOptions)(nil) }
+func (t placeholderEnum) Options() pref.ProtoMessage        { return nil }
 func (t placeholderEnum) Values() pref.EnumValueDescriptors { return &emptyEnumValues }
 func (t placeholderEnum) Format(s fmt.State, r rune)        { formatDesc(s, r, t) }
 func (t placeholderEnum) ProtoType(pref.EnumDescriptor)     {}
diff --git a/reflect/prototype/protofile.go b/reflect/prototype/protofile.go
index a765ccd..7ff1af0 100644
--- a/reflect/prototype/protofile.go
+++ b/reflect/prototype/protofile.go
@@ -13,10 +13,7 @@
 // the parent type.
 package prototype
 
-import (
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/reflect/protoreflect"
-)
+import "github.com/golang/protobuf/v2/reflect/protoreflect"
 
 // Every struct has a "meta" struct embedded within it as a pointer.
 // The meta type provides additional data structures for efficient lookup on
@@ -44,7 +41,7 @@
 	Enums      []Enum
 	Extensions []Extension
 	Services   []Service
-	Options    *descriptorV1.FileOptions
+	Options    protoreflect.ProtoMessage
 
 	*fileMeta
 }
@@ -54,7 +51,12 @@
 //
 // Fields that reference an enum or message that is being declared within the
 // same File can be represented using a placeholder descriptor. NewFile will
-// automatically resolve the placeholder to point to the concrete type.
+// automatically resolve the placeholder to point to a concrete descriptor.
+// Alternatively, a reference descriptor obtained via Enum.Reference or
+// Message.Reference can be used instead. The placeholder approach makes it
+// possible to declare the file descriptor as a single File literal and
+// is generally easier to use. The reference approach is more performant,
+// but also more error prone.
 //
 // The caller must relinquish full ownership of the input t and must not
 // access or mutate any fields. The input must not contain slices that are
@@ -67,9 +69,29 @@
 	if err := validateFile(ft); err != nil {
 		return nil, err
 	}
+
+	// TODO: When using reference descriptors, it is vital that all enums and
+	// messages are touched so that they are initialized before returning.
+	// Otherwise, reference descriptors may still be invalid.
+	//
+	// We can remove this once validateFile is implemented, since it will
+	// inherently touch all the necessary messages and enums.
+	visitMessages(ft)
+
 	return ft, nil
 }
 
+func visitMessages(d interface {
+	Enums() protoreflect.EnumDescriptors
+	Messages() protoreflect.MessageDescriptors
+}) {
+	d.Enums()
+	ms := d.Messages()
+	for i := 0; i < ms.Len(); i++ {
+		visitMessages(ms.Get(i))
+	}
+}
+
 // Message is a constructor for protoreflect.MessageDescriptor.
 type Message struct {
 	Name            protoreflect.Name
@@ -79,11 +101,19 @@
 	Messages        []Message
 	Enums           []Enum
 	Extensions      []Extension
-	Options         *descriptorV1.MessageOptions
+	Options         protoreflect.ProtoMessage
 
 	*messageMeta
 }
 
+// Reference returns m as a reference protoreflect.MessageDescriptor,
+// which can be used to satisfy internal dependencies within a proto file.
+// Methods on the returned descriptor are not valid until the file that this
+// message belongs to has been constructed via NewFile.
+func (m *Message) Reference() protoreflect.MessageDescriptor {
+	return messageDesc{m}
+}
+
 // Field is a constructor for protoreflect.FieldDescriptor.
 type Field struct {
 	Name        protoreflect.Name
@@ -95,7 +125,7 @@
 	OneofName   protoreflect.Name
 	MessageType protoreflect.MessageDescriptor
 	EnumType    protoreflect.EnumDescriptor
-	Options     *descriptorV1.FieldOptions
+	Options     protoreflect.ProtoMessage
 
 	*fieldMeta
 }
@@ -103,7 +133,7 @@
 // Oneof is a constructor for protoreflect.OneofDescriptor.
 type Oneof struct {
 	Name    protoreflect.Name
-	Options *descriptorV1.OneofOptions
+	Options protoreflect.ProtoMessage
 
 	*oneofMeta
 }
@@ -118,7 +148,7 @@
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
-	Options      *descriptorV1.FieldOptions
+	Options      protoreflect.ProtoMessage
 
 	*extensionMeta
 }
@@ -127,16 +157,24 @@
 type Enum struct {
 	Name    protoreflect.Name
 	Values  []EnumValue
-	Options *descriptorV1.EnumOptions
+	Options protoreflect.ProtoMessage
 
 	*enumMeta
 }
 
+// Reference returns e as a reference protoreflect.EnumDescriptor,
+// which can be used to satisfy internal dependencies within a proto file.
+// Methods on the returned descriptor are not valid until the file that this
+// enum belongs to has been constructed via NewFile.
+func (e *Enum) Reference() protoreflect.EnumDescriptor {
+	return enumDesc{e}
+}
+
 // EnumValue is a constructor for protoreflect.EnumValueDescriptor.
 type EnumValue struct {
 	Name    protoreflect.Name
 	Number  protoreflect.EnumNumber
-	Options *descriptorV1.EnumValueOptions
+	Options protoreflect.ProtoMessage
 
 	*enumValueMeta
 }
@@ -145,7 +183,7 @@
 type Service struct {
 	Name    protoreflect.Name
 	Methods []Method
-	Options *descriptorV1.ServiceOptions
+	Options protoreflect.ProtoMessage
 
 	*serviceMeta
 }
@@ -157,7 +195,7 @@
 	OutputType        protoreflect.MessageDescriptor
 	IsStreamingClient bool
 	IsStreamingServer bool
-	Options           *descriptorV1.MethodOptions
+	Options           protoreflect.ProtoMessage
 
 	*methodMeta
 }
diff --git a/reflect/prototype/protofile_list.go b/reflect/prototype/protofile_list.go
index 0c9df72..b074ed9 100644
--- a/reflect/prototype/protofile_list.go
+++ b/reflect/prototype/protofile_list.go
@@ -8,15 +8,15 @@
 	"fmt"
 	"sync"
 
-	"github.com/golang/protobuf/v2/internal/pragma"
-	"github.com/golang/protobuf/v2/internal/set"
+	pragma "github.com/golang/protobuf/v2/internal/pragma"
+	pset "github.com/golang/protobuf/v2/internal/set"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
 type numbersMeta struct {
 	once sync.Once
 	ns   []pref.FieldNumber
-	nss  set.Ints
+	nss  pset.Ints
 }
 type numbers numbersMeta
 
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
index d5b4250..bad93e7 100644
--- a/reflect/prototype/protofile_type.go
+++ b/reflect/prototype/protofile_type.go
@@ -10,8 +10,7 @@
 	"strings"
 	"sync"
 
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/internal/pragma"
+	pragma "github.com/golang/protobuf/v2/internal/pragma"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
@@ -61,7 +60,7 @@
 func (t fileDesc) FullName() pref.FullName                          { return t.f.Package }
 func (t fileDesc) IsPlaceholder() bool                              { return false }
 func (t fileDesc) DescriptorProto() (pref.Message, bool)            { return nil, false }
-func (t fileDesc) Options() interface{}                             { return t.f.Options }
+func (t fileDesc) Options() pref.ProtoMessage                       { return t.f.Options }
 func (t fileDesc) Path() string                                     { return t.f.Path }
 func (t fileDesc) Package() pref.FullName                           { return t.f.Package }
 func (t fileDesc) Imports() pref.FileImports                        { return (*fileImports)(&t.f.Imports) }
@@ -158,6 +157,7 @@
 	ms messagesMeta
 	es enumsMeta
 	xs extensionsMeta
+	mo messageOptions
 }
 type messageDesc struct{ m *Message }
 
@@ -168,8 +168,8 @@
 func (t messageDesc) FullName() pref.FullName               { return t.m.fullName }
 func (t messageDesc) IsPlaceholder() bool                   { return false }
 func (t messageDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t messageDesc) Options() interface{}                  { return t.m.Options }
-func (t messageDesc) IsMapEntry() bool                      { return t.m.Options.GetMapEntry() }
+func (t messageDesc) Options() pref.ProtoMessage            { return t.m.Options }
+func (t messageDesc) IsMapEntry() bool                      { return t.m.mo.lazyInit(t).isMapEntry }
 func (t messageDesc) Fields() pref.FieldDescriptors         { return t.m.fs.lazyInit(t, t.m.Fields) }
 func (t messageDesc) Oneofs() pref.OneofDescriptors         { return t.m.os.lazyInit(t, t.m.Oneofs) }
 func (t messageDesc) RequiredNumbers() pref.FieldNumbers    { return t.m.ns.lazyInit(t.m.Fields) }
@@ -181,6 +181,22 @@
 func (t messageDesc) ProtoType(pref.MessageDescriptor)      {}
 func (t messageDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
+type messageOptions struct {
+	once       sync.Once
+	isMapEntry bool
+}
+
+func (p *messageOptions) lazyInit(m pref.MessageDescriptor) *messageOptions {
+	p.once.Do(func() {
+		if m.Options() != nil {
+			const mapEntryFieldNumber = 7 // google.protobuf.MessageOptions.map_entry
+			fs := m.Options().ProtoReflect().KnownFields()
+			p.isMapEntry = fs.Get(mapEntryFieldNumber).Bool()
+		}
+	})
+	return p
+}
+
 type fieldMeta struct {
 	inheritedMeta
 
@@ -189,6 +205,7 @@
 	ot oneofReference
 	mt messageReference
 	et enumReference
+	fo fieldOptions
 }
 type fieldDesc struct{ f *Field }
 
@@ -199,14 +216,14 @@
 func (t fieldDesc) FullName() pref.FullName                    { return t.f.fullName }
 func (t fieldDesc) IsPlaceholder() bool                        { return false }
 func (t fieldDesc) DescriptorProto() (pref.Message, bool)      { return nil, false }
-func (t fieldDesc) Options() interface{}                       { return t.f.Options }
+func (t fieldDesc) Options() pref.ProtoMessage                 { return t.f.Options }
 func (t fieldDesc) Number() pref.FieldNumber                   { return t.f.Number }
 func (t fieldDesc) Cardinality() pref.Cardinality              { return t.f.Cardinality }
 func (t fieldDesc) Kind() pref.Kind                            { return t.f.Kind }
 func (t fieldDesc) JSONName() string                           { return t.f.js.lazyInit(t.f) }
-func (t fieldDesc) IsPacked() bool                             { return fieldIsPacked(t) }
-func (t fieldDesc) IsMap() bool                                { return isMap(t) }
-func (t fieldDesc) IsWeak() bool                               { return t.f.Options.GetWeak() }
+func (t fieldDesc) IsPacked() bool                             { return t.f.fo.lazyInit(t).isPacked }
+func (t fieldDesc) IsWeak() bool                               { return t.f.fo.lazyInit(t).isWeak }
+func (t fieldDesc) IsMap() bool                                { return t.f.fo.lazyInit(t).isMap }
 func (t fieldDesc) Default() pref.Value                        { return t.f.dv.value(t, t.f.Default) }
 func (t fieldDesc) DefaultEnumValue() pref.EnumValueDescriptor { return t.f.dv.enum(t, t.f.Default) }
 func (t fieldDesc) HasDefault() bool                           { return t.f.Default.IsValid() }
@@ -218,44 +235,6 @@
 func (t fieldDesc) ProtoType(pref.FieldDescriptor)             {}
 func (t fieldDesc) ProtoInternal(pragma.DoNotImplement)        {}
 
-func fieldIsPacked(t fieldDesc) bool {
-	if t.f.Options != nil && t.f.Options.Packed != nil {
-		return *t.f.Options.Packed
-	}
-	// https://developers.google.com/protocol-buffers/docs/proto3:
-	// "In proto3, repeated fields of scalar numeric types use packed
-	// encoding by default."
-	return (t.f.syntax == pref.Proto3 &&
-		t.f.Cardinality == pref.Repeated &&
-		isScalarNumeric[t.f.Kind])
-}
-
-var isScalarNumeric = map[pref.Kind]bool{
-	pref.BoolKind:     true,
-	pref.EnumKind:     true,
-	pref.Int32Kind:    true,
-	pref.Sint32Kind:   true,
-	pref.Uint32Kind:   true,
-	pref.Int64Kind:    true,
-	pref.Sint64Kind:   true,
-	pref.Uint64Kind:   true,
-	pref.Sfixed32Kind: true,
-	pref.Fixed32Kind:  true,
-	pref.FloatKind:    true,
-	pref.Sfixed64Kind: true,
-	pref.Fixed64Kind:  true,
-	pref.DoubleKind:   true,
-}
-
-func isMap(t pref.FieldDescriptor) bool {
-	if t.Cardinality() == pref.Repeated && t.Kind() == pref.MessageKind {
-		if mt := t.MessageType(); mt != nil {
-			return mt.Options().(*descriptorV1.MessageOptions).GetMapEntry()
-		}
-	}
-	return false
-}
-
 type jsonName struct{ once sync.Once }
 
 func (p *jsonName) lazyInit(f *Field) string {
@@ -301,6 +280,77 @@
 	return p.otyp
 }
 
+type fieldOptions struct {
+	once     sync.Once
+	isPacked bool
+	isWeak   bool
+	isMap    bool
+}
+
+func (p *fieldOptions) lazyInit(f pref.FieldDescriptor) *fieldOptions {
+	p.once.Do(func() {
+		if f.Cardinality() == pref.Repeated {
+			// In proto3, repeated fields of scalar numeric types use
+			// packed encoding by default.
+			// See https://developers.google.com/protocol-buffers/docs/proto3
+			if f.Syntax() == pref.Proto3 {
+				p.isPacked = isScalarNumeric[f.Kind()]
+			}
+			if f.Kind() == pref.MessageKind {
+				p.isMap = f.MessageType().IsMapEntry()
+			}
+		}
+
+		if f.Options() != nil {
+			const packedFieldNumber = 2 // google.protobuf.FieldOptions.packed
+			const weakFieldNumber = 10  // google.protobuf.FieldOptions.weak
+			fs := f.Options().ProtoReflect().KnownFields()
+			if fs.Has(packedFieldNumber) {
+				p.isPacked = fs.Get(packedFieldNumber).Bool()
+			}
+			p.isWeak = fs.Get(weakFieldNumber).Bool()
+		}
+	})
+	return p
+}
+
+// isPacked reports whether the packed options is set.
+func isPacked(m pref.ProtoMessage) (isPacked bool) {
+	if m != nil {
+		const packedFieldNumber = 2 // google.protobuf.FieldOptions.packed
+		fs := m.ProtoReflect().KnownFields()
+		isPacked = fs.Get(packedFieldNumber).Bool()
+	}
+	return isPacked
+}
+
+// isWeak reports whether the weak options is set.
+func isWeak(m pref.ProtoMessage) (isWeak bool) {
+	if m != nil {
+		const weakFieldNumber = 10 // google.protobuf.FieldOptions.weak
+		fs := m.ProtoReflect().KnownFields()
+		isWeak = fs.Get(weakFieldNumber).Bool()
+	}
+	return isWeak
+}
+
+var isScalarNumeric = map[pref.Kind]bool{
+	pref.BoolKind:     true,
+	pref.EnumKind:     true,
+	pref.Int32Kind:    true,
+	pref.Sint32Kind:   true,
+	pref.Uint32Kind:   true,
+	pref.Int64Kind:    true,
+	pref.Sint64Kind:   true,
+	pref.Uint64Kind:   true,
+	pref.Sfixed32Kind: true,
+	pref.Fixed32Kind:  true,
+	pref.FloatKind:    true,
+	pref.Sfixed64Kind: true,
+	pref.Fixed64Kind:  true,
+	pref.DoubleKind:   true,
+}
+
 type oneofMeta struct {
 	inheritedMeta
 
@@ -315,7 +365,7 @@
 func (t oneofDesc) FullName() pref.FullName               { return t.o.fullName }
 func (t oneofDesc) IsPlaceholder() bool                   { return false }
 func (t oneofDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t oneofDesc) Options() interface{}                  { return t.o.Options }
+func (t oneofDesc) Options() pref.ProtoMessage            { return t.o.Options }
 func (t oneofDesc) Fields() pref.FieldDescriptors         { return t.o.fs.lazyInit(t) }
 func (t oneofDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
 func (t oneofDesc) ProtoType(pref.OneofDescriptor)        {}
@@ -338,14 +388,14 @@
 func (t extensionDesc) FullName() pref.FullName                    { return t.x.fullName }
 func (t extensionDesc) IsPlaceholder() bool                        { return false }
 func (t extensionDesc) DescriptorProto() (pref.Message, bool)      { return nil, false }
-func (t extensionDesc) Options() interface{}                       { return t.x.Options }
+func (t extensionDesc) Options() pref.ProtoMessage                 { return t.x.Options }
 func (t extensionDesc) Number() pref.FieldNumber                   { return t.x.Number }
 func (t extensionDesc) Cardinality() pref.Cardinality              { return t.x.Cardinality }
 func (t extensionDesc) Kind() pref.Kind                            { return t.x.Kind }
 func (t extensionDesc) JSONName() string                           { return "" }
-func (t extensionDesc) IsPacked() bool                             { return t.x.Options.GetPacked() }
-func (t extensionDesc) IsMap() bool                                { return false }
+func (t extensionDesc) IsPacked() bool                             { return isPacked(t.Options()) }
 func (t extensionDesc) IsWeak() bool                               { return false }
+func (t extensionDesc) IsMap() bool                                { return false }
 func (t extensionDesc) Default() pref.Value                        { return t.x.dv.value(t, t.x.Default) }
 func (t extensionDesc) DefaultEnumValue() pref.EnumValueDescriptor { return t.x.dv.enum(t, t.x.Default) }
 func (t extensionDesc) HasDefault() bool                           { return t.x.Default.IsValid() }
@@ -375,7 +425,7 @@
 func (t enumDesc) FullName() pref.FullName               { return t.e.fullName }
 func (t enumDesc) IsPlaceholder() bool                   { return false }
 func (t enumDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t enumDesc) Options() interface{}                  { return t.e.Options }
+func (t enumDesc) Options() pref.ProtoMessage            { return t.e.Options }
 func (t enumDesc) Values() pref.EnumValueDescriptors     { return t.e.vs.lazyInit(t, t.e.Values) }
 func (t enumDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
 func (t enumDesc) ProtoType(pref.EnumDescriptor)         {}
@@ -393,7 +443,7 @@
 func (t enumValueDesc) FullName() pref.FullName               { return t.v.fullName }
 func (t enumValueDesc) IsPlaceholder() bool                   { return false }
 func (t enumValueDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t enumValueDesc) Options() interface{}                  { return t.v.Options }
+func (t enumValueDesc) Options() pref.ProtoMessage            { return t.v.Options }
 func (t enumValueDesc) Number() pref.EnumNumber               { return t.v.Number }
 func (t enumValueDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
 func (t enumValueDesc) ProtoType(pref.EnumValueDescriptor)    {}
@@ -413,7 +463,7 @@
 func (t serviceDesc) FullName() pref.FullName               { return t.s.fullName }
 func (t serviceDesc) IsPlaceholder() bool                   { return false }
 func (t serviceDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t serviceDesc) Options() interface{}                  { return t.s.Options }
+func (t serviceDesc) Options() pref.ProtoMessage            { return t.s.Options }
 func (t serviceDesc) Methods() pref.MethodDescriptors       { return t.s.ms.lazyInit(t, t.s.Methods) }
 func (t serviceDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
 func (t serviceDesc) ProtoType(pref.ServiceDescriptor)      {}
@@ -434,7 +484,7 @@
 func (t methodDesc) FullName() pref.FullName               { return t.m.fullName }
 func (t methodDesc) IsPlaceholder() bool                   { return false }
 func (t methodDesc) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t methodDesc) Options() interface{}                  { return t.m.Options }
+func (t methodDesc) Options() pref.ProtoMessage            { return t.m.Options }
 func (t methodDesc) InputType() pref.MessageDescriptor     { return t.m.mit.lazyInit(t, &t.m.InputType) }
 func (t methodDesc) OutputType() pref.MessageDescriptor    { return t.m.mot.lazyInit(t, &t.m.OutputType) }
 func (t methodDesc) IsStreamingClient() bool               { return t.m.IsStreamingClient }
diff --git a/reflect/prototype/resolve_test.go b/reflect/prototype/resolve_test.go
new file mode 100644
index 0000000..de0b758
--- /dev/null
+++ b/reflect/prototype/resolve_test.go
@@ -0,0 +1,146 @@
+// 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 (
+	"testing"
+
+	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+)
+
+func TestResolve(t *testing.T) {
+	f := &File{
+		Syntax:  pref.Proto2,
+		Package: "test",
+		Messages: []Message{{
+			Name:   "FooMessage",
+			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			Messages: []Message{{
+				Name:   "FooMessage",
+				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			}, {
+				Name:   "BarMessage",
+				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+			}},
+			Enums: []Enum{{
+				Name:   "FooEnum",
+				Values: []EnumValue{{Name: "E", Number: 0}},
+			}, {
+				Name:   "BarEnum",
+				Values: []EnumValue{{Name: "E", Number: 0}},
+			}},
+		}, {
+			Name:   "BarMessage",
+			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
+		}},
+		Enums: []Enum{{
+			Name:   "FooEnum",
+			Values: []EnumValue{{Name: "E", Number: 0}},
+		}, {
+			Name:   "BarEnum",
+			Values: []EnumValue{{Name: "E", Number: 0}},
+		}},
+	}
+
+	fd, err := NewFile(f)
+	if err != nil {
+		t.Fatalf("NewFile() error: %v", err)
+	}
+
+	tests := []struct {
+		parent pref.Descriptor
+		name   pref.FullName
+		want   pref.Descriptor
+	}{{
+		parent: fd.Enums().Get(0),
+		name:   "test.Foo",
+		want:   nil,
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.BarEnum",
+		want:   fd.Enums().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.BarMessage",
+		want:   fd.Messages().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooMessage.BarMessage",
+		want:   fd.Messages().Get(0).Messages().Get(1),
+	}, {
+		parent: fd.Enums().Get(0),
+		name:   "test.FooMessage.Bar",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(1),
+		name:   "test.FooMessage.BarEnum",
+		want:   fd.Messages().Get(0).Enums().Get(1),
+	}, {
+		parent: fd.Messages().Get(1),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooEnum",
+		want:   fd.Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooEnum.NonExistent",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage.FooEnum",
+		want:   fd.Messages().Get(0).Enums().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage",
+		want:   fd.Messages().Get(0),
+	}, {
+		parent: fd.Messages().Get(0),
+		name:   "test.FooMessage.Fizz",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.FooMessage.FooMessage",
+		want:   fd.Messages().Get(0).Messages().Get(0),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.FooMessage.BarMessage",
+		want:   fd.Messages().Get(0).Messages().Get(1),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessage.FooMessage",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessage",
+		want:   fd.Messages().Get(1),
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "test.BarMessageExtra",
+		want:   nil,
+	}, {
+		parent: fd.Messages().Get(0).Messages().Get(0),
+		name:   "taste.BarMessage",
+		want:   nil,
+	}}
+
+	for _, tt := range tests {
+		got := resolveReference(tt.parent, tt.name)
+		if got != tt.want {
+			fullName := func(d pref.Descriptor) string {
+				if d == nil {
+					return "<nil>"
+				}
+				return string(d.FullName())
+			}
+			t.Errorf("resolveReference(%v, %v) = %v, want %v", fullName(tt.parent), tt.name, fullName(got), fullName(tt.want))
+		}
+	}
+}
diff --git a/reflect/prototype/standalone.go b/reflect/prototype/standalone.go
index fb973df..95a9619 100644
--- a/reflect/prototype/standalone.go
+++ b/reflect/prototype/standalone.go
@@ -5,7 +5,6 @@
 package prototype
 
 import (
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/internal/errors"
 	"github.com/golang/protobuf/v2/reflect/protoreflect"
 )
@@ -21,11 +20,12 @@
 	Fields          []Field
 	Oneofs          []Oneof
 	ExtensionRanges [][2]protoreflect.FieldNumber
-	Options         *descriptorV1.MessageOptions
+	Options         protoreflect.ProtoMessage
 
-	fields fieldsMeta
-	oneofs oneofsMeta
-	nums   numbersMeta
+	fields  fieldsMeta
+	oneofs  oneofsMeta
+	nums    numbersMeta
+	options messageOptions
 }
 
 // NewMessage creates a new protoreflect.MessageDescriptor.
@@ -64,7 +64,7 @@
 		for i, f := range t.Fields {
 			// Resolve placeholder messages with a concrete standalone message.
 			// If this fails, validateMessage will complain about it later.
-			if !f.Options.GetWeak() && f.MessageType != nil && f.MessageType.IsPlaceholder() {
+			if f.MessageType != nil && f.MessageType.IsPlaceholder() && !isWeak(f.Options) {
 				if m, ok := ms[f.MessageType.FullName()]; ok {
 					t.Fields[i].MessageType = m
 				}
@@ -85,7 +85,7 @@
 	Syntax   protoreflect.Syntax
 	FullName protoreflect.FullName
 	Values   []EnumValue
-	Options  *descriptorV1.EnumOptions
+	Options  protoreflect.ProtoMessage
 
 	vals enumValuesMeta
 }
@@ -112,7 +112,7 @@
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
-	Options      *descriptorV1.FieldOptions
+	Options      protoreflect.ProtoMessage
 
 	dv defaultValue
 }
diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go
index 0802acd..d13b138 100644
--- a/reflect/prototype/standalone_type.go
+++ b/reflect/prototype/standalone_type.go
@@ -20,8 +20,8 @@
 func (t standaloneMessage) FullName() pref.FullName               { return t.m.FullName }
 func (t standaloneMessage) IsPlaceholder() bool                   { return false }
 func (t standaloneMessage) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t standaloneMessage) Options() interface{}                  { return t.m.Options }
-func (t standaloneMessage) IsMapEntry() bool                      { return t.m.Options.GetMapEntry() }
+func (t standaloneMessage) Options() pref.ProtoMessage            { return t.m.Options }
+func (t standaloneMessage) IsMapEntry() bool                      { return t.m.options.lazyInit(t).isMapEntry }
 func (t standaloneMessage) Fields() pref.FieldDescriptors         { return t.m.fields.lazyInit(t, t.m.Fields) }
 func (t standaloneMessage) Oneofs() pref.OneofDescriptors         { return t.m.oneofs.lazyInit(t, t.m.Oneofs) }
 func (t standaloneMessage) RequiredNumbers() pref.FieldNumbers    { return t.m.nums.lazyInit(t.m.Fields) }
@@ -42,7 +42,7 @@
 func (t standaloneEnum) FullName() pref.FullName               { return t.e.FullName }
 func (t standaloneEnum) IsPlaceholder() bool                   { return false }
 func (t standaloneEnum) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t standaloneEnum) Options() interface{}                  { return t.e.Options }
+func (t standaloneEnum) Options() pref.ProtoMessage            { return t.e.Options }
 func (t standaloneEnum) Values() pref.EnumValueDescriptors     { return t.e.vals.lazyInit(t, t.e.Values) }
 func (t standaloneEnum) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
 func (t standaloneEnum) ProtoType(pref.EnumDescriptor)         {}
@@ -57,14 +57,14 @@
 func (t standaloneExtension) FullName() pref.FullName               { return t.x.FullName }
 func (t standaloneExtension) IsPlaceholder() bool                   { return false }
 func (t standaloneExtension) DescriptorProto() (pref.Message, bool) { return nil, false }
-func (t standaloneExtension) Options() interface{}                  { return t.x.Options }
+func (t standaloneExtension) Options() pref.ProtoMessage            { return t.x.Options }
 func (t standaloneExtension) Number() pref.FieldNumber              { return t.x.Number }
 func (t standaloneExtension) Cardinality() pref.Cardinality         { return t.x.Cardinality }
 func (t standaloneExtension) Kind() pref.Kind                       { return t.x.Kind }
 func (t standaloneExtension) JSONName() string                      { return "" }
-func (t standaloneExtension) IsPacked() bool                        { return t.x.Options.GetPacked() }
-func (t standaloneExtension) IsMap() bool                           { return false }
+func (t standaloneExtension) IsPacked() bool                        { return isPacked(t.Options()) }
 func (t standaloneExtension) IsWeak() bool                          { return false }
+func (t standaloneExtension) IsMap() bool                           { return false }
 func (t standaloneExtension) Default() pref.Value                   { return t.x.dv.value(t, t.x.Default) }
 func (t standaloneExtension) DefaultEnumValue() pref.EnumValueDescriptor {
 	return t.x.dv.enum(t, t.x.Default)
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
index 65e5d78..698f0d0 100644
--- a/reflect/prototype/type_test.go
+++ b/reflect/prototype/type_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package prototype
+package prototype_test
 
 import (
 	"fmt"
@@ -13,118 +13,32 @@
 	"testing"
 
 	protoV1 "github.com/golang/protobuf/proto"
-	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-
 	scalar "github.com/golang/protobuf/v2/internal/scalar"
+	pdesc "github.com/golang/protobuf/v2/reflect/protodesc"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+	ptype "github.com/golang/protobuf/v2/reflect/prototype"
+
+	descriptorpb "github.com/golang/protobuf/v2/types/descriptor"
 )
 
-// TestDescriptors tests that the implementations do not declare additional
-// methods that do not exist on the interface types.
-func TestDescriptors(t *testing.T) {
-	tests := []interface{}{
-		[]pref.FileDescriptor{placeholderFile{}, fileDesc{}},
-		[]pref.MessageDescriptor{placeholderMessage{}, standaloneMessage{}, messageDesc{}},
-		[]pref.FieldDescriptor{standaloneExtension{}, fieldDesc{}, extensionDesc{}},
-		[]pref.OneofDescriptor{oneofDesc{}},
-		[]pref.EnumDescriptor{placeholderEnum{}, standaloneEnum{}, enumDesc{}},
-		[]pref.EnumValueDescriptor{enumValueDesc{}},
-		[]pref.ServiceDescriptor{serviceDesc{}},
-		[]pref.MethodDescriptor{methodDesc{}},
-
-		[]pref.FileImports{(*fileImports)(nil)},
-		[]pref.MessageDescriptors{(*messages)(nil)},
-		[]pref.FieldNumbers{(*numbers)(nil)},
-		[]pref.FieldRanges{(*ranges)(nil)},
-		[]pref.FieldDescriptors{(*fields)(nil), (*oneofFields)(nil)},
-		[]pref.OneofDescriptors{(*oneofs)(nil)},
-		[]pref.ExtensionDescriptors{(*extensions)(nil)},
-		[]pref.EnumDescriptors{(*enums)(nil)},
-		[]pref.EnumValueDescriptors{(*enumValues)(nil)},
-		[]pref.ServiceDescriptors{(*services)(nil)},
-		[]pref.MethodDescriptors{(*methods)(nil)},
-	}
-
-	for _, tt := range tests {
-		v := reflect.ValueOf(tt) // []T where T is an interface
-		ifaceType := v.Type().Elem()
-		for i := 0; i < v.Len(); i++ {
-			implType := v.Index(i).Elem().Type()
-
-			var hasName bool
-			for j := 0; j < implType.NumMethod(); j++ {
-				if name := implType.Method(j).Name; name == "Format" {
-					hasName = true
-				} else if _, ok := ifaceType.MethodByName(name); !ok {
-					t.Errorf("spurious method: %v.%v", implType, name)
-				}
-			}
-			if !hasName {
-				t.Errorf("missing method: %v.Format", implType)
-			}
-		}
-	}
-}
-
-// TestDescriptorAccessors tests that descriptorAccessors is up-to-date.
-func TestDescriptorAccessors(t *testing.T) {
-	ignore := map[string]bool{
-		"DefaultEnumValue": true,
-		"DescriptorByName": true,
-		"ProtoType":        true,
-	}
-	rt := reflect.TypeOf((*pref.Descriptor)(nil)).Elem()
-	for i := 0; i < rt.NumMethod(); i++ {
-		ignore[rt.Method(i).Name] = true
-	}
-
-	for rt, m := range descriptorAccessors {
-		got := map[string]bool{}
-		for _, s := range m {
-			got[s] = true
-		}
-		want := map[string]bool{}
-		for i := 0; i < rt.NumMethod(); i++ {
-			want[rt.Method(i).Name] = true
-		}
-
-		// Check if descriptorAccessors contains a non-existent accessor.
-		// If this test fails, remove the accessor from descriptorAccessors.
-		for s := range got {
-			if !want[s] && !ignore[s] {
-				t.Errorf("%v.%v does not exist", rt, s)
-			}
-		}
-
-		// Check if there are new protoreflect interface methods that are not
-		// handled by the formatter. If this fails, either add the method to
-		// ignore or add them to descriptorAccessors.
-		for s := range want {
-			if !got[s] && !ignore[s] {
-				t.Errorf("%v.%v is not called by formatter", rt, s)
-			}
-		}
-	}
-}
-
-// TODO: Test NewFileFromDescriptorProto with imported files.
+// TODO: Test protodesc.NewFile with imported files.
 
 func TestFile(t *testing.T) {
-	f1 := &File{
+	f1 := &ptype.File{
 		Syntax:  pref.Proto2,
 		Path:    "path/to/file.proto",
 		Package: "test",
-		Options: &descriptorV1.FileOptions{Deprecated: scalar.Bool(true)},
-		Messages: []Message{{
+		Options: &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		Messages: []ptype.Message{{
 			Name: "A", // "test.A"
-			Options: &descriptorV1.MessageOptions{
+			Options: &descriptorpb.MessageOptions{
 				MapEntry:   scalar.Bool(true),
 				Deprecated: scalar.Bool(true),
 			},
-			Fields: []Field{{
+			Fields: []ptype.Field{{
 				Name:        "key", // "test.A.key"
 				Number:      1,
-				Options:     &descriptorV1.FieldOptions{Deprecated: scalar.Bool(true)},
+				Options:     &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
 				Cardinality: pref.Optional,
 				Kind:        pref.StringKind,
 			}, {
@@ -132,11 +46,11 @@
 				Number:      2,
 				Cardinality: pref.Optional,
 				Kind:        pref.MessageKind,
-				MessageType: PlaceholderMessage("test.B"),
+				MessageType: ptype.PlaceholderMessage("test.B"),
 			}},
 		}, {
 			Name: "B", // "test.B"
-			Fields: []Field{{
+			Fields: []ptype.Field{{
 				Name:        "field_one", // "test.B.field_one"
 				Number:      1,
 				Cardinality: pref.Optional,
@@ -150,14 +64,14 @@
 				Cardinality: pref.Optional,
 				Kind:        pref.EnumKind,
 				Default:     pref.ValueOf(pref.EnumNumber(1)),
-				EnumType:    PlaceholderEnum("test.E1"),
+				EnumType:    ptype.PlaceholderEnum("test.E1"),
 				OneofName:   "O2",
 			}, {
 				Name:        "field_three", // "test.B.field_three"
 				Number:      3,
 				Cardinality: pref.Optional,
 				Kind:        pref.MessageKind,
-				MessageType: PlaceholderMessage("test.C"),
+				MessageType: ptype.PlaceholderMessage("test.C"),
 				OneofName:   "O2",
 			}, {
 				Name:        "field_four", // "test.B.field_four"
@@ -165,24 +79,24 @@
 				Number:      4,
 				Cardinality: pref.Repeated,
 				Kind:        pref.MessageKind,
-				MessageType: PlaceholderMessage("test.A"),
+				MessageType: ptype.PlaceholderMessage("test.A"),
 			}, {
 				Name:        "field_five", // "test.B.field_five"
 				Number:      5,
 				Cardinality: pref.Repeated,
 				Kind:        pref.Int32Kind,
-				Options:     &descriptorV1.FieldOptions{Packed: scalar.Bool(true)},
+				Options:     &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
 			}, {
 				Name:        "field_six", // "test.B.field_six"
 				Number:      6,
 				Cardinality: pref.Required,
 				Kind:        pref.BytesKind,
 			}},
-			Oneofs: []Oneof{
+			Oneofs: []ptype.Oneof{
 				{
 					Name: "O1", // "test.B.O1"
-					Options: &descriptorV1.OneofOptions{
-						UninterpretedOption: []*descriptorV1.UninterpretedOption{
+					Options: &descriptorpb.OneofOptions{
+						UninterpretedOption: []*descriptorpb.UninterpretedOption{
 							{StringValue: []byte("option")},
 						},
 					},
@@ -192,219 +106,219 @@
 			ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}, {3000, 3001}},
 		}, {
 			Name: "C", // "test.C"
-			Messages: []Message{{
+			Messages: []ptype.Message{{
 				Name:   "A", // "test.C.A"
-				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Required, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("dead\xbe\xef"))}},
+				Fields: []ptype.Field{{Name: "F", Number: 1, Cardinality: pref.Required, Kind: pref.BytesKind, Default: pref.ValueOf([]byte("dead\xbe\xef"))}},
 			}},
-			Enums: []Enum{{
+			Enums: []ptype.Enum{{
 				Name:   "E1", // "test.C.E1"
-				Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
+				Values: []ptype.EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
 			}},
-			Extensions: []Extension{{
+			Extensions: []ptype.Extension{{
 				Name:         "X", // "test.C.X"
 				Number:       1000,
 				Cardinality:  pref.Repeated,
 				Kind:         pref.MessageKind,
-				Options:      &descriptorV1.FieldOptions{Packed: scalar.Bool(false)},
-				MessageType:  PlaceholderMessage("test.C"),
-				ExtendedType: PlaceholderMessage("test.B"),
+				Options:      &descriptorpb.FieldOptions{Packed: scalar.Bool(false)},
+				MessageType:  ptype.PlaceholderMessage("test.C"),
+				ExtendedType: ptype.PlaceholderMessage("test.B"),
 			}},
 		}},
-		Enums: []Enum{{
+		Enums: []ptype.Enum{{
 			Name:    "E1", // "test.E1"
-			Options: &descriptorV1.EnumOptions{Deprecated: scalar.Bool(true)},
-			Values: []EnumValue{
+			Options: &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+			Values: []ptype.EnumValue{
 				{
 					Name:    "FOO",
 					Number:  0,
-					Options: &descriptorV1.EnumValueOptions{Deprecated: scalar.Bool(true)},
+					Options: &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
 				},
 				{Name: "BAR", Number: 1},
 			},
 		}},
-		Extensions: []Extension{{
+		Extensions: []ptype.Extension{{
 			Name:         "X", // "test.X"
 			Number:       1000,
 			Cardinality:  pref.Repeated,
 			Kind:         pref.MessageKind,
-			Options:      &descriptorV1.FieldOptions{Packed: scalar.Bool(true)},
-			MessageType:  PlaceholderMessage("test.C"),
-			ExtendedType: PlaceholderMessage("test.B"),
+			Options:      &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
+			MessageType:  ptype.PlaceholderMessage("test.C"),
+			ExtendedType: ptype.PlaceholderMessage("test.B"),
 		}},
-		Services: []Service{{
+		Services: []ptype.Service{{
 			Name:    "S", // "test.S"
-			Options: &descriptorV1.ServiceOptions{Deprecated: scalar.Bool(true)},
-			Methods: []Method{{
+			Options: &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+			Methods: []ptype.Method{{
 				Name:              "M", // "test.S.M"
-				InputType:         PlaceholderMessage("test.A"),
-				OutputType:        PlaceholderMessage("test.C.A"),
+				InputType:         ptype.PlaceholderMessage("test.A"),
+				OutputType:        ptype.PlaceholderMessage("test.C.A"),
 				IsStreamingClient: true,
 				IsStreamingServer: true,
-				Options:           &descriptorV1.MethodOptions{Deprecated: scalar.Bool(true)},
+				Options:           &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
 			}},
 		}},
 	}
-	fd1, err := NewFile(f1)
+	fd1, err := ptype.NewFile(f1)
 	if err != nil {
-		t.Fatalf("NewFile() error: %v", err)
+		t.Fatalf("prototype.NewFile() error: %v", err)
 	}
 
-	f2 := &descriptorV1.FileDescriptorProto{
+	f2 := &descriptorpb.FileDescriptorProto{
 		Syntax:  scalar.String("proto2"),
 		Name:    scalar.String("path/to/file.proto"),
 		Package: scalar.String("test"),
-		Options: &descriptorV1.FileOptions{Deprecated: scalar.Bool(true)},
-		MessageType: []*descriptorV1.DescriptorProto{{
+		Options: &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
+		MessageType: []*descriptorpb.DescriptorProto{{
 			Name: scalar.String("A"),
-			Options: &descriptorV1.MessageOptions{
+			Options: &descriptorpb.MessageOptions{
 				MapEntry:   scalar.Bool(true),
 				Deprecated: scalar.Bool(true),
 			},
-			Field: []*descriptorV1.FieldDescriptorProto{{
+			Field: []*descriptorpb.FieldDescriptorProto{{
 				Name:    scalar.String("key"),
 				Number:  scalar.Int32(1),
-				Options: &descriptorV1.FieldOptions{Deprecated: scalar.Bool(true)},
-				Label:   descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:    descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				Options: &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
 			}, {
 				Name:     scalar.String("value"),
 				Number:   scalar.Int32(2),
-				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
 				TypeName: scalar.String(".test.B"),
 			}},
 		}, {
 			Name: scalar.String("B"),
-			Field: []*descriptorV1.FieldDescriptorProto{{
+			Field: []*descriptorpb.FieldDescriptorProto{{
 				Name:         scalar.String("field_one"),
 				Number:       scalar.Int32(1),
-				Label:        descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:         descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.StringKind).Enum(),
 				DefaultValue: scalar.String("hello, \"world!\"\n"),
 				OneofIndex:   scalar.Int32(0),
 			}, {
 				Name:         scalar.String("field_two"),
 				JsonName:     scalar.String("Field2"),
 				Number:       scalar.Int32(2),
-				Label:        descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:         descriptorV1.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
+				Label:        descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:         descriptorpb.FieldDescriptorProto_Type(pref.EnumKind).Enum(),
 				DefaultValue: scalar.String("BAR"),
 				TypeName:     scalar.String(".test.E1"),
 				OneofIndex:   scalar.Int32(1),
 			}, {
 				Name:       scalar.String("field_three"),
 				Number:     scalar.Int32(3),
-				Label:      descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:       descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				Label:      descriptorpb.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:       descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
 				TypeName:   scalar.String(".test.C"),
 				OneofIndex: scalar.Int32(1),
 			}, {
 				Name:     scalar.String("field_four"),
 				JsonName: scalar.String("Field4"),
 				Number:   scalar.Int32(4),
-				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
-				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
 				TypeName: scalar.String(".test.A"),
 			}, {
 				Name:    scalar.String("field_five"),
 				Number:  scalar.Int32(5),
-				Label:   descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
-				Type:    descriptorV1.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
-				Options: &descriptorV1.FieldOptions{Packed: scalar.Bool(true)},
+				Label:   descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:    descriptorpb.FieldDescriptorProto_Type(pref.Int32Kind).Enum(),
+				Options: &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
 			}, {
 				Name:   scalar.String("field_six"),
 				Number: scalar.Int32(6),
-				Label:  descriptorV1.FieldDescriptorProto_Label(pref.Required).Enum(),
-				Type:   descriptorV1.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+				Label:  descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+				Type:   descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
 			}},
-			OneofDecl: []*descriptorV1.OneofDescriptorProto{
+			OneofDecl: []*descriptorpb.OneofDescriptorProto{
 				{
 					Name: scalar.String("O1"),
-					Options: &descriptorV1.OneofOptions{
-						UninterpretedOption: []*descriptorV1.UninterpretedOption{
+					Options: &descriptorpb.OneofOptions{
+						UninterpretedOption: []*descriptorpb.UninterpretedOption{
 							{StringValue: []byte("option")},
 						},
 					},
 				},
 				{Name: scalar.String("O2")},
 			},
-			ExtensionRange: []*descriptorV1.DescriptorProto_ExtensionRange{
+			ExtensionRange: []*descriptorpb.DescriptorProto_ExtensionRange{
 				{Start: scalar.Int32(1000), End: scalar.Int32(2000)},
 				{Start: scalar.Int32(3000), End: scalar.Int32(3001)},
 			},
 		}, {
 			Name: scalar.String("C"),
-			NestedType: []*descriptorV1.DescriptorProto{{
+			NestedType: []*descriptorpb.DescriptorProto{{
 				Name: scalar.String("A"),
-				Field: []*descriptorV1.FieldDescriptorProto{{
+				Field: []*descriptorpb.FieldDescriptorProto{{
 					Name:         scalar.String("F"),
 					Number:       scalar.Int32(1),
-					Label:        descriptorV1.FieldDescriptorProto_Label(pref.Required).Enum(),
-					Type:         descriptorV1.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
+					Label:        descriptorpb.FieldDescriptorProto_Label(pref.Required).Enum(),
+					Type:         descriptorpb.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
 					DefaultValue: scalar.String(`dead\276\357`),
 				}},
 			}},
-			EnumType: []*descriptorV1.EnumDescriptorProto{{
+			EnumType: []*descriptorpb.EnumDescriptorProto{{
 				Name: scalar.String("E1"),
-				Value: []*descriptorV1.EnumValueDescriptorProto{
+				Value: []*descriptorpb.EnumValueDescriptorProto{
 					{Name: scalar.String("FOO"), Number: scalar.Int32(0)},
 					{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
 				},
 			}},
-			Extension: []*descriptorV1.FieldDescriptorProto{{
+			Extension: []*descriptorpb.FieldDescriptorProto{{
 				Name:     scalar.String("X"),
 				Number:   scalar.Int32(1000),
-				Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
-				Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+				Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
 				TypeName: scalar.String(".test.C"),
 				Extendee: scalar.String(".test.B"),
 			}},
 		}},
-		EnumType: []*descriptorV1.EnumDescriptorProto{{
+		EnumType: []*descriptorpb.EnumDescriptorProto{{
 			Name:    scalar.String("E1"),
-			Options: &descriptorV1.EnumOptions{Deprecated: scalar.Bool(true)},
-			Value: []*descriptorV1.EnumValueDescriptorProto{
+			Options: &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
+			Value: []*descriptorpb.EnumValueDescriptorProto{
 				{
 					Name:    scalar.String("FOO"),
 					Number:  scalar.Int32(0),
-					Options: &descriptorV1.EnumValueOptions{Deprecated: scalar.Bool(true)},
+					Options: &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
 				},
 				{Name: scalar.String("BAR"), Number: scalar.Int32(1)},
 			},
 		}},
-		Extension: []*descriptorV1.FieldDescriptorProto{{
+		Extension: []*descriptorpb.FieldDescriptorProto{{
 			Name:     scalar.String("X"),
 			Number:   scalar.Int32(1000),
-			Label:    descriptorV1.FieldDescriptorProto_Label(pref.Repeated).Enum(),
-			Type:     descriptorV1.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
-			Options:  &descriptorV1.FieldOptions{Packed: scalar.Bool(true)},
+			Label:    descriptorpb.FieldDescriptorProto_Label(pref.Repeated).Enum(),
+			Type:     descriptorpb.FieldDescriptorProto_Type(pref.MessageKind).Enum(),
+			Options:  &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
 			TypeName: scalar.String(".test.C"),
 			Extendee: scalar.String(".test.B"),
 		}},
-		Service: []*descriptorV1.ServiceDescriptorProto{{
+		Service: []*descriptorpb.ServiceDescriptorProto{{
 			Name:    scalar.String("S"),
-			Options: &descriptorV1.ServiceOptions{Deprecated: scalar.Bool(true)},
-			Method: []*descriptorV1.MethodDescriptorProto{{
+			Options: &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
+			Method: []*descriptorpb.MethodDescriptorProto{{
 				Name:            scalar.String("M"),
 				InputType:       scalar.String(".test.A"),
 				OutputType:      scalar.String(".test.C.A"),
 				ClientStreaming: scalar.Bool(true),
 				ServerStreaming: scalar.Bool(true),
-				Options:         &descriptorV1.MethodOptions{Deprecated: scalar.Bool(true)},
+				Options:         &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
 			}},
 		}},
 	}
-	fd2, err := NewFileFromDescriptorProto(f2, nil)
+	fd2, err := pdesc.NewFile(f2, nil)
 	if err != nil {
-		t.Fatalf("NewFileFromDescriptorProto() error: %v", err)
+		t.Fatalf("protodesc.NewFile() error: %v", err)
 	}
 
 	tests := []struct {
 		name string
 		desc pref.FileDescriptor
 	}{
-		{"NewFile", fd1},
-		{"NewFileFromDescriptorProto", fd2},
+		{"prototype.NewFile", fd1},
+		{"protodesc.NewFile", fd2},
 	}
 	for _, tt := range tests {
 		tt := tt
@@ -431,7 +345,7 @@
 		"Path":          "path/to/file.proto",
 		"Package":       pref.FullName("test"),
 		"IsPlaceholder": false,
-		"Options":       &descriptorV1.FileOptions{Deprecated: scalar.Bool(true)},
+		"Options":       &descriptorpb.FileOptions{Deprecated: scalar.Bool(true)},
 		"Messages": M{
 			"Len": 3,
 			"Get:0": M{
@@ -442,7 +356,7 @@
 				"FullName":      pref.FullName("test.A"),
 				"IsPlaceholder": false,
 				"IsMapEntry":    true,
-				"Options": &descriptorV1.MessageOptions{
+				"Options": &descriptorpb.MessageOptions{
 					MapEntry:   scalar.Bool(true),
 					Deprecated: scalar.Bool(true),
 				},
@@ -456,7 +370,7 @@
 						"Number":       pref.FieldNumber(1),
 						"Cardinality":  pref.Optional,
 						"Kind":         pref.StringKind,
-						"Options":      &descriptorV1.FieldOptions{Deprecated: scalar.Bool(true)},
+						"Options":      &descriptorpb.FieldOptions{Deprecated: scalar.Bool(true)},
 						"JSONName":     "key",
 						"IsPacked":     false,
 						"IsMap":        false,
@@ -546,8 +460,8 @@
 					"ByName:O1": M{
 						"FullName": pref.FullName("test.B.O1"),
 						"Index":    0,
-						"Options": &descriptorV1.OneofOptions{
-							UninterpretedOption: []*descriptorV1.UninterpretedOption{
+						"Options": &descriptorpb.OneofOptions{
+							UninterpretedOption: []*descriptorpb.UninterpretedOption{
 								{StringValue: []byte("option")},
 							},
 						},
@@ -604,13 +518,13 @@
 			"Len": 1,
 			"Get:0": M{
 				"Name":    pref.Name("E1"),
-				"Options": &descriptorV1.EnumOptions{Deprecated: scalar.Bool(true)},
+				"Options": &descriptorpb.EnumOptions{Deprecated: scalar.Bool(true)},
 				"Values": M{
 					"Len":        2,
 					"ByName:Foo": nil,
 					"ByName:FOO": M{
 						"FullName": pref.FullName("test.FOO"),
-						"Options":  &descriptorV1.EnumValueOptions{Deprecated: scalar.Bool(true)},
+						"Options":  &descriptorpb.EnumValueOptions{Deprecated: scalar.Bool(true)},
 					},
 					"ByNumber:2": nil,
 					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
@@ -627,7 +541,7 @@
 				"IsPacked":     true,
 				"MessageType":  M{"FullName": pref.FullName("test.C"), "IsPlaceholder": false},
 				"ExtendedType": M{"FullName": pref.FullName("test.B"), "IsPlaceholder": false},
-				"Options":      &descriptorV1.FieldOptions{Packed: scalar.Bool(true)},
+				"Options":      &descriptorpb.FieldOptions{Packed: scalar.Bool(true)},
 			},
 		},
 		"Services": M{
@@ -637,7 +551,7 @@
 				"Parent":   M{"FullName": pref.FullName("test")},
 				"Name":     pref.Name("S"),
 				"FullName": pref.FullName("test.S"),
-				"Options":  &descriptorV1.ServiceOptions{Deprecated: scalar.Bool(true)},
+				"Options":  &descriptorpb.ServiceOptions{Deprecated: scalar.Bool(true)},
 				"Methods": M{
 					"Len": 1,
 					"Get:0": M{
@@ -648,7 +562,7 @@
 						"OutputType":        M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
 						"IsStreamingClient": true,
 						"IsStreamingServer": true,
-						"Options":           &descriptorV1.MethodOptions{Deprecated: scalar.Bool(true)},
+						"Options":           &descriptorpb.MethodOptions{Deprecated: scalar.Bool(true)},
 					},
 				},
 			},
@@ -684,13 +598,20 @@
 	checkAccessors(t, "", reflect.ValueOf(fd), want)
 }
 func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]interface{}) {
+	p0 := p
+	defer func() {
+		if ex := recover(); ex != nil {
+			t.Errorf("panic at %v: %v", p, ex)
+		}
+	}()
+
 	if rv.Interface() == nil {
 		t.Errorf("%v is nil, want non-nil", p)
 		return
 	}
 	for s, v := range want {
 		// Call the accessor method.
-		p := p + "." + s
+		p = p0 + "." + s
 		var rets []reflect.Value
 		if i := strings.IndexByte(s, ':'); i >= 0 {
 			// Accessor method takes in a single argument, which is encoded
@@ -912,138 +833,3 @@
 	}
 	return string(b)
 }
-
-func TestResolve(t *testing.T) {
-	f := &File{
-		Syntax:  pref.Proto2,
-		Package: "test",
-		Messages: []Message{{
-			Name:   "FooMessage",
-			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
-			Messages: []Message{{
-				Name:   "FooMessage",
-				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
-			}, {
-				Name:   "BarMessage",
-				Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
-			}},
-			Enums: []Enum{{
-				Name:   "FooEnum",
-				Values: []EnumValue{{Name: "E", Number: 0}},
-			}, {
-				Name:   "BarEnum",
-				Values: []EnumValue{{Name: "E", Number: 0}},
-			}},
-		}, {
-			Name:   "BarMessage",
-			Fields: []Field{{Name: "F", Number: 1, Cardinality: pref.Optional, Kind: pref.BytesKind}},
-		}},
-		Enums: []Enum{{
-			Name:   "FooEnum",
-			Values: []EnumValue{{Name: "E", Number: 0}},
-		}, {
-			Name:   "BarEnum",
-			Values: []EnumValue{{Name: "E", Number: 0}},
-		}},
-	}
-
-	fd, err := NewFile(f)
-	if err != nil {
-		t.Fatalf("NewFile() error: %v", err)
-	}
-
-	tests := []struct {
-		parent pref.Descriptor
-		name   pref.FullName
-		want   pref.Descriptor
-	}{{
-		parent: fd.Enums().Get(0),
-		name:   "test.Foo",
-		want:   nil,
-	}, {
-		parent: fd.Enums().Get(0),
-		name:   "test.FooEnum",
-		want:   fd.Enums().Get(0),
-	}, {
-		parent: fd.Enums().Get(0),
-		name:   "test.BarEnum",
-		want:   fd.Enums().Get(1),
-	}, {
-		parent: fd.Enums().Get(0),
-		name:   "test.BarMessage",
-		want:   fd.Messages().Get(1),
-	}, {
-		parent: fd.Enums().Get(0),
-		name:   "test.FooMessage.BarMessage",
-		want:   fd.Messages().Get(0).Messages().Get(1),
-	}, {
-		parent: fd.Enums().Get(0),
-		name:   "test.FooMessage.Bar",
-		want:   nil,
-	}, {
-		parent: fd.Messages().Get(1),
-		name:   "test.FooMessage.BarEnum",
-		want:   fd.Messages().Get(0).Enums().Get(1),
-	}, {
-		parent: fd.Messages().Get(1),
-		name:   "test.FooEnum",
-		want:   fd.Enums().Get(0),
-	}, {
-		parent: fd.Messages().Get(0),
-		name:   "test.FooEnum",
-		want:   fd.Enums().Get(0),
-	}, {
-		parent: fd.Messages().Get(0),
-		name:   "test.FooEnum.NonExistent",
-		want:   nil,
-	}, {
-		parent: fd.Messages().Get(0),
-		name:   "test.FooMessage.FooEnum",
-		want:   fd.Messages().Get(0).Enums().Get(0),
-	}, {
-		parent: fd.Messages().Get(0),
-		name:   "test.FooMessage",
-		want:   fd.Messages().Get(0),
-	}, {
-		parent: fd.Messages().Get(0),
-		name:   "test.FooMessage.Fizz",
-		want:   nil,
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "test.FooMessage.FooMessage",
-		want:   fd.Messages().Get(0).Messages().Get(0),
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "test.FooMessage.BarMessage",
-		want:   fd.Messages().Get(0).Messages().Get(1),
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "test.BarMessage.FooMessage",
-		want:   nil,
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "test.BarMessage",
-		want:   fd.Messages().Get(1),
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "test.BarMessageExtra",
-		want:   nil,
-	}, {
-		parent: fd.Messages().Get(0).Messages().Get(0),
-		name:   "taste.BarMessage",
-		want:   nil,
-	}}
-
-	for _, tt := range tests {
-		got := resolveReference(tt.parent, tt.name)
-		if got != tt.want {
-			fullName := func(d pref.Descriptor) string {
-				if d == nil {
-					return "<nil>"
-				}
-				return string(d.FullName())
-			}
-			t.Errorf("resolveReference(%v, %v) = %v, want %v", fullName(tt.parent), tt.name, fullName(got), fullName(tt.want))
-		}
-	}
-}
diff --git a/reflect/prototype/validate.go b/reflect/prototype/validate.go
index be1090b..934237b 100644
--- a/reflect/prototype/validate.go
+++ b/reflect/prototype/validate.go
@@ -26,9 +26,7 @@
 //	* Placeholder messages and types may only be for weak fields.
 //	* Placeholder full names must be valid.
 //	* The name of each descriptor must be valid.
-//	* Options are consistent with constructor fields:
-//		Message.IsMapEntry and Message.Options.MapEntry
-//		Field.IsPacked and Field.Options.Packed
+//	* Options are of the correct Go type (e.g. *descriptorpb.MessageOptions).
 
 func validateFile(t pref.FileDescriptor) error {
 	return nil