Explorar el Código

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>
Damien Neil hace 7 años
padre
commit
204f1c0ad8

+ 6 - 5
cmd/protoc-gen-go-grpc/internal_gengogrpc/grpc.go

@@ -10,6 +10,7 @@ import (
 	"strconv"
 	"strings"
 
+	descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
 	"github.com/golang/protobuf/v2/protogen"
 )
 
@@ -56,7 +57,7 @@ func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.Generated
 	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 @@ func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.Generated
 	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 @@ func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.Generated
 	// 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 @@ func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.Generated
 	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 @@ func genClientMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.Gene
 	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), "{")

+ 0 - 116
cmd/protoc-gen-go-grpc/internal_gengogrpc/options.go

@@ -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
-}

+ 8 - 8
cmd/protoc-gen-go/internal_gengo/main.go

@@ -237,13 +237,13 @@ func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum
 	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 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 	}
 
 	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 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 		}
 		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 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 
 	// 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 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, me
 		}
 		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 genExtension(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo,
 
 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"
 }
 

+ 0 - 122
cmd/protoc-gen-go/internal_gengo/options.go

@@ -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
-}

+ 3 - 1
internal/cmd/pbdump/pbdump.go

@@ -17,6 +17,8 @@ import (
 	"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 @@ func (fs fields) messageDescriptor(name protoreflect.FullName) prototype.Message
 			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)

+ 2 - 1
internal/cmd/pbdump/pbdump_test.go

@@ -67,7 +67,7 @@ func TestFields(t *testing.T) {
 					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 @@ func TestFields(t *testing.T) {
 			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 {

+ 15 - 10
internal/encoding/pack/pack_test.go

@@ -11,6 +11,7 @@ import (
 	"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 @@ var msgDesc = func() pref.MessageDescriptor {
 		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 @@ var msgDesc = func() pref.MessageDescriptor {
 	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)

+ 10 - 5
internal/impl/legacy_message.go

@@ -13,6 +13,8 @@ import (
 	"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 @@ func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, t reflect.Type,
 		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 @@ func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, t reflect.Type,
 		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 @@ func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, t reflect.Type,
 			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),

+ 2 - 0
internal/impl/legacy_test.go

@@ -99,6 +99,8 @@ func TestLegacy(t *testing.T) {
 					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.

+ 3 - 1
internal/impl/message_test.go

@@ -14,6 +14,8 @@ import (
 	"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 @@ func TestMapScalars(t *testing.T) {
 					{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)},
 			}),
 		}
 	}

+ 22 - 49
reflect/protoreflect/type.go

@@ -104,57 +104,30 @@ type Descriptor interface {
 	// 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            |
+	//	+---------------------+------------------------------------------+
+	//
+	// 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)).
 	//
-	// 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)
+	// TODO: Return ProtoMessage instead of interface{}.
+	Options() interface{}
 
 	doNotImplement
 }

+ 0 - 29
reflect/prototype/descriptor.go

@@ -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
-}

+ 12 - 9
reflect/prototype/placeholder_type.go

@@ -7,6 +7,7 @@ package prototype
 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 @@ var (
 
 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 @@ type placeholderMessage struct {
 	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 @@ type placeholderEnum struct {
 	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)     {}

+ 22 - 18
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 @@ import (
 
 // 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 @@ func NewFile(t *File) (protoreflect.FileDescriptor, error) {
 // 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 @@ type Field struct {
 	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 @@ type Extension struct {
 	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 EnumValue struct {
 type Service struct {
 	Name    protoreflect.Name
 	Methods []Method
+	Options *descriptorV1.ServiceOptions
 
 	*serviceMeta
 }
@@ -154,6 +157,7 @@ type Method struct {
 	OutputType        protoreflect.MessageDescriptor
 	IsStreamingClient bool
 	IsStreamingServer bool
+	Options           *descriptorV1.MethodOptions
 
 	*methodMeta
 }

+ 17 - 40
reflect/prototype/protofile_desc.go

@@ -64,6 +64,7 @@ func NewFileFromDescriptorProto(fd *descriptorV1.FileDescriptorProto, r *protore
 	}
 	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 @@ func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, syntax pro
 	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 @@ func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, syntax pro
 				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 @@ func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, syntax pro
 				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 @@ func messagesFromDescriptorProto(mds []*descriptorV1.DescriptorProto, syntax pro
 	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 @@ func extensionsFromDescriptorProto(xds []*descriptorV1.FieldDescriptorProto, r *
 		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 @@ func servicesFromDescriptorProto(sds []*descriptorV1.ServiceDescriptorProto, r *
 	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

+ 183 - 150
reflect/prototype/protofile_type.go

@@ -10,6 +10,7 @@ import (
 	"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 @@ type inheritedMeta struct {
 	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 @@ func (m *inheritedMeta) init(nb *nameBuilder, parent pref.Descriptor, index int,
 }
 
 type fileMeta struct {
-	desc descriptorFileMeta
-	opts descriptorOptionsMeta
-
 	ms messagesMeta
 	es enumsMeta
 	xs extensionsMeta
@@ -59,25 +54,25 @@ func newFile(f *File) fileDesc {
 	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 messageMeta struct {
 }
 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 fieldMeta struct {
 }
 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 oneofMeta struct {
 }
 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 extensionMeta struct {
 }
 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) Format(s fmt.State, r rune)          { formatDesc(s, r, t
 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 enumMeta struct {
 }
 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 serviceMeta struct {
 }
 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 methodMeta struct {
 }
 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

+ 5 - 3
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 @@ import (
 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 @@ func NewMessages(ts []*StandaloneMessage) ([]protoreflect.MessageDescriptor, err
 		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 @@ type StandaloneEnum struct {
 	Syntax   protoreflect.Syntax
 	FullName protoreflect.FullName
 	Values   []EnumValue
+	Options  *descriptorV1.EnumOptions
 
 	vals enumValuesMeta
 }
@@ -107,11 +109,11 @@ type StandaloneExtension struct {
 	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
 }

+ 55 - 55
reflect/prototype/standalone_type.go

@@ -13,64 +13,64 @@ import (
 
 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)   {}

+ 98 - 27
reflect/prototype/type_test.go

@@ -109,12 +109,17 @@ func TestFile(t *testing.T) {
 		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 @@ func TestFile(t *testing.T) {
 				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 @@ func TestFile(t *testing.T) {
 				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 @@ func TestFile(t *testing.T) {
 				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 @@ func TestFile(t *testing.T) {
 		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 @@ func TestFile(t *testing.T) {
 				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 @@ func TestFile(t *testing.T) {
 			}},
 		}},
 		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 @@ func TestFile(t *testing.T) {
 			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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 		"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 				"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 						"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 					"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 		"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 				"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 				"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 @@ func testFileAccessors(t *testing.T, fd pref.FileDescriptor) {
 						"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 @@ func checkAccessors(t *testing.T, p string, rv reflect.Value, want map[string]in
 		// 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)
 		}
 	}
 }

+ 3 - 0
reflect/prototype/validate.go

@@ -26,6 +26,9 @@ import (
 //	* 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