Skip to content

cmd/compile, runtime, reflect: pointers to go:notinheap types must be stored indirectly in interfaces #42076

@randall77

Description

@randall77

(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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions