From: Robert Griesemer Date: Sun, 31 Oct 2021 17:37:15 +0000 (-0700) Subject: cmd/compile/internal/types2: differently named types are not assignable X-Git-Tag: go1.18beta1~646 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d2b512160e4fc9dd69947ad9566c2dab1a135694;p=gostls13.git 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 Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go index e4db0554f3..2f85802701 100644 --- a/src/cmd/compile/internal/types2/operand.go +++ b/src/cmd/compile/internal/types2/operand.go @@ -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,46 +332,48 @@ 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 - }) - }) - 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) + // 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 (in %s) to %s", V.typ, Vp, T) + errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp) return false } return true }) - case Tp != 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 = Tp.is(func(T *term) bool { - ok, code = x.assignableTo(check, T.typ, reason) + 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 { - errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp) + errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T) return false } return true }) + return ok, code } - return ok, code + return false, _IncompatibleAssign } // kind2tok translates syntax.LitKinds into token.Tokens. diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2 new file mode 100644 index 0000000000..524a0cbae3 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2 @@ -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)) +} diff --git a/src/cmd/compile/internal/types2/testdata/spec/assignability.go2 b/src/cmd/compile/internal/types2/testdata/spec/assignability.go2 index ab8f9c08b2..fb28358bbb 100644 --- a/src/cmd/compile/internal/types2/testdata/spec/assignability.go2 +++ b/src/cmd/compile/internal/types2/testdata/spec/assignability.go2 @@ -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