Skip to content

Commit 9e1eea6

Browse files
committed
go/types: detect constraint type inference cycles
This is a port of CL 347300 to go/types. The test was adjusted to match the differing error positioning in go/types: errors are placed on the ast.CallExpr.Fun, rather than the Lparen. Change-Id: I3dee5d7d75bae18817cc1f04ab0d357d3a6a8198 Reviewed-on: https://go-review.googlesource.com/c/go/+/348689 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent b86e8dd commit 9e1eea6

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

src/go/types/infer.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package types
99

1010
import (
11+
"fmt"
1112
"go/token"
1213
"strings"
1314
)
@@ -404,6 +405,34 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type, report bool) (t
404405
}
405406
}
406407

408+
// The data structure of each (provided or inferred) type represents a graph, where
409+
// each node corresponds to a type and each (directed) vertice points to a component
410+
// type. The substitution process described above repeatedly replaces type parameter
411+
// nodes in these graphs with the graphs of the types the type parameters stand for,
412+
// which creates a new (possibly bigger) graph for each type.
413+
// The substitution process will not stop if the replacement graph for a type parameter
414+
// also contains that type parameter.
415+
// For instance, for [A interface{ *A }], without any type argument provided for A,
416+
// unification produces the type list [*A]. Substituting A in *A with the value for
417+
// A will lead to infinite expansion by producing [**A], [****A], [********A], etc.,
418+
// because the graph A -> *A has a cycle through A.
419+
// Generally, cycles may occur across multiple type parameters and inferred types
420+
// (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]).
421+
// We eliminate cycles by walking the graphs for all type parameters. If a cycle
422+
// through a type parameter is detected, cycleFinder nils out the respectice type
423+
// which kills the cycle; this also means that the respective type could not be
424+
// inferred.
425+
//
426+
// TODO(gri) If useful, we could report the respective cycle as an error. We don't
427+
// do this now because type inference will fail anyway, and furthermore,
428+
// constraints with cycles of this kind cannot currently be satisfied by
429+
// any user-suplied type. But should that change, reporting an error
430+
// would be wrong.
431+
w := cycleFinder{tparams, types, make(map[Type]bool)}
432+
for _, t := range tparams {
433+
w.typ(t) // t != nil
434+
}
435+
407436
// dirty tracks the indices of all types that may still contain type parameters.
408437
// We know that nil type entries and entries corresponding to provided (non-nil)
409438
// type arguments are clean, so exclude them from the start.
@@ -452,3 +481,98 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type, report bool) (t
452481

453482
return
454483
}
484+
485+
type cycleFinder struct {
486+
tparams []*TypeParam
487+
types []Type
488+
seen map[Type]bool
489+
}
490+
491+
func (w *cycleFinder) typ(typ Type) {
492+
if w.seen[typ] {
493+
// We have seen typ before. If it is one of the type parameters
494+
// in tparams, iterative substitution will lead to infinite expansion.
495+
// Nil out the corresponding type which effectively kills the cycle.
496+
if tpar, _ := typ.(*TypeParam); tpar != nil {
497+
if i := tparamIndex(w.tparams, tpar); i >= 0 {
498+
// cycle through tpar
499+
w.types[i] = nil
500+
}
501+
}
502+
// If we don't have one of our type parameters, the cycle is due
503+
// to an ordinary recursive type and we can just stop walking it.
504+
return
505+
}
506+
w.seen[typ] = true
507+
defer delete(w.seen, typ)
508+
509+
switch t := typ.(type) {
510+
case *Basic, *top:
511+
// nothing to do
512+
513+
case *Array:
514+
w.typ(t.elem)
515+
516+
case *Slice:
517+
w.typ(t.elem)
518+
519+
case *Struct:
520+
w.varList(t.fields)
521+
522+
case *Pointer:
523+
w.typ(t.base)
524+
525+
// case *Tuple:
526+
// This case should not occur because tuples only appear
527+
// in signatures where they are handled explicitly.
528+
529+
case *Signature:
530+
// There are no "method types" so we should never see a recv.
531+
assert(t.recv == nil)
532+
if t.params != nil {
533+
w.varList(t.params.vars)
534+
}
535+
if t.results != nil {
536+
w.varList(t.results.vars)
537+
}
538+
539+
case *Union:
540+
for _, t := range t.terms {
541+
w.typ(t.typ)
542+
}
543+
544+
case *Interface:
545+
for _, m := range t.methods {
546+
w.typ(m.typ)
547+
}
548+
for _, t := range t.embeddeds {
549+
w.typ(t)
550+
}
551+
552+
case *Map:
553+
w.typ(t.key)
554+
w.typ(t.elem)
555+
556+
case *Chan:
557+
w.typ(t.elem)
558+
559+
case *Named:
560+
for _, tpar := range t.TypeArgs().list() {
561+
w.typ(tpar)
562+
}
563+
564+
case *TypeParam:
565+
if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil {
566+
w.typ(w.types[i])
567+
}
568+
569+
default:
570+
panic(fmt.Sprintf("unexpected %T", typ))
571+
}
572+
}
573+
574+
func (w *cycleFinder) varList(list []*Var) {
575+
for _, v := range list {
576+
w.typ(v.typ)
577+
}
578+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 f1[P interface{ *P }]() {}
8+
func f2[P interface{ func(P) }]() {}
9+
func f3[P, Q interface{ func(Q) P }]() {}
10+
func f4[P interface{ *Q }, Q interface{ func(P) }]() {}
11+
func f5[P interface{ func(P) }]() {}
12+
func f6[P interface { *Tree[P] }, Q any ]() {}
13+
14+
func _() {
15+
f1 /* ERROR cannot infer P */ ()
16+
f2 /* ERROR cannot infer P */ ()
17+
f3 /* ERROR cannot infer P */ ()
18+
f4 /* ERROR cannot infer P */ ()
19+
f5 /* ERROR cannot infer P */ ()
20+
f6 /* ERROR cannot infer P */ ()
21+
}
22+
23+
type Tree[P any] struct {
24+
left, right *Tree[P]
25+
data P
26+
}
27+
28+
// test case from issue
29+
30+
func foo[Src interface { func() Src }]() Src {
31+
return foo[Src]
32+
}
33+
34+
func _() {
35+
foo /* ERROR cannot infer Src */ ()
36+
}

0 commit comments

Comments
 (0)