diff options
| author | Joe Tsai <joetsai@digital-static.net> | 2025-06-27 10:59:44 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-06-27 16:34:47 -0700 |
| commit | f1e6ae2f6f5424f9f5c6dc915866d4f457c1483e (patch) | |
| tree | cdbb82a774256a41b0ddbd38651d109b0b1c49df | |
| parent | e81c624656e415626c7ac3a97768f5c2717979a4 (diff) | |
| download | go-f1e6ae2f6f5424f9f5c6dc915866d4f457c1483e.tar.xz | |
reflect: fix TypeAssert on nil interface values
In the Go language a type assertion of a nil interface value
will always report false:
var err error
v, ok := err.(error) // always reports (nil, false)
Consequently, assertion on a reflect.Value.Interface()
will also report false:
var err error
rv := ValueOf(&err).Elem()
v, ok := rv.Interface().(error) // reports (nil, false)
However, prior to this change, a TypeAssert would report true:
var err error
rv := ValueOf(&err).Elem()
v, ok := TypeAssert[error](rv) // reports (nil, true)
when it should report false.
This fixes TypeAssert to match the Go language by
pushing the typ != v.typ check to the very end after
we have validated that neither v nor T are interface kinds.
Fixes #74404
Change-Id: Ie14d5cf18c8370c3e27ce4bdf4570c89519d8a16
Reviewed-on: https://go-review.googlesource.com/c/go/+/684675
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Mateusz Poliwczak <mpoliwczak34@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
| -rw-r--r-- | src/reflect/all_test.go | 5 | ||||
| -rw-r--r-- | src/reflect/value.go | 68 |
2 files changed, 39 insertions, 34 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index fb1a29d060..cd3e306a57 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -8719,6 +8719,11 @@ func TestTypeAssert(t *testing.T) { testTypeAssert(t, any(int(1)), int(1), true) testTypeAssert(t, any(int(1)), byte(0), false) testTypeAssert(t, fmt.Stringer(vv), vv, true) + + testTypeAssert(t, any(nil), any(nil), false) + testTypeAssert(t, any(nil), error(nil), false) + testTypeAssert(t, error(nil), any(nil), false) + testTypeAssert(t, error(nil), error(nil), false) } func testTypeAssert[T comparable, V any](t *testing.T, val V, wantVal T, wantOk bool) { diff --git a/src/reflect/value.go b/src/reflect/value.go index 68b97e9229..ffdf789648 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1514,46 +1514,46 @@ func TypeAssert[T any](v Value) (T, bool) { } typ := abi.TypeFor[T]() - if typ != v.typ() { - // We can't just return false here: - // - // var zero T - // return zero, false - // - // since this function should work in the same manner as v.Interface().(T) does. - // Thus we have to handle two cases specially. - // Return the element inside the interface. - // - // T is a concrete type and v is an interface. For example: - // - // var v any = int(1) - // val := ValueOf(&v).Elem() - // TypeAssert[int](val) == val.Interface().(int) - // - // T is a interface and v is an interface, but the iface types are different. For example: - // - // var v any = &someError{} - // val := ValueOf(&v).Elem() - // TypeAssert[error](val) == val.Interface().(error) - if v.kind() == Interface { - v, ok := packIfaceValueIntoEmptyIface(v).(T) - return v, ok - } + // If v is an interface, return the element inside the interface. + // + // T is a concrete type and v is an interface. For example: + // + // var v any = int(1) + // val := ValueOf(&v).Elem() + // TypeAssert[int](val) == val.Interface().(int) + // + // T is a interface and v is a non-nil interface value. For example: + // + // var v any = &someError{} + // val := ValueOf(&v).Elem() + // TypeAssert[error](val) == val.Interface().(error) + // + // T is a interface and v is a nil interface value. For example: + // + // var v error = nil + // val := ValueOf(&v).Elem() + // TypeAssert[error](val) == val.Interface().(error) + if v.kind() == Interface { + v, ok := packIfaceValueIntoEmptyIface(v).(T) + return v, ok + } - // T is an interface, v is a concrete type. For example: - // - // TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any) - // TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error) - if typ.Kind() == abi.Interface { - v, ok := packEface(v).(T) - return v, ok - } + // If T is an interface and v is a concrete type. For example: + // + // TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any) + // TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error) + if typ.Kind() == abi.Interface { + v, ok := packEface(v).(T) + return v, ok + } + // Both v and T must be concrete types. + // The only way for an type-assertion to match is if the types are equal. + if typ != v.typ() { var zero T return zero, false } - if v.flag&flagIndir == 0 { return *(*T)(unsafe.Pointer(&v.ptr)), true } |
