From 526b2ef0ea3a13d7e9af635918ef3ef86353f220 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Tue, 9 Nov 2021 11:14:37 -0500 Subject: [PATCH] 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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/conversions.go | 119 +++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index c171b2c8d61..f73e6a09648 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -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 }