reflect/protoreflect: add Descriptor.Options method

Add a method to fetch descriptor options. Since options are proto
messages (e.g., google.protobuf.FieldOptions), and proto message
packages depend on the protoreflect package, returning the actual option
type would cause a dependency cycle. Instead, we return an interface
value which can be type asserted to the appropriate concrete type.

Add options support to the prototype package.

Some of the prototype constructors included fields (such as
Field.IsPacked) which represent information from the options
(such as google.protobuf.FieldOptions.packed). To avoid confusion about
the canonical source of information, drop these fields in favor of the
options.

Drop the unimplemented Descriptor.DescriptorOptionsProto.

Change-Id: I66579b6a7d10d99eb6977402a247306a78913e74
Reviewed-on: https://go-review.googlesource.com/c/144277
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go b/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
index e35f7c3..a8283e1 100644
--- a/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
+++ b/cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go
@@ -10,6 +10,7 @@
 	"strconv"
 	"strings"
 
+	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/protogen"
 )
 
@@ -56,7 +57,7 @@
 	g.P("// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.")
 
 	// Client interface.
-	if serviceOptions(gen, service).GetDeprecated() {
+	if service.Desc.Options().(*descpb.ServiceOptions).GetDeprecated() {
 		g.P("//")
 		g.P(deprecationComment)
 	}
@@ -77,7 +78,7 @@
 	g.P()
 
 	// NewClient factory.
-	if serviceOptions(gen, service).GetDeprecated() {
+	if service.Desc.Options().(*descpb.ServiceOptions).GetDeprecated() {
 		g.P(deprecationComment)
 	}
 	g.P("func New", clientName, " (cc *", ident("grpc.ClientConn"), ") ", clientName, " {")
@@ -102,7 +103,7 @@
 	// Server interface.
 	serverType := service.GoName + "Server"
 	g.P("// ", serverType, " is the server API for ", service.GoName, " service.")
-	if serviceOptions(gen, service).GetDeprecated() {
+	if service.Desc.Options().(*descpb.ServiceOptions).GetDeprecated() {
 		g.P("//")
 		g.P(deprecationComment)
 	}
@@ -117,7 +118,7 @@
 	g.P()
 
 	// Server registration.
-	if serviceOptions(gen, service).GetDeprecated() {
+	if service.Desc.Options().(*descpb.ServiceOptions).GetDeprecated() {
 		g.P(deprecationComment)
 	}
 	serviceDescVar := "_" + service.GoName + "_serviceDesc"
@@ -189,7 +190,7 @@
 	service := method.ParentService
 	sname := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name())
 
-	if methodOptions(gen, method).GetDeprecated() {
+	if method.Desc.Options().(*descpb.MethodOptions).GetDeprecated() {
 		g.P(deprecationComment)
 	}
 	g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{")
diff --git a/cmd/protoc-gen-go-grpc/internal_gengogrpc/options.go b/cmd/protoc-gen-go-grpc/internal_gengogrpc/options.go
deleted file mode 100644
index fad4aa0..0000000
--- a/cmd/protoc-gen-go-grpc/internal_gengogrpc/options.go
+++ /dev/null
@@ -1,116 +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.
-
-// This file contains functions for fetching the options for a protoreflect descriptor.
-//
-// TODO: Replace this with the appropriate protoreflect API, once it exists.
-
-package internal_gengogrpc
-
-import (
-	"github.com/golang/protobuf/proto"
-	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/protogen"
-	"github.com/golang/protobuf/v2/reflect/protoreflect"
-)
-
-// serviceOptions returns the options for a service.
-func serviceOptions(gen *protogen.Plugin, service *protogen.Service) *descpb.ServiceOptions {
-	d := getDescriptorProto(gen, service.Desc, service.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.ServiceDescriptorProto).GetOptions()
-}
-
-// methodOptions returns the options for a method.
-func methodOptions(gen *protogen.Plugin, method *protogen.Method) *descpb.MethodOptions {
-	d := getDescriptorProto(gen, method.Desc, method.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.MethodDescriptorProto).GetOptions()
-}
-
-func getDescriptorProto(gen *protogen.Plugin, desc protoreflect.Descriptor, path []int32) proto.Message {
-	var p proto.Message
-	// Look up the FileDescriptorProto.
-	for {
-		if fdesc, ok := desc.(protoreflect.FileDescriptor); ok {
-			file, ok := gen.FileByName(fdesc.Path())
-			if !ok {
-				return nil
-			}
-			p = file.Proto
-			break
-		}
-		var ok bool
-		desc, ok = desc.Parent()
-		if !ok {
-			return nil
-		}
-	}
-	const (
-		// field numbers in FileDescriptorProto
-		filePackageField   = 2 // package
-		fileMessageField   = 4 // message_type
-		fileEnumField      = 5 // enum_type
-		fileServiceField   = 6 // service
-		fileExtensionField = 7 // extension
-		// field numbers in DescriptorProto
-		messageFieldField     = 2 // field
-		messageMessageField   = 3 // nested_type
-		messageEnumField      = 4 // enum_type
-		messageExtensionField = 6 // extension
-		messageOneofField     = 8 // oneof_decl
-		// field numbers in EnumDescriptorProto
-		enumValueField = 2 // value
-		// field numbers in ServiceDescriptorProto
-		serviceMethodField = 2 // method
-	)
-	for len(path) > 1 {
-		switch d := p.(type) {
-		case *descpb.FileDescriptorProto:
-			switch path[0] {
-			case fileMessageField:
-				p = d.MessageType[path[1]]
-			case fileEnumField:
-				p = d.EnumType[path[1]]
-			case fileServiceField:
-				p = d.Service[path[1]]
-			default:
-				return nil
-			}
-		case *descpb.DescriptorProto:
-			switch path[0] {
-			case messageFieldField:
-				p = d.Field[path[1]]
-			case messageMessageField:
-				p = d.NestedType[path[1]]
-			case messageEnumField:
-				p = d.EnumType[path[1]]
-			default:
-				return nil
-			}
-		case *descpb.EnumDescriptorProto:
-			switch path[0] {
-			case enumValueField:
-				p = d.Value[path[1]]
-			default:
-				return nil
-			}
-		case *descpb.ServiceDescriptorProto:
-			switch path[0] {
-			case serviceMethodField:
-				p = d.Method[path[1]]
-			default:
-				return nil
-			}
-		default:
-			return nil
-		}
-		path = path[2:]
-	}
-	return p
-}
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index 154e95a..b5b0f5a 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -237,13 +237,13 @@
 	g.PrintLeadingComments(enum.Location)
 	g.Annotate(enum.GoIdent.GoName, enum.Location)
 	g.P("type ", enum.GoIdent, " int32",
-		deprecationComment(enumOptions(gen, enum).GetDeprecated()))
+		deprecationComment(enum.Desc.Options().(*descpb.EnumOptions).GetDeprecated()))
 	g.P("const (")
 	for _, value := range enum.Values {
 		g.PrintLeadingComments(value.Location)
 		g.Annotate(value.GoIdent.GoName, value.Location)
 		g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(),
-			deprecationComment(enumValueOptions(gen, value).GetDeprecated()))
+			deprecationComment(value.Desc.Options().(*descpb.EnumValueOptions).GetDeprecated()))
 	}
 	g.P(")")
 	g.P()
@@ -333,7 +333,7 @@
 	}
 
 	hasComment := g.PrintLeadingComments(message.Location)
-	if messageOptions(gen, message).GetDeprecated() {
+	if message.Desc.Options().(*descpb.MessageOptions).GetDeprecated() {
 		if hasComment {
 			g.P("//")
 		}
@@ -371,13 +371,13 @@
 		}
 		g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
 		g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
-			deprecationComment(fieldOptions(gen, field).GetDeprecated()))
+			deprecationComment(field.Desc.Options().(*descpb.FieldOptions).GetDeprecated()))
 	}
 	g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
 
 	if message.Desc.ExtensionRanges().Len() > 0 {
 		var tags []string
-		if messageOptions(gen, message).GetMessageSetWireFormat() {
+		if message.Desc.Options().(*descpb.MessageOptions).GetMessageSetWireFormat() {
 			tags = append(tags, `protobuf_messageset:"1"`)
 		}
 		tags = append(tags, `json:"-"`)
@@ -413,7 +413,7 @@
 
 	// ExtensionRangeArray
 	if extranges := message.Desc.ExtensionRanges(); extranges.Len() > 0 {
-		if messageOptions(gen, message).GetMessageSetWireFormat() {
+		if message.Desc.Options().(*descpb.MessageOptions).GetMessageSetWireFormat() {
 			g.P("func (m *", message.GoIdent, ") MarshalJSON() ([]byte, error) {")
 			g.P("return ", protogen.GoIdent{
 				GoImportPath: protoPackage,
@@ -545,7 +545,7 @@
 		}
 		goType, pointer := fieldGoType(g, field)
 		defaultValue := fieldDefaultValue(g, message, field)
-		if fieldOptions(gen, field).GetDeprecated() {
+		if field.Desc.Options().(*descpb.FieldOptions).GetDeprecated() {
 			g.P(deprecationComment(true))
 		}
 		g.Annotate(message.GoIdent.GoName+".Get"+field.GoName, field.Location)
@@ -836,7 +836,7 @@
 
 func isExtensionMessageSetElement(gen *protogen.Plugin, extension *protogen.Extension) bool {
 	return extension.ParentMessage != nil &&
-		messageOptions(gen, extension.ExtendedType).GetMessageSetWireFormat() &&
+		extension.ExtendedType.Desc.Options().(*descpb.MessageOptions).GetMessageSetWireFormat() &&
 		extension.Desc.Name() == "message_set_extension"
 }
 
diff --git a/cmd/protoc-gen-go/internal_gengo/options.go b/cmd/protoc-gen-go/internal_gengo/options.go
deleted file mode 100644
index 427fe40..0000000
--- a/cmd/protoc-gen-go/internal_gengo/options.go
+++ /dev/null
@@ -1,122 +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.
-
-// This file contains functions for fetching the options for a protoreflect descriptor.
-//
-// TODO: Replace this with the appropriate protoreflect API, once it exists.
-
-package internal_gengo
-
-import (
-	"github.com/golang/protobuf/proto"
-	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/protogen"
-	"github.com/golang/protobuf/v2/reflect/protoreflect"
-)
-
-// messageOptions returns the MessageOptions for a message.
-func messageOptions(gen *protogen.Plugin, message *protogen.Message) *descpb.MessageOptions {
-	d := getDescriptorProto(gen, message.Desc, message.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.DescriptorProto).GetOptions()
-}
-
-// fieldOptions returns the FieldOptions for a message.
-func fieldOptions(gen *protogen.Plugin, field *protogen.Field) *descpb.FieldOptions {
-	d := getDescriptorProto(gen, field.Desc, field.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.FieldDescriptorProto).GetOptions()
-}
-
-// enumOptions returns the EnumOptions for an enum
-func enumOptions(gen *protogen.Plugin, enum *protogen.Enum) *descpb.EnumOptions {
-	d := getDescriptorProto(gen, enum.Desc, enum.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.EnumDescriptorProto).GetOptions()
-}
-
-// enumValueOptions returns the EnumValueOptions for an enum value
-func enumValueOptions(gen *protogen.Plugin, value *protogen.EnumValue) *descpb.EnumValueOptions {
-	d := getDescriptorProto(gen, value.Desc, value.Location.Path)
-	if d == nil {
-		return nil
-	}
-	return d.(*descpb.EnumValueDescriptorProto).GetOptions()
-}
-
-func getDescriptorProto(gen *protogen.Plugin, desc protoreflect.Descriptor, path []int32) proto.Message {
-	var p proto.Message
-	// Look up the FileDescriptorProto.
-	for {
-		if fdesc, ok := desc.(protoreflect.FileDescriptor); ok {
-			file, ok := gen.FileByName(fdesc.Path())
-			if !ok {
-				return nil
-			}
-			p = file.Proto
-			break
-		}
-		var ok bool
-		desc, ok = desc.Parent()
-		if !ok {
-			return nil
-		}
-	}
-	const (
-		// field numbers in FileDescriptorProto
-		filePackageField   = 2 // package
-		fileMessageField   = 4 // message_type
-		fileEnumField      = 5 // enum_type
-		fileExtensionField = 7 // extension
-		// field numbers in DescriptorProto
-		messageFieldField     = 2 // field
-		messageMessageField   = 3 // nested_type
-		messageEnumField      = 4 // enum_type
-		messageExtensionField = 6 // extension
-		messageOneofField     = 8 // oneof_decl
-		// field numbers in EnumDescriptorProto
-		enumValueField = 2 // value
-	)
-	for len(path) > 1 {
-		switch d := p.(type) {
-		case *descpb.FileDescriptorProto:
-			switch path[0] {
-			case fileMessageField:
-				p = d.MessageType[path[1]]
-			case fileEnumField:
-				p = d.EnumType[path[1]]
-			default:
-				return nil
-			}
-		case *descpb.DescriptorProto:
-			switch path[0] {
-			case messageFieldField:
-				p = d.Field[path[1]]
-			case messageMessageField:
-				p = d.NestedType[path[1]]
-			case messageEnumField:
-				p = d.EnumType[path[1]]
-			default:
-				return nil
-			}
-		case *descpb.EnumDescriptorProto:
-			switch path[0] {
-			case enumValueField:
-				p = d.Value[path[1]]
-			default:
-				return nil
-			}
-		default:
-			return nil
-		}
-		path = path[2:]
-	}
-	return p
-}
diff --git a/internal/cmd/pbdump/pbdump.go b/internal/cmd/pbdump/pbdump.go
index 1a4b0c1..ec65a55 100644
--- a/internal/cmd/pbdump/pbdump.go
+++ b/internal/cmd/pbdump/pbdump.go
@@ -17,6 +17,8 @@
 	"strconv"
 	"strings"
 
+	protoV1 "github.com/golang/protobuf/proto"
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/internal/encoding/pack"
 	"github.com/golang/protobuf/v2/internal/encoding/wire"
 	"github.com/golang/protobuf/v2/reflect/protoreflect"
@@ -227,7 +229,7 @@
 			protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind,
 			protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind:
 			f.Cardinality = protoreflect.Repeated
-			f.IsPacked = true
+			f.Options = &descriptorV1.FieldOptions{Packed: protoV1.Bool(true)}
 		case protoreflect.MessageKind, protoreflect.GroupKind:
 			s := name.Append(protoreflect.Name(fmt.Sprintf("M%d", n)))
 			f.MessageType = prototype.PlaceholderMessage(s)
diff --git a/internal/cmd/pbdump/pbdump_test.go b/internal/cmd/pbdump/pbdump_test.go
index 0d03c26..ddc20a1 100644
--- a/internal/cmd/pbdump/pbdump_test.go
+++ b/internal/cmd/pbdump/pbdump_test.go
@@ -67,7 +67,7 @@
 					Name: "M20",
 					Fields: []ptype.Field{
 						{Name: "f30", Number: 30, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: ptype.PlaceholderMessage("M.M10.M20.M30")},
-						{Name: "f31", Number: 31, Cardinality: pref.Repeated, IsPacked: true, Kind: pref.Int32Kind},
+						{Name: "f31", Number: 31, Cardinality: pref.Repeated, Kind: pref.Int32Kind},
 					},
 					Messages: []ptype.Message{{
 						Name: "M30",
@@ -87,6 +87,7 @@
 			return x.FullName() == y.FullName()
 		}),
 		cmpopts.IgnoreFields(ptype.Field{}, "Default"),
+		cmpopts.IgnoreFields(ptype.Field{}, "Options"),
 		cmpopts.IgnoreUnexported(ptype.Message{}, ptype.Field{}),
 	}
 	for _, tt := range tests {
diff --git a/internal/encoding/pack/pack_test.go b/internal/encoding/pack/pack_test.go
index 293dcdb..dcaf328 100644
--- a/internal/encoding/pack/pack_test.go
+++ b/internal/encoding/pack/pack_test.go
@@ -11,6 +11,7 @@
 	"math"
 	"testing"
 
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 	ptype "github.com/golang/protobuf/v2/reflect/prototype"
 	"github.com/google/go-cmp/cmp"
@@ -21,16 +22,16 @@
 		Syntax:   pref.Proto2,
 		FullName: "Message",
 		Fields: []ptype.Field{
-			{Name: "F1", Number: 1, Cardinality: pref.Repeated, Kind: pref.BoolKind, IsPacked: true},
-			{Name: "F2", Number: 2, Cardinality: pref.Repeated, Kind: pref.Int64Kind, IsPacked: true},
-			{Name: "F3", Number: 3, Cardinality: pref.Repeated, Kind: pref.Sint64Kind, IsPacked: true},
-			{Name: "F4", Number: 4, Cardinality: pref.Repeated, Kind: pref.Uint64Kind, IsPacked: true},
-			{Name: "F5", Number: 5, Cardinality: pref.Repeated, Kind: pref.Fixed32Kind, IsPacked: true},
-			{Name: "F6", Number: 6, Cardinality: pref.Repeated, Kind: pref.Sfixed32Kind, IsPacked: true},
-			{Name: "F7", Number: 7, Cardinality: pref.Repeated, Kind: pref.FloatKind, IsPacked: true},
-			{Name: "F8", Number: 8, Cardinality: pref.Repeated, Kind: pref.Fixed64Kind, IsPacked: true},
-			{Name: "F9", Number: 9, Cardinality: pref.Repeated, Kind: pref.Sfixed64Kind, IsPacked: true},
-			{Name: "F10", Number: 10, Cardinality: pref.Repeated, Kind: pref.DoubleKind, IsPacked: true},
+			{Name: "F1", Number: 1, Cardinality: pref.Repeated, Kind: pref.BoolKind, Options: packedOpt(true)},
+			{Name: "F2", Number: 2, Cardinality: pref.Repeated, Kind: pref.Int64Kind, Options: packedOpt(true)},
+			{Name: "F3", Number: 3, Cardinality: pref.Repeated, Kind: pref.Sint64Kind, Options: packedOpt(true)},
+			{Name: "F4", Number: 4, Cardinality: pref.Repeated, Kind: pref.Uint64Kind, Options: packedOpt(true)},
+			{Name: "F5", Number: 5, Cardinality: pref.Repeated, Kind: pref.Fixed32Kind, Options: packedOpt(true)},
+			{Name: "F6", Number: 6, Cardinality: pref.Repeated, Kind: pref.Sfixed32Kind, Options: packedOpt(true)},
+			{Name: "F7", Number: 7, Cardinality: pref.Repeated, Kind: pref.FloatKind, Options: packedOpt(true)},
+			{Name: "F8", Number: 8, Cardinality: pref.Repeated, Kind: pref.Fixed64Kind, Options: packedOpt(true)},
+			{Name: "F9", Number: 9, Cardinality: pref.Repeated, Kind: pref.Sfixed64Kind, Options: packedOpt(true)},
+			{Name: "F10", Number: 10, Cardinality: pref.Repeated, Kind: pref.DoubleKind, Options: packedOpt(true)},
 			{Name: "F11", Number: 11, Cardinality: pref.Optional, Kind: pref.StringKind},
 			{Name: "F12", Number: 12, Cardinality: pref.Optional, Kind: pref.BytesKind},
 			{Name: "F13", Number: 13, Cardinality: pref.Optional, Kind: pref.MessageKind, MessageType: ptype.PlaceholderMessage("Message")},
@@ -42,6 +43,10 @@
 	return mtyp
 }()
 
+func packedOpt(b bool) *descriptorV1.FieldOptions {
+	return &descriptorV1.FieldOptions{Packed: &b}
+}
+
 // dhex decodes a hex-string and returns the bytes and panics if s is invalid.
 func dhex(s string) []byte {
 	b, err := hex.DecodeString(s)
diff --git a/internal/impl/legacy_message.go b/internal/impl/legacy_message.go
index b30c667..12c1832 100644
--- a/internal/impl/legacy_message.go
+++ b/internal/impl/legacy_message.go
@@ -13,6 +13,8 @@
 	"sync"
 	"unicode"
 
+	protoV1 "github.com/golang/protobuf/proto"
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/internal/encoding/text"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 	ptype "github.com/golang/protobuf/v2/reflect/prototype"
@@ -179,6 +181,9 @@
 		t = t.Elem()
 	}
 
+	f.Options = &descriptorV1.FieldOptions{
+		Packed: protoV1.Bool(false),
+	}
 	for len(tag) > 0 {
 		i := strings.IndexByte(tag, ',')
 		if i < 0 {
@@ -251,9 +256,9 @@
 		case strings.HasPrefix(s, "json="):
 			f.JSONName = s[len("json="):]
 		case s == "packed":
-			f.IsPacked = true
+			*f.Options.Packed = true
 		case strings.HasPrefix(s, "weak="):
-			f.IsWeak = true
+			f.Options.Weak = protoV1.Bool(true)
 			f.MessageType = ptype.PlaceholderMessage(pref.FullName(s[len("weak="):]))
 		case strings.HasPrefix(s, "def="):
 			// The default tag is special in that everything afterwards is the
@@ -333,9 +338,9 @@
 			f.MessageType = mv.ProtoReflect().Type()
 		} else if t.Kind() == reflect.Map {
 			m := &ptype.StandaloneMessage{
-				Syntax:     parent.Syntax,
-				FullName:   parent.FullName.Append(mapEntryName(f.Name)),
-				IsMapEntry: true,
+				Syntax:   parent.Syntax,
+				FullName: parent.FullName.Append(mapEntryName(f.Name)),
+				Options:  &descriptorV1.MessageOptions{MapEntry: protoV1.Bool(true)},
 				Fields: []ptype.Field{
 					ms.parseField(tagKey, "", "", t.Key(), nil),
 					ms.parseField(tagVal, "", "", t.Elem(), nil),
diff --git a/internal/impl/legacy_test.go b/internal/impl/legacy_test.go
index e182d17..fc3a2ee 100644
--- a/internal/impl/legacy_test.go
+++ b/internal/impl/legacy_test.go
@@ -99,6 +99,8 @@
 					switch name {
 					case "Index":
 						// Ignore index since legacy descriptors have no parent.
+					case "Options":
+						// Ignore descriptor options since protos are not cmperable.
 					case "Messages", "Enums":
 						// Ignore nested message and enum declarations since
 						// legacy descriptors are all created standalone.
diff --git a/internal/impl/message_test.go b/internal/impl/message_test.go
index 2fe53ed..ce8bc70 100644
--- a/internal/impl/message_test.go
+++ b/internal/impl/message_test.go
@@ -14,6 +14,8 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 
 	"github.com/golang/protobuf/proto"
+	protoV1 "github.com/golang/protobuf/proto"
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 	ptype "github.com/golang/protobuf/v2/reflect/prototype"
 )
@@ -461,7 +463,7 @@
 					{Name: "key", Number: 1, Cardinality: pref.Optional, Kind: keyKind},
 					{Name: "value", Number: 2, Cardinality: pref.Optional, Kind: valKind},
 				},
-				IsMapEntry: true,
+				Options: &descriptorV1.MessageOptions{MapEntry: protoV1.Bool(true)},
 			}),
 		}
 	}
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index a729c3b..6c9e69f 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -104,57 +104,30 @@
 	// Support for this functionality is optional and may return (nil, false).
 	DescriptorProto() (Message, bool)
 
-	// DescriptorOptions is a helper for accessing the proto options specified
-	// on any of the descriptor types. It is functionally equivalent to
-	// accessing the "options" field in the descriptor and providing a set of
-	// efficient lookup methods.
+	// Options returns the descriptor options. The caller must not modify
+	// the returned value.
 	//
-	// Support for this functionality is optional and may return (nil, false).
-	DescriptorOptions() (DescriptorOptions, bool)
-
-	doNotImplement
-}
-
-// DescriptorOptions is a wrapper around proto options.
-//
-// The proto message type for each descriptor type is as follows:
-//	+---------------------+----------------------------------+
-//	| Go type             | Proto message type               |
-//	+---------------------+----------------------------------+
-//	| FileDescriptor      | google.protobuf.FileOptions      |
-//	| MessageDescriptor   | google.protobuf.MessageOptions   |
-//	| FieldDescriptor     | google.protobuf.FieldOptions     |
-//	| OneofDescriptor     | google.protobuf.OneofOptions     |
-//	| EnumDescriptor      | google.protobuf.EnumOptions      |
-//	| EnumValueDescriptor | google.protobuf.EnumValueOptions |
-//	| ServiceDescriptor   | google.protobuf.ServiceOptions   |
-//	| MethodDescriptor    | google.protobuf.MethodOptions    |
-//	+---------------------+----------------------------------+
-//
-// The values returned by Get, ByName, and ByNumber are considered frozen and
-// mutating operations must not be performed on values returned by them.
-type DescriptorOptions interface {
-	// Len reports the total number of option fields,
-	// including both fields declared in the options proto and extensions, and
-	// including fields that are declared but not set in the proto file.
-	Len() int
-
-	// Get returns the ith field. It panics if out of bounds.
-	// The FieldDescriptor is guaranteed to be non-nil, while the Value
-	// may be invalid if the proto file did not specify this option.
-	Get(i int) (FieldDescriptor, Value)
-
-	// ByName looks a field up by full name and
-	// returns (nil, Value{}) if not found.
+	// To avoid a dependency cycle, this function returns an interface value.
+	// The proto message type returned for each descriptor type is as follows:
+	//	+---------------------+------------------------------------------+
+	//	| Go type             | Proto message type                       |
+	//	+---------------------+------------------------------------------+
+	//	| FileDescriptor      | google.protobuf.FileOptions              |
+	//	| MessageDescriptor   | google.protobuf.MessageOptions           |
+	//	| FieldDescriptor     | google.protobuf.FieldOptions             |
+	//	| OneofDescriptor     | google.protobuf.OneofOptions             |
+	//	| EnumDescriptor      | google.protobuf.EnumOptions              |
+	//	| EnumValueDescriptor | google.protobuf.EnumValueOptions         |
+	//	| ServiceDescriptor   | google.protobuf.ServiceOptions           |
+	//	| MethodDescriptor    | google.protobuf.MethodOptions            |
+	//	+---------------------+------------------------------------------+
 	//
-	// As a special-case, non-extension fields in the options type can be
-	// directly accessed by the field name alone (e.g., "map_entry" may be used
-	// instead of "google.protobuf.MessageOptions.map_entry").
-	ByName(s FullName) (FieldDescriptor, Value)
-
-	// ByNumber looks a field up by the field number and
-	// returns (nil, Value{}) if not found.
-	ByNumber(n FieldNumber) (FieldDescriptor, Value)
+	// 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)).
+	//
+	// TODO: Return ProtoMessage instead of interface{}.
+	Options() interface{}
 
 	doNotImplement
 }
diff --git a/reflect/prototype/descriptor.go b/reflect/prototype/descriptor.go
deleted file mode 100644
index 031c51e..0000000
--- a/reflect/prototype/descriptor.go
+++ /dev/null
@@ -1,29 +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 (
-	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
-)
-
-// TODO: This cannot be implemented without proto.Unmarshal.
-
-type descriptorFileMeta struct{}
-
-func (p *descriptorFileMeta) lazyInit(t fileDesc) (pref.Message, bool) {
-	return nil, false
-}
-
-type descriptorSubMeta struct{}
-
-func (p *descriptorSubMeta) lazyInit(t pref.Descriptor) (pref.Message, bool) {
-	return nil, false
-}
-
-type descriptorOptionsMeta struct{}
-
-func (p *descriptorOptionsMeta) lazyInit(t pref.Descriptor) (pref.DescriptorOptions, bool) {
-	return nil, false
-}
diff --git a/reflect/prototype/placeholder_type.go b/reflect/prototype/placeholder_type.go
index 2035111..559dbf6 100644
--- a/reflect/prototype/placeholder_type.go
+++ b/reflect/prototype/placeholder_type.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/internal/pragma"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
@@ -26,21 +27,21 @@
 
 type placeholderName pref.FullName
 
-func (t placeholderName) Parent() (pref.Descriptor, bool)                   { return nil, false }
-func (t placeholderName) Index() int                                        { return 0 }
-func (t placeholderName) Syntax() pref.Syntax                               { return 0 }
-func (t placeholderName) Name() pref.Name                                   { return pref.FullName(t).Name() }
-func (t placeholderName) FullName() pref.FullName                           { return pref.FullName(t) }
-func (t placeholderName) IsPlaceholder() bool                               { return true }
-func (t placeholderName) DescriptorProto() (pref.Message, bool)             { return nil, false }
-func (t placeholderName) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
-func (t placeholderName) ProtoInternal(pragma.DoNotImplement)               {}
+func (t placeholderName) Parent() (pref.Descriptor, bool)       { return nil, false }
+func (t placeholderName) Index() int                            { return 0 }
+func (t placeholderName) Syntax() pref.Syntax                   { return 0 }
+func (t placeholderName) Name() pref.Name                       { return pref.FullName(t).Name() }
+func (t placeholderName) FullName() pref.FullName               { return pref.FullName(t) }
+func (t placeholderName) IsPlaceholder() bool                   { return true }
+func (t placeholderName) DescriptorProto() (pref.Message, bool) { return nil, false }
+func (t placeholderName) ProtoInternal(pragma.DoNotImplement)   {}
 
 type placeholderFile struct {
 	path string
 	placeholderName
 }
 
+func (t placeholderFile) Options() interface{}                           { return (*descriptorV1.FileOptions)(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 }
@@ -56,6 +57,7 @@
 	placeholderName
 }
 
+func (t placeholderMessage) Options() interface{}                  { return (*descriptorV1.MessageOptions)(nil) }
 func (t placeholderMessage) IsMapEntry() bool                      { return false }
 func (t placeholderMessage) Fields() pref.FieldDescriptors         { return &emptyFields }
 func (t placeholderMessage) Oneofs() pref.OneofDescriptors         { return &emptyOneofs }
@@ -71,6 +73,7 @@
 	placeholderName
 }
 
+func (t placeholderEnum) Options() interface{}              { return (*descriptorV1.EnumOptions)(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 21ba412..a765ccd 100644
--- a/reflect/prototype/protofile.go
+++ b/reflect/prototype/protofile.go
@@ -14,6 +14,7 @@
 package prototype
 
 import (
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
@@ -35,15 +36,15 @@
 
 // File is a constructor for protoreflect.FileDescriptor.
 type File struct {
-	Syntax  protoreflect.Syntax
-	Path    string
-	Package protoreflect.FullName
-	Imports []protoreflect.FileImport
-
+	Syntax     protoreflect.Syntax
+	Path       string
+	Package    protoreflect.FullName
+	Imports    []protoreflect.FileImport
 	Messages   []Message
 	Enums      []Enum
 	Extensions []Extension
 	Services   []Service
+	Options    *descriptorV1.FileOptions
 
 	*fileMeta
 }
@@ -72,14 +73,13 @@
 // Message is a constructor for protoreflect.MessageDescriptor.
 type Message struct {
 	Name            protoreflect.Name
-	IsMapEntry      bool
 	Fields          []Field
 	Oneofs          []Oneof
 	ExtensionRanges [][2]protoreflect.FieldNumber
-
-	Messages   []Message
-	Enums      []Enum
-	Extensions []Extension
+	Messages        []Message
+	Enums           []Enum
+	Extensions      []Extension
+	Options         *descriptorV1.MessageOptions
 
 	*messageMeta
 }
@@ -91,19 +91,19 @@
 	Cardinality protoreflect.Cardinality
 	Kind        protoreflect.Kind
 	JSONName    string
-	IsPacked    bool
-	IsWeak      bool
 	Default     protoreflect.Value
 	OneofName   protoreflect.Name
 	MessageType protoreflect.MessageDescriptor
 	EnumType    protoreflect.EnumDescriptor
+	Options     *descriptorV1.FieldOptions
 
 	*fieldMeta
 }
 
 // Oneof is a constructor for protoreflect.OneofDescriptor.
 type Oneof struct {
-	Name protoreflect.Name
+	Name    protoreflect.Name
+	Options *descriptorV1.OneofOptions
 
 	*oneofMeta
 }
@@ -114,27 +114,29 @@
 	Number       protoreflect.FieldNumber
 	Cardinality  protoreflect.Cardinality
 	Kind         protoreflect.Kind
-	IsPacked     bool
 	Default      protoreflect.Value
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
+	Options      *descriptorV1.FieldOptions
 
 	*extensionMeta
 }
 
 // Enum is a constructor for protoreflect.EnumDescriptor.
 type Enum struct {
-	Name   protoreflect.Name
-	Values []EnumValue
+	Name    protoreflect.Name
+	Values  []EnumValue
+	Options *descriptorV1.EnumOptions
 
 	*enumMeta
 }
 
 // EnumValue is a constructor for protoreflect.EnumValueDescriptor.
 type EnumValue struct {
-	Name   protoreflect.Name
-	Number protoreflect.EnumNumber
+	Name    protoreflect.Name
+	Number  protoreflect.EnumNumber
+	Options *descriptorV1.EnumValueOptions
 
 	*enumValueMeta
 }
@@ -143,6 +145,7 @@
 type Service struct {
 	Name    protoreflect.Name
 	Methods []Method
+	Options *descriptorV1.ServiceOptions
 
 	*serviceMeta
 }
@@ -154,6 +157,7 @@
 	OutputType        protoreflect.MessageDescriptor
 	IsStreamingClient bool
 	IsStreamingServer bool
+	Options           *descriptorV1.MethodOptions
 
 	*methodMeta
 }
diff --git a/reflect/prototype/protofile_desc.go b/reflect/prototype/protofile_desc.go
index cc942fc..b4ea5c3 100644
--- a/reflect/prototype/protofile_desc.go
+++ b/reflect/prototype/protofile_desc.go
@@ -64,6 +64,7 @@
 	}
 	f.Path = fd.GetName()
 	f.Package = protoreflect.FullName(fd.GetPackage())
+	f.Options = fd.GetOptions()
 
 	f.Imports = make([]protoreflect.FileImport, len(fd.GetDependency()))
 	for _, i := range fd.GetPublicDependency() {
@@ -119,25 +120,15 @@
 	for _, md := range mds {
 		var m Message
 		m.Name = protoreflect.Name(md.GetName())
-		m.IsMapEntry = md.GetOptions().GetMapEntry()
+		m.Options = md.GetOptions()
 		for _, fd := range md.GetField() {
 			var f Field
 			f.Name = protoreflect.Name(fd.GetName())
 			f.Number = protoreflect.FieldNumber(fd.GetNumber())
 			f.Cardinality = protoreflect.Cardinality(fd.GetLabel())
 			f.Kind = protoreflect.Kind(fd.GetType())
+			f.Options = fd.GetOptions()
 			f.JSONName = fd.GetJsonName()
-			if opts := fd.GetOptions(); opts != nil && opts.Packed != nil {
-				f.IsPacked = *opts.Packed
-			} else {
-				// https://developers.google.com/protocol-buffers/docs/proto3:
-				// "In proto3, repeated fields of scalar numeric types use packed
-				// encoding by default."
-				f.IsPacked = (syntax == protoreflect.Proto3 &&
-					f.Cardinality == protoreflect.Repeated &&
-					isScalarNumeric[f.Kind])
-			}
-			f.IsWeak = fd.GetOptions().GetWeak()
 			if fd.DefaultValue != nil {
 				f.Default, err = parseDefault(fd.GetDefaultValue(), f.Kind)
 				if err != nil {
@@ -157,7 +148,7 @@
 				if err != nil {
 					return nil, err
 				}
-				if f.IsWeak && !f.EnumType.IsPlaceholder() {
+				if f.Options.GetWeak() && !f.EnumType.IsPlaceholder() {
 					f.EnumType = PlaceholderEnum(f.EnumType.FullName())
 				}
 			case protoreflect.MessageKind, protoreflect.GroupKind:
@@ -165,16 +156,20 @@
 				if err != nil {
 					return nil, err
 				}
-				if f.IsWeak && !f.MessageType.IsPlaceholder() {
+				if f.Options.GetWeak() && !f.MessageType.IsPlaceholder() {
 					f.MessageType = PlaceholderMessage(f.MessageType.FullName())
 				}
 			}
 			m.Fields = append(m.Fields, f)
 		}
 		for _, od := range md.GetOneofDecl() {
-			m.Oneofs = append(m.Oneofs, Oneof{Name: protoreflect.Name(od.GetName())})
+			m.Oneofs = append(m.Oneofs, Oneof{
+				Name:    protoreflect.Name(od.GetName()),
+				Options: od.Options,
+			})
 		}
 		for _, xr := range md.GetExtensionRange() {
+			// TODO: Extension range options.
 			m.ExtensionRanges = append(m.ExtensionRanges, [2]protoreflect.FieldNumber{
 				protoreflect.FieldNumber(xr.GetStart()),
 				protoreflect.FieldNumber(xr.GetEnd()),
@@ -199,31 +194,16 @@
 	return ms, nil
 }
 
-var isScalarNumeric = map[protoreflect.Kind]bool{
-	protoreflect.BoolKind:     true,
-	protoreflect.EnumKind:     true,
-	protoreflect.Int32Kind:    true,
-	protoreflect.Sint32Kind:   true,
-	protoreflect.Uint32Kind:   true,
-	protoreflect.Int64Kind:    true,
-	protoreflect.Sint64Kind:   true,
-	protoreflect.Uint64Kind:   true,
-	protoreflect.Sfixed32Kind: true,
-	protoreflect.Fixed32Kind:  true,
-	protoreflect.FloatKind:    true,
-	protoreflect.Sfixed64Kind: true,
-	protoreflect.Fixed64Kind:  true,
-	protoreflect.DoubleKind:   true,
-}
-
 func enumsFromDescriptorProto(eds []*descriptorV1.EnumDescriptorProto, r *protoregistry.Files) (es []Enum, err error) {
 	for _, ed := range eds {
 		var e Enum
 		e.Name = protoreflect.Name(ed.GetName())
+		e.Options = ed.GetOptions()
 		for _, vd := range ed.GetValue() {
 			e.Values = append(e.Values, EnumValue{
-				Name:   protoreflect.Name(vd.GetName()),
-				Number: protoreflect.EnumNumber(vd.GetNumber()),
+				Name:    protoreflect.Name(vd.GetName()),
+				Number:  protoreflect.EnumNumber(vd.GetNumber()),
+				Options: vd.Options,
 			})
 		}
 		es = append(es, e)
@@ -238,12 +218,7 @@
 		x.Number = protoreflect.FieldNumber(xd.GetNumber())
 		x.Cardinality = protoreflect.Cardinality(xd.GetLabel())
 		x.Kind = protoreflect.Kind(xd.GetType())
-		// TODO: When a proto3 file extends a proto2 message (permitted only for
-		// extending descriptor options), does the extension have proto2 or proto3
-		// semantics? If the latter, repeated, scalar, numeric, proto3 extension
-		// fields should default to packed. If the former, perhaps the extension syntax
-		// should be protoreflect.Proto2.
-		x.IsPacked = xd.GetOptions().GetPacked()
+		x.Options = xd.GetOptions()
 		if xd.DefaultValue != nil {
 			x.Default, err = parseDefault(xd.GetDefaultValue(), x.Kind)
 			if err != nil {
@@ -275,9 +250,11 @@
 	for _, sd := range sds {
 		var s Service
 		s.Name = protoreflect.Name(sd.GetName())
+		s.Options = sd.GetOptions()
 		for _, md := range sd.GetMethod() {
 			var m Method
 			m.Name = protoreflect.Name(md.GetName())
+			m.Options = md.GetOptions()
 			m.InputType, err = findMessageDescriptor(md.GetInputType(), r)
 			if err != nil {
 				return nil, err
diff --git a/reflect/prototype/protofile_type.go b/reflect/prototype/protofile_type.go
index 66e8a62..83901b8 100644
--- a/reflect/prototype/protofile_type.go
+++ b/reflect/prototype/protofile_type.go
@@ -10,6 +10,7 @@
 	"strings"
 	"sync"
 
+	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/internal/pragma"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
@@ -20,9 +21,6 @@
 	index    int
 	syntax   pref.Syntax
 	fullName pref.FullName
-
-	desc descriptorSubMeta
-	opts descriptorOptionsMeta
 }
 
 func (m *inheritedMeta) init(nb *nameBuilder, parent pref.Descriptor, index int, name pref.Name, child bool) {
@@ -41,9 +39,6 @@
 }
 
 type fileMeta struct {
-	desc descriptorFileMeta
-	opts descriptorOptionsMeta
-
 	ms messagesMeta
 	es enumsMeta
 	xs extensionsMeta
@@ -59,25 +54,25 @@
 	f.fileMeta = new(fileMeta)
 	return fileDesc{f}
 }
-func (t fileDesc) Parent() (pref.Descriptor, bool)                   { return nil, false }
-func (t fileDesc) Index() int                                        { return 0 }
-func (t fileDesc) Syntax() pref.Syntax                               { return t.f.Syntax }
-func (t fileDesc) Name() pref.Name                                   { return t.f.Package.Name() }
-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 t.f.desc.lazyInit(t) }
-func (t fileDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
-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) }
-func (t fileDesc) Messages() pref.MessageDescriptors                 { return t.f.ms.lazyInit(t, t.f.Messages) }
-func (t fileDesc) Enums() pref.EnumDescriptors                       { return t.f.es.lazyInit(t, t.f.Enums) }
-func (t fileDesc) Extensions() pref.ExtensionDescriptors             { return t.f.xs.lazyInit(t, t.f.Extensions) }
-func (t fileDesc) Services() pref.ServiceDescriptors                 { return t.f.ss.lazyInit(t, t.f.Services) }
-func (t fileDesc) DescriptorByName(s pref.FullName) pref.Descriptor  { return t.f.ds.lookup(t, s) }
-func (t fileDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t fileDesc) ProtoType(pref.FileDescriptor)                     {}
-func (t fileDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t fileDesc) Parent() (pref.Descriptor, bool)                  { return nil, false }
+func (t fileDesc) Index() int                                       { return 0 }
+func (t fileDesc) Syntax() pref.Syntax                              { return t.f.Syntax }
+func (t fileDesc) Name() pref.Name                                  { return t.f.Package.Name() }
+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) 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) }
+func (t fileDesc) Messages() pref.MessageDescriptors                { return t.f.ms.lazyInit(t, t.f.Messages) }
+func (t fileDesc) Enums() pref.EnumDescriptors                      { return t.f.es.lazyInit(t, t.f.Enums) }
+func (t fileDesc) Extensions() pref.ExtensionDescriptors            { return t.f.xs.lazyInit(t, t.f.Extensions) }
+func (t fileDesc) Services() pref.ServiceDescriptors                { return t.f.ss.lazyInit(t, t.f.Services) }
+func (t fileDesc) DescriptorByName(s pref.FullName) pref.Descriptor { return t.f.ds.lookup(t, s) }
+func (t fileDesc) Format(s fmt.State, r rune)                       { formatDesc(s, r, t) }
+func (t fileDesc) ProtoType(pref.FileDescriptor)                    {}
+func (t fileDesc) ProtoInternal(pragma.DoNotImplement)              {}
 
 // descriptorsMeta is a lazily initialized map of all descriptors declared in
 // the file by full name.
@@ -166,25 +161,25 @@
 }
 type messageDesc struct{ m *Message }
 
-func (t messageDesc) Parent() (pref.Descriptor, bool)                   { return t.m.parent, true }
-func (t messageDesc) Index() int                                        { return t.m.index }
-func (t messageDesc) Syntax() pref.Syntax                               { return t.m.syntax }
-func (t messageDesc) Name() pref.Name                                   { return t.m.Name }
-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 t.m.desc.lazyInit(t) }
-func (t messageDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
-func (t messageDesc) IsMapEntry() bool                                  { return t.m.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) }
-func (t messageDesc) ExtensionRanges() pref.FieldRanges                 { return (*ranges)(&t.m.ExtensionRanges) }
-func (t messageDesc) Messages() pref.MessageDescriptors                 { return t.m.ms.lazyInit(t, t.m.Messages) }
-func (t messageDesc) Enums() pref.EnumDescriptors                       { return t.m.es.lazyInit(t, t.m.Enums) }
-func (t messageDesc) Extensions() pref.ExtensionDescriptors             { return t.m.xs.lazyInit(t, t.m.Extensions) }
-func (t messageDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t messageDesc) ProtoType(pref.MessageDescriptor)                  {}
-func (t messageDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t messageDesc) Parent() (pref.Descriptor, bool)       { return t.m.parent, true }
+func (t messageDesc) Index() int                            { return t.m.index }
+func (t messageDesc) Syntax() pref.Syntax                   { return t.m.syntax }
+func (t messageDesc) Name() pref.Name                       { return t.m.Name }
+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) 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) }
+func (t messageDesc) ExtensionRanges() pref.FieldRanges     { return (*ranges)(&t.m.ExtensionRanges) }
+func (t messageDesc) Messages() pref.MessageDescriptors     { return t.m.ms.lazyInit(t, t.m.Messages) }
+func (t messageDesc) Enums() pref.EnumDescriptors           { return t.m.es.lazyInit(t, t.m.Enums) }
+func (t messageDesc) Extensions() pref.ExtensionDescriptors { return t.m.xs.lazyInit(t, t.m.Extensions) }
+func (t messageDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t messageDesc) ProtoType(pref.MessageDescriptor)      {}
+func (t messageDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type fieldMeta struct {
 	inheritedMeta
@@ -197,35 +192,64 @@
 }
 type fieldDesc struct{ f *Field }
 
-func (t fieldDesc) Parent() (pref.Descriptor, bool)                   { return t.f.parent, true }
-func (t fieldDesc) Index() int                                        { return t.f.index }
-func (t fieldDesc) Syntax() pref.Syntax                               { return t.f.syntax }
-func (t fieldDesc) Name() pref.Name                                   { return t.f.Name }
-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 t.f.desc.lazyInit(t) }
-func (t fieldDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.f.opts.lazyInit(t) }
-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 t.f.IsPacked }
-func (t fieldDesc) IsMap() bool                                       { return isMap(t) }
-func (t fieldDesc) IsWeak() bool                                      { return t.f.IsWeak }
-func (t fieldDesc) Default() pref.Value                               { return t.f.dv.lazyInit(t, t.f.Default) }
-func (t fieldDesc) HasDefault() bool                                  { return t.f.Default.IsValid() }
-func (t fieldDesc) OneofType() pref.OneofDescriptor                   { return t.f.ot.lazyInit(t, t.f.OneofName) }
-func (t fieldDesc) ExtendedType() pref.MessageDescriptor              { return nil }
-func (t fieldDesc) MessageType() pref.MessageDescriptor               { return t.f.mt.lazyInit(t, &t.f.MessageType) }
-func (t fieldDesc) EnumType() pref.EnumDescriptor                     { return t.f.et.lazyInit(t, &t.f.EnumType) }
-func (t fieldDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t fieldDesc) ProtoType(pref.FieldDescriptor)                    {}
-func (t fieldDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t fieldDesc) Parent() (pref.Descriptor, bool)       { return t.f.parent, true }
+func (t fieldDesc) Index() int                            { return t.f.index }
+func (t fieldDesc) Syntax() pref.Syntax                   { return t.f.syntax }
+func (t fieldDesc) Name() pref.Name                       { return t.f.Name }
+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) 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) Default() pref.Value                   { return t.f.dv.lazyInit(t, t.f.Default) }
+func (t fieldDesc) HasDefault() bool                      { return t.f.Default.IsValid() }
+func (t fieldDesc) OneofType() pref.OneofDescriptor       { return t.f.ot.lazyInit(t, t.f.OneofName) }
+func (t fieldDesc) ExtendedType() pref.MessageDescriptor  { return nil }
+func (t fieldDesc) MessageType() pref.MessageDescriptor   { return t.f.mt.lazyInit(t, &t.f.MessageType) }
+func (t fieldDesc) EnumType() pref.EnumDescriptor         { return t.f.et.lazyInit(t, &t.f.EnumType) }
+func (t fieldDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+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.IsMapEntry()
+			return mt.Options().(*descriptorV1.MessageOptions).GetMapEntry()
 		}
 	}
 	return false
@@ -283,18 +307,18 @@
 }
 type oneofDesc struct{ o *Oneof }
 
-func (t oneofDesc) Parent() (pref.Descriptor, bool)                   { return t.o.parent, true }
-func (t oneofDesc) Index() int                                        { return t.o.index }
-func (t oneofDesc) Syntax() pref.Syntax                               { return t.o.syntax }
-func (t oneofDesc) Name() pref.Name                                   { return t.o.Name }
-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 t.o.desc.lazyInit(t) }
-func (t oneofDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.o.opts.lazyInit(t) }
-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)                    {}
-func (t oneofDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t oneofDesc) Parent() (pref.Descriptor, bool)       { return t.o.parent, true }
+func (t oneofDesc) Index() int                            { return t.o.index }
+func (t oneofDesc) Syntax() pref.Syntax                   { return t.o.syntax }
+func (t oneofDesc) Name() pref.Name                       { return t.o.Name }
+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) 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)        {}
+func (t oneofDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type extensionMeta struct {
 	inheritedMeta
@@ -306,24 +330,24 @@
 }
 type extensionDesc struct{ x *Extension }
 
-func (t extensionDesc) Parent() (pref.Descriptor, bool)                   { return t.x.parent, true }
-func (t extensionDesc) Syntax() pref.Syntax                               { return t.x.syntax }
-func (t extensionDesc) Index() int                                        { return t.x.index }
-func (t extensionDesc) Name() pref.Name                                   { return t.x.Name }
-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 t.x.desc.lazyInit(t) }
-func (t extensionDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.x.opts.lazyInit(t) }
-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.IsPacked }
-func (t extensionDesc) IsMap() bool                                       { return false }
-func (t extensionDesc) IsWeak() bool                                      { return false }
-func (t extensionDesc) Default() pref.Value                               { return t.x.dv.lazyInit(t, t.x.Default) }
-func (t extensionDesc) HasDefault() bool                                  { return t.x.Default.IsValid() }
-func (t extensionDesc) OneofType() pref.OneofDescriptor                   { return nil }
+func (t extensionDesc) Parent() (pref.Descriptor, bool)       { return t.x.parent, true }
+func (t extensionDesc) Syntax() pref.Syntax                   { return t.x.syntax }
+func (t extensionDesc) Index() int                            { return t.x.index }
+func (t extensionDesc) Name() pref.Name                       { return t.x.Name }
+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) 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 extIsPacked(t) }
+func (t extensionDesc) IsMap() bool                           { return false }
+func (t extensionDesc) IsWeak() bool                          { return false }
+func (t extensionDesc) Default() pref.Value                   { return t.x.dv.lazyInit(t, t.x.Default) }
+func (t extensionDesc) HasDefault() bool                      { return t.x.Default.IsValid() }
+func (t extensionDesc) OneofType() pref.OneofDescriptor       { return nil }
 func (t extensionDesc) ExtendedType() pref.MessageDescriptor {
 	return t.x.xt.lazyInit(t, &t.x.ExtendedType)
 }
@@ -335,6 +359,15 @@
 func (t extensionDesc) ProtoType(pref.FieldDescriptor)      {}
 func (t extensionDesc) ProtoInternal(pragma.DoNotImplement) {}
 
+func extIsPacked(t extensionDesc) bool {
+	// TODO: When a proto3 file extends a proto2 message (permitted only for
+	// extending descriptor options), does the extension have proto2 or proto3
+	// semantics? If the latter, repeated, scalar, numeric, proto3 extension
+	// fields should default to packed. If the former, perhaps the extension syntax
+	// should be protoreflect.Proto2.
+	return t.x.Options.GetPacked()
+}
+
 type enumMeta struct {
 	inheritedMeta
 
@@ -342,36 +375,36 @@
 }
 type enumDesc struct{ e *Enum }
 
-func (t enumDesc) Parent() (pref.Descriptor, bool)                   { return t.e.parent, true }
-func (t enumDesc) Index() int                                        { return t.e.index }
-func (t enumDesc) Syntax() pref.Syntax                               { return t.e.syntax }
-func (t enumDesc) Name() pref.Name                                   { return t.e.Name }
-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 t.e.desc.lazyInit(t) }
-func (t enumDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.e.opts.lazyInit(t) }
-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)                     {}
-func (t enumDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t enumDesc) Parent() (pref.Descriptor, bool)       { return t.e.parent, true }
+func (t enumDesc) Index() int                            { return t.e.index }
+func (t enumDesc) Syntax() pref.Syntax                   { return t.e.syntax }
+func (t enumDesc) Name() pref.Name                       { return t.e.Name }
+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) 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)         {}
+func (t enumDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type enumValueMeta struct {
 	inheritedMeta
 }
 type enumValueDesc struct{ v *EnumValue }
 
-func (t enumValueDesc) Parent() (pref.Descriptor, bool)                   { return t.v.parent, true }
-func (t enumValueDesc) Index() int                                        { return t.v.index }
-func (t enumValueDesc) Syntax() pref.Syntax                               { return t.v.syntax }
-func (t enumValueDesc) Name() pref.Name                                   { return t.v.Name }
-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 t.v.desc.lazyInit(t) }
-func (t enumValueDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.v.opts.lazyInit(t) }
-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)                {}
-func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t enumValueDesc) Parent() (pref.Descriptor, bool)       { return t.v.parent, true }
+func (t enumValueDesc) Index() int                            { return t.v.index }
+func (t enumValueDesc) Syntax() pref.Syntax                   { return t.v.syntax }
+func (t enumValueDesc) Name() pref.Name                       { return t.v.Name }
+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) 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)    {}
+func (t enumValueDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type serviceMeta struct {
 	inheritedMeta
@@ -380,18 +413,18 @@
 }
 type serviceDesc struct{ s *Service }
 
-func (t serviceDesc) Parent() (pref.Descriptor, bool)                   { return t.s.parent, true }
-func (t serviceDesc) Index() int                                        { return t.s.index }
-func (t serviceDesc) Syntax() pref.Syntax                               { return t.s.syntax }
-func (t serviceDesc) Name() pref.Name                                   { return t.s.Name }
-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 t.s.desc.lazyInit(t) }
-func (t serviceDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.s.opts.lazyInit(t) }
-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)                  {}
-func (t serviceDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t serviceDesc) Parent() (pref.Descriptor, bool)       { return t.s.parent, true }
+func (t serviceDesc) Index() int                            { return t.s.index }
+func (t serviceDesc) Syntax() pref.Syntax                   { return t.s.syntax }
+func (t serviceDesc) Name() pref.Name                       { return t.s.Name }
+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) 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)      {}
+func (t serviceDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type methodMeta struct {
 	inheritedMeta
@@ -401,21 +434,21 @@
 }
 type methodDesc struct{ m *Method }
 
-func (t methodDesc) Parent() (pref.Descriptor, bool)                   { return t.m.parent, true }
-func (t methodDesc) Index() int                                        { return t.m.index }
-func (t methodDesc) Syntax() pref.Syntax                               { return t.m.syntax }
-func (t methodDesc) Name() pref.Name                                   { return t.m.Name }
-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 t.m.desc.lazyInit(t) }
-func (t methodDesc) DescriptorOptions() (pref.DescriptorOptions, bool) { return t.m.opts.lazyInit(t) }
-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 }
-func (t methodDesc) IsStreamingServer() bool                           { return t.m.IsStreamingServer }
-func (t methodDesc) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t methodDesc) ProtoType(pref.MethodDescriptor)                   {}
-func (t methodDesc) ProtoInternal(pragma.DoNotImplement)               {}
+func (t methodDesc) Parent() (pref.Descriptor, bool)       { return t.m.parent, true }
+func (t methodDesc) Index() int                            { return t.m.index }
+func (t methodDesc) Syntax() pref.Syntax                   { return t.m.syntax }
+func (t methodDesc) Name() pref.Name                       { return t.m.Name }
+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) 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 }
+func (t methodDesc) IsStreamingServer() bool               { return t.m.IsStreamingServer }
+func (t methodDesc) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t methodDesc) ProtoType(pref.MethodDescriptor)       {}
+func (t methodDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
 type defaultValue struct {
 	once sync.Once
diff --git a/reflect/prototype/standalone.go b/reflect/prototype/standalone.go
index 7489385..bd420ed 100644
--- a/reflect/prototype/standalone.go
+++ b/reflect/prototype/standalone.go
@@ -5,6 +5,7 @@
 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"
 )
@@ -17,10 +18,10 @@
 type StandaloneMessage struct {
 	Syntax          protoreflect.Syntax
 	FullName        protoreflect.FullName
-	IsMapEntry      bool
 	Fields          []Field
 	Oneofs          []Oneof
 	ExtensionRanges [][2]protoreflect.FieldNumber
+	Options         *descriptorV1.MessageOptions
 
 	fields fieldsMeta
 	oneofs oneofsMeta
@@ -63,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.IsWeak && f.MessageType != nil && f.MessageType.IsPlaceholder() {
+			if !f.Options.GetWeak() && f.MessageType != nil && f.MessageType.IsPlaceholder() {
 				if m, ok := ms[f.MessageType.FullName()]; ok {
 					t.Fields[i].MessageType = m
 				}
@@ -84,6 +85,7 @@
 	Syntax   protoreflect.Syntax
 	FullName protoreflect.FullName
 	Values   []EnumValue
+	Options  *descriptorV1.EnumOptions
 
 	vals enumValuesMeta
 }
@@ -107,11 +109,11 @@
 	Number       protoreflect.FieldNumber
 	Cardinality  protoreflect.Cardinality
 	Kind         protoreflect.Kind
-	IsPacked     bool
 	Default      protoreflect.Value
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
+	Options      *descriptorV1.FieldOptions
 
 	dv defaultValue
 }
diff --git a/reflect/prototype/standalone_type.go b/reflect/prototype/standalone_type.go
index 1c91e33..90609aa 100644
--- a/reflect/prototype/standalone_type.go
+++ b/reflect/prototype/standalone_type.go
@@ -13,64 +13,64 @@
 
 type standaloneMessage struct{ m *StandaloneMessage }
 
-func (t standaloneMessage) Parent() (pref.Descriptor, bool)                   { return nil, false }
-func (t standaloneMessage) Index() int                                        { return 0 }
-func (t standaloneMessage) Syntax() pref.Syntax                               { return t.m.Syntax }
-func (t standaloneMessage) Name() pref.Name                                   { return t.m.FullName.Name() }
-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) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
-func (t standaloneMessage) IsMapEntry() bool                                  { return t.m.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) }
-func (t standaloneMessage) ExtensionRanges() pref.FieldRanges                 { return (*ranges)(&t.m.ExtensionRanges) }
-func (t standaloneMessage) Messages() pref.MessageDescriptors                 { return &emptyMessages }
-func (t standaloneMessage) Enums() pref.EnumDescriptors                       { return &emptyEnums }
-func (t standaloneMessage) Extensions() pref.ExtensionDescriptors             { return &emptyExtensions }
-func (t standaloneMessage) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t standaloneMessage) ProtoType(pref.MessageDescriptor)                  {}
-func (t standaloneMessage) ProtoInternal(pragma.DoNotImplement)               {}
+func (t standaloneMessage) Parent() (pref.Descriptor, bool)       { return nil, false }
+func (t standaloneMessage) Index() int                            { return 0 }
+func (t standaloneMessage) Syntax() pref.Syntax                   { return t.m.Syntax }
+func (t standaloneMessage) Name() pref.Name                       { return t.m.FullName.Name() }
+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) 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) }
+func (t standaloneMessage) ExtensionRanges() pref.FieldRanges     { return (*ranges)(&t.m.ExtensionRanges) }
+func (t standaloneMessage) Messages() pref.MessageDescriptors     { return &emptyMessages }
+func (t standaloneMessage) Enums() pref.EnumDescriptors           { return &emptyEnums }
+func (t standaloneMessage) Extensions() pref.ExtensionDescriptors { return &emptyExtensions }
+func (t standaloneMessage) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t standaloneMessage) ProtoType(pref.MessageDescriptor)      {}
+func (t standaloneMessage) ProtoInternal(pragma.DoNotImplement)   {}
 
 type standaloneEnum struct{ e *StandaloneEnum }
 
-func (t standaloneEnum) Parent() (pref.Descriptor, bool)                   { return nil, false }
-func (t standaloneEnum) Index() int                                        { return 0 }
-func (t standaloneEnum) Syntax() pref.Syntax                               { return t.e.Syntax }
-func (t standaloneEnum) Name() pref.Name                                   { return t.e.FullName.Name() }
-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) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
-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)                     {}
-func (t standaloneEnum) ProtoInternal(pragma.DoNotImplement)               {}
+func (t standaloneEnum) Parent() (pref.Descriptor, bool)       { return nil, false }
+func (t standaloneEnum) Index() int                            { return 0 }
+func (t standaloneEnum) Syntax() pref.Syntax                   { return t.e.Syntax }
+func (t standaloneEnum) Name() pref.Name                       { return t.e.FullName.Name() }
+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) 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)         {}
+func (t standaloneEnum) ProtoInternal(pragma.DoNotImplement)   {}
 
 type standaloneExtension struct{ x *StandaloneExtension }
 
-func (t standaloneExtension) Parent() (pref.Descriptor, bool)                   { return nil, false }
-func (t standaloneExtension) Index() int                                        { return 0 }
-func (t standaloneExtension) Syntax() pref.Syntax                               { return t.x.Syntax }
-func (t standaloneExtension) Name() pref.Name                                   { return t.x.FullName.Name() }
-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) DescriptorOptions() (pref.DescriptorOptions, bool) { return nil, false }
-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.IsPacked }
-func (t standaloneExtension) IsMap() bool                                       { return false }
-func (t standaloneExtension) IsWeak() bool                                      { return false }
-func (t standaloneExtension) Default() pref.Value                               { return t.x.dv.lazyInit(t, t.x.Default) }
-func (t standaloneExtension) HasDefault() bool                                  { return t.x.Default.IsValid() }
-func (t standaloneExtension) OneofType() pref.OneofDescriptor                   { return nil }
-func (t standaloneExtension) MessageType() pref.MessageDescriptor               { return t.x.MessageType }
-func (t standaloneExtension) EnumType() pref.EnumDescriptor                     { return t.x.EnumType }
-func (t standaloneExtension) ExtendedType() pref.MessageDescriptor              { return t.x.ExtendedType }
-func (t standaloneExtension) Format(s fmt.State, r rune)                        { formatDesc(s, r, t) }
-func (t standaloneExtension) ProtoType(pref.FieldDescriptor)                    {}
-func (t standaloneExtension) ProtoInternal(pragma.DoNotImplement)               {}
+func (t standaloneExtension) Parent() (pref.Descriptor, bool)       { return nil, false }
+func (t standaloneExtension) Index() int                            { return 0 }
+func (t standaloneExtension) Syntax() pref.Syntax                   { return t.x.Syntax }
+func (t standaloneExtension) Name() pref.Name                       { return t.x.FullName.Name() }
+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) 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) IsWeak() bool                          { return false }
+func (t standaloneExtension) Default() pref.Value                   { return t.x.dv.lazyInit(t, t.x.Default) }
+func (t standaloneExtension) HasDefault() bool                      { return t.x.Default.IsValid() }
+func (t standaloneExtension) OneofType() pref.OneofDescriptor       { return nil }
+func (t standaloneExtension) MessageType() pref.MessageDescriptor   { return t.x.MessageType }
+func (t standaloneExtension) EnumType() pref.EnumDescriptor         { return t.x.EnumType }
+func (t standaloneExtension) ExtendedType() pref.MessageDescriptor  { return t.x.ExtendedType }
+func (t standaloneExtension) Format(s fmt.State, r rune)            { formatDesc(s, r, t) }
+func (t standaloneExtension) ProtoType(pref.FieldDescriptor)        {}
+func (t standaloneExtension) ProtoInternal(pragma.DoNotImplement)   {}
diff --git a/reflect/prototype/type_test.go b/reflect/prototype/type_test.go
index c5fe70f..cf961f1 100644
--- a/reflect/prototype/type_test.go
+++ b/reflect/prototype/type_test.go
@@ -109,12 +109,17 @@
 		Syntax:  pref.Proto2,
 		Path:    "path/to/file.proto",
 		Package: "test",
+		Options: &descriptorV1.FileOptions{Deprecated: protoV1.Bool(true)},
 		Messages: []Message{{
-			Name:       "A", // "test.A"
-			IsMapEntry: true,
+			Name: "A", // "test.A"
+			Options: &descriptorV1.MessageOptions{
+				MapEntry:   protoV1.Bool(true),
+				Deprecated: protoV1.Bool(true),
+			},
 			Fields: []Field{{
 				Name:        "key", // "test.A.key"
 				Number:      1,
+				Options:     &descriptorV1.FieldOptions{Deprecated: protoV1.Bool(true)},
 				Cardinality: pref.Optional,
 				Kind:        pref.StringKind,
 			}, {
@@ -161,7 +166,7 @@
 				Number:      5,
 				Cardinality: pref.Repeated,
 				Kind:        pref.Int32Kind,
-				IsPacked:    true,
+				Options:     &descriptorV1.FieldOptions{Packed: protoV1.Bool(true)},
 			}, {
 				Name:        "field_six", // "test.B.field_six"
 				Number:      6,
@@ -169,7 +174,14 @@
 				Kind:        pref.BytesKind,
 			}},
 			Oneofs: []Oneof{
-				{Name: "O1"}, // "test.B.O1"
+				{
+					Name: "O1", // "test.B.O1"
+					Options: &descriptorV1.OneofOptions{
+						UninterpretedOption: []*descriptorV1.UninterpretedOption{
+							{StringValue: []byte("option")},
+						},
+					},
+				},
 				{Name: "O2"}, // "test.B.O2"
 			},
 			ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}, {3000, 3001}},
@@ -188,32 +200,42 @@
 				Number:       1000,
 				Cardinality:  pref.Repeated,
 				Kind:         pref.MessageKind,
-				IsPacked:     false,
+				Options:      &descriptorV1.FieldOptions{Packed: protoV1.Bool(false)},
 				MessageType:  PlaceholderMessage("test.C"),
 				ExtendedType: PlaceholderMessage("test.B"),
 			}},
 		}},
 		Enums: []Enum{{
-			Name:   "E1", // "test.E1"
-			Values: []EnumValue{{Name: "FOO", Number: 0}, {Name: "BAR", Number: 1}},
+			Name:    "E1", // "test.E1"
+			Options: &descriptorV1.EnumOptions{Deprecated: protoV1.Bool(true)},
+			Values: []EnumValue{
+				{
+					Name:    "FOO",
+					Number:  0,
+					Options: &descriptorV1.EnumValueOptions{Deprecated: protoV1.Bool(true)},
+				},
+				{Name: "BAR", Number: 1},
+			},
 		}},
 		Extensions: []Extension{{
 			Name:         "X", // "test.X"
 			Number:       1000,
 			Cardinality:  pref.Repeated,
 			Kind:         pref.MessageKind,
-			IsPacked:     true,
+			Options:      &descriptorV1.FieldOptions{Packed: protoV1.Bool(true)},
 			MessageType:  PlaceholderMessage("test.C"),
 			ExtendedType: PlaceholderMessage("test.B"),
 		}},
 		Services: []Service{{
-			Name: "S", // "test.S"
+			Name:    "S", // "test.S"
+			Options: &descriptorV1.ServiceOptions{Deprecated: protoV1.Bool(true)},
 			Methods: []Method{{
 				Name:              "M", // "test.S.M"
 				InputType:         PlaceholderMessage("test.A"),
 				OutputType:        PlaceholderMessage("test.C.A"),
 				IsStreamingClient: true,
 				IsStreamingServer: true,
+				Options:           &descriptorV1.MethodOptions{Deprecated: protoV1.Bool(true)},
 			}},
 		}},
 	}
@@ -226,14 +248,19 @@
 		Syntax:  protoV1.String("proto2"),
 		Name:    protoV1.String("path/to/file.proto"),
 		Package: protoV1.String("test"),
+		Options: &descriptorV1.FileOptions{Deprecated: protoV1.Bool(true)},
 		MessageType: []*descriptorV1.DescriptorProto{{
-			Name:    protoV1.String("A"),
-			Options: &descriptorV1.MessageOptions{MapEntry: protoV1.Bool(true)},
+			Name: protoV1.String("A"),
+			Options: &descriptorV1.MessageOptions{
+				MapEntry:   protoV1.Bool(true),
+				Deprecated: protoV1.Bool(true),
+			},
 			Field: []*descriptorV1.FieldDescriptorProto{{
-				Name:   protoV1.String("key"),
-				Number: protoV1.Int32(1),
-				Label:  descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
-				Type:   descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
+				Name:    protoV1.String("key"),
+				Number:  protoV1.Int32(1),
+				Options: &descriptorV1.FieldOptions{Deprecated: protoV1.Bool(true)},
+				Label:   descriptorV1.FieldDescriptorProto_Label(pref.Optional).Enum(),
+				Type:    descriptorV1.FieldDescriptorProto_Type(pref.StringKind).Enum(),
 			}, {
 				Name:     protoV1.String("value"),
 				Number:   protoV1.Int32(2),
@@ -286,7 +313,14 @@
 				Type:   descriptorV1.FieldDescriptorProto_Type(pref.BytesKind).Enum(),
 			}},
 			OneofDecl: []*descriptorV1.OneofDescriptorProto{
-				{Name: protoV1.String("O1")},
+				{
+					Name: protoV1.String("O1"),
+					Options: &descriptorV1.OneofOptions{
+						UninterpretedOption: []*descriptorV1.UninterpretedOption{
+							{StringValue: []byte("option")},
+						},
+					},
+				},
 				{Name: protoV1.String("O2")},
 			},
 			ExtensionRange: []*descriptorV1.DescriptorProto_ExtensionRange{
@@ -322,9 +356,14 @@
 			}},
 		}},
 		EnumType: []*descriptorV1.EnumDescriptorProto{{
-			Name: protoV1.String("E1"),
+			Name:    protoV1.String("E1"),
+			Options: &descriptorV1.EnumOptions{Deprecated: protoV1.Bool(true)},
 			Value: []*descriptorV1.EnumValueDescriptorProto{
-				{Name: protoV1.String("FOO"), Number: protoV1.Int32(0)},
+				{
+					Name:    protoV1.String("FOO"),
+					Number:  protoV1.Int32(0),
+					Options: &descriptorV1.EnumValueOptions{Deprecated: protoV1.Bool(true)},
+				},
 				{Name: protoV1.String("BAR"), Number: protoV1.Int32(1)},
 			},
 		}},
@@ -338,13 +377,15 @@
 			Extendee: protoV1.String(".test.B"),
 		}},
 		Service: []*descriptorV1.ServiceDescriptorProto{{
-			Name: protoV1.String("S"),
+			Name:    protoV1.String("S"),
+			Options: &descriptorV1.ServiceOptions{Deprecated: protoV1.Bool(true)},
 			Method: []*descriptorV1.MethodDescriptorProto{{
 				Name:            protoV1.String("M"),
 				InputType:       protoV1.String(".test.A"),
 				OutputType:      protoV1.String(".test.C.A"),
 				ClientStreaming: protoV1.Bool(true),
 				ServerStreaming: protoV1.Bool(true),
+				Options:         &descriptorV1.MethodOptions{Deprecated: protoV1.Bool(true)},
 			}},
 		}},
 	}
@@ -385,6 +426,7 @@
 		"Path":          "path/to/file.proto",
 		"Package":       pref.FullName("test"),
 		"IsPlaceholder": false,
+		"Options":       &descriptorV1.FileOptions{Deprecated: protoV1.Bool(true)},
 		"Messages": M{
 			"Len": 3,
 			"Get:0": M{
@@ -395,6 +437,10 @@
 				"FullName":      pref.FullName("test.A"),
 				"IsPlaceholder": false,
 				"IsMapEntry":    true,
+				"Options": &descriptorV1.MessageOptions{
+					MapEntry:   protoV1.Bool(true),
+					Deprecated: protoV1.Bool(true),
+				},
 				"Fields": M{
 					"Len": 2,
 					"ByNumber:1": M{
@@ -405,6 +451,7 @@
 						"Number":       pref.FieldNumber(1),
 						"Cardinality":  pref.Optional,
 						"Kind":         pref.StringKind,
+						"Options":      &descriptorV1.FieldOptions{Deprecated: protoV1.Bool(true)},
 						"JSONName":     "key",
 						"IsPacked":     false,
 						"IsMap":        false,
@@ -494,6 +541,11 @@
 					"ByName:O1": M{
 						"FullName": pref.FullName("test.B.O1"),
 						"Index":    0,
+						"Options": &descriptorV1.OneofOptions{
+							UninterpretedOption: []*descriptorV1.UninterpretedOption{
+								{StringValue: []byte("option")},
+							},
+						},
 						"Fields": M{
 							"Len":   1,
 							"Get:0": M{"FullName": pref.FullName("test.B.field_one")},
@@ -546,11 +598,15 @@
 		"Enums": M{
 			"Len": 1,
 			"Get:0": M{
-				"Name": pref.Name("E1"),
+				"Name":    pref.Name("E1"),
+				"Options": &descriptorV1.EnumOptions{Deprecated: protoV1.Bool(true)},
 				"Values": M{
 					"Len":        2,
 					"ByName:Foo": nil,
-					"ByName:FOO": M{"FullName": pref.FullName("test.FOO")},
+					"ByName:FOO": M{
+						"FullName": pref.FullName("test.FOO"),
+						"Options":  &descriptorV1.EnumValueOptions{Deprecated: protoV1.Bool(true)},
+					},
 					"ByNumber:2": nil,
 					"ByNumber:1": M{"FullName": pref.FullName("test.BAR")},
 				},
@@ -566,6 +622,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: protoV1.Bool(true)},
 			},
 		},
 		"Services": M{
@@ -575,6 +632,7 @@
 				"Parent":   M{"FullName": pref.FullName("test")},
 				"Name":     pref.Name("S"),
 				"FullName": pref.FullName("test.S"),
+				"Options":  &descriptorV1.ServiceOptions{Deprecated: protoV1.Bool(true)},
 				"Methods": M{
 					"Len": 1,
 					"Get:0": M{
@@ -585,6 +643,7 @@
 						"OutputType":        M{"FullName": pref.FullName("test.C.A"), "IsPlaceholder": false},
 						"IsStreamingClient": true,
 						"IsStreamingServer": true,
+						"Options":           &descriptorV1.MethodOptions{Deprecated: protoV1.Bool(true)},
 					},
 				},
 			},
@@ -659,14 +718,26 @@
 		// Check that the accessor output matches.
 		if want, ok := v.(map[string]interface{}); ok {
 			checkAccessors(t, p, rets[0], want)
-		} else {
-			got := rets[0].Interface()
-			if pv, ok := got.(pref.Value); ok {
-				got = pv.Interface()
-			}
-			if want := v; !reflect.DeepEqual(got, want) {
+			continue
+		}
+
+		got := rets[0].Interface()
+		if pv, ok := got.(pref.Value); ok {
+			got = pv.Interface()
+		}
+
+		// Compare with proto.Equal if possible.
+		gotMsg, gotMsgOK := got.(protoV1.Message)
+		wantMsg, wantMsgOK := v.(protoV1.Message)
+		if gotMsgOK && wantMsgOK {
+			if !protoV1.Equal(gotMsg, wantMsg) {
 				t.Errorf("%v = %v, want %v", p, got, want)
 			}
+			continue
+		}
+
+		if want := v; !reflect.DeepEqual(got, want) {
+			t.Errorf("%v = %v, want %v", p, got, want)
 		}
 	}
 }
diff --git a/reflect/prototype/validate.go b/reflect/prototype/validate.go
index c336f17..be1090b 100644
--- a/reflect/prototype/validate.go
+++ b/reflect/prototype/validate.go
@@ -26,6 +26,9 @@
 //	* 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
 
 func validateFile(t pref.FileDescriptor) error {
 	return nil