internal/impl: handle irregular messages implementing proto.Message

When encountering a type that does not have a MessageInfo, don't assume
that it's a legacy message that doesn't implement proto.Message. Add a
set of test messages exercising this case (panics prior to the
internal/impl change).

Change-Id: Ic1ec5ecfbe92278fbef44284ff52a0e0622a158c
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/182477
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/testprotos/irregular/irregular.go b/internal/testprotos/irregular/irregular.go
new file mode 100644
index 0000000..cc9c549
--- /dev/null
+++ b/internal/testprotos/irregular/irregular.go
@@ -0,0 +1,141 @@
+// 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 irregular
+
+import (
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/reflect/protodesc"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/types/descriptorpb"
+)
+
+type IrregularMessage struct {
+	set   bool
+	value string
+}
+
+func (m *IrregularMessage) ProtoReflect() pref.Message { return (*message)(m) }
+
+type message IrregularMessage
+
+func (m *message) Descriptor() pref.MessageDescriptor { return descriptor.Messages().Get(0) }
+func (m *message) Type() pref.MessageType             { return nil }
+func (m *message) KnownFields() pref.KnownFields      { return (*known)(m) }
+func (m *message) UnknownFields() pref.UnknownFields  { return (*unknown)(m) }
+func (m *message) New() pref.Message                  { return &message{} }
+func (m *message) Interface() pref.ProtoMessage       { return (*IrregularMessage)(m) }
+
+type known IrregularMessage
+
+func (m *known) Len() int {
+	if m.set {
+		return 1
+	}
+	return 0
+}
+
+func (m *known) Has(num pref.FieldNumber) bool {
+	switch num {
+	case fieldS:
+		return m.set
+	}
+	return false
+}
+
+func (m *known) Get(num pref.FieldNumber) pref.Value {
+	switch num {
+	case fieldS:
+		return pref.ValueOf(m.value)
+	}
+	return pref.Value{}
+}
+
+func (m *known) Set(num pref.FieldNumber, v pref.Value) {
+	switch num {
+	case fieldS:
+		m.value = v.String()
+	default:
+		panic("unknown field")
+	}
+}
+
+func (m *known) Clear(num pref.FieldNumber) {
+	switch num {
+	case fieldS:
+		m.value = ""
+		m.set = false
+	default:
+		panic("unknown field")
+	}
+}
+
+func (m *known) WhichOneof(name pref.Name) pref.FieldNumber {
+	return 0
+}
+
+func (m *known) Range(f func(pref.FieldNumber, pref.Value) bool) {
+	if m.set {
+		f(fieldS, pref.ValueOf(m.value))
+	}
+}
+
+func (m *known) NewMessage(num pref.FieldNumber) pref.Message {
+	panic("not a message field")
+}
+
+func (m *known) ExtensionTypes() pref.ExtensionFieldTypes {
+	return (*exttypes)(m)
+}
+
+type unknown IrregularMessage
+
+func (m *unknown) Len() int                                          { return 0 }
+func (m *unknown) Get(pref.FieldNumber) pref.RawFields               { return nil }
+func (m *unknown) Set(pref.FieldNumber, pref.RawFields)              {}
+func (m *unknown) Range(func(pref.FieldNumber, pref.RawFields) bool) {}
+func (m *unknown) IsSupported() bool                                 { return false }
+
+type exttypes IrregularMessage
+
+func (m *exttypes) Len() int                                     { return 0 }
+func (m *exttypes) Register(pref.ExtensionType)                  { panic("not extendable") }
+func (m *exttypes) Remove(pref.ExtensionType)                    {}
+func (m *exttypes) ByNumber(pref.FieldNumber) pref.ExtensionType { return nil }
+func (m *exttypes) ByName(pref.FullName) pref.ExtensionType      { return nil }
+func (m *exttypes) Range(func(pref.ExtensionType) bool)          {}
+
+const fieldS = pref.FieldNumber(1)
+
+var descriptor = func() pref.FileDescriptor {
+	p := &descriptorpb.FileDescriptorProto{}
+	if err := prototext.Unmarshal([]byte(descriptorText), p); err != nil {
+		panic(err)
+	}
+	file, err := protodesc.NewFile(p, nil)
+	if err != nil {
+		panic(err)
+	}
+	return file
+}()
+
+func file_irregular_irregular_proto_init() { _ = descriptor }
+
+const descriptorText = `
+  name: "internal/testprotos/irregular/irregular.proto"
+  package: "goproto.proto.thirdparty"
+  message_type {
+    name: "IrregularMessage"
+    field {
+      name: "s"
+      number: 1
+      label: LABEL_OPTIONAL
+      type: TYPE_STRING
+      json_name: "s"
+    }
+  }
+  options {
+    go_package: "google.golang.org/protobuf/internal/testprotos/irregular"
+  }
+`