all: unify protoV1.ExtensionDesc and proto.ExtensionType

Change protoV1.ExtensionDesc to directly implement ExtensionType
rather than delegating to one.

Unify the previous types protoiface.ExtensionDescV1 and
filetype.Extension in impl.ExtensionInfo. The protoV1.ExtensionDesc
type becomes an alias to ExtensionInfo.

This gives us:

  - Just one implementation of ExtensionType.
  - Generated foopb.E_Ext vars are canonical ExtensionTypes.
  - Generated foopb.E_Ext vars are also v1.ExtensionDescs for backwards
    compatibility.
  - Conversion between legacy and modern representations happens
    transparently when lazily initializing an ExtensionInfo.

Overall, a simplification for users of generated code, since they can
mostly ignore the ExtensionDesc/ExtentionType distinction and use the
same value in either the old or new API.

This is change 3/5 in a series of commits changing protoV1.ExtensionDesc
to directly implement protoreflect.ExtensionType.

1. [v2] Add protoimpl.ExtensionInfo as an alias for
   protoiface.ExtensionDescV1.

2. [v1] Update references to protoimpl.ExtensionInfo to use
   protoiface.ExtensionInfo.

3. [v2] Create protoimpl.ExtensionInfo (an alias to a new type in
   the impl package) and remove protoiface.ExtensionDescV1.

4. [v1] Remove unneeded explicit conversions between ExtensionDesc and
   ExtensionType (since the former now directly implements the latter).

5. [v2] Remove stub conversion functions.

Change-Id: I96ee890541ec11b2412e1a72c9d7b96e4d7f66b4
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/189563
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/api_export.go b/internal/impl/api_export.go
index a149612..41f7b7e 100644
--- a/internal/impl/api_export.go
+++ b/internal/impl/api_export.go
@@ -10,7 +10,6 @@
 
 	"google.golang.org/protobuf/encoding/prototext"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
-	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
 // Export is a zero-length named type that exists only to export a set of
@@ -90,12 +89,14 @@
 	return string(b)
 }
 
-// ExtensionDescFromType returns the legacy protoiface.ExtensionDescV1 for t.
-func (Export) ExtensionDescFromType(t pref.ExtensionType) *piface.ExtensionDescV1 {
+// ExtensionDescFromType returns the legacy protoV1.ExtensionDesc for t.
+func (Export) ExtensionDescFromType(t pref.ExtensionType) *ExtensionInfo {
 	return legacyExtensionDescFromType(t)
 }
 
 // ExtensionTypeFromDesc returns the v2 protoreflect.ExtensionType for d.
-func (Export) ExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
-	return legacyExtensionTypeFromDesc(d)
+//
+// TODO: Remove after updating v1 to no longer call this.
+func (Export) ExtensionTypeFromDesc(d *ExtensionInfo) pref.ExtensionType {
+	return d
 }
diff --git a/internal/impl/extension.go b/internal/impl/extension.go
new file mode 100644
index 0000000..e7083e5
--- /dev/null
+++ b/internal/impl/extension.go
@@ -0,0 +1,172 @@
+// 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 impl
+
+import (
+	"reflect"
+	"sync"
+	"sync/atomic"
+
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	piface "google.golang.org/protobuf/runtime/protoiface"
+)
+
+// ExtensionInfo implements ExtensionType.
+//
+// This type contains a number of exported fields for legacy compatibility.
+// The only non-deprecated use of this type is through the methods of the
+// ExtensionType interface.
+type ExtensionInfo struct {
+	// An ExtensionInfo may exist in several stages of initialization.
+	//
+	// extensionInfoUninitialized: Some or all of the legacy exported
+	// fields may be set, but none of the unexported fields have been
+	// initialized. This is the starting state for an ExtensionInfo
+	// in legacy generated code.
+	//
+	// extensionInfoDescInit: The desc and tdesc fields have been
+	// set, but the descriptor is not otherwise initialized. Legacy
+	// exported fields may or may not be set. This is the starting state
+	// for an ExtensionInfo in new generated code. Calling the Descriptor
+	// method will not trigger lazy initialization, although any other
+	// method will.
+	//
+	// extensionInfoFullInit: The ExtensionInfo is fully initialized.
+	// This state is only entered after lazy initialization is complete.
+	init uint32
+	mu   sync.Mutex
+
+	desc   pref.ExtensionDescriptor
+	tdesc  extensionTypeDescriptor
+	goType reflect.Type
+	conv   Converter
+
+	// TODO: Remove after updating v2 to not set this.
+	Type interface{}
+
+	// ExtendedType is a typed nil-pointer to the parent message type that
+	// is being extended. It is possible for this to be unpopulated in v2
+	// since the message may no longer implement the MessageV1 interface.
+	//
+	// Deprecated: Use the ExtendedType method instead.
+	ExtendedType piface.MessageV1
+
+	// ExtensionType is zero value of the extension type.
+	//
+	// For historical reasons, reflect.TypeOf(ExtensionType) and Type.GoType
+	// may not be identical:
+	//	* for scalars (except []byte), where ExtensionType uses *T,
+	//	while Type.GoType uses T.
+	//	* for repeated fields, where ExtensionType uses []T,
+	//	while Type.GoType uses *[]T.
+	//
+	// Deprecated: Use the GoType method instead.
+	ExtensionType interface{}
+
+	// Field is the field number of the extension.
+	//
+	// Deprecated: Use the Descriptor().Number method instead.
+	Field int32
+
+	// Name is the fully qualified name of extension.
+	//
+	// Deprecated: Use the Descriptor().FullName method instead.
+	Name string
+
+	// Tag is the protobuf struct tag used in the v1 API.
+	//
+	// Deprecated: Do not use.
+	Tag string
+
+	// Filename is the proto filename in which the extension is defined.
+	//
+	// Deprecated: Use Descriptor().ParentFile().Path() instead.
+	Filename string
+}
+
+// Stages of initialization: See the ExtensionInfo.init field.
+const (
+	extensionInfoUninitialized = 0
+	extensionInfoDescInit      = 1
+	extensionInfoFullInit      = 2
+)
+
+func InitExtensionInfo(xi *ExtensionInfo, xd pref.ExtensionDescriptor, goType reflect.Type) {
+	if xi.desc != nil {
+		return
+	}
+	xi.desc = xd
+	xi.goType = goType
+
+	xi.tdesc.ExtensionDescriptor = xi.desc
+	xi.tdesc.xi = xi
+	xi.init = extensionInfoDescInit
+}
+
+func (xi *ExtensionInfo) New() pref.Value {
+	return xi.lazyInit().New()
+}
+func (xi *ExtensionInfo) Zero() pref.Value {
+	return xi.lazyInit().Zero()
+}
+func (xi *ExtensionInfo) ValueOf(v interface{}) pref.Value {
+	return xi.lazyInit().PBValueOf(reflect.ValueOf(v))
+}
+func (xi *ExtensionInfo) InterfaceOf(v pref.Value) interface{} {
+	return xi.lazyInit().GoValueOf(v).Interface()
+}
+func (xi *ExtensionInfo) GoType() reflect.Type {
+	xi.lazyInit()
+	return xi.goType
+}
+func (xi *ExtensionInfo) Descriptor() pref.ExtensionTypeDescriptor {
+	if atomic.LoadUint32(&xi.init) == extensionInfoUninitialized {
+		xi.lazyInitSlow()
+	}
+	return &xi.tdesc
+}
+
+func (xi *ExtensionInfo) lazyInit() Converter {
+	if atomic.LoadUint32(&xi.init) != extensionInfoFullInit {
+		xi.lazyInitSlow()
+	}
+	return xi.conv
+}
+
+func (xi *ExtensionInfo) lazyInitSlow() {
+	xi.mu.Lock()
+	defer xi.mu.Unlock()
+
+	if xi.init == extensionInfoFullInit {
+		return
+	}
+	atomic.StoreUint32(&xi.init, extensionInfoFullInit)
+
+	if xi.desc == nil {
+		xi.initFromLegacy()
+	} else if xi.desc.Cardinality() == pref.Repeated {
+		// Cardinality is initialized lazily, so we defer consulting it until here.
+		xi.goType = reflect.PtrTo(reflect.SliceOf(xi.goType))
+	}
+	xi.conv = NewConverter(xi.goType, xi.desc)
+	xi.tdesc.ExtensionDescriptor = xi.desc
+	xi.tdesc.xi = xi
+
+	if xi.ExtensionType == nil {
+		xi.initToLegacy()
+	}
+}
+
+type extensionTypeDescriptor struct {
+	pref.ExtensionDescriptor
+	xi *ExtensionInfo
+}
+
+func (xtd *extensionTypeDescriptor) Type() pref.ExtensionType {
+	return xtd.xi
+}
+func (xtd *extensionTypeDescriptor) Descriptor() pref.ExtensionDescriptor {
+	return xtd.ExtensionDescriptor
+}
diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go
index 2da4d71..4622484 100644
--- a/internal/impl/legacy_extension.go
+++ b/internal/impl/legacy_extension.go
@@ -15,52 +15,38 @@
 	piface "google.golang.org/protobuf/runtime/protoiface"
 )
 
-// legacyExtensionDescKey is a comparable version of protoiface.ExtensionDescV1
-// suitable for use as a key in a map.
-type legacyExtensionDescKey struct {
-	typeV2        pref.ExtensionType
-	extendedType  reflect.Type
-	extensionType reflect.Type
-	field         int32
-	name          string
-	tag           string
-	filename      string
-}
+var legacyExtensionInfoCache sync.Map // map[protoreflect.ExtensionType]*ExtensionInfo
 
-func legacyExtensionDescKeyOf(d *piface.ExtensionDescV1) legacyExtensionDescKey {
-	return legacyExtensionDescKey{
-		d.Type,
-		reflect.TypeOf(d.ExtendedType),
-		reflect.TypeOf(d.ExtensionType),
-		d.Field, d.Name, d.Tag, d.Filename,
-	}
-}
-
-var (
-	legacyExtensionTypeCache sync.Map // map[legacyExtensionDescKey]protoreflect.ExtensionType
-	legacyExtensionDescCache sync.Map // map[protoreflect.ExtensionType]*protoiface.ExtensionDescV1
-)
-
-// legacyExtensionDescFromType converts a v2 protoreflect.ExtensionType to a
-// protoiface.ExtensionDescV1. The returned ExtensionDesc must not be mutated.
-func legacyExtensionDescFromType(xt pref.ExtensionType) *piface.ExtensionDescV1 {
-	// Fast-path: check whether an extension desc is already nested within.
-	if xt, ok := xt.(interface {
-		ProtoLegacyExtensionDesc() *piface.ExtensionDescV1
-	}); ok {
-		if d := xt.ProtoLegacyExtensionDesc(); d != nil {
-			return d
-		}
+// legacyExtensionDescFromType converts a protoreflect.ExtensionType to an
+// ExtensionInfo. The returned ExtensionInfo must not be mutated.
+func legacyExtensionDescFromType(xt pref.ExtensionType) *ExtensionInfo {
+	// Fast-path: check whether this is an ExtensionInfo.
+	if xt, ok := xt.(*ExtensionInfo); ok {
+		return xt
 	}
 
 	// Fast-path: check the cache for whether this ExtensionType has already
-	// been converted to a legacy descriptor.
-	if d, ok := legacyExtensionDescCache.Load(xt); ok {
-		return d.(*piface.ExtensionDescV1)
+	// been converted to an ExtensionInfo.
+	if d, ok := legacyExtensionInfoCache.Load(xt); ok {
+		return d.(*ExtensionInfo)
 	}
 
-	// Determine the parent type if possible.
-	xd := xt.Descriptor()
+	tt := xt.GoType()
+	if xt.Descriptor().Cardinality() == pref.Repeated {
+		tt = tt.Elem().Elem()
+	}
+	xi := &ExtensionInfo{}
+	InitExtensionInfo(xi, xt.Descriptor().Descriptor(), tt)
+	xi.lazyInit() // populate legacy fields
+
+	if xi, ok := legacyExtensionInfoCache.LoadOrStore(xt, xi); ok {
+		return xi.(*ExtensionInfo)
+	}
+	return xi
+}
+
+func (xi *ExtensionInfo) initToLegacy() {
+	xd := xi.desc
 	var parent piface.MessageV1
 	messageName := xd.ContainingMessage().FullName()
 	if mt, _ := preg.GlobalTypes.FindMessageByName(messageName); mt != nil {
@@ -80,7 +66,7 @@
 
 	// Determine the v1 extension type, which is unfortunately not the same as
 	// the v2 ExtensionType.GoType.
-	extType := xt.GoType()
+	extType := xi.goType
 	switch extType.Kind() {
 	case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
 		extType = reflect.PtrTo(extType) // T -> *T for singular scalar fields
@@ -123,43 +109,21 @@
 		filename = fd.Path()
 	}
 
-	// Construct and return a ExtensionDescV1.
-	d := &piface.ExtensionDescV1{
-		Type:          xt,
-		ExtendedType:  parent,
-		ExtensionType: reflect.Zero(extType).Interface(),
-		Field:         int32(xd.Number()),
-		Name:          string(xd.FullName()),
-		Tag:           ptag.Marshal(xd, enumName),
-		Filename:      filename,
-	}
-	if d, ok := legacyExtensionDescCache.LoadOrStore(xt, d); ok {
-		return d.(*piface.ExtensionDescV1)
-	}
-	return d
+	xi.ExtendedType = parent
+	xi.ExtensionType = reflect.Zero(extType).Interface()
+	xi.Field = int32(xd.Number())
+	xi.Name = string(xd.FullName())
+	xi.Tag = ptag.Marshal(xd, enumName)
+	xi.Filename = filename
 }
 
-// legacyExtensionTypeFromDesc converts a protoiface.ExtensionDescV1 to a
-// v2 protoreflect.ExtensionType. The returned descriptor type takes ownership
-// of the input extension desc. The input must not be mutated so long as the
-// returned type is still in use.
-func legacyExtensionTypeFromDesc(d *piface.ExtensionDescV1) pref.ExtensionType {
-	// Fast-path: check whether an extension type is already nested within.
-	if d.Type != nil {
-		return d.Type
-	}
-
-	// Fast-path: check the cache for whether this ExtensionType has already
-	// been converted from a legacy descriptor.
-	dk := legacyExtensionDescKeyOf(d)
-	if t, ok := legacyExtensionTypeCache.Load(dk); ok {
-		return t.(pref.ExtensionType)
-	}
-
+// initFromLegacy initializes an ExtensionInfo from
+// the contents of the deprecated exported fields of the type.
+func (xi *ExtensionInfo) initFromLegacy() {
 	// Resolve enum or message dependencies.
 	var ed pref.EnumDescriptor
 	var md pref.MessageDescriptor
-	t := reflect.TypeOf(d.ExtensionType)
+	t := reflect.TypeOf(xi.ExtensionType)
 	isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
 	isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
 	if isOptional || isRepeated {
@@ -181,70 +145,27 @@
 	if ed != nil {
 		evs = ed.Values()
 	}
-	fd := ptag.Unmarshal(d.Tag, t, evs).(*filedesc.Field)
+	fd := ptag.Unmarshal(xi.Tag, t, evs).(*filedesc.Field)
 
 	// Construct a v2 ExtensionType.
 	xd := &filedesc.Extension{L2: new(filedesc.ExtensionL2)}
 	xd.L0.ParentFile = filedesc.SurrogateProto2
-	xd.L0.FullName = pref.FullName(d.Name)
-	xd.L1.Number = pref.FieldNumber(d.Field)
+	xd.L0.FullName = pref.FullName(xi.Name)
+	xd.L1.Number = pref.FieldNumber(xi.Field)
 	xd.L2.Cardinality = fd.L1.Cardinality
 	xd.L1.Kind = fd.L1.Kind
 	xd.L2.IsPacked = fd.L1.IsPacked
 	xd.L2.Default = fd.L1.Default
-	xd.L1.Extendee = Export{}.MessageDescriptorOf(d.ExtendedType)
+	xd.L1.Extendee = Export{}.MessageDescriptorOf(xi.ExtendedType)
 	xd.L2.Enum = ed
 	xd.L2.Message = md
-	tt := reflect.TypeOf(d.ExtensionType)
+	tt := reflect.TypeOf(xi.ExtensionType)
 	if isOptional {
 		tt = tt.Elem()
 	} else if isRepeated {
 		tt = reflect.PtrTo(tt)
 	}
-	xt := LegacyExtensionTypeOf(xd, tt)
 
-	// Cache the conversion for both directions.
-	legacyExtensionDescCache.LoadOrStore(xt, d)
-	if xt, ok := legacyExtensionTypeCache.LoadOrStore(dk, xt); ok {
-		return xt.(pref.ExtensionType)
-	}
-	return xt
+	xi.desc = xd
+	xi.goType = tt
 }
-
-// LegacyExtensionTypeOf returns a protoreflect.ExtensionType where the
-// element type of the field is t.
-//
-// This is exported for testing purposes.
-func LegacyExtensionTypeOf(xd pref.ExtensionDescriptor, t reflect.Type) pref.ExtensionType {
-	xt := &legacyExtensionType{
-		typ:  t,
-		conv: NewConverter(t, xd),
-	}
-	xt.desc = &extDesc{xd, xt}
-	return xt
-}
-
-type legacyExtensionType struct {
-	desc pref.ExtensionTypeDescriptor
-	typ  reflect.Type
-	conv Converter
-}
-
-func (x *legacyExtensionType) GoType() reflect.Type { return x.typ }
-func (x *legacyExtensionType) New() pref.Value      { return x.conv.New() }
-func (x *legacyExtensionType) Zero() pref.Value     { return x.conv.Zero() }
-func (x *legacyExtensionType) ValueOf(v interface{}) pref.Value {
-	return x.conv.PBValueOf(reflect.ValueOf(v))
-}
-func (x *legacyExtensionType) InterfaceOf(v pref.Value) interface{} {
-	return x.conv.GoValueOf(v).Interface()
-}
-func (x *legacyExtensionType) Descriptor() pref.ExtensionTypeDescriptor { return x.desc }
-
-type extDesc struct {
-	pref.ExtensionDescriptor
-	xt *legacyExtensionType
-}
-
-func (t *extDesc) Type() pref.ExtensionType             { return t.xt }
-func (t *extDesc) Descriptor() pref.ExtensionDescriptor { return t.ExtensionDescriptor }
diff --git a/internal/impl/legacy_test.go b/internal/impl/legacy_test.go
index 63b084d..e306e6b 100644
--- a/internal/impl/legacy_test.go
+++ b/internal/impl/legacy_test.go
@@ -59,7 +59,9 @@
 func mustMakeExtensionType(fileDesc, extDesc string, t reflect.Type, r pdesc.Resolver) pref.ExtensionType {
 	s := fmt.Sprintf(`name:"test.proto" syntax:"proto2" %s extension:[{%s}]`, fileDesc, extDesc)
 	xd := mustMakeFileDesc(s, r).Extensions().Get(0)
-	return pimpl.LegacyExtensionTypeOf(xd, t)
+	xi := &pimpl.ExtensionInfo{}
+	pimpl.InitExtensionInfo(xi, xd, t)
+	return xi
 }
 
 func mustMakeFileDesc(s string, r pdesc.Resolver) pref.FileDescriptor {
@@ -141,56 +143,56 @@
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_bool" number:10010 label:LABEL_REPEATED type:TYPE_BOOL extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]bool)(nil)), depReg,
+			reflect.TypeOf(false), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_int32" number:10011 label:LABEL_REPEATED type:TYPE_INT32 extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]int32)(nil)), depReg,
+			reflect.TypeOf(int32(0)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_uint32" number:10012 label:LABEL_REPEATED type:TYPE_UINT32 extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]uint32)(nil)), depReg,
+			reflect.TypeOf(uint32(0)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_float" number:10013 label:LABEL_REPEATED type:TYPE_FLOAT extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]float32)(nil)), depReg,
+			reflect.TypeOf(float32(0)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_string" number:10014 label:LABEL_REPEATED type:TYPE_STRING extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]string)(nil)), depReg,
+			reflect.TypeOf(""), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:"legacy.proto"`,
 			`name:"repeated_bytes" number:10015 label:LABEL_REPEATED type:TYPE_BYTES extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[][]byte)(nil)), depReg,
+			reflect.TypeOf(([]byte)(nil)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
 			`name:"repeated_enum_v1" number:10016 label:LABEL_REPEATED type:TYPE_ENUM type_name:".google.golang.org.proto2_20180125.Message.ChildEnum" extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]proto2_20180125.Message_ChildEnum)(nil)), depReg,
+			reflect.TypeOf(proto2_20180125.Message_ChildEnum(0)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:["legacy.proto", "proto2.v1.0.0-20180125-92554152/test.proto"]`,
 			`name:"repeated_message_v1" number:10017 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".google.golang.org.proto2_20180125.Message.ChildMessage" extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]*proto2_20180125.Message_ChildMessage)(nil)), depReg,
+			reflect.TypeOf((*proto2_20180125.Message_ChildMessage)(nil)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:["legacy.proto", "enum2.proto"]`,
 			`name:"repeated_enum_v2" number:10018 label:LABEL_REPEATED type:TYPE_ENUM type_name:".EnumProto2" extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[]EnumProto2)(nil)), depReg,
+			reflect.TypeOf(EnumProto2(0)), depReg,
 		),
 		mustMakeExtensionType(
 			`package:"fizz.buzz" dependency:["legacy.proto", "enum-messages.proto"]`,
 			`name:"repeated_message_v2" number:10019 label:LABEL_REPEATED type:TYPE_MESSAGE type_name:".EnumMessages" extendee:".LegacyTestMessage"`,
-			reflect.TypeOf((*[](*EnumMessages))(nil)), depReg,
+			reflect.TypeOf((*EnumMessages)(nil)), depReg,
 		),
 	}
 
-	extensionDescs = []*piface.ExtensionDescV1{{
+	extensionDescs = []*pimpl.ExtensionInfo{{
 		ExtendedType:  (*LegacyTestMessage)(nil),
 		ExtensionType: (*bool)(nil),
 		Field:         10000,
@@ -463,7 +465,7 @@
 
 			wantType := extensionTypes[i]
 			wantDesc := extensionDescs[i]
-			gotType := pimpl.Export{}.ExtensionTypeFromDesc(wantDesc)
+			gotType := (pref.ExtensionType)(wantDesc)
 			gotDesc := pimpl.Export{}.ExtensionDescFromType(wantType)
 
 			// TODO: We need a test package to compare descriptors.
@@ -528,7 +530,8 @@
 			}
 
 			opts = cmp.Options{
-				cmpopts.IgnoreFields(piface.ExtensionDescV1{}, "Type"),
+				cmpopts.IgnoreFields(pimpl.ExtensionInfo{}, "ExtensionType"),
+				cmpopts.IgnoreUnexported(pimpl.ExtensionInfo{}),
 			}
 			if diff := cmp.Diff(wantDesc, gotDesc, opts); diff != "" {
 				t.Errorf("ExtensionDesc mismatch (-want, +got):\n%v", diff)