unique: handle zero-size types

Fixes #69458

Change-Id: Ic7fda7f556522780b2819138dfc1277137398692
Reviewed-on: https://go-review.googlesource.com/c/go/+/613397
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Cuong Manh Le 2024-09-16 23:58:40 +07:00 committed by Gopher Robot
parent aa06c94054
commit 2927aa10ab
2 changed files with 14 additions and 3 deletions

View File

@ -10,9 +10,11 @@ import (
"internal/weak" "internal/weak"
"runtime" "runtime"
"sync" "sync"
_ "unsafe" "unsafe"
) )
var zero uintptr
// Handle is a globally unique identity for some value of type T. // Handle is a globally unique identity for some value of type T.
// //
// Two handles compare equal exactly if the two values used to create the handles // Two handles compare equal exactly if the two values used to create the handles
@ -32,6 +34,9 @@ func (h Handle[T]) Value() T {
func Make[T comparable](value T) Handle[T] { func Make[T comparable](value T) Handle[T] {
// Find the map for type T. // Find the map for type T.
typ := abi.TypeFor[T]() typ := abi.TypeFor[T]()
if typ.Size() == 0 {
return Handle[T]{(*T)(unsafe.Pointer(&zero))}
}
ma, ok := uniqueMaps.Load(typ) ma, ok := uniqueMaps.Load(typ)
if !ok { if !ok {
// This is a good time to initialize cleanup, since we must go through // This is a good time to initialize cleanup, since we must go through

View File

@ -31,6 +31,7 @@ type testStruct struct {
z float64 z float64
b string b string
} }
type testZeroSize struct{}
func TestHandle(t *testing.T) { func TestHandle(t *testing.T) {
testHandle(t, testString("foo")) testHandle(t, testString("foo"))
@ -45,6 +46,7 @@ func TestHandle(t *testing.T) {
}) })
testHandle(t, testStruct{0.5, "184"}) testHandle(t, testStruct{0.5, "184"})
testHandle(t, testEface("hello")) testHandle(t, testEface("hello"))
testHandle(t, testZeroSize(struct{}{}))
} }
func testHandle[T comparable](t *testing.T, value T) { func testHandle[T comparable](t *testing.T, value T) {
@ -65,15 +67,19 @@ func testHandle[T comparable](t *testing.T, value T) {
t.Error("v0 != v1") t.Error("v0 != v1")
} }
drainMaps(t) drainMaps[T](t)
checkMapsFor(t, value) checkMapsFor(t, value)
}) })
} }
// drainMaps ensures that the internal maps are drained. // drainMaps ensures that the internal maps are drained.
func drainMaps(t *testing.T) { func drainMaps[T comparable](t *testing.T) {
t.Helper() t.Helper()
if unsafe.Sizeof(*(new(T))) == 0 {
return // zero-size types are not inserted.
}
wait := make(chan struct{}, 1) wait := make(chan struct{}, 1)
// Set up a one-time notification for the next time the cleanup runs. // Set up a one-time notification for the next time the cleanup runs.