> 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.