(apologies for the abundance of grammatical and spelling errors that occurred during editing and of course only became visible after hitting "send")
On Thu, 20 Jun 2024 at 13:24, Axel Wagner <axel.wagner...@googlemail.com> wrote: > On Thu, 20 Jun 2024 at 12:28, Oliver Eikemeier < > eikeme...@fillmore-labs.com> wrote: > >> 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. >> > > Re-visiting your example, it is indeed interesting that I realized before. > I'll note that you need to involve `unsafe.Pointer` to observe this, as you > may not compare pointers to different types. And `unsafe.Pointer`'s > behaviors are somewhat left up to the implementation. > > The spec ays: <https://go.dev/ref/spec#Package_unsafe> > > A Pointer is a pointer type <https://go.dev/ref/spec#Pointer_types> but a >> Pointer value may not be dereferenced >> <https://go.dev/ref/spec#Address_operators>. Any pointer or value of core >> type <https://go.dev/ref/spec#Core_types> uintptr can be converted >> <https://go.dev/ref/spec#Conversions> to a type of core type Pointer and >> vice versa. The effect of converting between Pointer and uintptr is >> implementation-defined. > > > The thing that stands out is that it leaves the effect of converting to > `uintptr` up to the implementation, but nothing else. So any other rule, as > far as the language is concerned, should be derived from the rules for > pointer types (except that they can not be dereferenced). > > One contradiction here is that this section references the definition of a > pointer type, which says > > A pointer type denotes the set of all pointers to variables >> <https://go.dev/ref/spec#Variables> of a given type, called the *base >> type* of the pointer. >> > > This contradicts the nature of `unsafe.Pointer`, though, which does not > have a base type. That difference is what causes the trouble: When the spec > defines pointer comparisons by > > 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 <https://go.dev/ref/spec#Size_and_alignment_guarantees> >> variables may or may not be equal. > > > 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. > > 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". For > that, we need to look at the definition of `println` > <https://go.dev/ref/spec#Bootstrapping>: > > 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 end >> >> Implementation restriction: print and println need not accept arbitrary >> argument types, but printing of boolean, numeric, and string types >> <https://go.dev/ref/spec#Types> must be supported. >> > Notably, this says nothing about what happens if you pass a pointer type. > That is thus left up to the implementation. In effect, it does the same as > `fmt.Println` ultimately: It converts the pointer to `uintptr`, which as > we've seen above is left to the implementation. > > Note that if you *don't* convert an `unsafe.Pointer` to `uintptr`, you > have no way to argue that they "are actually equal". And if you don't use > `unsafe.Pointer` you have no way to compare pointers to variables of > different types. So, the only way to observe that behavior is to enter what > is implementation-defined. > > So, yes. Your example is interesting, but still within spec. > > 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. > > >> >> I do not believe the specification is clear on this. Otherwise I don’t >> think this is urgent. But it seems to pop up repeatedly. >> >> Cheers >> Oliver >> >> Am 19.06.2024 um 23:16 schrieb 'Axel Wagner' via golang-nuts < >> golang-nuts@googlegroups.com>: >> >> The spec says both. It says >> >>> Two distinct zero-size variables may have the same address in memory. >> >> And it says >> >>> Pointers to distinct zero-size variables may or may not be equal. >> >> >> The former means what you say. The latter means what Ian says. >> >> Then I have two pointers. Where in the spec is “the result of comparison >>> of pointers may change over time”? >>> >> >>> For example (Go Playground <https://go.dev/play/p/acknRHBvi0P>): >>> >>> 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: false >>> >>> and &b is not even a pointer to a zero-sized variable. >>> >> >> -- 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfE0oAV4uQYVvWLwG1uBxgnum4r7-MdBqu8%2BDfURWg_FUw%40mail.gmail.com.