cmd/compile/internal/types2: differently named types are not assignable

When checking assignability, a value of a named type (incl. a type parameter)
can never be assigned to a variable of a differently named type. Specifically,
if the types are two differently named type parameters, then values of one are
never assignable to variables of the other.

This CL clarifies the assignabiliy rules and simplifies the implementation.

Fixes #49242.

Change-Id: Id72a2c9bed5cdb726855e7a707137db1009e7953
Reviewed-on: https://go-review.googlesource.com/c/go/+/360274
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-10-31 10:37:15 -07:00
parent 82f902ae8e
commit d2b512160e
3 changed files with 95 additions and 46 deletions

View File

@ -317,19 +317,11 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
}
}
// common case: if we don't have type parameters, we're done
// optimization: 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...)
@ -340,25 +332,36 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
}
}
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
})
// x's type V is not a named type and T is a type parameter, and
// x is assignable to each specific type in T's type set.
if !hasName(V) && Tp != nil {
ok := false
code := _IncompatibleAssign
Tp.is(func(T *term) bool {
if T == nil {
return false // no specific types
}
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
})
case Vp != nil:
return ok, code
}
// x's type V is a type parameter and T is not a named type,
// and values x' of each specific type in V's type set are
// assignable to T.
if Vp != nil && !hasName(T) {
x := *x // don't clobber outer x
ok = Vp.is(func(V *term) bool {
ok := false
code := _IncompatibleAssign
Vp.is(func(V *term) bool {
if V == nil {
return false // no specific types
}
x.typ = V.typ
ok, code = x.assignableTo(check, T, reason)
if !ok {
@ -367,19 +370,10 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
}
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
}
return ok, code
return false, _IncompatibleAssign
}
// kind2tok translates syntax.LitKinds into token.Tokens.

View File

@ -0,0 +1,27 @@
// 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
func _[P int](x P) int {
return x // ERROR cannot use x .* as int value in return statement
}
func _[P int]() int {
return P /* ERROR cannot use P\(1\) .* as int value in return statement */ (1)
}
func _[P int](x int) P {
return x // ERROR cannot use x .* as P value in return statement
}
func _[P, Q any](x P) Q {
return x // ERROR cannot use x .* as Q value in return statement
}
// test case from issue
func F[G interface{ uint }]() int {
f := func(uint) int { return 0 }
return f(G /* ERROR cannot use G\(1\) .* as uint value in argument to f */ (1))
}

View File

@ -109,20 +109,48 @@ func _[
)
var (
_ _CC = C
_ _SC = C
_ _RC = C
_ _CC = C // ERROR cannot use C .* as _CC value
_ _SC = C // ERROR cannot use C .* as _SC value
_ _RC = C // ERROR cannot use C .* as _RC value
_ CC = _CC(nil)
_ SC = _CC(nil)
_ RC = _CC(nil)
_ CC = _CC /* ERROR cannot use _CC\(nil\) .* as CC value */ (nil)
_ SC = _CC /* ERROR cannot use _CC\(nil\) .* as SC value */ (nil)
_ RC = _CC /* ERROR cannot use _CC\(nil\) .* as RC value */ (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
_ CC = C // ERROR cannot use C .* as CC value
_ SC = C // ERROR cannot use C .* as SC value
_ RC = C // ERROR cannot use C .* as RC value
)
}
// "x's type V is not a named type and T is a type parameter, and x is assignable to each specific type in T's type set."
func _[
TP0 any,
TP1 ~_Chan,
TP2 ~chan int | ~chan byte,
]() {
var (
_ TP0 = c // ERROR cannot use c .* as TP0 value
_ TP0 = C // ERROR cannot use C .* as TP0 value
_ TP1 = c
_ TP1 = C // ERROR cannot use C .* as TP1 value
_ TP2 = c // ERROR .* cannot assign chan int to chan byte
)
}
// "x's type V is a type parameter and T is not a named type, and values x' of each specific type in V's type set are assignable to T."
func _[
TP0 Interface,
TP1 ~_Chan,
TP2 ~chan int | ~chan byte,
](X0 TP0, X1 TP1, X2 TP2) {
i = X0
I = X0
c = X1
C = X1 // ERROR cannot use X1 .* as Chan value
c = X2 // ERROR .* cannot assign chan byte \(in TP2\) to chan int
}
// "x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type"
func _[TP Interface](X TP) {
b = nil // ERROR cannot use untyped nil