I'm observing some strange behavior and could use some help understanding it.
The specification says: “Pointers to distinct zero-size variables may or may not be equal.”
With the following program (Go Playground):
var ( a struct{} b struct{} eq = &a == &b ) func f1() { println("&a:", &a) println("&b:", &b) println("&a == &b:", eq) }I'll get
&a: 0x537580 &b: 0x537580 &a == &b: falseOkay, a and b are empty structs, do not escape, so they share the same address - fine. Also, some optimizer sees that a and b are different variables, so their addresses must be different, and decides to make &a == &b a constant - wrong, but I can live with that.
My question would be: Is this behavior expected, somehow defined by the specification, or is it undefined behavior?
Let's try to confuse the optimizer a little (Go Playground):
var ( a struct{} b struct{} aa = &a ba = &b eq = aa == ba ) func f1() { println("&a:", aa) println("&b:", ba) println("&a == &b:", eq) }results in
&a: 0x5375a0 &b: 0x5375a0 &a == &b: trueMission accomplished, too complicated to calculate in advance. But globals are bad, so (Go Playground):
func f2() { var ( a struct{} b struct{} aa = &a ba = &b eq = aa == ba ) println("&a:", aa) println("&b:", ba) println("&a == &b:", eq) }Seems like inlining helps generate false answers.
The interesting part here is that I can create two pointers (which may or may not be equal per specification), but depending on how I compare them I get different results.
There was a similar discussion here: https://groups.google.com/g/golang-nuts/c/JBVqWYFdtC4/m/VJC2OLJcAQAJ
On Wed, Jun 19, 2024 at 9:59 AM Oliver Eikemeier <eike...@fillmore-labs.com> wrote:
> The specification says: “Pointers to distinct zero-size variables may or may not be equal.”
The specs says, as you say above, "Pointers to distinct zero-size
variables may or may not be equal.” That means that you can't predict
the result of any given comparison of addresses of zero-sized
variables.
Could be true, could be false, could change each time you
do the comparison. So this behavior is permitted by the spec.
> The interesting part here is that I can create two pointers (which may or may not be equal per specification), but depending on how I compare them I get different results.
Yes, as the spec permits.
The specs says, as you say above, "Pointers to distinct zero-size
variables may or may not be equal.” That means that you can't predict
the result of any given comparison of addresses of zero-sized
variables.I'm not sure I read it that way. I would interpret it as “taking pointer to distinct zero-size variables may or may not result in equal pointers”.But not being able to have a repeatable result of a pointer comparison seems strange to me. The specification says “you'll get some pointers”. Fine.
Two distinct zero-size variables may have the same address in memory.
Pointers to distinct zero-size variables may or may not be equal.
Then I have two pointers. Where in the spec is “the result of comparison of pointers may change over time”?
For example (Go Playground):func f3() {
var (
a struct{}
b int
aa = unsafe.Pointer(&a)
ba = unsafe.Pointer(&b)
eq = aa == ba
)
println("&a:", aa)
println("&b:", ba)
println("&a == &b:", eq)
}gives&a: 0xc000046738&b: 0xc000046738&a == &b: falseand &b is not even a pointer to a zero-sized variable.Could be true, could be false, could change each time you
do the comparison. So this behavior is permitted by the spec.I'm not sure about the “undefined behavior over time”. I get that the compiler lays out the memory however it sees fit, but then I should have unchanging values in my variables.> The interesting part here is that I can create two pointers (which may or may not be equal per specification), but depending on how I compare them I get different results.
Yes, as the spec permits.I assume this has little relevance in practice, but it is surprising.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/64e58302-59a6-4cbf-859e-aa6b8a2b6068n%40googlegroups.com.
&a: 0xc0000466f8 *int
&b: 0xc0000466f8 *int
&a == &b: false
Let me start with this: I’m fine with the behavior, it is exotic enough that even the optimizer should be allowed to assume something that doesn’t hold during runtime, since it simplifies things.What I think is at least surprising and at worst lacking is the documentation.It mentions “pointers” (plural) to zero-sized variables, but for this behavior it is sufficient when only one pointer derives from a pointer to a zero-sized variable, as demonstrated in the example below.
APointer
is a pointer type but aPointer
value may not be dereferenced. Any pointer or value of core typeuintptr
can be converted to a type of core typePointer
and vice versa. The effect of converting betweenPointer
anduintptr
is implementation-defined.
A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer.
Pointer types are comparable.
Two pointer values are equal if they point to the same variable or if both have value nil
.
Pointers to distinct zero-size variables may or may not be equal.
Current implementations provide several built-in functions useful during bootstrapping. These functions are documented for completeness but are not guaranteed to stay in the language. They do not return a result.
Function Behavior print prints all arguments; formatting of arguments is implementation-specific println like print but prints spaces between arguments and a newline at the endImplementation restriction:
println
need not accept arbitrary argument types, but printing of boolean, numeric, and string types must be supported.
I’m advocating for at least a FAQ article,
but also think the specification should be adapted, for clarity but also for the fact that only one pointer pointing to a zero-sized variable can compare differently to anything over time, even things having the same address value.
Am 20.06.2024 um 13:24 schrieb Axel Wagner <axel.wa...@googlemail.com>:We can see that 1. `unsafe.Pointer` is definitionally a pointer type, 2. in your example, the two `unsafe.Pointers` do not point to the same variable and do not have the value `nil` and 3. are not pointing at distinct zero-sized variables. So their comparison should be `false`, which is what your example observes.
&a: 0xc0000466f8 *int
&b: 0xc0000466f8 *int
&a == &b: false
p1 == p2: true
All of this would be fine. What makes your example confusing is that you use `println` to output them and see that they "have the same value".
Note that if you *don't* convert an `unsafe.Pointer` to `uintptr`, you have no way to argue that they "are actually equal"
I’m advocating for at least a FAQ article,I tend to agree, though I'm not sure how to phrase that, beyond saying "do not make any assumptions about the identity pointers to zero-sized variables or with zero-sized base types or that where derived from one of those" and I'm not sure how helpful that is.
but also think the specification should be adapted, for clarity but also for the fact that only one pointer pointing to a zero-sized variable can compare differently to anything over time, even things having the same address value.Note that "address value" is not something that exists within the spec. As for clarifying the spec here, maybe. I do think the behavior is covered, as I outlined above. And as above, I'm not sure how to clarify it further, while still leaving up the space we want to left open.
Am 20.06.2024 um 13:24 schrieb Axel Wagner <axel.wa...@googlemail.com>:We can see that 1. `unsafe.Pointer` is definitionally a pointer type, 2. in your example, the two `unsafe.Pointers` do not point to the same variable and do not have the value `nil` and 3. are not pointing at distinct zero-sized variables. So their comparison should be `false`, which is what your example observes.Does it (Go Playground)?func f5() {var (a struct{}b intaa = (*int)(unsafe.Pointer(&a))ba = (*int)(unsafe.Pointer(&b))eq = aa == ba)println("&a:", aa, reflect.TypeOf(aa).String())println("&b:", ba, reflect.TypeOf(ba).String())println("&a == &b:", eq)cmpIntPtr(aa, ba)}func cmpIntPtr(p1 *int, p2 *int) {fmt.Println("p1 == p2:", p1 == p2)}&a: 0xc0000466f8 *int &b: 0xc0000466f8 *int &a == &b: false p1 == p2: true
I’m advocating for at least a FAQ article,I tend to agree, though I'm not sure how to phrase that, beyond saying "do not make any assumptions about the identity pointers to zero-sized variables or with zero-sized base types or that where derived from one of those" and I'm not sure how helpful that is.It seems like a frequently asked question to me ;)
On Jun 20, 2024, at 7:12 AM, 'Axel Wagner' via golang-nuts <golan...@googlegroups.com> wrote:
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfGOWoQYrQ9qm_9%2BdyvEObJV3JP%2BhwwFj9%2Bgo%3DqusRVL7A%40mail.gmail.com.
I would add that I think the Go team should strive to remove as much “implementation defined” or “undefined” from the spec as possible. Forgo the optimizations. It’s this language that makes C++ such a pain to work with especially in a cross platform manner.I think you can safely narrow the spec in this area without issue - since it would have been very difficult to use anyway. Maybe some highly specialized code that relied on a particular Go implementation might.
On Jun 20, 2024, at 7:39 AM, Axel Wagner <axel.wa...@googlemail.com> wrote:
Am 20.06.2024 um 14:12 schrieb Axel Wagner <axel.wa...@googlemail.com>:On Thu, 20 Jun 2024 at 13:48, Oliver Eikemeier <eike...@fillmore-labs.com> wrote:I think you should acknowledge that the spec *is* already trying to be very clear about this. The fact that you can poke holes in it is because writing a spec unambiguously is hard.
But the two quoted phrases are there in the best attempt to clearly state that you can not rely on any assumptions about pointers to zero-sized variables - within the somewhat abstract framework of the spec. Feel free to propose a better one, I'm not actually arguing against that.
Am 20.06.2024 um 14:21 schrieb Robert Engels <ren...@ix.netcom.com>:I would add that I think the Go team should strive to remove as much “implementation defined” or “undefined” from the spec as possible. Forgo the optimizations. It’s this language that makes C++ such a pain to work with especially in a cross platform manner.I think you can safely narrow the spec in this area without issue - since it would have been very difficult to use anyway. Maybe some highly specialized code that relied on a particular Go implementation might.
&a: 0xc0000466f8 *int
&b: 0xc0000466f8 *int
0 false
2 false