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.

Reply via email to