The Go Programming Language says in Section 13.2 that this is code is safe and x
will always
be visible to the garbage collector:
pb := (*int16)(unsafe.Pointer(
uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
*pb = 42
And that this code is unsafe, because x
is temporarily not visible to the
garbage collector, which could move it, making pb
a dangling pointer:
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
pb := (*int16)(unsafe.Pointer(tmp))
*pb = 42
But I can't see the difference between these two examples.
In the case described as safe, after uintptr
has been called, the only
reference to the x
is the uintptr
value, isn't it? There's a Pointer
to it on the same line, but it was an argument to uintptr
, which has run,
so nothing is referencing the arguments, and so the Pointer
is not live and the uintptr
is the only reference to the object.
I can't see how storing the uintptr
in a local variable instead of as an
expression intermediate value makes it any more safe. Aren't local variables
like tmp
removed in compiler phases anyway, becoming anonymous dataflow edges,
so that the generated code should be semantically equivalent? Or does Go have
some rules for when garbage collection can run? Such as having safepoints only
between statements? But the code in the first example has method calls so I
would presume they would always be safepoints?