Skip to content

Commit 435718e

Browse files
committed
cmd/compile/internal/types2: avoid infinite recursion in unification
If the type T inferred for a type parameter P is P itself (or a derived type containing P), a subsequent unification step leads to infinite recursion: at each encounter of P with the already inferred type T (which is or contains P), P stands for that T and the recursive matching process continues with T, which inevitably contains P again and recursion never terminates. This CL introduces a set of masks, one for each type parameter. When a type parameter is encountered for which a type has already been inferred, the type parameter is "masked" for the recursive matching of the inferred type. Masking makes the type parameter "invisible" such that it will be handled like any other type and not unpacked further. Fixes #48619. For #48656. Change-Id: Ic1d938322be51fd44323ea14f925303f58b27c97 Reviewed-on: https://go-review.googlesource.com/c/go/+/352832 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
1 parent b8a6017 commit 435718e

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2021 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+
func f[P any](a, _ P) {
8+
var x int
9+
f(a, x /* ERROR type int of x does not match P */)
10+
f(x, a /* ERROR type P of a does not match inferred type int for P */)
11+
}
12+
13+
func g[P any](a, b P) {
14+
g(a, b)
15+
g(&a, &b)
16+
g([]P{}, []P{})
17+
}
18+
19+
func h[P any](a, b P) {
20+
h(&a, &b)
21+
h([]P{a}, []P{b})
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2021 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+
// TODO(gri) Still need better error positions and message here.
8+
// But this doesn't crash anymore.
9+
10+
func f[P /* ERROR does not match \*Q */ interface{*Q}, Q any](p P, q Q) {
11+
_ = f[P]
12+
_ = f[/* ERROR cannot infer P */ *P]
13+
}

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ func (u *unifier) unify(x, y Type) bool {
6363
type tparamsList struct {
6464
unifier *unifier
6565
tparams []*TypeParam
66+
// For each tparams element, there is a corresponding mask bit in masks.
67+
// If set, the corresponding type parameter is masked and doesn't appear
68+
// as a type parameter with tparamsList.index.
69+
masks []bool
6670
// For each tparams element, there is a corresponding type slot index in indices.
6771
// index < 0: unifier.types[-index-1] == nil
6872
// index == 0: no type slot allocated yet
@@ -103,9 +107,14 @@ func (d *tparamsList) init(tparams []*TypeParam) {
103107
}
104108
}
105109
d.tparams = tparams
110+
d.masks = make([]bool, len(tparams))
106111
d.indices = make([]int, len(tparams))
107112
}
108113

114+
// mask and unmask permit the masking/unmasking of the i'th type parameter of d.
115+
func (d *tparamsList) mask(i int) { d.masks[i] = true }
116+
func (d *tparamsList) unmask(i int) { d.masks[i] = false }
117+
109118
// join unifies the i'th type parameter of x with the j'th type parameter of y.
110119
// If both type parameters already have a type associated with them and they are
111120
// not joined, join fails and returns false.
@@ -149,11 +158,13 @@ func (u *unifier) join(i, j int) bool {
149158
return true
150159
}
151160

152-
// If typ is a type parameter of d, index returns the type parameter index.
161+
// If typ is an unmasked type parameter of d, index returns the type parameter index.
153162
// Otherwise, the result is < 0.
154163
func (d *tparamsList) index(typ Type) int {
155164
if tpar, ok := typ.(*TypeParam); ok {
156-
return tparamIndex(d.tparams, tpar)
165+
if i := tparamIndex(d.tparams, tpar); i >= 0 && !d.masks[i] {
166+
return i
167+
}
157168
}
158169
return -1
159170
}
@@ -246,7 +257,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
246257
}
247258
}
248259

249-
// Cases where at least one of x or y is a type parameter.
260+
// Cases where at least one of x or y is an (unmasked) type parameter.
250261
switch i, j := u.x.index(x), u.y.index(y); {
251262
case i >= 0 && j >= 0:
252263
// both x and y are type parameters
@@ -259,6 +270,12 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
259270
case i >= 0:
260271
// x is a type parameter, y is not
261272
if tx := u.x.at(i); tx != nil {
273+
// The inferred type tx may be or contain x again but we don't
274+
// want to "unpack" it again when unifying tx with y: tx is the
275+
// inferred type. Mask type parameter x for this recursion, so
276+
// that subsequent encounters treat x like an ordinary type.
277+
u.x.mask(i)
278+
defer u.x.unmask(i)
262279
return u.nifyEq(tx, y, p)
263280
}
264281
// otherwise, infer type from y
@@ -268,6 +285,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
268285
case j >= 0:
269286
// y is a type parameter, x is not
270287
if ty := u.y.at(j); ty != nil {
288+
// see comment above
289+
u.y.mask(j)
290+
defer u.y.unmask(j)
271291
return u.nifyEq(x, ty, p)
272292
}
273293
// otherwise, infer type from x

0 commit comments

Comments
 (0)