1
0
mirror of https://github.com/golang/go synced 2024-11-23 22:30:05 -07:00

go/types: check non-generic conversions first

This is a clean port of CL 361269 to go/types.

Change-Id: I2caaf08eabdf1707ae83ec1e628fd26f21b2b8e5
Reviewed-on: https://go-review.googlesource.com/c/go/+/362616
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Findley 2021-11-09 11:14:37 -05:00
parent 81f37a72ea
commit 526b2ef0ea

View File

@ -120,64 +120,8 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
return true
}
// determine type parameter operands with specific type terms
Vp, _ := under(x.typ).(*TypeParam)
Tp, _ := under(T).(*TypeParam)
if Vp != nil && !Vp.hasTerms() {
Vp = nil
}
if Tp != nil && !Tp.hasTerms() {
Tp = nil
}
errorf := func(format string, args ...interface{}) {
if check != nil && cause != nil {
msg := check.sprintf(format, args...)
if *cause != "" {
msg += "\n\t" + *cause
}
*cause = msg
}
}
// generic cases with specific type terms
// (generic operands cannot be constants, so we can ignore x.val)
switch {
case Vp != nil && Tp != nil:
return Vp.is(func(V *term) bool {
return Tp.is(func(T *term) bool {
if !convertibleToImpl(check, V.typ, T.typ, cause) {
errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
return false
}
return true
})
})
case Vp != nil:
return Vp.is(func(V *term) bool {
if !convertibleToImpl(check, V.typ, T, cause) {
errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
return false
}
return true
})
case Tp != nil:
return Tp.is(func(T *term) bool {
if !convertibleToImpl(check, x.typ, T.typ, cause) {
errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
return false
}
return true
})
}
// non-generic case
return convertibleToImpl(check, x.typ, T, cause)
}
// convertibleToImpl should only be called by convertibleTo
func convertibleToImpl(check *Checker, V, T Type, cause *string) bool {
// "V and T have identical underlying types if tags are ignored"
V := x.typ
Vu := under(V)
Tu := under(T)
if IdenticalIgnoreTags(Vu, Tu) {
@ -241,6 +185,67 @@ func convertibleToImpl(check *Checker, V, T Type, cause *string) bool {
}
}
// optimization: if we don't have type parameters, we're done
Vp, _ := Vu.(*TypeParam)
Tp, _ := Tu.(*TypeParam)
if Vp == nil && Tp == nil {
return false
}
errorf := func(format string, args ...interface{}) {
if check != nil && cause != nil {
msg := check.sprintf(format, args...)
if *cause != "" {
msg += "\n\t" + *cause
}
*cause = msg
}
}
// generic cases with specific type terms
// (generic operands cannot be constants, so we can ignore x.val)
switch {
case Vp != nil && Tp != nil:
x := *x // don't clobber outer x
return Vp.is(func(V *term) bool {
if V == nil {
return false // no specific types
}
x.typ = V.typ
return Tp.is(func(T *term) bool {
if !x.convertibleTo(check, T.typ, cause) {
errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
return false
}
return true
})
})
case Vp != nil:
x := *x // don't clobber outer x
return Vp.is(func(V *term) bool {
if V == nil {
return false // no specific types
}
x.typ = V.typ
if !x.convertibleTo(check, T, cause) {
errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
return false
}
return true
})
case Tp != nil:
return Tp.is(func(T *term) bool {
if T == nil {
return false // no specific types
}
if !x.convertibleTo(check, T.typ, cause) {
errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
return false
}
return true
})
}
return false
}