If you don't care about cgo on Darwin, ignore this.
Some background (see issue
21897 for details):
Darwin has CoreFoundation reference types. They are named CF*Ref, and act like pointers. The root of the hierarchy is CFTypeRef, akin to void* in the C world. There are many "subclasses", like CFStringRef, CFNumberRef, etc. A subclass can be cast up to CFTypeRef, and a CFTypeRef can be downcast to a subclass (assuming you know which one it should be). These types are opaque in other respects, requiring calls into CoreFoundation library functions to extract data from them.
When we translate these types to Go using cgo, they become pointer types, because they are declared as such in the CoreFoundation headers.
CFTypeRef -> unsafe.Pointer
CFStringRef -> *struct{...}
and so on.
The problem is that not all of the values stored in these types are actually pointers. CFNumberRef and CFDateRef, in particular, are implemented (sometimes) by packing their content into the high bits of a pointer and marking them specially using the low bits. When such a beast reaches the Go side, the garbage collector might barf on it, because it is something which has pointer type, but points somewhere strange (the middle of the first page of memory, or an unused mspan, ...).
As a result, it is fundamentally dangerous to pass a CFNumberRef or a CFDateRef to Go. And because either of those can be cast up to CFTypeRef, a CFTypeRef is dangerous also. There's no easy way to defend against this problem, other than to not pass them to Go at all. But that's hard, as they are both Core and Foundational. Almost anything you do Darwin-specific in cgo will run into CFTypeRef, at least.
I'm proposing that we change CFTypeRef, CFNumberRef, and CFDateRef to translate on the Go side to uintptr instead of a pointer type. That way, the garbage collector doesn't treat these items as pointers and the problem goes away. [Option 2: translate all the CF*Ref types to uintptr.] Because the only thing you can do with a CF*Ref is to call C functions on it, it shouldn't affect operations we actually want to do on these types - you'd have to do a cgo call to do anything with them. In particular, even though they are pointers you can't dereference them.
Unfortunately there are two exceptions: Casts and nil. Hence this email.
Right now, you can cast from C.CFStringRef to C.CFTypeRef, because both are pointer types (the former is *struct{...}, the latter is unsafe.Pointer). After my proposal, that is no longer possible, because you can't cast directly from a concrete pointer type to a uintptr. You need a double cast with unsafe.Pointer as the intermediary.
Right now, you can assign nil to a C.CFTypeRef. After my proposal, you'd have to assign 0 instead.
[Under option 2, the cast problem goes away, but the nil vs. 0 problem still exists.]
I started to dismiss these shenanigans as unlikely, but it turns out they exist even in the std library. See crypto/x509/root_cgo_darwin.go.
So what to do? I've written a go fix to find instances of these problems and fix them. For casts, it inserts the intermediate unsafe.Pointer cast in the appropriate places, and [still TBD] it replaces nil for 0 when appropriate. The problem with go fix is that it isn't perfect. There are cases where the typechecker in go fix is incomplete, and hence can miss instances which would then fail during compilation.
How do people feel about this? We'd essentially be breaking a possibly substantial fraction of Darwin cgo code and then offering a go fix module which would hopefully fix most, but probably not all, errors. Any remaining fixes would have to be done by hand. Hand fixing should be really easy, but could be tedious.
So we'd be trading a moderately painful one-time fix to avoid the random crashes we'd otherwise continue to see in cgo Darwin programs. Sound worth it?
Option 1 vs. Option 2? Option 2 is a little bit easier go-fix-wise and manual-fix-wise, but it is changing more types and thus may be impacting more total code.