internal/fileinit: make fileinit responsible for registering concrete options types

Changes:
* Remove protoreflect.OptionsMessage and use protoreflect.ProtoMessage
now that the generated options natively implement reflection.
* Make registration of concrete option types the responsibility of
internal/fileinit since the init logic of descriptor.pb.go uses that.
* Remove equivalent logic in protoc-gen-go to special-case descriptor.

Change-Id: Id814651fafa4d888ff4532d59c6a4f9b68145157
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/171465
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index a125405..77f5de3 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -36,7 +36,6 @@
 	protoimplPackage     = protogen.GoImportPath("github.com/golang/protobuf/v2/runtime/protoimpl")
 	protoreflectPackage  = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoreflect")
 	protoregistryPackage = protogen.GoImportPath("github.com/golang/protobuf/v2/reflect/protoregistry")
-	prototypePackage     = protogen.GoImportPath("github.com/golang/protobuf/v2/internal/prototype")
 )
 
 type fileInfo struct {
diff --git a/cmd/protoc-gen-go/internal_gengo/reflect.go b/cmd/protoc-gen-go/internal_gengo/reflect.go
index 129a735..4c684e4 100644
--- a/cmd/protoc-gen-go/internal_gengo/reflect.go
+++ b/cmd/protoc-gen-go/internal_gengo/reflect.go
@@ -7,7 +7,6 @@
 import (
 	"fmt"
 	"math"
-	"strings"
 
 	"github.com/golang/protobuf/v2/proto"
 	"github.com/golang/protobuf/v2/protogen"
@@ -163,19 +162,6 @@
 	g.P("TypesRegistry: ", protoregistryPackage.Ident("GlobalTypes"), ",")
 	g.P("}.Init()")
 
-	// The descriptor proto needs to register the option types with the
-	// prototype so that the package can properly handle those option types.
-	//
-	// TODO: Should this be handled by fileinit at runtime?
-	if f.Desc.Path() == "google/protobuf/descriptor.proto" && f.Desc.Package() == "google.protobuf" {
-		for _, m := range f.allMessages {
-			name := m.GoIdent.GoName
-			if strings.HasSuffix(name, "Options") {
-				g.P(prototypePackage.Ident("X"), ".Register", name, "((*", name, ")(nil))")
-			}
-		}
-	}
-
 	// Set inputs to nil to allow GC to reclaim resources.
 	g.P(rawDescVarName(f), " = nil")
 	g.P(goTypesVarName(f), " = nil")
diff --git a/internal/descopts/options.go b/internal/descopts/options.go
new file mode 100644
index 0000000..58284b6
--- /dev/null
+++ b/internal/descopts/options.go
@@ -0,0 +1,29 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package descopts contains the nil pointers to concrete descriptor options.
+//
+// This package exists as a form of reverse dependency injection so that certain
+// packages (e.g., internal/fileinit can avoid a direct dependency on the
+// descriptor proto package).
+package descopts
+
+import pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+
+// These variables are set by the init function in descriptor.pb.go via logic
+// in internal/fileinit. In other words, so long as the descriptor proto package
+// is linked in, these variables will be populated.
+//
+// Each variable is populated with a nil pointer to the options struct.
+var (
+	File           pref.ProtoMessage
+	Enum           pref.ProtoMessage
+	EnumValue      pref.ProtoMessage
+	Message        pref.ProtoMessage
+	Field          pref.ProtoMessage
+	Oneof          pref.ProtoMessage
+	ExtensionRange pref.ProtoMessage
+	Service        pref.ProtoMessage
+	Method         pref.ProtoMessage
+)
diff --git a/internal/fileinit/desc.go b/internal/fileinit/desc.go
index b53a90f..f305ee0 100644
--- a/internal/fileinit/desc.go
+++ b/internal/fileinit/desc.go
@@ -13,9 +13,9 @@
 	"reflect"
 	"sync"
 
+	descopts "github.com/golang/protobuf/v2/internal/descopts"
 	pimpl "github.com/golang/protobuf/v2/internal/impl"
 	pragma "github.com/golang/protobuf/v2/internal/pragma"
-	ptype "github.com/golang/protobuf/v2/internal/prototype"
 	pfmt "github.com/golang/protobuf/v2/internal/typefmt"
 	"github.com/golang/protobuf/v2/proto"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
@@ -158,6 +158,33 @@
 		fb.ExtensionOutputTypes[i] = &fd.allExtensions[i]
 	}
 
+	// As a special-case for descriptor.proto,
+	// locally register concrete message type for the options.
+	if fd.Path() == "google/protobuf/descriptor.proto" && fd.Package() == "google.protobuf" {
+		for i := range fd.allMessages {
+			switch fd.allMessages[i].Name() {
+			case "FileOptions":
+				descopts.File = messageGoTypes[i].(pref.ProtoMessage)
+			case "EnumOptions":
+				descopts.Enum = messageGoTypes[i].(pref.ProtoMessage)
+			case "EnumValueOptions":
+				descopts.EnumValue = messageGoTypes[i].(pref.ProtoMessage)
+			case "MessageOptions":
+				descopts.Message = messageGoTypes[i].(pref.ProtoMessage)
+			case "FieldOptions":
+				descopts.Field = messageGoTypes[i].(pref.ProtoMessage)
+			case "OneofOptions":
+				descopts.Oneof = messageGoTypes[i].(pref.ProtoMessage)
+			case "ExtensionRangeOptions":
+				descopts.ExtensionRange = messageGoTypes[i].(pref.ProtoMessage)
+			case "ServiceOptions":
+				descopts.Service = messageGoTypes[i].(pref.ProtoMessage)
+			case "MethodOptions":
+				descopts.Method = messageGoTypes[i].(pref.ProtoMessage)
+			}
+		}
+	}
+
 	// Register file and type descriptors.
 	if fb.FilesRegistry != nil {
 		if err := fb.FilesRegistry.Register(fd); err != nil {
@@ -230,8 +257,8 @@
 func (fd *fileDesc) Name() pref.Name                 { return fd.Package().Name() }
 func (fd *fileDesc) FullName() pref.FullName         { return fd.Package() }
 func (fd *fileDesc) IsPlaceholder() bool             { return false }
-func (fd *fileDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.FileOptions(), fd.lazyInit().options)
+func (fd *fileDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.File, fd.lazyInit().options)
 }
 func (fd *fileDesc) Path() string                                     { return fd.path }
 func (fd *fileDesc) Package() pref.FullName                           { return fd.protoPackage }
@@ -270,8 +297,8 @@
 
 func (ed *enumDesc) GoType() reflect.Type            { return ed.lazyInit().typ }
 func (ed *enumDesc) New(n pref.EnumNumber) pref.Enum { return ed.lazyInit().new(n) }
-func (ed *enumDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.EnumOptions(), ed.lazyInit().options)
+func (ed *enumDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Enum, ed.lazyInit().options)
 }
 func (ed *enumDesc) Values() pref.EnumValueDescriptors { return &ed.lazyInit().values }
 func (ed *enumDesc) ReservedNames() pref.Names         { return &ed.lazyInit().resvNames }
@@ -283,8 +310,8 @@
 	return ed.lazy
 }
 
-func (ed *enumValueDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.EnumValueOptions(), ed.options)
+func (ed *enumValueDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.EnumValue, ed.options)
 }
 func (ed *enumValueDesc) Number() pref.EnumNumber            { return ed.number }
 func (ed *enumValueDesc) Format(s fmt.State, r rune)         { pfmt.FormatDesc(s, r, ed) }
@@ -346,8 +373,8 @@
 	}
 )
 
-func (md *messageDesc) options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.MessageOptions(), md.lazyInit().options)
+func (md *messageDesc) options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Message, md.lazyInit().options)
 }
 func (md *messageDesc) IsMapEntry() bool                   { return md.isMapEntry }
 func (md *messageDesc) Fields() pref.FieldDescriptors      { return &md.lazyInit().fields }
@@ -356,8 +383,8 @@
 func (md *messageDesc) ReservedRanges() pref.FieldRanges   { return &md.lazyInit().resvRanges }
 func (md *messageDesc) RequiredNumbers() pref.FieldNumbers { return &md.lazyInit().reqNumbers }
 func (md *messageDesc) ExtensionRanges() pref.FieldRanges  { return &md.lazyInit().extRanges }
-func (md *messageDesc) ExtensionRangeOptions(i int) pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.ExtensionRangeOptions(), md.lazyInit().extRangeOptions[i])
+func (md *messageDesc) ExtensionRangeOptions(i int) pref.ProtoMessage {
+	return unmarshalOptions(descopts.ExtensionRange, md.lazyInit().extRangeOptions[i])
 }
 func (md *messageDesc) Enums() pref.EnumDescriptors           { return &md.enums }
 func (md *messageDesc) Messages() pref.MessageDescriptors     { return &md.messages }
@@ -386,13 +413,13 @@
 	}
 	return messageDescriptor{mb}
 }
-func (mt messageType) GoType() reflect.Type               { return mt.lazyInit().typ }
-func (mt messageType) New() pref.Message                  { return mt.lazyInit().new() }
-func (mt messageType) Options() pref.OptionsMessage       { return mt.options() }
-func (md messageDescriptor) Options() pref.OptionsMessage { return md.options() }
+func (mt messageType) GoType() reflect.Type             { return mt.lazyInit().typ }
+func (mt messageType) New() pref.Message                { return mt.lazyInit().new() }
+func (mt messageType) Options() pref.ProtoMessage       { return mt.options() }
+func (md messageDescriptor) Options() pref.ProtoMessage { return md.options() }
 
-func (fd *fieldDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.FieldOptions(), fd.options)
+func (fd *fieldDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Field, fd.options)
 }
 func (fd *fieldDesc) Number() pref.FieldNumber                   { return fd.number }
 func (fd *fieldDesc) Cardinality() pref.Cardinality              { return fd.cardinality }
@@ -412,8 +439,8 @@
 func (fd *fieldDesc) Format(s fmt.State, r rune)                 { pfmt.FormatDesc(s, r, fd) }
 func (fd *fieldDesc) ProtoType(pref.FieldDescriptor)             {}
 
-func (od *oneofDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.OneofOptions(), od.options)
+func (od *oneofDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Oneof, od.options)
 }
 func (od *oneofDesc) Fields() pref.FieldDescriptors  { return &od.fields }
 func (od *oneofDesc) Format(s fmt.State, r rune)     { pfmt.FormatDesc(s, r, od) }
@@ -458,8 +485,8 @@
 func (xd *extensionDesc) New() pref.Value                      { return xd.lazyInit().new() }
 func (xd *extensionDesc) ValueOf(v interface{}) pref.Value     { return xd.lazyInit().valueOf(v) }
 func (xd *extensionDesc) InterfaceOf(v pref.Value) interface{} { return xd.lazyInit().interfaceOf(v) }
-func (xd *extensionDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.FieldOptions(), xd.lazyInit().options)
+func (xd *extensionDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Field, xd.lazyInit().options)
 }
 func (xd *extensionDesc) Number() pref.FieldNumber                   { return xd.number }
 func (xd *extensionDesc) Cardinality() pref.Cardinality              { return xd.lazyInit().cardinality }
@@ -514,8 +541,8 @@
 	}
 )
 
-func (sd *serviceDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.ServiceOptions(), sd.lazyInit().options)
+func (sd *serviceDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Service, sd.lazyInit().options)
 }
 func (sd *serviceDesc) Methods() pref.MethodDescriptors     { return &sd.lazyInit().methods }
 func (sd *serviceDesc) Format(s fmt.State, r rune)          { pfmt.FormatDesc(s, r, sd) }
@@ -526,8 +553,8 @@
 	return sd.lazy
 }
 
-func (md *methodDesc) Options() pref.OptionsMessage {
-	return unmarshalOptions(ptype.X.MethodOptions(), md.options)
+func (md *methodDesc) Options() pref.ProtoMessage {
+	return unmarshalOptions(descopts.Method, md.options)
 }
 func (md *methodDesc) InputType() pref.MessageDescriptor   { return md.inputType }
 func (md *methodDesc) OutputType() pref.MessageDescriptor  { return md.outputType }
@@ -558,10 +585,10 @@
 func (s *fullName) Name() pref.Name         { return pref.Name(s.fullName[len(s.fullName)-s.shortLen:]) }
 func (s *fullName) FullName() pref.FullName { return s.fullName }
 
-func unmarshalOptions(p pref.OptionsMessage, b []byte) pref.OptionsMessage {
+func unmarshalOptions(p pref.ProtoMessage, b []byte) pref.ProtoMessage {
 	if b != nil {
 		// TODO: Consider caching the unmarshaled options message.
-		p = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.OptionsMessage)
+		p = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
 		if err := proto.Unmarshal(b, p.(proto.Message)); err != nil {
 			panic(err)
 		}
diff --git a/internal/prototype/options.go b/internal/prototype/options.go
deleted file mode 100644
index ec5c857..0000000
--- a/internal/prototype/options.go
+++ /dev/null
@@ -1,52 +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"
-)
-
-// X provides functionality internal to the protobuf module.
-//
-// WARNING: The compatibility agreement covers nothing except for functionality
-// needed to keep existing generated messages operational. The Go authors
-// are not responsible for breakages that occur due to unauthorized usages.
-var X internal
-
-type internal struct{}
-
-// optionTypes contains typed nil-pointers to each of the options types.
-// These are populated at init time by the descriptor package.
-var optionTypes struct {
-	File           pref.OptionsMessage
-	Enum           pref.OptionsMessage
-	EnumValue      pref.OptionsMessage
-	Message        pref.OptionsMessage
-	Field          pref.OptionsMessage
-	Oneof          pref.OptionsMessage
-	ExtensionRange pref.OptionsMessage
-	Service        pref.OptionsMessage
-	Method         pref.OptionsMessage
-}
-
-func (internal) FileOptions() pref.OptionsMessage           { return optionTypes.File }
-func (internal) EnumOptions() pref.OptionsMessage           { return optionTypes.Enum }
-func (internal) EnumValueOptions() pref.OptionsMessage      { return optionTypes.EnumValue }
-func (internal) MessageOptions() pref.OptionsMessage        { return optionTypes.Message }
-func (internal) FieldOptions() pref.OptionsMessage          { return optionTypes.Field }
-func (internal) OneofOptions() pref.OptionsMessage          { return optionTypes.Oneof }
-func (internal) ExtensionRangeOptions() pref.OptionsMessage { return optionTypes.ExtensionRange }
-func (internal) ServiceOptions() pref.OptionsMessage        { return optionTypes.Service }
-func (internal) MethodOptions() pref.OptionsMessage         { return optionTypes.Method }
-
-func (internal) RegisterFileOptions(m pref.OptionsMessage)           { optionTypes.File = m }
-func (internal) RegisterEnumOptions(m pref.OptionsMessage)           { optionTypes.Enum = m }
-func (internal) RegisterEnumValueOptions(m pref.OptionsMessage)      { optionTypes.EnumValue = m }
-func (internal) RegisterMessageOptions(m pref.OptionsMessage)        { optionTypes.Message = m }
-func (internal) RegisterFieldOptions(m pref.OptionsMessage)          { optionTypes.Field = m }
-func (internal) RegisterOneofOptions(m pref.OptionsMessage)          { optionTypes.Oneof = m }
-func (internal) RegisterExtensionRangeOptions(m pref.OptionsMessage) { optionTypes.ExtensionRange = m }
-func (internal) RegisterServiceOptions(m pref.OptionsMessage)        { optionTypes.Service = m }
-func (internal) RegisterMethodOptions(m pref.OptionsMessage)         { optionTypes.Method = m }
diff --git a/internal/prototype/placeholder_type.go b/internal/prototype/placeholder_type.go
index 8930aab..a99bc38 100644
--- a/internal/prototype/placeholder_type.go
+++ b/internal/prototype/placeholder_type.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 
+	descopts "github.com/golang/protobuf/v2/internal/descopts"
 	pragma "github.com/golang/protobuf/v2/internal/pragma"
 	pfmt "github.com/golang/protobuf/v2/internal/typefmt"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
@@ -42,7 +43,7 @@
 	placeholderName
 }
 
-func (t placeholderFile) Options() pref.OptionsMessage                   { return optionTypes.File }
+func (t placeholderFile) Options() pref.ProtoMessage                     { return descopts.File }
 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 }
@@ -58,26 +59,26 @@
 	placeholderName
 }
 
-func (t placeholderMessage) Options() pref.OptionsMessage                  { return optionTypes.Message }
-func (t placeholderMessage) IsMapEntry() bool                              { return false }
-func (t placeholderMessage) Fields() pref.FieldDescriptors                 { return &emptyFields }
-func (t placeholderMessage) Oneofs() pref.OneofDescriptors                 { return &emptyOneofs }
-func (t placeholderMessage) ReservedNames() pref.Names                     { return &emptyNames }
-func (t placeholderMessage) ReservedRanges() pref.FieldRanges              { return &emptyFieldRanges }
-func (t placeholderMessage) RequiredNumbers() pref.FieldNumbers            { return &emptyNumbers }
-func (t placeholderMessage) ExtensionRanges() pref.FieldRanges             { return &emptyFieldRanges }
-func (t placeholderMessage) ExtensionRangeOptions(int) pref.OptionsMessage { panic("out of bounds") }
-func (t placeholderMessage) Enums() pref.EnumDescriptors                   { return &emptyEnums }
-func (t placeholderMessage) Messages() pref.MessageDescriptors             { return &emptyMessages }
-func (t placeholderMessage) Extensions() pref.ExtensionDescriptors         { return &emptyExtensions }
-func (t placeholderMessage) Format(s fmt.State, r rune)                    { pfmt.FormatDesc(s, r, t) }
-func (t placeholderMessage) ProtoType(pref.MessageDescriptor)              {}
+func (t placeholderMessage) Options() pref.ProtoMessage                  { return descopts.Message }
+func (t placeholderMessage) IsMapEntry() bool                            { return false }
+func (t placeholderMessage) Fields() pref.FieldDescriptors               { return &emptyFields }
+func (t placeholderMessage) Oneofs() pref.OneofDescriptors               { return &emptyOneofs }
+func (t placeholderMessage) ReservedNames() pref.Names                   { return &emptyNames }
+func (t placeholderMessage) ReservedRanges() pref.FieldRanges            { return &emptyFieldRanges }
+func (t placeholderMessage) RequiredNumbers() pref.FieldNumbers          { return &emptyNumbers }
+func (t placeholderMessage) ExtensionRanges() pref.FieldRanges           { return &emptyFieldRanges }
+func (t placeholderMessage) ExtensionRangeOptions(int) pref.ProtoMessage { panic("out of bounds") }
+func (t placeholderMessage) Enums() pref.EnumDescriptors                 { return &emptyEnums }
+func (t placeholderMessage) Messages() pref.MessageDescriptors           { return &emptyMessages }
+func (t placeholderMessage) Extensions() pref.ExtensionDescriptors       { return &emptyExtensions }
+func (t placeholderMessage) Format(s fmt.State, r rune)                  { pfmt.FormatDesc(s, r, t) }
+func (t placeholderMessage) ProtoType(pref.MessageDescriptor)            {}
 
 type placeholderEnum struct {
 	placeholderName
 }
 
-func (t placeholderEnum) Options() pref.OptionsMessage      { return optionTypes.Enum }
+func (t placeholderEnum) Options() pref.ProtoMessage        { return descopts.Enum }
 func (t placeholderEnum) Values() pref.EnumValueDescriptors { return &emptyEnumValues }
 func (t placeholderEnum) ReservedNames() pref.Names         { return &emptyNames }
 func (t placeholderEnum) ReservedRanges() pref.EnumRanges   { return &emptyEnumRanges }
diff --git a/internal/prototype/protofile.go b/internal/prototype/protofile.go
index 6a9ad19..601d474 100644
--- a/internal/prototype/protofile.go
+++ b/internal/prototype/protofile.go
@@ -47,7 +47,7 @@
 	Path    string
 	Package protoreflect.FullName
 	Imports []protoreflect.FileImport
-	Options protoreflect.OptionsMessage
+	Options protoreflect.ProtoMessage
 
 	Enums      []Enum
 	Messages   []Message
@@ -111,8 +111,8 @@
 	ReservedNames         []protoreflect.Name
 	ReservedRanges        [][2]protoreflect.FieldNumber
 	ExtensionRanges       [][2]protoreflect.FieldNumber
-	ExtensionRangeOptions []protoreflect.OptionsMessage
-	Options               protoreflect.OptionsMessage
+	ExtensionRangeOptions []protoreflect.ProtoMessage
+	Options               protoreflect.ProtoMessage
 	IsMapEntry            bool
 
 	Enums      []Enum
@@ -141,7 +141,7 @@
 	OneofName   protoreflect.Name
 	MessageType protoreflect.MessageDescriptor
 	EnumType    protoreflect.EnumDescriptor
-	Options     protoreflect.OptionsMessage
+	Options     protoreflect.ProtoMessage
 	IsPacked    OptionalBool
 	IsWeak      bool
 
@@ -151,7 +151,7 @@
 // Oneof is a constructor for protoreflect.OneofDescriptor.
 type Oneof struct {
 	Name    protoreflect.Name
-	Options protoreflect.OptionsMessage
+	Options protoreflect.ProtoMessage
 
 	*oneofMeta
 }
@@ -166,7 +166,7 @@
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
-	Options      protoreflect.OptionsMessage
+	Options      protoreflect.ProtoMessage
 	IsPacked     OptionalBool
 
 	*extensionMeta
@@ -178,7 +178,7 @@
 	Values         []EnumValue
 	ReservedNames  []protoreflect.Name
 	ReservedRanges [][2]protoreflect.EnumNumber
-	Options        protoreflect.OptionsMessage
+	Options        protoreflect.ProtoMessage
 
 	*enumMeta
 }
@@ -195,7 +195,7 @@
 type EnumValue struct {
 	Name    protoreflect.Name
 	Number  protoreflect.EnumNumber
-	Options protoreflect.OptionsMessage
+	Options protoreflect.ProtoMessage
 
 	*enumValueMeta
 }
@@ -204,7 +204,7 @@
 type Service struct {
 	Name    protoreflect.Name
 	Methods []Method
-	Options protoreflect.OptionsMessage
+	Options protoreflect.ProtoMessage
 
 	*serviceMeta
 }
@@ -216,7 +216,7 @@
 	OutputType        protoreflect.MessageDescriptor
 	IsStreamingClient bool
 	IsStreamingServer bool
-	Options           protoreflect.OptionsMessage
+	Options           protoreflect.ProtoMessage
 
 	*methodMeta
 }
diff --git a/internal/prototype/protofile_type.go b/internal/prototype/protofile_type.go
index 421fe9e..1db0b9a 100644
--- a/internal/prototype/protofile_type.go
+++ b/internal/prototype/protofile_type.go
@@ -10,6 +10,7 @@
 	"strings"
 	"sync"
 
+	descopts "github.com/golang/protobuf/v2/internal/descopts"
 	pragma "github.com/golang/protobuf/v2/internal/pragma"
 	pfmt "github.com/golang/protobuf/v2/internal/typefmt"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
@@ -48,7 +49,7 @@
 type fileDesc struct{ f *File }
 
 // altOptions returns m as is if it is non-nil. Otherwise, it returns alt.
-func altOptions(m, alt pref.OptionsMessage) pref.OptionsMessage {
+func altOptions(m, alt pref.ProtoMessage) pref.ProtoMessage {
 	if m != nil {
 		return m
 	}
@@ -68,7 +69,7 @@
 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) Options() pref.OptionsMessage                     { return altOptions(t.f.Options, optionTypes.File) }
+func (t fileDesc) Options() pref.ProtoMessage                       { return altOptions(t.f.Options, descopts.File) }
 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) }
@@ -174,8 +175,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.m.Options, optionTypes.Message)
+func (t messageDesc) Options() pref.ProtoMessage {
+	return altOptions(t.m.Options, descopts.Message)
 }
 func (t messageDesc) IsMapEntry() bool                   { return t.m.IsMapEntry }
 func (t messageDesc) Fields() pref.FieldDescriptors      { return t.m.fs.lazyInit(t, t.m.Fields) }
@@ -184,7 +185,7 @@
 func (t messageDesc) ReservedRanges() pref.FieldRanges   { return (*fieldRanges)(&t.m.ReservedRanges) }
 func (t messageDesc) RequiredNumbers() pref.FieldNumbers { return t.m.ns.lazyInit(t.m.Fields) }
 func (t messageDesc) ExtensionRanges() pref.FieldRanges  { return (*fieldRanges)(&t.m.ExtensionRanges) }
-func (t messageDesc) ExtensionRangeOptions(i int) pref.OptionsMessage {
+func (t messageDesc) ExtensionRangeOptions(i int) pref.ProtoMessage {
 	return extensionRangeOptions(i, len(t.m.ExtensionRanges), t.m.ExtensionRangeOptions)
 }
 func (t messageDesc) Enums() pref.EnumDescriptors           { return t.m.es.lazyInit(t, t.m.Enums) }
@@ -194,16 +195,16 @@
 func (t messageDesc) ProtoType(pref.MessageDescriptor)      {}
 func (t messageDesc) ProtoInternal(pragma.DoNotImplement)   {}
 
-func extensionRangeOptions(i, n int, ms []pref.OptionsMessage) pref.OptionsMessage {
+func extensionRangeOptions(i, n int, ms []pref.ProtoMessage) pref.ProtoMessage {
 	if i < 0 || i >= n {
 		panic("out of bounds")
 	}
-	var m pref.OptionsMessage
+	var m pref.ProtoMessage
 	if i < len(ms) {
 		m = ms[i]
 	}
 	if m == nil {
-		m = optionTypes.ExtensionRange
+		m = descopts.ExtensionRange
 	}
 	return m
 }
@@ -225,7 +226,7 @@
 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) Options() pref.OptionsMessage    { return altOptions(t.f.Options, optionTypes.Field) }
+func (t fieldDesc) Options() pref.ProtoMessage      { return altOptions(t.f.Options, descopts.Field) }
 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 }
@@ -326,7 +327,7 @@
 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) Options() pref.OptionsMessage        { return altOptions(t.o.Options, optionTypes.Oneof) }
+func (t oneofDesc) Options() pref.ProtoMessage          { return altOptions(t.o.Options, descopts.Oneof) }
 func (t oneofDesc) Fields() pref.FieldDescriptors       { return t.o.fs.lazyInit(t) }
 func (t oneofDesc) Format(s fmt.State, r rune)          { pfmt.FormatDesc(s, r, t) }
 func (t oneofDesc) ProtoType(pref.OneofDescriptor)      {}
@@ -348,8 +349,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.x.Options, optionTypes.Field)
+func (t extensionDesc) Options() pref.ProtoMessage {
+	return altOptions(t.x.Options, descopts.Field)
 }
 func (t extensionDesc) Number() pref.FieldNumber      { return t.x.Number }
 func (t extensionDesc) Cardinality() pref.Cardinality { return t.x.Cardinality }
@@ -390,7 +391,7 @@
 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) Options() pref.OptionsMessage        { return altOptions(t.e.Options, optionTypes.Enum) }
+func (t enumDesc) Options() pref.ProtoMessage          { return altOptions(t.e.Options, descopts.Enum) }
 func (t enumDesc) Values() pref.EnumValueDescriptors   { return t.e.vs.lazyInit(t, t.e.Values) }
 func (t enumDesc) ReservedNames() pref.Names           { return (*names)(&t.e.ReservedNames) }
 func (t enumDesc) ReservedRanges() pref.EnumRanges     { return (*enumRanges)(&t.e.ReservedRanges) }
@@ -409,8 +410,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.v.Options, optionTypes.EnumValue)
+func (t enumValueDesc) Options() pref.ProtoMessage {
+	return altOptions(t.v.Options, descopts.EnumValue)
 }
 func (t enumValueDesc) Number() pref.EnumNumber             { return t.v.Number }
 func (t enumValueDesc) Format(s fmt.State, r rune)          { pfmt.FormatDesc(s, r, t) }
@@ -430,8 +431,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.s.Options, optionTypes.Service)
+func (t serviceDesc) Options() pref.ProtoMessage {
+	return altOptions(t.s.Options, descopts.Service)
 }
 func (t serviceDesc) Methods() pref.MethodDescriptors     { return t.s.ms.lazyInit(t, t.s.Methods) }
 func (t serviceDesc) Format(s fmt.State, r rune)          { pfmt.FormatDesc(s, r, t) }
@@ -452,7 +453,7 @@
 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) Options() pref.OptionsMessage        { return altOptions(t.m.Options, optionTypes.Method) }
+func (t methodDesc) Options() pref.ProtoMessage          { return altOptions(t.m.Options, descopts.Method) }
 func (t methodDesc) InputType() pref.MessageDescriptor   { return t.m.mit.lazyInit(t, &t.m.InputType) }
 func (t methodDesc) OutputType() pref.MessageDescriptor  { return t.m.mot.lazyInit(t, &t.m.OutputType) }
 func (t methodDesc) IsStreamingClient() bool             { return t.m.IsStreamingClient }
diff --git a/internal/prototype/standalone.go b/internal/prototype/standalone.go
index 5dcf3a9..4184e82 100644
--- a/internal/prototype/standalone.go
+++ b/internal/prototype/standalone.go
@@ -22,8 +22,8 @@
 	ReservedNames         []protoreflect.Name
 	ReservedRanges        [][2]protoreflect.FieldNumber
 	ExtensionRanges       [][2]protoreflect.FieldNumber
-	ExtensionRangeOptions []protoreflect.OptionsMessage
-	Options               protoreflect.OptionsMessage
+	ExtensionRangeOptions []protoreflect.ProtoMessage
+	Options               protoreflect.ProtoMessage
 	IsMapEntry            bool
 
 	fields fieldsMeta
@@ -90,7 +90,7 @@
 	Values         []EnumValue
 	ReservedNames  []protoreflect.Name
 	ReservedRanges [][2]protoreflect.EnumNumber
-	Options        protoreflect.OptionsMessage
+	Options        protoreflect.ProtoMessage
 
 	vals enumValuesMeta
 }
@@ -117,7 +117,7 @@
 	MessageType  protoreflect.MessageDescriptor
 	EnumType     protoreflect.EnumDescriptor
 	ExtendedType protoreflect.MessageDescriptor
-	Options      protoreflect.OptionsMessage
+	Options      protoreflect.ProtoMessage
 	IsPacked     OptionalBool
 
 	dv defaultValue
diff --git a/internal/prototype/standalone_type.go b/internal/prototype/standalone_type.go
index 1eeb6f6..4dd681b 100644
--- a/internal/prototype/standalone_type.go
+++ b/internal/prototype/standalone_type.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 
+	descopts "github.com/golang/protobuf/v2/internal/descopts"
 	pragma "github.com/golang/protobuf/v2/internal/pragma"
 	pfmt "github.com/golang/protobuf/v2/internal/typefmt"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
@@ -20,8 +21,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.m.Options, optionTypes.Message)
+func (t standaloneMessage) Options() pref.ProtoMessage {
+	return altOptions(t.m.Options, descopts.Message)
 }
 func (t standaloneMessage) IsMapEntry() bool              { return t.m.IsMapEntry }
 func (t standaloneMessage) Fields() pref.FieldDescriptors { return t.m.fields.lazyInit(t, t.m.Fields) }
@@ -34,7 +35,7 @@
 func (t standaloneMessage) ExtensionRanges() pref.FieldRanges {
 	return (*fieldRanges)(&t.m.ExtensionRanges)
 }
-func (t standaloneMessage) ExtensionRangeOptions(i int) pref.OptionsMessage {
+func (t standaloneMessage) ExtensionRangeOptions(i int) pref.ProtoMessage {
 	return extensionRangeOptions(i, len(t.m.ExtensionRanges), t.m.ExtensionRangeOptions)
 }
 func (t standaloneMessage) Enums() pref.EnumDescriptors           { return &emptyEnums }
@@ -52,8 +53,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.e.Options, optionTypes.Enum)
+func (t standaloneEnum) Options() pref.ProtoMessage {
+	return altOptions(t.e.Options, descopts.Enum)
 }
 func (t standaloneEnum) Values() pref.EnumValueDescriptors   { return t.e.vals.lazyInit(t, t.e.Values) }
 func (t standaloneEnum) ReservedNames() pref.Names           { return (*names)(&t.e.ReservedNames) }
@@ -70,8 +71,8 @@
 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) Options() pref.OptionsMessage {
-	return altOptions(t.x.Options, optionTypes.Field)
+func (t standaloneExtension) Options() pref.ProtoMessage {
+	return altOptions(t.x.Options, descopts.Field)
 }
 func (t standaloneExtension) Number() pref.FieldNumber      { return t.x.Number }
 func (t standaloneExtension) Cardinality() pref.Cardinality { return t.x.Cardinality }
diff --git a/internal/prototype/type_test.go b/internal/prototype/type_test.go
index 07c0aab..f2ce64b 100644
--- a/internal/prototype/type_test.go
+++ b/internal/prototype/type_test.go
@@ -114,7 +114,7 @@
 			ReservedNames:   []pref.Name{"fizz", "buzz"},
 			ReservedRanges:  [][2]pref.FieldNumber{{100, 200}, {300, 301}},
 			ExtensionRanges: [][2]pref.FieldNumber{{1000, 2000}, {3000, 3001}},
-			ExtensionRangeOptions: []pref.OptionsMessage{
+			ExtensionRangeOptions: []pref.ProtoMessage{
 				0: (*descriptorpb.ExtensionRangeOptions)(nil),
 				1: new(descriptorpb.ExtensionRangeOptions),
 			},
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index a3538d1..66a3e5b 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -100,15 +100,11 @@
 	//
 	// This method returns a typed nil-pointer if no options are present.
 	// The caller must import the descriptor package to use this.
-	Options() OptionsMessage
+	Options() ProtoMessage
 
 	doNotImplement
 }
 
-// An OptionsMessage is a google.protobuf.XXXOptions message, defined here
-// as an interface value to avoid a dependency cycle.
-type OptionsMessage interface{}
-
 // FileDescriptor describes the types in a complete proto file and
 // corresponds with the google.protobuf.FileDescriptorProto message.
 //
@@ -215,7 +211,7 @@
 	// The caller must not modify the returned message.
 	//
 	// This method may return a nil interface value if no options are present.
-	ExtensionRangeOptions(i int) OptionsMessage
+	ExtensionRangeOptions(i int) ProtoMessage
 
 	// Messages is a list of nested message declarations.
 	Messages() MessageDescriptors
diff --git a/types/descriptor/descriptor.pb.go b/types/descriptor/descriptor.pb.go
index 3c38f61..ee31611 100644
--- a/types/descriptor/descriptor.pb.go
+++ b/types/descriptor/descriptor.pb.go
@@ -4,7 +4,6 @@
 package descriptor_proto
 
 import (
-	prototype "github.com/golang/protobuf/v2/internal/prototype"
 	protoreflect "github.com/golang/protobuf/v2/reflect/protoreflect"
 	protoregistry "github.com/golang/protobuf/v2/reflect/protoregistry"
 	protoiface "github.com/golang/protobuf/v2/runtime/protoiface"
@@ -3291,15 +3290,6 @@
 		FilesRegistry:      protoregistry.GlobalFiles,
 		TypesRegistry:      protoregistry.GlobalTypes,
 	}.Init()
-	prototype.X.RegisterExtensionRangeOptions((*ExtensionRangeOptions)(nil))
-	prototype.X.RegisterFileOptions((*FileOptions)(nil))
-	prototype.X.RegisterMessageOptions((*MessageOptions)(nil))
-	prototype.X.RegisterFieldOptions((*FieldOptions)(nil))
-	prototype.X.RegisterOneofOptions((*OneofOptions)(nil))
-	prototype.X.RegisterEnumOptions((*EnumOptions)(nil))
-	prototype.X.RegisterEnumValueOptions((*EnumValueOptions)(nil))
-	prototype.X.RegisterServiceOptions((*ServiceOptions)(nil))
-	prototype.X.RegisterMethodOptions((*MethodOptions)(nil))
 	xxx_File_google_protobuf_descriptor_proto_rawDesc = nil
 	xxx_File_google_protobuf_descriptor_proto_goTypes = nil
 	xxx_File_google_protobuf_descriptor_proto_depIdxs = nil