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.

Reply via email to