}
case *Named:
- // Two named types unify if their type names originate
- // in the same type declaration. If they are instantiated,
- // their type argument lists must unify.
+ // Two named non-interface types unify if their type names originate
+ // in the same type declaration. If they are instantiated, their type
+ // argument lists must unify.
+ // If one or both named types are interfaces, the types unify if the
+ // respective methods unify (per the rules for interface unification).
if y, ok := y.(*Named); ok {
- sameOrig := indenticalOrigin(x, y)
if enableInterfaceInference {
- xu := x.under()
- yu := y.under()
- xi, _ := xu.(*Interface)
- yi, _ := yu.(*Interface)
- // If one or both defined types are interfaces, use interface unification,
- // unless they originated in the same type declaration.
- if xi != nil && yi != nil {
- // If both interfaces originate in the same declaration,
- // their methods unify if the type parameters unify.
- // Unify the type parameters rather than the methods in
- // case the type parameters are not used in the methods
- // (and to preserve existing behavior in this case).
- if sameOrig {
- xargs := x.TypeArgs().list()
- yargs := y.TypeArgs().list()
- assert(len(xargs) == len(yargs))
- for i, xarg := range xargs {
- if !u.nify(xarg, yargs[i], p) {
- return false
- }
- }
- return true
- }
- return u.nify(xu, yu, p)
- }
- // We don't have two interfaces. If we have one, make sure it's in xi.
- if yi != nil {
- xi = yi
- y = x
- }
- // If xi is an interface, use interface unification.
- if xi != nil {
+ xi, _ := x.under().(*Interface)
+ yi, _ := y.under().(*Interface)
+ // If one or both of x and y are interfaces, use interface unification.
+ switch {
+ case xi != nil && yi != nil:
+ return u.nify(xi, yi, p)
+ case xi != nil:
return u.nify(xi, y, p)
+ case yi != nil:
+ return u.nify(x, yi, p)
}
// In all other cases, the type arguments and origins must match.
}
return false
}
}
- return sameOrig
+ return indenticalOrigin(x, y)
}
case *TypeParam:
}
case *Named:
- // Two named types unify if their type names originate
- // in the same type declaration. If they are instantiated,
- // their type argument lists must unify.
+ // Two named non-interface types unify if their type names originate
+ // in the same type declaration. If they are instantiated, their type
+ // argument lists must unify.
+ // If one or both named types are interfaces, the types unify if the
+ // respective methods unify (per the rules for interface unification).
if y, ok := y.(*Named); ok {
- sameOrig := indenticalOrigin(x, y)
if enableInterfaceInference {
- xu := x.under()
- yu := y.under()
- xi, _ := xu.(*Interface)
- yi, _ := yu.(*Interface)
- // If one or both defined types are interfaces, use interface unification,
- // unless they originated in the same type declaration.
- if xi != nil && yi != nil {
- // If both interfaces originate in the same declaration,
- // their methods unify if the type parameters unify.
- // Unify the type parameters rather than the methods in
- // case the type parameters are not used in the methods
- // (and to preserve existing behavior in this case).
- if sameOrig {
- xargs := x.TypeArgs().list()
- yargs := y.TypeArgs().list()
- assert(len(xargs) == len(yargs))
- for i, xarg := range xargs {
- if !u.nify(xarg, yargs[i], p) {
- return false
- }
- }
- return true
- }
- return u.nify(xu, yu, p)
- }
- // We don't have two interfaces. If we have one, make sure it's in xi.
- if yi != nil {
- xi = yi
- y = x
- }
- // If xi is an interface, use interface unification.
- if xi != nil {
+ xi, _ := x.under().(*Interface)
+ yi, _ := y.under().(*Interface)
+ // If one or both of x and y are interfaces, use interface unification.
+ switch {
+ case xi != nil && yi != nil:
+ return u.nify(xi, yi, p)
+ case xi != nil:
return u.nify(xi, y, p)
+ case yi != nil:
+ return u.nify(x, yi, p)
}
// In all other cases, the type arguments and origins must match.
}
return false
}
}
- return sameOrig
+ return indenticalOrigin(x, y)
}
case *TypeParam:
// another test case from the issue
func _() {
- X(Interface[*F /* ERROR "got 1 arguments but 2 type parameters" */ [string]](Impl{}))
+ X /* ERROR "cannot infer Q" */ (Interface[*F /* ERROR "got 1 arguments but 2 type parameters" */ [string]](Impl{}))
}
func X[Q Qer](fs Interface[Q]) {
--- /dev/null
+// 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
+
+// The type parameter P is not used in interface T1.
+// T1 is a defined parameterized interface type which
+// can be assigned to any other interface with the same
+// methods. We cannot infer a type argument in this case
+// because any type would do.
+
+type T1[P any] interface{ m() }
+
+func g[P any](T1[P]) {}
+
+func _() {
+ var x T1[int]
+ g /* ERROR "cannot infer P" */ (x)
+ g[int](x) // int is ok for P
+ g[string](x) // string is also ok for P!
+}
+
+// This is analogous to the above example,
+// but uses two interface types of the same structure.
+
+type T2[P any] interface{ m() }
+
+func _() {
+ var x T2[int]
+ g /* ERROR "cannot infer P" */ (x)
+ g[int](x) // int is ok for P
+ g[string](x) // string is also ok for P!
+}
+
+// Analogous to the T2 example but using an unparameterized interface T3.
+
+type T3 interface{ m() }
+
+func _() {
+ var x T3
+ g /* ERROR "cannot infer P" */ (x)
+ g[int](x) // int is ok for P
+ g[string](x) // string is also ok for P!
+}
+
+// The type parameter P is not used in struct S.
+// S is a defined parameterized (non-interface) type which can only
+// be assigned to another type S with the same type argument.
+// Therefore we can infer a type argument in this case.
+
+type S[P any] struct{}
+
+func g4[P any](S[P]) {}
+
+func _() {
+ var x S[int]
+ g4(x) // we can infer int for P
+ g4[int](x) // int is the correct type argument
+ g4[string](x /* ERROR "cannot use x (variable of type S[int]) as S[string] value in argument to g4[string]" */)
+}
+
+// This is similar to the first example but here T1 is a component
+// of a func type. In this case we should be able to infer a type
+// argument for P because component types must be identical even
+// in the case of interfaces.
+// This is a short-coming of type inference at the moment, but it
+// is better to not be able to infer a type here (we can always
+// supply one), than to infer the wrong type in other cases (see
+// below). Finally, if we decide to accept go.dev/issues/8082,
+// the behavior here is correct.
+
+func g5[P any](func(T1[P])) {}
+
+func _() {
+ var f func(T1[int])
+ g5 /* ERROR "cannot infer P" */ (f)
+ g5[int](f)
+ g5[string](f /* ERROR "cannot use f (variable of type func(T1[int])) as func(T1[string]) value in argument to g5[string]" */)
+}
+
+// This example would fail if we were to infer the type argument int for P
+// exactly because any type argument would be ok for the first argument.
+// Choosing the wrong type would cause the second argument to not match.
+
+type T[P any] interface{}
+
+func g6[P any](T[P], P) {}
+
+func _() {
+ var x T[int]
+ g6(x, 1.2)
+ g6(x, "")
+}
func main() {
tr := &taskResult{&taskDefinition{}}
- use(Value[string](tr))
+ use[string](Value[string](tr))
}
}
func main() {
- use(Value[int](1))
+ use[int](Value[int](1))
}