1
0
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:
Robert Griesemer 2021-11-01 12:14:25 -07:00
parent c3cb1ecc36
commit d39c873966
5 changed files with 77 additions and 10 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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)

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

@ -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