mirror of
https://github.com/golang/go.git
synced 2025-10-19 11:03:18 +00:00
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>
This commit is contained in:
parent
e81c624656
commit
f1e6ae2f6f
2 changed files with 41 additions and 36 deletions
|
@ -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) {
|
||||
|
|
|
@ -1514,46 +1514,46 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||
}
|
||||
|
||||
typ := abi.TypeFor[T]()
|
||||
|
||||
// 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() {
|
||||
// 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
|
||||
}
|
||||
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
if v.flag&flagIndir == 0 {
|
||||
return *(*T)(unsafe.Pointer(&v.ptr)), true
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue