internal/filedesc: don't close over descopts values

It is possible for filedesc to construct a lazy options decoder before
the descriptor package has been imported. For example, top-level enum
values are eagerly decoded, so a generated proto package can construct a
lazy options decoder for an enum value at init time.

Don't close over the variables in descopts. Instead, close over a pointer
to the variable.

Panic with an informative message in the case where options are decoded
before the descriptor package has been initialized.

Change-Id: I277a57602b083cb7bbf92c8114c50b467e59521f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/188820
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
index 9e2b21c..ae9358a 100644
--- a/internal/filedesc/desc_lazy.go
+++ b/internal/filedesc/desc_lazy.go
@@ -183,7 +183,7 @@
 			b = b[m:]
 		}
 	}
-	fd.L2.Options = fd.builder.optionsUnmarshaler(descopts.File, rawOptions)
+	fd.L2.Options = fd.builder.optionsUnmarshaler(&descopts.File, rawOptions)
 }
 
 func (ed *Enum) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -220,7 +220,7 @@
 			ed.L2.Values.List[i].unmarshalFull(b, sb, ed.L0.ParentFile, ed, i)
 		}
 	}
-	ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(descopts.Enum, rawOptions)
+	ed.L2.Options = ed.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Enum, rawOptions)
 }
 
 func unmarshalEnumReservedRange(b []byte) (r [2]pref.EnumNumber) {
@@ -277,7 +277,7 @@
 			b = b[m:]
 		}
 	}
-	vd.L1.Options = pf.builder.optionsUnmarshaler(descopts.EnumValue, rawOptions)
+	vd.L1.Options = pf.builder.optionsUnmarshaler(&descopts.EnumValue, rawOptions)
 }
 
 func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -303,7 +303,7 @@
 				md.L2.ReservedRanges.List = append(md.L2.ReservedRanges.List, unmarshalMessageReservedRange(v))
 			case fieldnum.DescriptorProto_ExtensionRange:
 				r, rawOptions := unmarshalMessageExtensionRange(v)
-				opts := md.L0.ParentFile.builder.optionsUnmarshaler(descopts.ExtensionRange, rawOptions)
+				opts := md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.ExtensionRange, rawOptions)
 				md.L2.ExtensionRanges.List = append(md.L2.ExtensionRanges.List, r)
 				md.L2.ExtensionRangeOptions = append(md.L2.ExtensionRangeOptions, opts)
 			case fieldnum.DescriptorProto_EnumType:
@@ -339,7 +339,7 @@
 			od.unmarshalFull(b, sb, md.L0.ParentFile, md, i)
 		}
 	}
-	md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(descopts.Message, rawOptions)
+	md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions)
 }
 
 func (md *Message) unmarshalOptions(b []byte) {
@@ -476,7 +476,7 @@
 			fd.L1.Message = PlaceholderMessage(name)
 		}
 	}
-	fd.L1.Options = pf.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+	fd.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Field, rawOptions)
 }
 
 func (fd *Field) unmarshalOptions(b []byte) {
@@ -530,7 +530,7 @@
 			b = b[m:]
 		}
 	}
-	od.L1.Options = pf.builder.optionsUnmarshaler(descopts.Oneof, rawOptions)
+	od.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Oneof, rawOptions)
 }
 
 func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) {
@@ -576,7 +576,7 @@
 			xd.L2.Message = PlaceholderMessage(name)
 		}
 	}
-	xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Field, rawOptions)
+	xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Field, rawOptions)
 }
 
 func (xd *Extension) unmarshalOptions(b []byte) {
@@ -626,7 +626,7 @@
 			sd.L2.Methods.List[i].unmarshalFull(b, sb, sd.L0.ParentFile, sd, i)
 		}
 	}
-	sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(descopts.Service, rawOptions)
+	sd.L2.Options = sd.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Service, rawOptions)
 }
 
 func (md *Method) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd pref.Descriptor, i int) {
@@ -666,7 +666,7 @@
 			b = b[m:]
 		}
 	}
-	md.L1.Options = pf.builder.optionsUnmarshaler(descopts.Method, rawOptions)
+	md.L1.Options = pf.builder.optionsUnmarshaler(&descopts.Method, rawOptions)
 }
 
 // appendOptions appends src to dst, where the returned slice is never nil.
@@ -678,7 +678,11 @@
 	return append(dst, src...)
 }
 
-func (db *DescBuilder) optionsUnmarshaler(p pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
+// optionsUnmarshaler constructs a lazy unmarshal function for an options message.
+//
+// The type of message to unmarshal to is passed as a pointer since the
+// vars in descopts may not yet be populated at the time this function is called.
+func (db *DescBuilder) optionsUnmarshaler(p *pref.ProtoMessage, b []byte) func() pref.ProtoMessage {
 	if b == nil {
 		return nil
 	}
@@ -686,7 +690,10 @@
 	var once sync.Once
 	return func() pref.ProtoMessage {
 		once.Do(func() {
-			opts = reflect.New(reflect.TypeOf(p).Elem()).Interface().(pref.ProtoMessage)
+			if *p == nil {
+				panic("Descriptor.Options called without importing the descriptor package")
+			}
+			opts = reflect.New(reflect.TypeOf(*p).Elem()).Interface().(pref.ProtoMessage)
 			if err := (proto.UnmarshalOptions{
 				AllowPartial: true,
 				Resolver:     db.TypeResolver,