(This bug is a consequence of the fixes for issue #40954.)
package main
import "reflect"
//go:notinheap
type T struct {
x int
}
func main() {
reflect.ValueOf((*T)(nil)).Pointer()
}
This program panics with:
panic: can't call pointer on a non-pointer Value
goroutine 1 [running]:
reflect.Value.pointer(...)
/Users/khr/sandbox/ro/src/reflect/value.go:95
reflect.Value.Pointer(0x1081e40, 0x0, 0x16, 0x0)
/Users/khr/sandbox/ro/src/reflect/value.go:1457 +0x1b6
main.main()
/Users/khr/gowork/tmp1.go:11 +0x9b
reflect is confused by a type that claims to be a pointer, but has no pointer bits. It has no pointer bits because pointers to go:notinheap types are treated as uintptrs by the compiler.
More generally, we can't ever convert *T to unsafe.Pointer, as that's the equivalent of converting a uintptr, potentially containing a bad pointer value, to a pointer. Particularly, we can't store a *T in the data field of an interface, or in reflect.Value.ptr. We have to store them indirectly instead.
This issue originally came up with the a call to reflect.DeepEqual which ended up calling Pointer on one of these types.
Note that reflect.DeepEqual fundamentally doesn't work on incomplete types like this. They are represented as struct{}, so pointer equality doesn't really work. This program:
package main
import (
"reflect"
"unsafe"
)
//go:notinheap
type T struct {
}
var a [2]int
func main() {
x := (*T)(unsafe.Pointer(&a[0]))
y := (*T)(unsafe.Pointer(&a[1]))
println(reflect.DeepEqual(x, y))
}
Will print true after this issue is fixed, although one would probably expect it to return false. Something about incomplete types and deepequal just don't play well together.
But it shouldn't panic.
(This bug is a consequence of the fixes for issue #40954.)
This program panics with:
reflectis confused by a type that claims to be a pointer, but has no pointer bits. It has no pointer bits because pointers togo:notinheaptypes are treated as uintptrs by the compiler.More generally, we can't ever convert
*Ttounsafe.Pointer, as that's the equivalent of converting auintptr, potentially containing a bad pointer value, to a pointer. Particularly, we can't store a*Tin the data field of an interface, or inreflect.Value.ptr. We have to store them indirectly instead.This issue originally came up with the a call to
reflect.DeepEqualwhich ended up callingPointeron one of these types.Note that
reflect.DeepEqualfundamentally doesn't work on incomplete types like this. They are represented asstruct{}, so pointer equality doesn't really work. This program:Will print
trueafter this issue is fixed, although one would probably expect it to returnfalse. Something about incomplete types and deepequal just don't play well together.But it shouldn't panic.