// `func(float64)`,
// },
- {`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
+ {`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
`f`,
[]string{`string`, `*string`},
`func(x string)`,
},
- {`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `*int`},
`func(x []int)`,
},
- {`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`},
`func(x []int)`,
},
- {`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func(x []int)`,
},
- {`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
+ {`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
- {`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
+ {`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
- {`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
+ {`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
},
- {`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
+ {`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
},
- {`package i0; import lib "generic_lib"; func _() { lib.F(42) }`,
+
+ {`package i0; import "lib"; func _() { lib.F(42) }`,
`F`,
[]string{`int`},
`func(int)`,
},
+
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
`T`,
[]string{`int`},
[]string{`[]int`, `int`},
`struct{x []int; y int}`,
},
- {`package type4; import lib "generic_lib"; var _ lib.T[int]`,
+ {`package type4; import "lib"; var _ lib.T[int]`,
`T`,
[]string{`int`},
`[]int`,
}
for _, test := range tests {
- const lib = `package generic_lib
+ const lib = `package lib
func F[P any](P) {}
}
}
- // If a constraint has a core type, unify the corresponding type parameter with it.
- for _, tpar := range tparams {
- if ctype := adjCoreType(tpar); ctype != nil {
- if !u.unify(tpar, ctype) {
- // TODO(gri) improve error message by providing the type arguments
- // which we know already
- check.errorf(pos, "%s does not match %s", tpar, ctype)
- return nil, 0
+ // Repeatedly apply constraint type inference as long as
+ // there are still unknown type arguments and progress is
+ // being made.
+ //
+ // This is an O(n^2) algorithm where n is the number of
+ // type parameters: if there is progress (and iteration
+ // continues), at least one type argument is inferred
+ // per iteration and we have a doubly nested loop.
+ // In practice this is not a problem because the number
+ // of type parameters tends to be very small (< 5 or so).
+ // (It should be possible for unification to efficiently
+ // signal newly inferred type arguments; then the loops
+ // here could handle the respective type parameters only,
+ // but that will come at a cost of extra complexity which
+ // may not be worth it.)
+ for n := u.x.unknowns(); n > 0; {
+ nn := n
+
+ for i, tpar := range tparams {
+ // If there is a core term (i.e., a core type with tilde information)
+ // unify the type parameter with the core type.
+ if core, single := coreTerm(tpar); core != nil {
+ // A type parameter can be unified with its core type in two cases.
+ tx := u.x.at(i)
+ switch {
+ case tx != nil:
+ // The corresponding type argument tx is known.
+ // In this case, if the core type has a tilde, the type argument's underlying
+ // type must match the core type, otherwise the type argument and the core type
+ // must match.
+ // If tx is an external type parameter, don't consider its underlying type
+ // (which is an interface). Core type unification will attempt to unify against
+ // core.typ.
+ // Note also that even with inexact unification we cannot leave away the under
+ // call here because it's possible that both tx and core.typ are named types,
+ // with under(tx) being a (named) basic type matching core.typ. Such cases do
+ // not match with inexact unification.
+ if core.tilde && !isTypeParam(tx) {
+ tx = under(tx)
+ }
+ if !u.unify(tx, core.typ) {
+ // TODO(gri) improve error message by providing the type arguments
+ // which we know already
+ // Don't use term.String() as it always qualifies types, even if they
+ // are in the current package.
+ tilde := ""
+ if core.tilde {
+ tilde = "~"
+ }
+ check.errorf(pos, "%s does not match %s%s", tpar, tilde, core.typ)
+ return nil, 0
+ }
+
+ case single && !core.tilde:
+ // The corresponding type argument tx is unknown and there's a single
+ // specific type and no tilde.
+ // In this case the type argument must be that single type; set it.
+ u.x.set(i, core.typ)
+
+ default:
+ // Unification is not possible and no progress was made.
+ continue
+ }
+
+ // The number of known type arguments may have changed.
+ nn = u.x.unknowns()
+ if nn == 0 {
+ break // all type arguments are known
+ }
}
}
+
+ assert(nn <= n)
+ if nn == n {
+ break // no progress
+ }
+ n = nn
}
// u.x.types() now contains the incoming type arguments plus any additional type
- // arguments which were inferred from core types. The newly inferred non-
- // nil entries may still contain references to other type parameters.
+ // arguments which were inferred from core terms. The newly inferred non-nil
+ // entries may still contain references to other type parameters.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
return
}
-// adjCoreType returns the core type of tpar unless the
-// type parameter embeds a single, possibly named type,
-// in which case it returns that single type instead.
-// (The core type is always the underlying type of that
-// single type.)
-func adjCoreType(tpar *TypeParam) Type {
- var single *term
- if tpar.is(func(t *term) bool {
- if single == nil && t != nil {
- single = t
- return true
- }
- return false // zero or more than one terms
- }) {
+// If the type parameter has a single specific type S, coreTerm returns (S, true).
+// Otherwise, if tpar has a core type T, it returns a term corresponding to that
+// core type and false. In that case, if any term of tpar has a tilde, the core
+// term has a tilde. In all other cases coreTerm returns (nil, false).
+func coreTerm(tpar *TypeParam) (*term, bool) {
+ n := 0
+ var single *term // valid if n == 1
+ var tilde bool
+ tpar.is(func(t *term) bool {
+ if t == nil {
+ assert(n == 0)
+ return false // no terms
+ }
+ n++
+ single = t
+ if t.tilde {
+ tilde = true
+ }
+ return true
+ })
+ if n == 1 {
if debug {
- assert(under(single.typ) == coreType(tpar))
+ assert(debug && under(single.typ) == coreType(tpar))
}
- return single.typ
+ return single, true
+ }
+ if typ := coreType(tpar); typ != nil {
+ // A core type is always an underlying type.
+ // If any term of tpar has a tilde, we don't
+ // have a precise core type and we must return
+ // a tilde as well.
+ return &term{tilde, typ}, false
}
- return coreType(tpar)
+ return nil, false
}
type cycleFinder struct {
type any interface{}
-func f0[A any, B interface{~*C}, C interface{~*D}, D interface{~*A}](A, B, C, D) {}
+func f0[A any, B interface{*C}, C interface{*D}, D interface{*A}](A, B, C, D) {}
func _() {
f := f0[string]
f("a", nil, nil, nil)
f0("a", nil, nil, nil)
}
-func f1[A any, B interface{~*A}](A, B) {}
+func f1[A any, B interface{*A}](A, B) {}
func _() {
f := f1[int]
f(int(0), new(int))
f1(int(0), new(int))
}
-func f2[A any, B interface{~[]A}](A, B) {}
+func f2[A any, B interface{[]A}](A, B) {}
func _() {
f := f2[byte]
f(byte(0), []byte{})
// f3(x, &x, &x)
// }
-func f4[A any, B interface{~[]C}, C interface{~*A}](A, B, C) {}
+func f4[A any, B interface{[]C}, C interface{*A}](A, B, C) {}
func _() {
f := f4[int]
var x int
f4(x, []*int{}, &x)
}
-func f5[A interface{~struct{b B; c C}}, B any, C interface{~*B}](x B) A { panic(0) }
+func f5[A interface{struct{b B; c C}}, B any, C interface{*B}](x B) A { panic(0) }
func _() {
x := f5(1.2)
var _ float64 = x.b
type Setter[B any] interface {
Set(string)
- ~*B
+ *B
}
func FromStrings[T interface{}, PT Setter[T]](s []string) []T {
result := make([]T, len(s))
for i, v := range s {
// The type of &result[i] is *T which is in the type list
- // of Setter2, so we can convert it to PT.
+ // of Setter, so we can convert it to PT.
p := PT(&result[i])
// PT has a Set method.
p.Set(v)
}
// recursive inference
-type Tr[A any, B ~*C, C ~*D, D ~*A] int
+type Tr[A any, B *C, C *D, D *A] int
func _() {
var x Tr[string]
var y Tr[string, ***string, **string, *string]
}
// other patterns of inference
-type To0[A any, B ~[]A] int
-type To1[A any, B ~struct{a A}] int
-type To2[A any, B ~[][]A] int
-type To3[A any, B ~[3]*A] int
-type To4[A any, B any, C ~struct{a A; b B}] int
+type To0[A any, B []A] int
+type To1[A any, B struct{a A}] int
+type To2[A any, B [][]A] int
+type To3[A any, B [3]*A] int
+type To4[A any, B any, C struct{a A; b B}] int
func _() {
var _ To0[int]
var _ To1[int]
related1(si, "foo" /* ERROR cannot use "foo" */ )
}
-func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
+func related2[Elem any, Slice interface{[]Elem}](e Elem, s Slice) {}
func _() {
// related2 can be called with explicit instantiation.
related3[int, []int]()
related3[byte, List[byte]]()
- // Alternatively, the 2nd type argument can be inferred
- // from the first one through constraint type inference.
- related3[int]()
-
- // The inferred type is the core type of the Slice
- // type parameter.
- var _ []int = related3[int]()
-
- // It is not the defined parameterized type List.
- type anotherList []float32
- var _ anotherList = related3[float32]() // valid
- var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
+ // The 2nd type argument cannot be inferred from the first
+ // one because there's two possible choices: []Elem and
+ // List[Elem].
+ related3[int]( /* ERROR cannot infer Slice */ )
}
return deref(p)
}
-func addrOfCopy[V any, P ~*V](v V) P {
+func addrOfCopy[V any, P *V](v V) P {
return &v
}
package p
-func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
+func f[F interface{*Q}, G interface{*R}, Q, R any](q Q, r R) {}
func _() {
f[*float64, *int](1, 2)
--- /dev/null
+// Copyright 2022 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
+
+// Constraint type inference should be independent of the
+// ordering of the type parameter declarations. Try all
+// permutations in the test case below.
+// Permutations produced by https://go.dev/play/p/PHcZNGJTEBZ.
+
+func f00[S1 ~[]E1, S2 ~[]E2, E1 ~byte, E2 ~byte](S1, S2) {}
+func f01[S2 ~[]E2, S1 ~[]E1, E1 ~byte, E2 ~byte](S1, S2) {}
+func f02[E1 ~byte, S1 ~[]E1, S2 ~[]E2, E2 ~byte](S1, S2) {}
+func f03[S1 ~[]E1, E1 ~byte, S2 ~[]E2, E2 ~byte](S1, S2) {}
+func f04[S2 ~[]E2, E1 ~byte, S1 ~[]E1, E2 ~byte](S1, S2) {}
+func f05[E1 ~byte, S2 ~[]E2, S1 ~[]E1, E2 ~byte](S1, S2) {}
+func f06[E2 ~byte, S2 ~[]E2, S1 ~[]E1, E1 ~byte](S1, S2) {}
+func f07[S2 ~[]E2, E2 ~byte, S1 ~[]E1, E1 ~byte](S1, S2) {}
+func f08[S1 ~[]E1, E2 ~byte, S2 ~[]E2, E1 ~byte](S1, S2) {}
+func f09[E2 ~byte, S1 ~[]E1, S2 ~[]E2, E1 ~byte](S1, S2) {}
+func f10[S2 ~[]E2, S1 ~[]E1, E2 ~byte, E1 ~byte](S1, S2) {}
+func f11[S1 ~[]E1, S2 ~[]E2, E2 ~byte, E1 ~byte](S1, S2) {}
+func f12[S1 ~[]E1, E1 ~byte, E2 ~byte, S2 ~[]E2](S1, S2) {}
+func f13[E1 ~byte, S1 ~[]E1, E2 ~byte, S2 ~[]E2](S1, S2) {}
+func f14[E2 ~byte, S1 ~[]E1, E1 ~byte, S2 ~[]E2](S1, S2) {}
+func f15[S1 ~[]E1, E2 ~byte, E1 ~byte, S2 ~[]E2](S1, S2) {}
+func f16[E1 ~byte, E2 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
+func f17[E2 ~byte, E1 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
+func f18[E2 ~byte, E1 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
+func f19[E1 ~byte, E2 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
+func f20[S2 ~[]E2, E2 ~byte, E1 ~byte, S1 ~[]E1](S1, S2) {}
+func f21[E2 ~byte, S2 ~[]E2, E1 ~byte, S1 ~[]E1](S1, S2) {}
+func f22[E1 ~byte, S2 ~[]E2, E2 ~byte, S1 ~[]E1](S1, S2) {}
+func f23[S2 ~[]E2, E1 ~byte, E2 ~byte, S1 ~[]E1](S1, S2) {}
+
+type myByte byte
+
+func _(a []byte, b []myByte) {
+ f00(a, b)
+ f01(a, b)
+ f02(a, b)
+ f03(a, b)
+ f04(a, b)
+ f05(a, b)
+ f06(a, b)
+ f07(a, b)
+ f08(a, b)
+ f09(a, b)
+ f10(a, b)
+ f11(a, b)
+ f12(a, b)
+ f13(a, b)
+ f14(a, b)
+ f15(a, b)
+ f16(a, b)
+ f17(a, b)
+ f18(a, b)
+ f19(a, b)
+ f20(a, b)
+ f21(a, b)
+ f22(a, b)
+ f23(a, b)
+}
+
+// Constraint type inference may have to iterate.
+// Again, the order of the type parameters shouldn't matter.
+
+func g0[S ~[]E, M ~map[string]S, E any](m M) {}
+func g1[M ~map[string]S, S ~[]E, E any](m M) {}
+func g2[E any, S ~[]E, M ~map[string]S](m M) {}
+func g3[S ~[]E, E any, M ~map[string]S](m M) {}
+func g4[M ~map[string]S, E any, S ~[]E](m M) {}
+func g5[E any, M ~map[string]S, S ~[]E](m M) {}
+
+func _(m map[string][]byte) {
+ g0(m)
+ g1(m)
+ g2(m)
+ g3(m)
+ g4(m)
+ g5(m)
+}
+
+// Worst-case scenario.
+// There are 10 unknown type parameters. In each iteration of
+// constraint type inference we infer one more, from right to left.
+// Each iteration looks repeatedly at all 11 type parameters,
+// requiring a total of 10*11 = 110 iterations with the current
+// implementation. Pathological case.
+
+func h[K any, J ~*K, I ~*J, H ~*I, G ~*H, F ~*G, E ~*F, D ~*E, C ~*D, B ~*C, A ~*B](x A) {}
+
+func _(x **********int) {
+ h(x)
+}
+
+// Examples with channel constraints and tilde.
+
+func ch1[P chan<- int]() (_ P) { return } // core(P) == chan<- int (single type, no tilde)
+func ch2[P ~chan int]() { return } // core(P) == ~chan<- int (tilde)
+func ch3[P chan E, E any](E) { return } // core(P) == chan<- E (single type, no tilde)
+func ch4[P chan E | ~chan<- E, E any](E) { return } // core(P) == ~chan<- E (tilde)
+func ch5[P chan int | chan<- int]() { return } // core(P) == chan<- int (not a single type)
+
+func _() {
+ // P can be inferred as there's a single specific type and no tilde.
+ var _ chan int = ch1 /* ERROR cannot use ch1.*value of type chan<- int */ ()
+ var _ chan<- int = ch1()
+
+ // P cannot be inferred as there's a tilde.
+ ch2( /* ERROR cannot infer P */ )
+ type myChan chan int
+ ch2[myChan]()
+
+ // P can be inferred as there's a single specific type and no tilde.
+ var e int
+ ch3(e)
+
+ // P cannot be inferred as there's more than one specific type and a tilde.
+ ch4( /* ERROR cannot infer P */ e)
+ _ = ch4[chan int]
+
+ // P cannot be inferred as there's more than one specific type.
+ ch5( /* ERROR cannot infer P */ )
+ ch5[chan<- int]()
+}
+
+// test case from issue
+
+func equal[M1 ~map[K1]V1, M2 ~map[K2]V2, K1, K2 ~uint32, V1, V2 ~string](m1 M1, m2 M2) bool {
+ if len(m1) != len(m2) {
+ return false
+ }
+ for k, v1 := range m1 {
+ if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 {
+ return false
+ }
+ }
+ return true
+}
+
+func equalFixed[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool {
+ if len(m1) != len(m2) {
+ return false
+ }
+ for k, v1 := range m1 {
+ if v2, ok := m2[K2(k)]; !ok || v1 != V1(v2) {
+ return false
+ }
+ }
+ return true
+}
+
+type (
+ someNumericID uint32
+ someStringID string
+)
+
+func _() {
+ foo := map[uint32]string{10: "bar"}
+ bar := map[someNumericID]someStringID{10: "bar"}
+ equal(foo, bar)
+}
}
}
+// unknowns returns the number of type parameters for which no type has been set yet.
+func (d *tparamsList) unknowns() int {
+ n := 0
+ for _, ti := range d.indices {
+ if ti <= 0 {
+ n++
+ }
+ }
+ return n
+}
+
// types returns the list of inferred types (via unification) for the type parameters
// described by d, and an index. If all types were inferred, the returned index is < 0.
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
// (see issue #50755 for a test case).
if enableCoreTypeUnification && !u.exact {
if isTypeParam(x) && !hasName(y) {
+ // Caution: This may not be correct in light of ~ constraints.
+ // See issue #51376.
+ // TODO(gri) investigate!
+ //
// When considering the type parameter for unification
- // we look at the adjusted core type (adjCoreType).
+ // we look at the adjusted core type (coreTerm).
// If the adjusted core type is a named type N; the
// corresponding core type is under(N). Since !u.exact
// and y doesn't have a name, unification will end up
`func(float64, *byte, ...[]byte)`,
},
- {`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
+ {`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
`f`,
[]string{`string`, `*string`},
`func(x string)`,
},
- {`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `*int`},
`func(x []int)`,
},
- {`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`},
`func(x []int)`,
},
- {`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
+ {`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func(x []int)`,
},
- {`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
+ {`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
- {`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
+ {`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
- {`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
+ {`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
`f`,
- []string{`int`, `chan<- int`},
+ []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
},
- {`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
+ {`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
},
+
{`package i0; import "lib"; func _() { lib.F(42) }`,
`F`,
[]string{`int`},
`func(int)`,
},
+
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
`T`,
[]string{`int`},
}
}
- // If a constraint has a core type, unify the corresponding type parameter with it.
- for _, tpar := range tparams {
- if ctype := adjCoreType(tpar); ctype != nil {
- if !u.unify(tpar, ctype) {
- // TODO(gri) improve error message by providing the type arguments
- // which we know already
- check.errorf(posn, _InvalidTypeArg, "%s does not match %s", tpar, ctype)
- return nil, 0
+ // Repeatedly apply constraint type inference as long as
+ // there are still unknown type arguments and progress is
+ // being made.
+ //
+ // This is an O(n^2) algorithm where n is the number of
+ // type parameters: if there is progress (and iteration
+ // continues), at least one type argument is inferred
+ // per iteration and we have a doubly nested loop.
+ // In practice this is not a problem because the number
+ // of type parameters tends to be very small (< 5 or so).
+ // (It should be possible for unification to efficiently
+ // signal newly inferred type arguments; then the loops
+ // here could handle the respective type parameters only,
+ // but that will come at a cost of extra complexity which
+ // may not be worth it.)
+ for n := u.x.unknowns(); n > 0; {
+ nn := n
+
+ for i, tpar := range tparams {
+ // If there is a core term (i.e., a core type with tilde information)
+ // unify the type parameter with the core type.
+ if core, single := coreTerm(tpar); core != nil {
+ // A type parameter can be unified with its core type in two cases.
+ tx := u.x.at(i)
+ switch {
+ case tx != nil:
+ // The corresponding type argument tx is known.
+ // In this case, if the core type has a tilde, the type argument's underlying
+ // type must match the core type, otherwise the type argument and the core type
+ // must match.
+ // If tx is an external type parameter, don't consider its underlying type
+ // (which is an interface). Core type unification will attempt to unify against
+ // core.typ.
+ // Note also that even with inexact unification we cannot leave away the under
+ // call here because it's possible that both tx and core.typ are named types,
+ // with under(tx) being a (named) basic type matching core.typ. Such cases do
+ // not match with inexact unification.
+ if core.tilde && !isTypeParam(tx) {
+ tx = under(tx)
+ }
+ if !u.unify(tx, core.typ) {
+ // TODO(gri) improve error message by providing the type arguments
+ // which we know already
+ // Don't use term.String() as it always qualifies types, even if they
+ // are in the current package.
+ tilde := ""
+ if core.tilde {
+ tilde = "~"
+ }
+ check.errorf(posn, _InvalidTypeArg, "%s does not match %s%s", tpar, tilde, core.typ)
+ return nil, 0
+ }
+
+ case single && !core.tilde:
+ // The corresponding type argument tx is unknown and there's a single
+ // specific type and no tilde.
+ // In this case the type argument must be that single type; set it.
+ u.x.set(i, core.typ)
+
+ default:
+ // Unification is not possible and no progress was made.
+ continue
+ }
+
+ // The number of known type arguments may have changed.
+ nn = u.x.unknowns()
+ if nn == 0 {
+ break // all type arguments are known
+ }
}
}
+
+ assert(nn <= n)
+ if nn == n {
+ break // no progress
+ }
+ n = nn
}
// u.x.types() now contains the incoming type arguments plus any additional type
- // arguments which were inferred from core types. The newly inferred non-
- // nil entries may still contain references to other type parameters.
+ // arguments which were inferred from core terms. The newly inferred non-nil
+ // entries may still contain references to other type parameters.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
return
}
-// adjCoreType returns the core type of tpar unless the
-// type parameter embeds a single, possibly named type,
-// in which case it returns that single type instead.
-// (The core type is always the underlying type of that
-// single type.)
-func adjCoreType(tpar *TypeParam) Type {
- var single *term
- if tpar.is(func(t *term) bool {
- if single == nil && t != nil {
- single = t
- return true
+// If the type parameter has a single specific type S, coreTerm returns (S, true).
+// Otherwise, if tpar has a core type T, it returns a term corresponding to that
+// core type and false. In that case, if any term of tpar has a tilde, the core
+// term has a tilde. In all other cases coreTerm returns (nil, false).
+func coreTerm(tpar *TypeParam) (*term, bool) {
+ n := 0
+ var single *term // valid if n == 1
+ var tilde bool
+ tpar.is(func(t *term) bool {
+ if t == nil {
+ assert(n == 0)
+ return false // no terms
}
- return false // zero or more than one terms
- }) {
+ n++
+ single = t
+ if t.tilde {
+ tilde = true
+ }
+ return true
+ })
+ if n == 1 {
if debug {
- assert(under(single.typ) == coreType(tpar))
+ assert(debug && under(single.typ) == coreType(tpar))
}
- return single.typ
+ return single, true
+ }
+ if typ := coreType(tpar); typ != nil {
+ // A core type is always an underlying type.
+ // If any term of tpar has a tilde, we don't
+ // have a precise core type and we must return
+ // a tilde as well.
+ return &term{tilde, typ}, false
}
- return coreType(tpar)
+ return nil, false
}
type cycleFinder struct {
type any interface{}
-func f0[A any, B interface{~*C}, C interface{~*D}, D interface{~*A}](A, B, C, D) {}
+func f0[A any, B interface{*C}, C interface{*D}, D interface{*A}](A, B, C, D) {}
func _() {
f := f0[string]
f("a", nil, nil, nil)
f0("a", nil, nil, nil)
}
-func f1[A any, B interface{~*A}](A, B) {}
+func f1[A any, B interface{*A}](A, B) {}
func _() {
f := f1[int]
f(int(0), new(int))
f1(int(0), new(int))
}
-func f2[A any, B interface{~[]A}](A, B) {}
+func f2[A any, B interface{[]A}](A, B) {}
func _() {
f := f2[byte]
f(byte(0), []byte{})
// f3(x, &x, &x)
// }
-func f4[A any, B interface{~[]C}, C interface{~*A}](A, B, C) {}
+func f4[A any, B interface{[]C}, C interface{*A}](A, B, C) {}
func _() {
f := f4[int]
var x int
f4(x, []*int{}, &x)
}
-func f5[A interface{~struct{b B; c C}}, B any, C interface{~*B}](x B) A { panic(0) }
+func f5[A interface{struct{b B; c C}}, B any, C interface{*B}](x B) A { panic(0) }
func _() {
x := f5(1.2)
var _ float64 = x.b
type Setter[B any] interface {
Set(string)
- ~*B
+ *B
}
func FromStrings[T interface{}, PT Setter[T]](s []string) []T {
}
// recursive inference
-type Tr[A any, B ~*C, C ~*D, D ~*A] int
+type Tr[A any, B *C, C *D, D *A] int
func _() {
var x Tr[string]
var y Tr[string, ***string, **string, *string]
}
// other patterns of inference
-type To0[A any, B ~[]A] int
-type To1[A any, B ~struct{a A}] int
-type To2[A any, B ~[][]A] int
-type To3[A any, B ~[3]*A] int
-type To4[A any, B any, C ~struct{a A; b B}] int
+type To0[A any, B []A] int
+type To1[A any, B struct{a A}] int
+type To2[A any, B [][]A] int
+type To3[A any, B [3]*A] int
+type To4[A any, B any, C struct{a A; b B}] int
func _() {
var _ To0[int]
var _ To1[int]
related1(si, "foo" /* ERROR cannot use "foo" */ )
}
-func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
+func related2[Elem any, Slice interface{[]Elem}](e Elem, s Slice) {}
func _() {
// related2 can be called with explicit instantiation.
related3[int, []int]()
related3[byte, List[byte]]()
- // Alternatively, the 2nd type argument can be inferred
- // from the first one through constraint type inference.
- related3[int]()
-
- // The inferred type is the core type of the Slice
- // type parameter.
- var _ []int = related3[int]()
-
- // It is not the defined parameterized type List.
- type anotherList []float32
- var _ anotherList = related3[float32]() // valid
- var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
+ // The 2nd type argument cannot be inferred from the first
+ // one because there's two possible choices: []Elem and
+ // List[Elem].
+ related3 /* ERROR cannot infer Slice */ [int]()
}
return deref(p)
}
-func addrOfCopy[V any, P ~*V](v V) P {
+func addrOfCopy[V any, P *V](v V) P {
return &v
}
package p
-func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
+func f[F interface{*Q}, G interface{*R}, Q, R any](q Q, r R) {}
func _() {
f[*float64, *int](1, 2)
--- /dev/null
+// Copyright 2022 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
+
+// Constraint type inference should be independent of the
+// ordering of the type parameter declarations. Try all
+// permutations in the test case below.
+// Permutations produced by https://go.dev/play/p/PHcZNGJTEBZ.
+
+func f00[S1 ~[]E1, S2 ~[]E2, E1 ~byte, E2 ~byte](S1, S2) {}
+func f01[S2 ~[]E2, S1 ~[]E1, E1 ~byte, E2 ~byte](S1, S2) {}
+func f02[E1 ~byte, S1 ~[]E1, S2 ~[]E2, E2 ~byte](S1, S2) {}
+func f03[S1 ~[]E1, E1 ~byte, S2 ~[]E2, E2 ~byte](S1, S2) {}
+func f04[S2 ~[]E2, E1 ~byte, S1 ~[]E1, E2 ~byte](S1, S2) {}
+func f05[E1 ~byte, S2 ~[]E2, S1 ~[]E1, E2 ~byte](S1, S2) {}
+func f06[E2 ~byte, S2 ~[]E2, S1 ~[]E1, E1 ~byte](S1, S2) {}
+func f07[S2 ~[]E2, E2 ~byte, S1 ~[]E1, E1 ~byte](S1, S2) {}
+func f08[S1 ~[]E1, E2 ~byte, S2 ~[]E2, E1 ~byte](S1, S2) {}
+func f09[E2 ~byte, S1 ~[]E1, S2 ~[]E2, E1 ~byte](S1, S2) {}
+func f10[S2 ~[]E2, S1 ~[]E1, E2 ~byte, E1 ~byte](S1, S2) {}
+func f11[S1 ~[]E1, S2 ~[]E2, E2 ~byte, E1 ~byte](S1, S2) {}
+func f12[S1 ~[]E1, E1 ~byte, E2 ~byte, S2 ~[]E2](S1, S2) {}
+func f13[E1 ~byte, S1 ~[]E1, E2 ~byte, S2 ~[]E2](S1, S2) {}
+func f14[E2 ~byte, S1 ~[]E1, E1 ~byte, S2 ~[]E2](S1, S2) {}
+func f15[S1 ~[]E1, E2 ~byte, E1 ~byte, S2 ~[]E2](S1, S2) {}
+func f16[E1 ~byte, E2 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
+func f17[E2 ~byte, E1 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
+func f18[E2 ~byte, E1 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
+func f19[E1 ~byte, E2 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
+func f20[S2 ~[]E2, E2 ~byte, E1 ~byte, S1 ~[]E1](S1, S2) {}
+func f21[E2 ~byte, S2 ~[]E2, E1 ~byte, S1 ~[]E1](S1, S2) {}
+func f22[E1 ~byte, S2 ~[]E2, E2 ~byte, S1 ~[]E1](S1, S2) {}
+func f23[S2 ~[]E2, E1 ~byte, E2 ~byte, S1 ~[]E1](S1, S2) {}
+
+type myByte byte
+
+func _(a []byte, b []myByte) {
+ f00(a, b)
+ f01(a, b)
+ f02(a, b)
+ f03(a, b)
+ f04(a, b)
+ f05(a, b)
+ f06(a, b)
+ f07(a, b)
+ f08(a, b)
+ f09(a, b)
+ f10(a, b)
+ f11(a, b)
+ f12(a, b)
+ f13(a, b)
+ f14(a, b)
+ f15(a, b)
+ f16(a, b)
+ f17(a, b)
+ f18(a, b)
+ f19(a, b)
+ f20(a, b)
+ f21(a, b)
+ f22(a, b)
+ f23(a, b)
+}
+
+// Constraint type inference may have to iterate.
+// Again, the order of the type parameters shouldn't matter.
+
+func g0[S ~[]E, M ~map[string]S, E any](m M) {}
+func g1[M ~map[string]S, S ~[]E, E any](m M) {}
+func g2[E any, S ~[]E, M ~map[string]S](m M) {}
+func g3[S ~[]E, E any, M ~map[string]S](m M) {}
+func g4[M ~map[string]S, E any, S ~[]E](m M) {}
+func g5[E any, M ~map[string]S, S ~[]E](m M) {}
+
+func _(m map[string][]byte) {
+ g0(m)
+ g1(m)
+ g2(m)
+ g3(m)
+ g4(m)
+ g5(m)
+}
+
+// Worst-case scenario.
+// There are 10 unknown type parameters. In each iteration of
+// constraint type inference we infer one more, from right to left.
+// Each iteration looks repeatedly at all 11 type parameters,
+// requiring a total of 10*11 = 110 iterations with the current
+// implementation. Pathological case.
+
+func h[K any, J ~*K, I ~*J, H ~*I, G ~*H, F ~*G, E ~*F, D ~*E, C ~*D, B ~*C, A ~*B](x A) {}
+
+func _(x **********int) {
+ h(x)
+}
+
+// Examples with channel constraints and tilde.
+
+func ch1[P chan<- int]() (_ P) { return } // core(P) == chan<- int (single type, no tilde)
+func ch2[P ~chan int]() { return } // core(P) == ~chan<- int (tilde)
+func ch3[P chan E, E any](E) { return } // core(P) == chan<- E (single type, no tilde)
+func ch4[P chan E | ~chan<- E, E any](E) { return } // core(P) == ~chan<- E (tilde)
+func ch5[P chan int | chan<- int]() { return } // core(P) == chan<- int (not a single type)
+
+func _() {
+ // P can be inferred as there's a single specific type and no tilde.
+ var _ chan int = ch1 /* ERROR cannot use ch1.*value of type chan<- int */ ()
+ var _ chan<- int = ch1()
+
+ // P cannot be inferred as there's a tilde.
+ ch2 /* ERROR cannot infer P */ ()
+ type myChan chan int
+ ch2[myChan]()
+
+ // P can be inferred as there's a single specific type and no tilde.
+ var e int
+ ch3(e)
+
+ // P cannot be inferred as there's more than one specific type and a tilde.
+ ch4 /* ERROR cannot infer P */ (e)
+ _ = ch4[chan int]
+
+ // P cannot be inferred as there's more than one specific type.
+ ch5 /* ERROR cannot infer P */ ()
+ ch5[chan<- int]()
+}
+
+// test case from issue
+
+func equal[M1 ~map[K1]V1, M2 ~map[K2]V2, K1, K2 ~uint32, V1, V2 ~string](m1 M1, m2 M2) bool {
+ if len(m1) != len(m2) {
+ return false
+ }
+ for k, v1 := range m1 {
+ if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 {
+ return false
+ }
+ }
+ return true
+}
+
+func equalFixed[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool {
+ if len(m1) != len(m2) {
+ return false
+ }
+ for k, v1 := range m1 {
+ if v2, ok := m2[K2(k)]; !ok || v1 != V1(v2) {
+ return false
+ }
+ }
+ return true
+}
+
+type (
+ someNumericID uint32
+ someStringID string
+)
+
+func _() {
+ foo := map[uint32]string{10: "bar"}
+ bar := map[someNumericID]someStringID{10: "bar"}
+ equal(foo, bar)
+}
}
}
+// unknowns returns the number of type parameters for which no type has been set yet.
+func (d *tparamsList) unknowns() int {
+ n := 0
+ for _, ti := range d.indices {
+ if ti <= 0 {
+ n++
+ }
+ }
+ return n
+}
+
// types returns the list of inferred types (via unification) for the type parameters
// described by d, and an index. If all types were inferred, the returned index is < 0.
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
// (see issue #50755 for a test case).
if enableCoreTypeUnification && !u.exact {
if isTypeParam(x) && !hasName(y) {
+ // Caution: This may not be correct in light of ~ constraints.
+ // See issue #51376.
+ // TODO(gri) investigate!
+ //
// When considering the type parameter for unification
- // we look at the adjusted core type (adjCoreType).
+ // we look at the adjusted core type (coreTerm).
// If the adjusted core type is a named type N; the
// corresponding core type is under(N). Since !u.exact
// and y doesn't have a name, unification will end up
return x
}
-func min[T int|string](x, y T) T {
+func min[T int | string](x, y T) T {
if x < y {
return x
}
return y
}
-func max[T ~float64](x, y T) T {
+func max[T ~int | ~float64](x, y T) T {
if x > y {
return x
}
// Some random type parameter lists with elided interfaces.
type (
- _ [T struct{}] struct{}
- _ [M map[K]V, K comparable, V any] struct{}
- _ [_ interface{}|int] struct{}
+ _[T struct{}] struct{}
+ _[M map[K]V, K comparable, V any] struct{}
+ _[_ interface{} | int] struct{}
)
type Setter[B any] interface {
Set(string)
- ~*B
+ *B
}
// Takes two type parameters where PT = *T
}
*/
-func f2[A any, B interface{ ~[]A }](_ A, _ B) {}
+func f2[A any, B interface{ []A }](_ A, _ B) {}
func f2x() {
f := f2[byte]
f(byte(0), []byte{})
}
*/
-func f4[A any, B interface{ ~[]C }, C interface{ ~*A }](_ A, _ B, c C) {}
+func f4[A any, B interface{ []C }, C interface{ *A }](_ A, _ B, c C) {}
func f4x() {
f := f4[int]
var x int
}
func f5[A interface {
- ~struct {
+ struct {
b B
c C
}
-}, B any, C interface{ ~*B }](x B) A {
+}, B any, C interface{ *B }](x B) A {
panic(0)
}
func f5x() {