go version)?1.9, 1.10, tip
Yes, but not with 1.8 or older releases.
go env)?linux amd64
https://play.golang.org/p/KyovZxzxQfS
Either "equal" or "unequal" should be printed. Go 1.8 printed "equal".
No output.
It seems that the problem is caused by CL 38338, which added a SSA rule to simplify a comparison of the addresses of two symbols (even when those addresses are numerically identical):
(EqPtr (Addr {a} x) (Addr {b} x)) -> (ConstBool [b2i(a == b)])
However, the negated form of this rule is missing, so the compiler still emits a numeric comparison of the two addresses for the second comparison in the code. Adding this rule optimizes away that comparison and fixes the program output (to "unequal"):
(NeqPtr (Addr {a} x) (Addr {b} x)) -> (ConstBool [b2i(a != b)])
Change https://golang.org/cl/102275 mentions this issue: cmd/compile/internal/ssa: consistent constant pointer comparisons
@randall77
I don't think this is a bug.
The language spec says:
Pointers to distinct zero-size variables may or may not be equal.
I admit that it's a bit weird that they are both equal and not equal at the same time. But a valid program should not be relying on the result in any case.
That said, I'm not adverse to the fix you suggested. It seems harmless enough.
I have read that line in the language spec, but have always understood it as saying that the equality may change from one Go release to the next; I didn't think of it as possibly causing inconsistent behaviour. The risk is that a program using an imported type may not necessarily know that the type is zero-width, and could be surprised by this behaviour.
Alternatively, instead of treating this as a bug fix, it could be considered an improvement to CL 38338, by optimizing the other kind of comparison as well.
I admit that it's a bit weird that they are both equal and not equal at the same time. But a valid program should not be relying on the result in any case.
I guess, although I think (a == b) || (a != b) should always evaluate to true, whether guaranteed by the spec or not.
I guess, although I think (a == b) || (a != b) should always evaluate to true, whether guaranteed by the spec or not.
I think this assumption is similar to the one that a == a should always evaluate to true. But it does not. Neither does your particular example always evaluate to true in, for example, sql (when a and/or b is NULL).
I'll weigh in for team bug.
The spec defines !p to mean "not p", and u == v and u != v to mean "u equals v" and "u not equals v", respectively. By composition, it follows that !(u == v) also means "u not equals v", which should then be the same as u != v. (This is basically @josharian's (a == b) || (a != b) argument framed differently.)
I argue this is a bug because it violates this constraint. This program should either print not equal 1\nnot equal 2\n, or nothing at all: https://play.golang.org/p/cPI8UBx-GBB
I acknowledge the spec could be more precise in its description of comparing pointers to zero-width variables, but I think the simplest interpretation is that "equal" and "not equal" are exclusive relations that hold indefinitely for any two values. I think if we really wanted to admit the current behavior, we should revise the spec to explicitly state that results may by different for each comparison (of pointer to zero-width variables).
Most helpful comment
I guess, although I think
(a == b) || (a != b)should always evaluate to true, whether guaranteed by the spec or not.