On Thursday, February 29, 2024 at 3:14:40 PM UTC+8 Axel Wagner wrote:
The loop var change *does* break compatibility. And it did so knowingly and - as clearly explained - was an exception. Stop arguing in bad faith. An exception. :D On Thu, Feb 29, 2024 at 7:03 AM tapi...@gmail.com <tapi...@gmail.com> wrote: On Wednesday, February 28, 2024 at 3:19:37 PM UTC+8 Axel Wagner wrote: 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. With Go version specified, nothing can be broken. For example, the loop var change in Go 1.22 doesn't break backwards compatibility. (Though this is not my opinion, ;D) 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 < golan...@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...@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...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c7b44255-13f1-434d-b569-f592c8e262d4n%40googlegroups.com <https://groups.google.com/d/msgid/golang-nuts/c7b44255-13f1-434d-b569-f592c8e262d4n%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/5227acc2-b8f1-4ce9-9d0c-829930967af4n%40googlegroups.com.