]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: fix TypeAssert on nil interface values
authorJoe Tsai <joetsai@digital-static.net>
Fri, 27 Jun 2025 17:59:44 +0000 (10:59 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 27 Jun 2025 23:34:47 +0000 (16:34 -0700)
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>
src/reflect/all_test.go
src/reflect/value.go

index fb1a29d0605ffe3fa22e246b23e178035d550eb3..cd3e306a5753e561dc564059b4499823ea0ca306 100644 (file)
@@ -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) {
index 68b97e922928e81f809eb685405941520830dbf2..ffdf7896482f7e189a37b724cece4bc55c146736 100644 (file)
@@ -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
-               }
-
-               // 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 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
+       }
+
+       // 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
        }