From: Robert Griesemer Date: Thu, 14 Oct 2021 17:04:10 +0000 (-0700) Subject: cmd/compile/internal/types2: implement generic conversions X-Git-Tag: go1.18beta1~831 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4cc6a919077f27d9255536d6539118e65c1650a2;p=gostls13.git cmd/compile/internal/types2: implement generic conversions Fixes #47150. Change-Id: I7531ca5917d4e52ca0b9211d6f2114495b19ba09 Reviewed-on: https://go-review.googlesource.com/c/go/+/356010 Trust: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go index 6c26a4c446..8cd74b8f9a 100644 --- a/src/cmd/compile/internal/types2/conversions.go +++ b/src/cmd/compile/internal/types2/conversions.go @@ -89,6 +89,39 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool { return true } + // TODO(gri) consider passing under(x.typ), under(T) into convertibleToImpl (optimization) + Vp, _ := under(x.typ).(*TypeParam) + Tp, _ := under(T).(*TypeParam) + + // generic cases + // (generic operands cannot be constants, so we can ignore x.val) + switch { + case Vp != nil && Tp != nil: + x := *x // don't modify outer x + return Vp.underIs(func(V Type) bool { + x.typ = V + return Tp.underIs(func(T Type) bool { + return x.convertibleToImpl(check, T) + }) + }) + case Vp != nil: + x := *x // don't modify outer x + return Vp.underIs(func(V Type) bool { + x.typ = V + return x.convertibleToImpl(check, T) + }) + case Tp != nil: + return Tp.underIs(func(T Type) bool { + return x.convertibleToImpl(check, T) + }) + } + + // non-generic case + return x.convertibleToImpl(check, T) +} + +// convertibleToImpl should only be called by convertibleTo +func (x *operand) convertibleToImpl(check *Checker, T Type) bool { // "x's type and T have identical underlying types if tags are ignored" V := x.typ Vu := under(V) diff --git a/src/cmd/compile/internal/types2/testdata/examples/conversions.go2 b/src/cmd/compile/internal/types2/testdata/examples/conversions.go2 new file mode 100644 index 0000000000..7e9e9745bb --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/examples/conversions.go2 @@ -0,0 +1,130 @@ +// 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 conversions + +import "unsafe" + +// "x is assignable to T" +// - tested via assignability tests + +// "x's type and T have identical underlying types if tags are ignored" + +func _[X ~int, T ~int](x X) T { return T(x) } +func _[X struct{f int "foo"}, T struct{f int "bar"}](x X) T { return T(x) } + +type Foo struct{f int "foo"} +type Bar struct{f int "bar"} +type Far struct{f float64 } + +func _[X Foo, T Bar](x X) T { return T(x) } +func _[X Foo|Bar, T Bar](x X) T { return T(x) } +func _[X Foo, T Foo|Bar](x X) T { return T(x) } +func _[X Foo, T Far](x X) T { return T(x /* ERROR cannot convert */ ) } + +// "x's type and T are unnamed pointer types and their pointer base types +// have identical underlying types if tags are ignored" + +func _[X ~*Foo, T ~*Bar](x X) T { return T(x) } +func _[X ~*Foo|~*Bar, T ~*Bar](x X) T { return T(x) } +func _[X ~*Foo, T ~*Foo|~*Bar](x X) T { return T(x) } +func _[X ~*Foo, T ~*Far](x X) T { return T(x /* ERROR cannot convert */ ) } + +// "x's type and T are both integer or floating point types" + +func _[X Integer, T Integer](x X) T { return T(x) } +func _[X Unsigned, T Integer](x X) T { return T(x) } +func _[X Float, T Integer](x X) T { return T(x) } + +func _[X Integer, T Unsigned](x X) T { return T(x) } +func _[X Unsigned, T Unsigned](x X) T { return T(x) } +func _[X Float, T Unsigned](x X) T { return T(x) } + +func _[X Integer, T Float](x X) T { return T(x) } +func _[X Unsigned, T Float](x X) T { return T(x) } +func _[X Float, T Float](x X) T { return T(x) } + +func _[X, T Integer|Unsigned|Float](x X) T { return T(x) } +func _[X, T Integer|~string](x X) T { return T(x /* ERROR cannot convert */ ) } + +// "x's type and T are both complex types" + +func _[X, T Complex](x X) T { return T(x) } +func _[X, T Float|Complex](x X) T { return T(x /* ERROR cannot convert */ ) } + +// "x is an integer or a slice of bytes or runes and T is a string type" + +type myInt int +type myString string + +func _[T ~string](x int) T { return T(x) } +func _[T ~string](x myInt) T { return T(x) } +func _[X Integer](x X) string { return string(x) } +func _[X Integer](x X) myString { return myString(x) } +func _[X Integer](x X) *string { return (*string)(x /* ERROR cannot convert */ ) } + +func _[T ~string](x []byte) T { return T(x) } +func _[T ~string](x []rune) T { return T(x) } +func _[X ~[]byte, T ~string](x X) T { return T(x) } +func _[X ~[]rune, T ~string](x X) T { return T(x) } +func _[X Integer|~[]byte|~[]rune, T ~string](x X) T { return T(x) } +func _[X Integer|~[]byte|~[]rune, T ~*string](x X) T { return T(x /* ERROR cannot convert */ ) } + +// "x is a string and T is a slice of bytes or runes" + +func _[T ~[]byte](x string) T { return T(x) } +func _[T ~[]rune](x string) T { return T(x) } +func _[T ~[]rune](x *string) T { return T(x /* ERROR cannot convert */ ) } + +func _[X ~string, T ~[]byte](x X) T { return T(x) } +func _[X ~string, T ~[]rune](x X) T { return T(x) } +func _[X ~string, T ~[]byte|~[]rune](x X) T { return T(x) } +func _[X ~*string, T ~[]byte|~[]rune](x X) T { return T(x /* ERROR cannot convert */ ) } + +// package unsafe: +// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer" + +type myUintptr uintptr + +func _[X ~uintptr](x X) unsafe.Pointer { return unsafe.Pointer(x) } +func _[T unsafe.Pointer](x myUintptr) T { return T(x) } +func _[T unsafe.Pointer](x int64) T { return T(x /* ERROR cannot convert */ ) } + +// "and vice versa" + +func _[T ~uintptr](x unsafe.Pointer) T { return T(x) } +func _[X unsafe.Pointer](x X) uintptr { return uintptr(x) } +func _[X unsafe.Pointer](x X) myUintptr { return myUintptr(x) } +func _[X unsafe.Pointer](x X) int64 { return int64(x /* ERROR cannot convert */ ) } + +// "x is a slice, T is a pointer-to-array type, +// and the slice and array types have identical element types." + +func _[X ~[]E, T ~*[10]E, E any](x X) T { return T(x) } +func _[X ~[]E, T ~[10]E, E any](x X) T { return T(x /* ERROR cannot convert */ ) } + +// ---------------------------------------------------------------------------- +// The following declarations can be replaced by the exported types of the +// constraints package once all builders support importing interfaces with +// type constraints. + +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} + +type Integer interface { + Signed | Unsigned +} + +type Float interface { + ~float32 | ~float64 +} + +type Complex interface { + ~complex64 | ~complex128 +}