Skip to content

Commit 1dd24d8

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: don't infer type argument for unused parameter in interfaces
Two interface types that are assignable don't have to be identical; specifically, if they are defined types, they can be different defined types. If those defined types specify type parameters which are never used, do not infer a type argument based on the instantiation of a matching defined type. Adjusted three existing tests where we inferred type arguments incorrectly. Fixes #60377. Change-Id: I91fb207235424b3cbc42b5fd93eee619e7541cb7 Reviewed-on: https://go-review.googlesource.com/c/go/+/498315 Auto-Submit: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent f9d114d commit 1dd24d8

File tree

6 files changed

+127
-77
lines changed

6 files changed

+127
-77
lines changed

src/cmd/compile/internal/types2/unify.go

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -613,45 +613,23 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
613613
}
614614

615615
case *Named:
616-
// Two named types unify if their type names originate
617-
// in the same type declaration. If they are instantiated,
618-
// their type argument lists must unify.
616+
// Two named non-interface types unify if their type names originate
617+
// in the same type declaration. If they are instantiated, their type
618+
// argument lists must unify.
619+
// If one or both named types are interfaces, the types unify if the
620+
// respective methods unify (per the rules for interface unification).
619621
if y, ok := y.(*Named); ok {
620-
sameOrig := indenticalOrigin(x, y)
621622
if enableInterfaceInference {
622-
xu := x.under()
623-
yu := y.under()
624-
xi, _ := xu.(*Interface)
625-
yi, _ := yu.(*Interface)
626-
// If one or both defined types are interfaces, use interface unification,
627-
// unless they originated in the same type declaration.
628-
if xi != nil && yi != nil {
629-
// If both interfaces originate in the same declaration,
630-
// their methods unify if the type parameters unify.
631-
// Unify the type parameters rather than the methods in
632-
// case the type parameters are not used in the methods
633-
// (and to preserve existing behavior in this case).
634-
if sameOrig {
635-
xargs := x.TypeArgs().list()
636-
yargs := y.TypeArgs().list()
637-
assert(len(xargs) == len(yargs))
638-
for i, xarg := range xargs {
639-
if !u.nify(xarg, yargs[i], p) {
640-
return false
641-
}
642-
}
643-
return true
644-
}
645-
return u.nify(xu, yu, p)
646-
}
647-
// We don't have two interfaces. If we have one, make sure it's in xi.
648-
if yi != nil {
649-
xi = yi
650-
y = x
651-
}
652-
// If xi is an interface, use interface unification.
653-
if xi != nil {
623+
xi, _ := x.under().(*Interface)
624+
yi, _ := y.under().(*Interface)
625+
// If one or both of x and y are interfaces, use interface unification.
626+
switch {
627+
case xi != nil && yi != nil:
628+
return u.nify(xi, yi, p)
629+
case xi != nil:
654630
return u.nify(xi, y, p)
631+
case yi != nil:
632+
return u.nify(x, yi, p)
655633
}
656634
// In all other cases, the type arguments and origins must match.
657635
}
@@ -669,7 +647,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
669647
return false
670648
}
671649
}
672-
return sameOrig
650+
return indenticalOrigin(x, y)
673651
}
674652

675653
case *TypeParam:

src/go/types/unify.go

Lines changed: 15 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/internal/types/testdata/fixedbugs/issue49541.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func _[A any](s S /* ERROR "got 1 arguments but 2 type parameters" */ [A]) {
2222
// another test case from the issue
2323

2424
func _() {
25-
X(Interface[*F /* ERROR "got 1 arguments but 2 type parameters" */ [string]](Impl{}))
25+
X /* ERROR "cannot infer Q" */ (Interface[*F /* ERROR "got 1 arguments but 2 type parameters" */ [string]](Impl{}))
2626
}
2727

2828
func X[Q Qer](fs Interface[Q]) {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
// The type parameter P is not used in interface T1.
8+
// T1 is a defined parameterized interface type which
9+
// can be assigned to any other interface with the same
10+
// methods. We cannot infer a type argument in this case
11+
// because any type would do.
12+
13+
type T1[P any] interface{ m() }
14+
15+
func g[P any](T1[P]) {}
16+
17+
func _() {
18+
var x T1[int]
19+
g /* ERROR "cannot infer P" */ (x)
20+
g[int](x) // int is ok for P
21+
g[string](x) // string is also ok for P!
22+
}
23+
24+
// This is analogous to the above example,
25+
// but uses two interface types of the same structure.
26+
27+
type T2[P any] interface{ m() }
28+
29+
func _() {
30+
var x T2[int]
31+
g /* ERROR "cannot infer P" */ (x)
32+
g[int](x) // int is ok for P
33+
g[string](x) // string is also ok for P!
34+
}
35+
36+
// Analogous to the T2 example but using an unparameterized interface T3.
37+
38+
type T3 interface{ m() }
39+
40+
func _() {
41+
var x T3
42+
g /* ERROR "cannot infer P" */ (x)
43+
g[int](x) // int is ok for P
44+
g[string](x) // string is also ok for P!
45+
}
46+
47+
// The type parameter P is not used in struct S.
48+
// S is a defined parameterized (non-interface) type which can only
49+
// be assigned to another type S with the same type argument.
50+
// Therefore we can infer a type argument in this case.
51+
52+
type S[P any] struct{}
53+
54+
func g4[P any](S[P]) {}
55+
56+
func _() {
57+
var x S[int]
58+
g4(x) // we can infer int for P
59+
g4[int](x) // int is the correct type argument
60+
g4[string](x /* ERROR "cannot use x (variable of type S[int]) as S[string] value in argument to g4[string]" */)
61+
}
62+
63+
// This is similar to the first example but here T1 is a component
64+
// of a func type. In this case we should be able to infer a type
65+
// argument for P because component types must be identical even
66+
// in the case of interfaces.
67+
// This is a short-coming of type inference at the moment, but it
68+
// is better to not be able to infer a type here (we can always
69+
// supply one), than to infer the wrong type in other cases (see
70+
// below). Finally, if we decide to accept go.dev/issues/8082,
71+
// the behavior here is correct.
72+
73+
func g5[P any](func(T1[P])) {}
74+
75+
func _() {
76+
var f func(T1[int])
77+
g5 /* ERROR "cannot infer P" */ (f)
78+
g5[int](f)
79+
g5[string](f /* ERROR "cannot use f (variable of type func(T1[int])) as func(T1[string]) value in argument to g5[string]" */)
80+
}
81+
82+
// This example would fail if we were to infer the type argument int for P
83+
// exactly because any type argument would be ok for the first argument.
84+
// Choosing the wrong type would cause the second argument to not match.
85+
86+
type T[P any] interface{}
87+
88+
func g6[P any](T[P], P) {}
89+
90+
func _() {
91+
var x T[int]
92+
g6(x, 1.2)
93+
g6(x, "")
94+
}

test/fixedbugs/issue53309.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ func use[T any](v Value[T]) {
3838

3939
func main() {
4040
tr := &taskResult{&taskDefinition{}}
41-
use(Value[string](tr))
41+
use[string](Value[string](tr))
4242
}

test/typeparam/issue53762.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ func use[T any](v Value[T]) {
1414
}
1515

1616
func main() {
17-
use(Value[int](1))
17+
use[int](Value[int](1))
1818
}

0 commit comments

Comments
 (0)