-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proto.Equal does not check for nested invalidity #1631
Comments
Via chat, exi also provided this reproducer: --- i/proto/encode_test.go
+++ w/proto/encode_test.go
@@ -53,6 +53,11 @@ func TestEncode(t *testing.T) {
if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
}
+ pe := proto.Equal(got, want)
+ pre := protoreflect.ValueOf(got.ProtoReflect()).Equal(protoreflect.ValueOf(want.ProtoReflect()))
+ if pe != pre {
+ t.Errorf("proto.Equal and protoreflect.Equal disagree; proto.Equal:\n%v\nprotoreflect.Equal:\n%v\ngot:\n%v\nwant:\n%v", pe, pre, prototext.Format(got), prototext.Format(want))
+ }
})
}
} …which makes TestEncode/nil_messages fail. |
Did some digging. https://go.dev/cl/196618 introduced the behavior (“proto/equal: equate nil”), https://go.dev/cl/464275 documented it. I tried to make protoreflect.Value.Equal do the same check that proto.Equal does: --- i/reflect/protoreflect/value_equal.go
+++ w/reflect/protoreflect/value_equal.go
@@ -92,6 +92,10 @@ func equalMessage(mx, my Message) bool {
return false
}
+ if validx, validy := mx.IsValid(), my.IsValid(); !validx || !validy {
+ return !validx && !validy
+ }
+
nx := 0
equal := true
mx.Range(func(fd FieldDescriptor, vx Value) bool { However, this makes
The printed messages are identical, but in delve, I can see the difference:
So the tests currently rely on messages being equal despite their “nil-ness” being different. @neild @dsnet Any intuition as to what we should do here? Thanks. |
If I recall correctly, we originally wanted to have The proto.Equal documentation about invalid messages was added relatively recently, in https://go.dev/cl/464275. I think the thing to do here is correct the documentation to accurately describe the implementation: |
Agree, I’ve been trying to follow the details of this, but have found it hard to understand. An empty message should be an empty message, whether that’s it being |
I regret not writing a more descriptive rationale for the reason for the change, but the timing of the commit aligns with @neild's hypothesis. |
What version of protobuf and what language are you using?
Version: go, 1.34.2
What did you do?
It's possible to construct a proto with a nested
nil
pointer value as a message, leading to an invalid nested message.This causes
proto.Equal
to returnfalse
if one of the top-level message isnil
but will returntrue
if they contain invalid message in nested fields.What did you expect to see?
Since the
proto.Equal
contract states that no message can be equal to an invalid message, it should return false if nested fields are invalid.What did you see instead?
proto.Equal
will return true even with nested invalid messages.Anything else we should know about your project / environment?
Adding this to equalMessage in
protoreflect/value_equal.go
, will lead to test failures inproto:encode_test
The text was updated successfully, but these errors were encountered: