]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/types2: fix conversions of constants to type parameter
authorRobert Griesemer <gri@golang.org>
Mon, 1 Nov 2021 19:14:25 +0000 (12:14 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 2 Nov 2021 16:11:43 +0000 (16:11 +0000)
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>
src/cmd/compile/internal/types2/conversions.go
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2 [new file with mode: 0644]
src/cmd/compile/internal/types2/testdata/spec/conversions.go2

index bc33b3a44b24deebd3824c89330784d705b95fb1..0e26a73cf8d67deef730ce222b585e6bf01ee6f6 100644 (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 {
index ae7b205e53dac4a6550091a08c21c757a24be6b3..eb5ec9f3fb0b999753b32807774e610848146653 100644 (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
index 380fb6489cf0adc343d93efa00393f7258e3a5c0..6d93a8a2273703c63fc0a7f8a83dea4206ba0c17 100644 (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)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2
new file mode 100644 (file)
index 0000000..3f25e0e
--- /dev/null
@@ -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 */))
+}
index 0acd2762a1d7c15d43c1f2cbc2abc7b9a613c9b8..942d9c0f6f0ddc3b1fad1c1381ca2a9146d5a594 100644 (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