mirror of
https://github.com/golang/go
synced 2024-11-17 20:54:48 -07:00
cmd/compile/internal/types2: fix conversions of constants to type parameter
When converting a constant to a type parameter, the result is never constant (type parameters are not constant types), but we still need to verfy that the constant is representable by each specific type in the type set of the type parameter. Fixes #49247. Change-Id: I460983c7070b33baadce25dd23210e10930cfb2c Reviewed-on: https://go-review.googlesource.com/c/go/+/360396 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
c3cb1ecc36
commit
d39c873966
@ -16,26 +16,45 @@ 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 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 {
|
||||
|
@ -743,6 +743,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
|
||||
|
@ -80,6 +80,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)
|
||||
|
20
src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2
vendored
Normal file
20
src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2
vendored
Normal 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 */))
|
||||
}
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user