]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: avoid infinite recursion in unification
authorRobert Griesemer <gri@golang.org>
Wed, 29 Sep 2021 03:42:45 +0000 (20:42 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 29 Sep 2021 15:12:05 +0000 (15:12 +0000)
This is an almost clean port of CL 352832 from types2 to go/types:
The nest files and unify.go where copied verbatim; unify.go was
adjusted with correct package name, a slightly different comment
was restored to what it was. The test files got adjustments for
error position. infer.go got a missing _Todo error code.

For #48619.
For #48656.

Change-Id: Ia1a2d09e8bb37a85032b4b7e7c7a0b08e8c793a5
Reviewed-on: https://go-review.googlesource.com/c/go/+/353029
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/go/types/infer.go
src/go/types/testdata/fixedbugs/issue48619.go2 [new file with mode: 0644]
src/go/types/testdata/fixedbugs/issue48656.go2 [new file with mode: 0644]
src/go/types/unify.go

index 0be01d31e8f1f98b25f4a656cceaa3d6bb847eb4..3348c294767e269a8a4088527a2d22aa68bc2e12 100644 (file)
@@ -128,7 +128,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                if inferred != tpar {
                        check.errorf(arg, _Todo, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
                } else {
-                       check.errorf(arg, 0, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+                       check.errorf(arg, _Todo, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
                }
        }
 
diff --git a/src/go/types/testdata/fixedbugs/issue48619.go2 b/src/go/types/testdata/fixedbugs/issue48619.go2
new file mode 100644 (file)
index 0000000..24650a3
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2021 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
+
+func f[P any](a, _ P) {
+       var x int
+       f(a, x /* ERROR type int of x does not match P */)
+       f(x, a /* ERROR type P of a does not match inferred type int for P */)
+}
+
+func g[P any](a, b P) {
+       g(a, b)
+       g(&a, &b)
+       g([]P{}, []P{})
+}
+
+func h[P any](a, b P) {
+       h(&a, &b)
+       h([]P{a}, []P{b})
+}
diff --git a/src/go/types/testdata/fixedbugs/issue48656.go2 b/src/go/types/testdata/fixedbugs/issue48656.go2
new file mode 100644 (file)
index 0000000..ee639e1
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2021 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
+
+// TODO(gri) Still need better error positions and message here.
+//           But this doesn't crash anymore.
+
+func f[P /* ERROR does not match \*Q */ interface{*Q}, Q any](p P, q Q) {
+       _ = f[P]
+        _ = f /* ERROR cannot infer P */ [*P]
+}
index ce78fc8241ca3cb65144dc3fa9298092b379d15e..984ba59e6d896f15a3ce8c78f9ddd29d575083bd 100644 (file)
@@ -9,7 +9,6 @@ package types
 import (
        "bytes"
        "fmt"
-       "sort"
 )
 
 // The unifier maintains two separate sets of type parameters x and y
@@ -64,6 +63,10 @@ func (u *unifier) unify(x, y Type) bool {
 type tparamsList struct {
        unifier *unifier
        tparams []*TypeParam
+       // For each tparams element, there is a corresponding mask bit in masks.
+       // If set, the corresponding type parameter is masked and doesn't appear
+       // as a type parameter with tparamsList.index.
+       masks []bool
        // For each tparams element, there is a corresponding type slot index in indices.
        // index  < 0: unifier.types[-index-1] == nil
        // index == 0: no type slot allocated yet
@@ -104,9 +107,14 @@ func (d *tparamsList) init(tparams []*TypeParam) {
                }
        }
        d.tparams = tparams
+       d.masks = make([]bool, len(tparams))
        d.indices = make([]int, len(tparams))
 }
 
+// mask and unmask permit the masking/unmasking of the i'th type parameter of d.
+func (d *tparamsList) mask(i int)   { d.masks[i] = true }
+func (d *tparamsList) unmask(i int) { d.masks[i] = false }
+
 // join unifies the i'th type parameter of x with the j'th type parameter of y.
 // If both type parameters already have a type associated with them and they are
 // not joined, join fails and returns false.
@@ -138,19 +146,25 @@ func (u *unifier) join(i, j int) bool {
        case ti > 0:
                // Only the type parameter for x has an inferred type. Use x slot for y.
                u.y.setIndex(j, ti)
+       // This case is handled like the default case.
+       // case tj > 0:
+       //      // Only the type parameter for y has an inferred type. Use y slot for x.
+       //      u.x.setIndex(i, tj)
        default:
-               // Either the type parameter for y has an inferred type, or neither type
-               // parameter has an inferred type. In either case, use y slot for x.
+               // Neither type parameter has an inferred type. Use y slot for x
+               // (or x slot for y, it doesn't matter).
                u.x.setIndex(i, tj)
        }
        return true
 }
 
-// If typ is a type parameter of d, index returns the type parameter index.
+// If typ is an unmasked type parameter of d, index returns the type parameter index.
 // Otherwise, the result is < 0.
 func (d *tparamsList) index(typ Type) int {
        if tpar, ok := typ.(*TypeParam); ok {
-               return tparamIndex(d.tparams, tpar)
+               if i := tparamIndex(d.tparams, tpar); i >= 0 && !d.masks[i] {
+                       return i
+               }
        }
        return -1
 }
@@ -243,7 +257,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
                }
        }
 
-       // Cases where at least one of x or y is a type parameter.
+       // Cases where at least one of x or y is an (unmasked) type parameter.
        switch i, j := u.x.index(x), u.y.index(y); {
        case i >= 0 && j >= 0:
                // both x and y are type parameters
@@ -256,6 +270,12 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
        case i >= 0:
                // x is a type parameter, y is not
                if tx := u.x.at(i); tx != nil {
+                       // The inferred type tx may be or contain x again but we don't
+                       // want to "unpack" it again when unifying tx with y: tx is the
+                       // inferred type. Mask type parameter x for this recursion, so
+                       // that subsequent encounters treat x like an ordinary type.
+                       u.x.mask(i)
+                       defer u.x.unmask(i)
                        return u.nifyEq(tx, y, p)
                }
                // otherwise, infer type from y
@@ -265,6 +285,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
        case j >= 0:
                // y is a type parameter, x is not
                if ty := u.y.at(j); ty != nil {
+                       // see comment above
+                       u.y.mask(j)
+                       defer u.y.unmask(j)
                        return u.nifyEq(x, ty, p)
                }
                // otherwise, infer type from x
@@ -399,8 +422,8 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
                                        p = p.prev
                                }
                                if debug {
-                                       assert(sort.IsSorted(byUniqueMethodName(a)))
-                                       assert(sort.IsSorted(byUniqueMethodName(b)))
+                                       assertSortedMethods(a)
+                                       assertSortedMethods(b)
                                }
                                for i, f := range a {
                                        g := b[i]