Skip to content

Commit 9133245

Browse files
committed
cmd/compile/internal/types2: detect constraint type inference cycles
See the detailed explanations in the code. Fixes #48136. Change-Id: I1667aabfbbff97967913b080c77e7ec04ea82feb Reviewed-on: https://go-review.googlesource.com/c/go/+/347300 Trust: Robert Griesemer <[email protected]> Trust: Cuong Manh Le <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 28dae3d commit 9133245

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

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

+124
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package types2
99
import (
1010
"bytes"
1111
"cmd/compile/internal/syntax"
12+
"fmt"
1213
)
1314

1415
const useConstraintTypeInference = true
@@ -409,6 +410,34 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type, report bool) (t
409410
}
410411
}
411412

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

458487
return
459488
}
489+
490+
type cycleFinder struct {
491+
tparams []*TypeParam
492+
types []Type
493+
seen map[Type]bool
494+
}
495+
496+
func (w *cycleFinder) typ(typ Type) {
497+
if w.seen[typ] {
498+
// We have seen typ before. If it is one of the type parameters
499+
// in tparams, iterative substitution will lead to infinite expansion.
500+
// Nil out the corresponding type which effectively kills the cycle.
501+
if tpar, _ := typ.(*TypeParam); tpar != nil {
502+
if i := tparamIndex(w.tparams, tpar); i >= 0 {
503+
// cycle through tpar
504+
w.types[i] = nil
505+
}
506+
}
507+
// If we don't have one of our type parameters, the cycle is due
508+
// to an ordinary recursive type and we can just stop walking it.
509+
return
510+
}
511+
w.seen[typ] = true
512+
defer delete(w.seen, typ)
513+
514+
switch t := typ.(type) {
515+
case *Basic, *top:
516+
// nothing to do
517+
518+
case *Array:
519+
w.typ(t.elem)
520+
521+
case *Slice:
522+
w.typ(t.elem)
523+
524+
case *Struct:
525+
w.varList(t.fields)
526+
527+
case *Pointer:
528+
w.typ(t.base)
529+
530+
// case *Tuple:
531+
// This case should not occur because tuples only appear
532+
// in signatures where they are handled explicitly.
533+
534+
case *Signature:
535+
// There are no "method types" so we should never see a recv.
536+
assert(t.recv == nil)
537+
if t.params != nil {
538+
w.varList(t.params.vars)
539+
}
540+
if t.results != nil {
541+
w.varList(t.results.vars)
542+
}
543+
544+
case *Union:
545+
for _, t := range t.terms {
546+
w.typ(t.typ)
547+
}
548+
549+
case *Interface:
550+
for _, m := range t.methods {
551+
w.typ(m.typ)
552+
}
553+
for _, t := range t.embeddeds {
554+
w.typ(t)
555+
}
556+
557+
case *Map:
558+
w.typ(t.key)
559+
w.typ(t.elem)
560+
561+
case *Chan:
562+
w.typ(t.elem)
563+
564+
case *Named:
565+
for _, tpar := range t.TArgs().list() {
566+
w.typ(tpar)
567+
}
568+
569+
case *TypeParam:
570+
if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil {
571+
w.typ(w.types[i])
572+
}
573+
574+
default:
575+
panic(fmt.Sprintf("unexpected %T", typ))
576+
}
577+
}
578+
579+
func (w *cycleFinder) varList(list []*Var) {
580+
for _, v := range list {
581+
w.typ(v.typ)
582+
}
583+
}
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)