Skip to content

Commit 34073a7

Browse files
cuonglmgopherbot
authored andcommitted
cmd/compile: avoid infinite recursion when inlining closures
CL 630696 changes budget for once-called closures, making them more inlinable. However, when recursive inlining involve both the closure and its parent, the inliner goes into an infinite loop: parent (a closure) -> closure -> parent -> ... The problem here dues to the closure name mangling, causing the inlined checking condition failed, since the closure name affects how the linker symbol generated. To fix this, just prevent the closure from inlining its parent into itself, avoid the infinite inlining loop. Fixes #71680 Change-Id: Ib27626d70f95e5f1c24a3eb1c8e6c3443b7d90c8 Reviewed-on: https://go-review.googlesource.com/c/go/+/649656 Reviewed-by: David Chase <[email protected]> Auto-Submit: Cuong Manh Le <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent a7489b8 commit 34073a7

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

src/cmd/compile/internal/inline/inl.go

+32
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,38 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
10091009
return false, 0, false
10101010
}
10111011

1012+
isClosureParent := func(closure, parent *ir.Func) bool {
1013+
for p := closure.ClosureParent; p != nil; p = p.ClosureParent {
1014+
if p == parent {
1015+
return true
1016+
}
1017+
}
1018+
return false
1019+
}
1020+
if isClosureParent(callerfn, callee) {
1021+
// Can't recursively inline a parent of the closure into itself.
1022+
if log && logopt.Enabled() {
1023+
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to closure parent: %s, %s", ir.FuncName(callerfn), ir.FuncName(callee)))
1024+
}
1025+
return false, 0, false
1026+
}
1027+
if isClosureParent(callee, callerfn) {
1028+
// Can't recursively inline a closure if there's a call to the parent in closure body.
1029+
if ir.Any(callee, func(node ir.Node) bool {
1030+
if call, ok := node.(*ir.CallExpr); ok {
1031+
if name, ok := call.Fun.(*ir.Name); ok && isClosureParent(callerfn, name.Func) {
1032+
return true
1033+
}
1034+
}
1035+
return false
1036+
}) {
1037+
if log && logopt.Enabled() {
1038+
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to closure parent: %s, %s", ir.FuncName(callerfn), ir.FuncName(callee)))
1039+
}
1040+
return false, 0, false
1041+
}
1042+
}
1043+
10121044
if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) {
10131045
// Runtime package must not be instrumented.
10141046
// Instrument skips runtime package. However, some runtime code can be

test/fixedbugs/issue71680.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// compile
2+
3+
// Copyright 2025 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 p
8+
9+
type Parser struct{}
10+
type Node struct{}
11+
12+
type parserState func(p *Parser) parserState
13+
14+
func parserStateData(root *Node) parserState {
15+
return func(p *Parser) parserState {
16+
return parserStateOpenMap(root)(p)
17+
}
18+
}
19+
20+
func parserStateOpenMap(root *Node) parserState {
21+
return func(p *Parser) parserState {
22+
switch {
23+
case p != nil:
24+
return parserStateData(root)(p)
25+
}
26+
return parserStateOpenMap(root)(p)
27+
}
28+
}

0 commit comments

Comments
 (0)