That would break backwards compatibility, though. And it would be a re-definition (i.e. existing code would compile, but behave differently at runtime) and is hence not allowed even under the Go 2 transition rules. I'm also not sure you can exclude *all* pointers to zero-sized variables. Note that `[0]T` is also zero-sized and you can convert slices (even empty ones) into array-pointers. And you can take the address of struct fields.
All of this to solve an honestly pretty small issue. It's a corner, yes. But it isn't a particularly sharp corner. On Wed, Feb 28, 2024 at 8:06 AM 'Brian Candler' via golang-nuts < golang-nuts@googlegroups.com> wrote: > > let's consider the two possible definitions: > > > > 1. Pointers to distinct zero-size variables are equal: [...] > > 2. Pointers to distinct zero-size variables are not equal: > > Another possibility: > > 3. Equality comparisons between pointers to zero-size variables are > forbidden at compile time. > 3a. If you wrap two such values in interfaces and try to compare them, > then you get a runtime panic, same as certain cases today > <https://go.dev/play/p/MgtUz2em65A>. > > Indeed, what if it were forbidden to take a pointer to a zero-sized > variable in the first place? There is nothing to point at, after all. > > On Wednesday 28 February 2024 at 07:17:24 UTC+7 Brien Colwell wrote: > >> I think the surprising part is that the comparison result can change for >> the same values because of the assumption that pointers never change. This >> is implied by the spec but easy to miss. >> >> "Pointers to distinct zero-size variables may or may not be equal." >> "Pointers to distinct zero-size variables may or may not be equal and the >> results may or may not be repeatable in any context." >> >> Agree once a programmer is aware of the behavior it can be avoided. >> >> Best, >> Brien >> >> >> On Feb 27, 2024, at 3:06 PM, 'Axel Wagner' via golang-nuts < >> golan...@googlegroups.com> wrote: >> >> On Tue, Feb 27, 2024 at 8:19 PM Marvin Renich <mr...@renich.org> wrote: >> >>> Prior to generics, the type of the >>> arguments to == were easily known to the programmer, and so it was >>> obvious when this "undefined" exception would raise its ugly head, and >>> you just didn't use it for empty struct types. But now, with generics, >>> this can only be classified as a glaring BUG in the spec. >> >> >> There is pretty much a 0% chance that we'd change the spec in this >> regard, at this point. It would mean that variable declarations like >> `[1<<30]struct{}` would have to allocate huge chunks of heap, to ensure >> that different index-expressions can have different addresses. And while >> there shouldn't be any code relying on that not happening for correctness, >> there is definitely code out there relying on it for performance (e.g. >> there is a pattern of adding struct fields like `_ [0]func()` to ensure a >> type is not comparable - such a struct would now change alignment and size). >> >> The optimization that variables of zero size can re-use the same address >> has been in Go since before Go 1. Given this, it is pretty much implied >> that comparison of those pointers will sometimes have weird results - the >> only question is, *which* results are weird. I agree that this is one of >> the weirder cases. But I don't think we can practically define `==` for >> pointers to zero-sized variables. >> >> I'll also point out that for generics specifically, I'm not sure *any* >> action would have a practical effect. If the type argument is not >> statically known, we also can't special-case it to take into account that >> it's a pointer to a zero-sized variable. Note that the triggered >> optimization isn't necessarily "these are pointers to zero-sized variables, >> hence I can do whatever I want" - it's "these are pointers to distinct >> variables, hence I can assume they are unequal". That is a generally useful >> optimization and it would still be applied to generic code. >> >> How can a programmer count on x == y having any meaning at all in code >>> like this: >>> >>> func IsEqual[T comparable](x, y T) bool { >>> return x == y >>> } >>> >>> if the definition of == for empty structs is undefined? >> >> >> The result is defined for empty structs, just not for *pointers* to empty >> structs. >> Note that `==` has other edge-cases as well. In particular, for floating >> point/complex type arguments, `==` is irreflexive (e.g. NaN is unequal to >> itself). >> I'm not sure that pointers to zero-sized variables make this >> significantly worse. >> >> >>> If we can at least agree that this ambiguity is no longer desirable, >>> >> >> I don't think we can agree on that, sorry. >> >> >>> let's consider the two possible definitions: >>> >>> 1. Pointers to distinct zero-size variables are equal: >>> >>> This allows the compiler to easily optimize virtual address usage, but >>> is inconsistent with the non-zero-size definition. >>> >> >> Please look at the issue I filed for some discussion of edge-cases we are >> unlikely to be able to cover satisfactorily. One obvious case is when >> converting them to `unsafe.Pointer`, in which case the compiler no longer >> knows that they point at zero-sized variables. Potentially, any such >> conversion would have to re-assign them the magic "zero-sized variable" >> address, which then would potentially lead to other weird comparison >> implications, when code assumes that two `unsafe.Pointer` pointing at >> distinct variables should have distinct addresses. >> >> We could probably make *more* such comparisons evaluate to `true`, but >> it's unlikely that we could ever cover *all* of them. It would potentially >> have prohibitive performance-impact on slicing operations, for example. >> >> 2. Pointers to distinct zero-size variables are not equal: >>> >>> This is consistent with the non-zero-size definition, but the >>> implementation would likely require distinct virtual addresses for >>> distinct variables. Whether this would require committed memory >>> corresponding to those virtual addresses is unclear to me. >>> >> >> I believe it would. In effect, `struct{}` would have to take at least one >> byte (as would a zero-sized array). >> >> >>> Definition 1 removes the distinction between empty struct values and >>> empty struct instances, and the only way for the programmer to get that >>> distinction back is by using a non-empty struct. >>> >>> On the other hand, definition 2 preserves the distinction. If a >>> programmer wants to have instances compare as equal, it is often very >>> easy to use instances of the empty type rather than instances of a >>> pointer to the empty type. Implement the methods on the type with value >>> receivers rather than pointer receivers. >>> >> >> I think if these arguments hold any water, the argument "the programmer >> just shouldn't use pointers to zero-sized variables, if they want defined >> semantics for ==" is just as valid. That is, if they have control over the >> type and we are willing to force them to make a decision aligning with our >> definition, why not force them to make a decision aligning with there not >> being a definition? >> >> >>> >>> ...Marvin >>> >>> -- >>> 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/Zd41i3rHLskqBuef%40basil.wdw >>> . >>> >> >> -- >> >> You received this message because you are subscribed to a topic in the >> Google Groups "golang-nuts" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/golang-nuts/JBVqWYFdtC4/unsubscribe. >> To unsubscribe from this group and all its topics, send an email to >> golang-nuts...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHVuxSduyWHG20DjzG1jvE0P06fEqC_FyHdDEWewjOjTg%40mail.gmail.com >> <https://groups.google.com/d/msgid/golang-nuts/CAEkBMfHVuxSduyWHG20DjzG1jvE0P06fEqC_FyHdDEWewjOjTg%40mail.gmail.com?utm_medium=email&utm_source=footer> >> . >> >> >> -- > 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/c7ead734-8ce6-4254-91ac-771b02e527ddn%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/c7ead734-8ce6-4254-91ac-771b02e527ddn%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- 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/CAEkBMfFyfNz-Vs7p74zHa4a%3DHqn%2B11fOTSyS504un7Ahdz8Obw%40mail.gmail.com.