unsafe.Pointer conversions

304 views
Skip to first unread message

Nigel van Keulen

unread,
Jul 26, 2023, 12:56:54 PM7/26/23
to golang-nuts
Since unsafe.Pointer can convert between types that have the same underlying type, can unsafe.Pointer also convert between function types who's arguments & return values have the same underlying type?

The code I would like to use this technique on:
```
type JSExtFunc func(this Value, args Args) interface{} 

func (f JSExtFunc) MarshalJS() js.Func { 
    // I am not sure if this is correct. 
    var function = *(*func(this js.Value, args []js.Value) interface{})(unsafe.Pointer(&f)) 
    return js.FuncOf(function)
 } 
// Arguments for wrapped functions. 
type Args []js.Value 
type Value js.Value
```

I have tried this out in the go playground, and it does seem to work.

However, this is undocumented in the language specification. 
How likely is this that this code would break, using the same version of Go?
Is this perhaps something to be added in the language specification?

Maybe even a more interesting question, breaking all safety guards, could this be extended by casting a function which takes a uint64 (`func myFunc(uint64)`) to a function which takes two uint32's? (`*(*func(uint32, uint32)(unsafe.Pointer(ptrToMyFuncValue)))`)

The latter is something I do not nescessarily desire an answer to, but it does make me curious.

Keith Randall

unread,
Jul 27, 2023, 12:31:29 AM7/27/23
to golang-nuts
On Wednesday, July 26, 2023 at 9:56:54 AM UTC-7 Nigel van Keulen wrote:
Since unsafe.Pointer can convert between types that have the same underlying type, can unsafe.Pointer also convert between function types who's arguments & return values have the same underlying type?

The code I would like to use this technique on:
```
type JSExtFunc func(this Value, args Args) interface{} 

func (f JSExtFunc) MarshalJS() js.Func { 
    // I am not sure if this is correct. 
    var function = *(*func(this js.Value, args []js.Value) interface{})(unsafe.Pointer(&f)) 
    return js.FuncOf(function)
 } 
// Arguments for wrapped functions. 
type Args []js.Value 
type Value js.Value
```

I have tried this out in the go playground, and it does seem to work.

However, this is undocumented in the language specification. 
How likely is this that this code would break, using the same version of Go?
Is this perhaps something to be added in the language specification?


Yes, that will probably work. It is the unsafe package though, so we don't promise it will continue to work.
If we were to specify it, we'd need to be more explicit about what "equivalent memory layout" means in rule 1 of pkg.go.dev/unsafe#Pointer . All function values technically have an equivalent memory layout (they are all just one pointer), so we'd need to say something about arguments+returns being the same size and recursively have equivalent layout.
 
Maybe even a more interesting question, breaking all safety guards, could this be extended by casting a function which takes a uint64 (`func myFunc(uint64)`) to a function which takes two uint32's? (`*(*func(uint32, uint32)(unsafe.Pointer(ptrToMyFuncValue)))`)


This will not work. The calling convention differs between one int64 and two int32s.
Reply all
Reply to author
Forward
0 new messages