mirror of
https://github.com/golang/go
synced 2024-11-24 07:30:10 -07:00
go/types: generalize assignability to generic types
This is a port of CL 357917 to go/types. Some error messages in assignability.go2 had to be adjusted. I left a TODO to investigate whether we should align error messages. Change-Id: Ia323ffe18bc08e82de62044f35b8b0f3edd7dc08 Reviewed-on: https://go-review.googlesource.com/c/go/+/360476 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
af8aafd570
commit
62b29b0359
@ -359,6 +359,7 @@ func TestIssue47243_TypedRHS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
|
func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
|
||||||
|
func TestSpec(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) }
|
||||||
func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) }
|
func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) }
|
||||||
func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", false) }
|
func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", false) }
|
||||||
|
|
||||||
|
@ -234,53 +234,46 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
|
|||||||
|
|
||||||
V := x.typ
|
V := x.typ
|
||||||
|
|
||||||
const debugAssignableTo = false
|
|
||||||
if debugAssignableTo && check != nil {
|
|
||||||
check.dump("V = %s", V)
|
|
||||||
check.dump("T = %s", T)
|
|
||||||
}
|
|
||||||
|
|
||||||
// x's type is identical to T
|
// x's type is identical to T
|
||||||
if Identical(V, T) {
|
if Identical(V, T) {
|
||||||
return true, 0
|
return true, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Vu := optype(V)
|
Vu := under(V)
|
||||||
Tu := optype(T)
|
Tu := under(T)
|
||||||
|
Vp, _ := Vu.(*TypeParam)
|
||||||
if debugAssignableTo && check != nil {
|
Tp, _ := Tu.(*TypeParam)
|
||||||
check.dump("Vu = %s", Vu)
|
|
||||||
check.dump("Tu = %s", Tu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// x is an untyped value representable by a value of type T.
|
// x is an untyped value representable by a value of type T.
|
||||||
if isUntyped(Vu) {
|
if isUntyped(Vu) {
|
||||||
if t, _ := under(T).(*TypeParam); t != nil {
|
assert(Vp == nil)
|
||||||
return t.is(func(t *term) bool {
|
if Tp != nil {
|
||||||
// TODO(gri) this could probably be more efficient
|
// T is a type parameter: x is assignable to T if it is
|
||||||
|
// representable by each specific type in the type set of T.
|
||||||
|
return Tp.is(func(t *term) bool {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if t.tilde {
|
// A term may be a tilde term but the underlying
|
||||||
// TODO(gri) We need to check assignability
|
// type of an untyped value doesn't change so we
|
||||||
// for the underlying type of x.
|
// don't need to do anything special.
|
||||||
}
|
newType, _, _ := check.implicitTypeAndValue(x, t.typ)
|
||||||
ok, _ := x.assignableTo(check, t.typ, reason)
|
return newType != nil
|
||||||
return ok
|
|
||||||
}), _IncompatibleAssign
|
}), _IncompatibleAssign
|
||||||
}
|
}
|
||||||
newType, _, _ := check.implicitTypeAndValue(x, Tu)
|
newType, _, _ := check.implicitTypeAndValue(x, T)
|
||||||
return newType != nil, _IncompatibleAssign
|
return newType != nil, _IncompatibleAssign
|
||||||
}
|
}
|
||||||
// Vu is typed
|
// Vu is typed
|
||||||
|
|
||||||
// x's type V and T have identical underlying types
|
// x's type V and T have identical underlying types
|
||||||
// and at least one of V or T is not a named type
|
// and at least one of V or T is not a named type
|
||||||
if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
|
// and neither is a type parameter.
|
||||||
|
if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) && Vp == nil && Tp == nil {
|
||||||
return true, 0
|
return true, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// T is an interface type and x implements T
|
// T is an interface type and x implements T and T is not a type parameter
|
||||||
if Ti, ok := Tu.(*Interface); ok {
|
if Ti, ok := Tu.(*Interface); ok {
|
||||||
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
|
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
|
||||||
if reason != nil {
|
if reason != nil {
|
||||||
@ -310,5 +303,67 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, _IncompatibleAssign
|
// common case: if we don't have type parameters, we're done
|
||||||
|
if Vp == nil && Tp == nil {
|
||||||
|
return false, _IncompatibleAssign
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine type parameter operands with specific type terms
|
||||||
|
if Vp != nil && !Vp.hasTerms() {
|
||||||
|
Vp = nil
|
||||||
|
}
|
||||||
|
if Tp != nil && !Tp.hasTerms() {
|
||||||
|
Tp = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errorf := func(format string, args ...interface{}) {
|
||||||
|
if check != nil && reason != nil {
|
||||||
|
msg := check.sprintf(format, args...)
|
||||||
|
if *reason != "" {
|
||||||
|
msg += "\n\t" + *reason
|
||||||
|
}
|
||||||
|
*reason = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
code := _IncompatibleAssign
|
||||||
|
switch {
|
||||||
|
case Vp != nil && Tp != nil:
|
||||||
|
x := *x // don't clobber outer x
|
||||||
|
ok = Vp.is(func(V *term) bool {
|
||||||
|
x.typ = V.typ
|
||||||
|
return Tp.is(func(T *term) bool {
|
||||||
|
ok, code = x.assignableTo(check, T.typ, reason)
|
||||||
|
if !ok {
|
||||||
|
errorf("cannot assign %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
case Vp != nil:
|
||||||
|
x := *x // don't clobber outer x
|
||||||
|
ok = Vp.is(func(V *term) bool {
|
||||||
|
x.typ = V.typ
|
||||||
|
ok, code = x.assignableTo(check, T, reason)
|
||||||
|
if !ok {
|
||||||
|
errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
case Tp != nil:
|
||||||
|
x := *x // don't clobber outer x
|
||||||
|
ok = Tp.is(func(T *term) bool {
|
||||||
|
ok, code = x.assignableTo(check, T.typ, reason)
|
||||||
|
if !ok {
|
||||||
|
errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok, code
|
||||||
}
|
}
|
||||||
|
236
src/go/types/testdata/spec/assignability.go2
vendored
Normal file
236
src/go/types/testdata/spec/assignability.go2
vendored
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// 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 assignability
|
||||||
|
|
||||||
|
// See the end of this package for the declarations
|
||||||
|
// of the types and variables used in these tests.
|
||||||
|
|
||||||
|
// "x's type is identical to T"
|
||||||
|
func _[TP any](X TP) {
|
||||||
|
b = b
|
||||||
|
a = a
|
||||||
|
l = l
|
||||||
|
s = s
|
||||||
|
p = p
|
||||||
|
f = f
|
||||||
|
i = i
|
||||||
|
m = m
|
||||||
|
c = c
|
||||||
|
d = d
|
||||||
|
|
||||||
|
B = B
|
||||||
|
A = A
|
||||||
|
L = L
|
||||||
|
S = S
|
||||||
|
P = P
|
||||||
|
F = F
|
||||||
|
I = I
|
||||||
|
M = M
|
||||||
|
C = C
|
||||||
|
D = D
|
||||||
|
X = X
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type V and T have identical underlying types and at least one
|
||||||
|
// of V or T is not a defined type and neither is a type parameter"
|
||||||
|
func _[TP1, TP2 Interface](X1 TP1, X2 TP2) {
|
||||||
|
b = B // ERROR cannot use B .* as int value
|
||||||
|
a = A
|
||||||
|
l = L
|
||||||
|
s = S
|
||||||
|
p = P
|
||||||
|
f = F
|
||||||
|
i = I
|
||||||
|
m = M
|
||||||
|
c = C
|
||||||
|
d = D
|
||||||
|
|
||||||
|
B = b // ERROR cannot use b .* as Basic value
|
||||||
|
A = a
|
||||||
|
L = l
|
||||||
|
S = s
|
||||||
|
P = p
|
||||||
|
F = f
|
||||||
|
I = i
|
||||||
|
M = m
|
||||||
|
C = c
|
||||||
|
D = d
|
||||||
|
X1 = i // ERROR cannot use i .* as TP1 value
|
||||||
|
X1 = X2 // ERROR cannot use X2 .* as TP1 value
|
||||||
|
}
|
||||||
|
|
||||||
|
// "T is an interface type and x implements T and T is not a type parameter"
|
||||||
|
func _[TP Interface](X TP) {
|
||||||
|
i = d // ERROR missing method m
|
||||||
|
i = D
|
||||||
|
i = X
|
||||||
|
X = i // ERROR cannot use i .* as TP value
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type"
|
||||||
|
type (
|
||||||
|
_SendChan = chan<- int
|
||||||
|
_RecvChan = <-chan int
|
||||||
|
|
||||||
|
SendChan _SendChan
|
||||||
|
RecvChan _RecvChan
|
||||||
|
)
|
||||||
|
|
||||||
|
func _[
|
||||||
|
_CC ~_Chan,
|
||||||
|
_SC ~_SendChan,
|
||||||
|
_RC ~_RecvChan,
|
||||||
|
|
||||||
|
CC Chan,
|
||||||
|
SC SendChan,
|
||||||
|
RC RecvChan,
|
||||||
|
]() {
|
||||||
|
var (
|
||||||
|
_ _SendChan = c
|
||||||
|
_ _RecvChan = c
|
||||||
|
_ _Chan = c
|
||||||
|
|
||||||
|
_ _SendChan = C
|
||||||
|
_ _RecvChan = C
|
||||||
|
_ _Chan = C
|
||||||
|
|
||||||
|
_ SendChan = c
|
||||||
|
_ RecvChan = c
|
||||||
|
_ Chan = c
|
||||||
|
|
||||||
|
_ SendChan = C // ERROR cannot use C .* as SendChan value
|
||||||
|
_ RecvChan = C // ERROR cannot use C .* as RecvChan value
|
||||||
|
_ Chan = C
|
||||||
|
_ Chan = make /* ERROR cannot use make\(chan Basic\) .* as Chan value */ (chan Basic)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ _CC = C
|
||||||
|
_ _SC = C
|
||||||
|
_ _RC = C
|
||||||
|
|
||||||
|
_ CC = _CC(nil)
|
||||||
|
_ SC = _CC(nil)
|
||||||
|
_ RC = _CC(nil)
|
||||||
|
|
||||||
|
_ CC = C
|
||||||
|
_ SC = C // ERROR cannot use C .* as SC value .* cannot assign Chan to SendChan
|
||||||
|
_ RC = C // ERROR cannot use C .* as RC value .* cannot assign Chan to RecvChan
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type"
|
||||||
|
// TODO(rfindley) error messages about untyped nil diverge from types2 here.
|
||||||
|
// Consider aligning them.
|
||||||
|
func _[TP Interface](X TP) {
|
||||||
|
b = nil // ERROR cannot use.*untyped nil
|
||||||
|
a = nil // ERROR cannot use.*untyped nil
|
||||||
|
l = nil
|
||||||
|
s = nil // ERROR cannot use.*untyped nil
|
||||||
|
p = nil
|
||||||
|
f = nil
|
||||||
|
i = nil
|
||||||
|
m = nil
|
||||||
|
c = nil
|
||||||
|
d = nil // ERROR cannot use.*untyped nil
|
||||||
|
|
||||||
|
B = nil // ERROR cannot use.*untyped nil
|
||||||
|
A = nil // ERROR cannot use.*untyped nil
|
||||||
|
L = nil
|
||||||
|
S = nil // ERROR cannot use.*untyped nil
|
||||||
|
P = nil
|
||||||
|
F = nil
|
||||||
|
I = nil
|
||||||
|
M = nil
|
||||||
|
C = nil
|
||||||
|
D = nil // ERROR cannot use.*untyped nil
|
||||||
|
X = nil // ERROR cannot use.*untyped nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is an untyped constant representable by a value of type T"
|
||||||
|
func _[
|
||||||
|
Int8 ~int8,
|
||||||
|
Int16 ~int16,
|
||||||
|
Int32 ~int32,
|
||||||
|
Int64 ~int64,
|
||||||
|
Int8_16 ~int8 | ~int16,
|
||||||
|
](
|
||||||
|
i8 Int8,
|
||||||
|
i16 Int16,
|
||||||
|
i32 Int32,
|
||||||
|
i64 Int64,
|
||||||
|
i8_16 Int8_16,
|
||||||
|
) {
|
||||||
|
b = 42
|
||||||
|
b = 42.0
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
i8 = -1 << 7
|
||||||
|
i8 = 1<<7 - 1
|
||||||
|
i16 = -1 << 15
|
||||||
|
i16 = 1<<15 - 1
|
||||||
|
i32 = -1 << 31
|
||||||
|
i32 = 1<<31 - 1
|
||||||
|
i64 = -1 << 63
|
||||||
|
i64 = 1<<63 - 1
|
||||||
|
|
||||||
|
i8_16 = -1 << 7
|
||||||
|
i8_16 = 1<<7 - 1
|
||||||
|
i8_16 = - /* ERROR cannot use .* as Int8_16 */ 1 << 15
|
||||||
|
i8_16 = 1 /* ERROR cannot use .* as Int8_16 */ <<15 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// proto-types for tests
|
||||||
|
|
||||||
|
type (
|
||||||
|
_Basic = int
|
||||||
|
_Array = [10]int
|
||||||
|
_Slice = []int
|
||||||
|
_Struct = struct{ f int }
|
||||||
|
_Pointer = *int
|
||||||
|
_Func = func(x int) string
|
||||||
|
_Interface = interface{ m() int }
|
||||||
|
_Map = map[string]int
|
||||||
|
_Chan = chan int
|
||||||
|
|
||||||
|
Basic _Basic
|
||||||
|
Array _Array
|
||||||
|
Slice _Slice
|
||||||
|
Struct _Struct
|
||||||
|
Pointer _Pointer
|
||||||
|
Func _Func
|
||||||
|
Interface _Interface
|
||||||
|
Map _Map
|
||||||
|
Chan _Chan
|
||||||
|
Defined _Struct
|
||||||
|
)
|
||||||
|
|
||||||
|
func (Defined) m() int
|
||||||
|
|
||||||
|
// proto-variables for tests
|
||||||
|
|
||||||
|
var (
|
||||||
|
b _Basic
|
||||||
|
a _Array
|
||||||
|
l _Slice
|
||||||
|
s _Struct
|
||||||
|
p _Pointer
|
||||||
|
f _Func
|
||||||
|
i _Interface
|
||||||
|
m _Map
|
||||||
|
c _Chan
|
||||||
|
d _Struct
|
||||||
|
|
||||||
|
B Basic
|
||||||
|
A Array
|
||||||
|
L Slice
|
||||||
|
S Struct
|
||||||
|
P Pointer
|
||||||
|
F Func
|
||||||
|
I Interface
|
||||||
|
M Map
|
||||||
|
C Chan
|
||||||
|
D Defined
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user