proto, internal/impl: make IsInitialized more consistent

Make the fast-path and slow-path versions of IsInitialized report
exactly the same errors: An errors.RequiredNotSet containing the
full name of one of the unset required fields.

Bugfix: Fast-path IsInitialized on a nil message reports an error only
when the message directly contains required fields.

Bugfix: Include fast-path IsInitialized in legacy messageIfaceWrapper.

Fixes golang/protobuf#887

Change-Id: Ia5e4b386f8c23f6f855d995f4a098b1338acbae3
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185397
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/isinit.go b/internal/impl/isinit.go
index 29f9ad8..a533cf4 100644
--- a/internal/impl/isinit.go
+++ b/internal/impl/isinit.go
@@ -7,15 +7,11 @@
 import (
 	"sync"
 
+	"google.golang.org/protobuf/internal/errors"
 	"google.golang.org/protobuf/proto"
 	pref "google.golang.org/protobuf/reflect/protoreflect"
 )
 
-type errRequiredNotSet struct{}
-
-func (errRequiredNotSet) Error() string        { return "proto: required field not set" }
-func (errRequiredNotSet) RequiredNotSet() bool { return true }
-
 func (mi *MessageInfo) isInitialized(msg proto.Message) error {
 	return mi.isInitializedPointer(pointerOfIface(msg))
 }
@@ -26,7 +22,12 @@
 		return nil
 	}
 	if p.IsNil() {
-		return errRequiredNotSet{}
+		for _, f := range mi.orderedCoderFields {
+			if f.isRequired {
+				return errors.RequiredNotSet(string(mi.PBType.Fields().ByNumber(f.num).FullName()))
+			}
+		}
+		return nil
 	}
 	if mi.extensionOffset.IsValid() {
 		e := p.Apply(mi.extensionOffset).Extensions()
@@ -41,7 +42,7 @@
 		fptr := p.Apply(f.offset)
 		if f.isPointer && fptr.Elem().IsNil() {
 			if f.isRequired {
-				return errRequiredNotSet{}
+				return errors.RequiredNotSet(string(mi.PBType.Fields().ByNumber(f.num).FullName()))
 			}
 			continue
 		}