]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: report inference instead of assignment failure when types can't...
authorRobert Griesemer <gri@golang.org>
Thu, 1 Jun 2023 22:02:54 +0000 (15:02 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 2 Jun 2023 16:28:54 +0000 (16:28 +0000)
Interface inference must only be used if we don't require exact
unification, otherwise we may infer types (that are reasonable)
but then fail with an assignment error.

Only checking if exact is set for defined (named) types is not
sufficient, we must also check outside. Oversight.

Fixes #60562.

Change-Id: I208a74bf7ed80bcb976ba9cc172715c83f9e3d0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/499996
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/unify.go
src/go/types/unify.go
src/internal/types/testdata/fixedbugs/issue60562.go [new file with mode: 0644]

index 3f54a2c2f2267f4bb723bdeae8d6e11fa9e1c748..5c7d24aff6f0a8c68387a1801decc83416e96f92 100644 (file)
@@ -387,12 +387,12 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                emode |= exact
        }
 
-       // If EnableInterfaceInference is set and both types are interfaces, one
-       // interface must have a subset of the methods of the other and corresponding
-       // method signatures must unify.
+       // If EnableInterfaceInference is set and we don't require exact unification,
+       // if both types are interfaces, one interface must have a subset of the
+       // methods of the other and corresponding method signatures must unify.
        // If only one type is an interface, all its methods must be present in the
        // other type and corresponding method signatures must unify.
-       if enableInterfaceInference {
+       if enableInterfaceInference && mode&exact == 0 {
                xi, _ := x.(*Interface)
                yi, _ := y.(*Interface)
                // If we have two interfaces, check the type terms for equivalence,
@@ -565,7 +565,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                }
 
        case *Interface:
-               assert(!enableInterfaceInference) // handled before this switch
+               assert(!enableInterfaceInference || mode&exact != 0) // handled before this switch
 
                // Two interface types unify if they have the same set of methods with
                // the same names, and corresponding function types unify.
index 217356f13ebe6f2daa24a928f5447b085daa51ef..00373e01f260c52f5f1776a161f1424b8212c0e8 100644 (file)
@@ -389,12 +389,12 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                emode |= exact
        }
 
-       // If EnableInterfaceInference is set and both types are interfaces, one
-       // interface must have a subset of the methods of the other and corresponding
-       // method signatures must unify.
+       // If EnableInterfaceInference is set and we don't require exact unification,
+       // if both types are interfaces, one interface must have a subset of the
+       // methods of the other and corresponding method signatures must unify.
        // If only one type is an interface, all its methods must be present in the
        // other type and corresponding method signatures must unify.
-       if enableInterfaceInference {
+       if enableInterfaceInference && mode&exact == 0 {
                xi, _ := x.(*Interface)
                yi, _ := y.(*Interface)
                // If we have two interfaces, check the type terms for equivalence,
@@ -567,7 +567,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                }
 
        case *Interface:
-               assert(!enableInterfaceInference) // handled before this switch
+               assert(!enableInterfaceInference || mode&exact != 0) // handled before this switch
 
                // Two interface types unify if they have the same set of methods with
                // the same names, and corresponding function types unify.
diff --git a/src/internal/types/testdata/fixedbugs/issue60562.go b/src/internal/types/testdata/fixedbugs/issue60562.go
new file mode 100644 (file)
index 0000000..b95fd9f
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2023 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
+
+type S[T any] struct{}
+
+func (S[T]) m(T) {}
+
+func f0[T any](chan S[T]) {}
+
+func _() {
+       var x chan interface{ m(int) }
+       f0(x /* ERROR "type chan interface{m(int)} of x does not match chan S[T] (cannot infer T)" */)
+}
+
+// variants of the theme
+
+func f1[T any]([]S[T]) {}
+
+func _() {
+       var x []interface{ m(int) }
+       f1(x /* ERROR "type []interface{m(int)} of x does not match []S[T] (cannot infer T)" */)
+}
+
+type I[T any] interface {
+       m(T)
+}
+
+func f2[T any](func(I[T])) {}
+
+func _() {
+       var x func(interface{ m(int) })
+       f2(x /* ERROR "type func(interface{m(int)}) of x does not match func(I[T]) (cannot infer T)" */)
+}
+
+func f3[T any](func(I[T])) {}
+
+func _() {
+       var x func(I[int])
+       f3(x) // but this is correct: I[T] and I[int] can be made identical with T == int
+}
+
+func f4[T any]([10]I[T]) {}
+
+func _() {
+       var x [10]interface{ I[int] }
+       f4(x /* ERROR "type [10]interface{I[int]} of x does not match [10]I[T] (cannot infer T)" */)
+}
+
+func f5[T any](I[T]) {}
+
+func _() {
+       var x interface {
+               m(int)
+               n()
+       }
+       // TODO(gri) this should not produce an error (go.dev/issues/60564)
+       f5(x /* ERROR "type interface{m(int); n()} of x does not match inferred type I[int] for I[T]" */)
+       f5[int](x) // ok
+}