Skip to content

Commit ccf84a9

Browse files
committed
cmd/compile: catch pointless recursion on function types
If a function type has no type parameters, note when it is visited and do not recur. (It must be visited at least once because of closures and their associated types occurring in a generic context). Fixes #51832. Change-Id: Iee20612ffd0a03b838b9e59615f4a0206fc8940b Reviewed-on: https://go-review.googlesource.com/c/go/+/406714 Reviewed-by: Keith Randall <[email protected]>
1 parent 0a30cf9 commit ccf84a9

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

src/cmd/compile/internal/typecheck/subr.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,8 @@ type Tsubster struct {
10131013
Vars map[*ir.Name]*ir.Name
10141014
// If non-nil, function to substitute an incomplete (TFORW) type.
10151015
SubstForwFunc func(*types.Type) *types.Type
1016+
// Prevent endless recursion on functions. See #51832.
1017+
Funcs map[*types.Type]bool
10161018
}
10171019

10181020
// Typ computes the type obtained by substituting any type parameter or shape in t
@@ -1030,7 +1032,8 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type {
10301032
}
10311033

10321034
func (ts *Tsubster) typ1(t *types.Type) *types.Type {
1033-
if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
1035+
hasParamOrShape := t.HasTParam() || t.HasShape()
1036+
if !hasParamOrShape && t.Kind() != types.TFUNC {
10341037
// Note: function types need to be copied regardless, as the
10351038
// types of closures may contain declarations that need
10361039
// to be copied. See #45738.
@@ -1066,10 +1069,10 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
10661069

10671070
var newsym *types.Sym
10681071
var neededTargs []*types.Type
1069-
var targsChanged bool
1072+
var targsChanged bool // == are there any substitutions from this
10701073
var forw *types.Type
10711074

1072-
if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
1075+
if t.Sym() != nil && hasParamOrShape {
10731076
// Need to test for t.HasTParam() again because of special TFUNC case above.
10741077
// Translate the type params for this type according to
10751078
// the tparam/targs mapping from subst.
@@ -1144,6 +1147,17 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
11441147
}
11451148

11461149
case types.TFUNC:
1150+
// watch out for endless recursion on plain function types that mention themselves, e.g. "type T func() T"
1151+
if !hasParamOrShape {
1152+
if ts.Funcs[t] { // Visit such function types only once.
1153+
return t
1154+
}
1155+
if ts.Funcs == nil {
1156+
// allocate lazily
1157+
ts.Funcs = make(map[*types.Type]bool)
1158+
}
1159+
ts.Funcs[t] = true
1160+
}
11471161
newrecvs := ts.tstruct(t.Recvs(), false)
11481162
newparams := ts.tstruct(t.Params(), false)
11491163
newresults := ts.tstruct(t.Results(), false)
@@ -1179,6 +1193,9 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type {
11791193
newt = types.NewSignature(t.Pkg(), newrecv, tparamfields,
11801194
newparams.FieldSlice(), newresults.FieldSlice())
11811195
}
1196+
if !hasParamOrShape {
1197+
delete(ts.Funcs, t)
1198+
}
11821199

11831200
case types.TINTER:
11841201
newt = ts.tinter(t, targsChanged)

test/typeparam/issue51832.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// compile
2+
3+
// Copyright 2022 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
type F func() F
10+
11+
func do[T any]() F {
12+
return nil
13+
}
14+
15+
type G[T any] func() G[T]
16+
17+
//go:noinline
18+
func dog[T any]() G[T] {
19+
return nil
20+
}
21+
22+
func main() {
23+
do[int]()
24+
dog[int]()
25+
}

0 commit comments

Comments
 (0)