mirror of
https://github.com/golang/go
synced 2024-11-17 14:04:48 -07:00
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 <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
2be5b84665
commit
4cc6a91907
@ -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)
|
||||
|
130
src/cmd/compile/internal/types2/testdata/examples/conversions.go2
vendored
Normal file
130
src/cmd/compile/internal/types2/testdata/examples/conversions.go2
vendored
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user