go/types: fix conversions of constants to type parameter

This is a port of both CL 360396 and CL 360796 to go/types. The latter
is added to avoid introducing an intermediate bug.

An error message was adjusted in issue49296.go2, with a TODO to switch
to the types2 error.

Change-Id: Iede294b69b4e097e53876498f039ee18667568c4
Reviewed-on: https://go-review.googlesource.com/c/go/+/360934
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-02 18:25:21 -04:00
parent a0f373ca08
commit 32d27527a6
6 changed files with 100 additions and 10 deletions

View File

@ -16,26 +16,47 @@ import (
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant_
var ok bool
var cause string
switch {
case constArg && isConstType(T):
// constant conversion (T cannot be a type parameter)
constConvertibleTo := func(T Type, val *constant.Value) bool {
switch t := asBasic(T); {
case representableConst(x.val, check, t, &x.val):
ok = true
case t == nil:
// nothing to do
case representableConst(x.val, check, t, val):
return true
case isInteger(x.typ) && isString(t):
codepoint := unicode.ReplacementChar
if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
codepoint = rune(i)
}
x.val = constant.MakeString(string(codepoint))
ok = true
if val != nil {
*val = constant.MakeString(string(codepoint))
}
return true
}
return false
}
var ok bool
var cause string
switch {
case constArg && isConstType(T):
// constant conversion
ok = constConvertibleTo(T, &x.val)
case constArg && isTypeParam(T):
// x is convertible to T if it is convertible
// to each specific type in the type set of T.
// If T's type set is empty, or if it doesn't
// have specific types, constant x cannot be
// converted.
ok = under(T).(*TypeParam).underIs(func(u Type) bool {
// t is nil if there are no specific type terms
// TODO(gri) add a cause in case of failure
return u != nil && constConvertibleTo(u, nil)
})
x.mode = value // type parameters are not constants
case x.convertibleTo(check, T, &cause):
// non-constant conversion
x.mode = value
ok = true
x.mode = value
}
if !ok {

View File

@ -681,6 +681,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion
}
case *TypeParam:
// TODO(gri) review this code - doesn't look quite right
ok := t.underIs(func(t Type) bool {
target, _, _ := check.implicitTypeAndValue(x, t)
return target != nil

View File

@ -82,6 +82,12 @@ func IsInterface(typ Type) bool {
return asInterface(typ) != nil
}
// isTypeParam reports whether typ is a type parameter.
func isTypeParam(typ Type) bool {
_, ok := under(typ).(*TypeParam)
return ok
}
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
return comparable(T, nil)

View File

@ -0,0 +1,20 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
func Add1024[T integer](s []T) {
for i, v := range s {
s[i] = v + 1024 // ERROR cannot convert 1024 \(untyped int constant\) to T
}
}
func f[T interface{ int8 }]() {
println(T(1024 /* ERROR cannot convert 1024 \(untyped int value\) to T */))
}

View File

@ -0,0 +1,21 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _[
T0 any,
T1 []int,
T2 ~float64 | ~complex128 | chan int,
]() {
// TODO(rfindley): the types2 error here is clearer.
_ = T0(nil /* ERROR cannot convert nil \(untyped nil value\) to T0 */ )
_ = T1(1 /* ERROR cannot convert 1 .* to T1 */ )
_ = T2(2 /* ERROR cannot convert 2 .* to T2 */ )
}
// test case from issue
func f[T interface{[]int}]() {
_ = T(1 /* ERROR cannot convert */ )
}

View File

@ -6,6 +6,27 @@ package conversions
import "unsafe"
// constant conversions
func _[T ~byte]() T { return 255 }
func _[T ~byte]() T { return 256 /* ERROR cannot use 256 .* as T value */ }
func _[T ~byte]() {
const _ = T /* ERROR T\(0\) .* is not constant */ (0)
var _ T = 255
var _ T = 256 // ERROR cannot use 256 .* as T value
}
func _[T ~string]() T { return T('a') }
func _[T ~int | ~string]() T { return T('a') }
func _[T ~byte | ~int | ~string]() T { return T(256 /* ERROR cannot convert 256 .* to T */ ) }
// implicit conversions never convert to string
func _[T ~string]() {
var _ string = 0 // ERROR cannot use .* as string value
var _ T = 0 // ERROR cannot use .* as T value
}
// "x is assignable to T"
// - tested via assignability tests