Skip to content

Commit 4f45b2b

Browse files
committed
cmd/compile: fix out of memory when inlining closure
CL 629195 strongly favor closure inlining, allowing closures to be inlined more aggressively. However, if the closure body contains a call to a function, which itself is one of the call arguments, it causes the infinite inlining. Fixing this by prevent this kind of functions from being inlinable. Fixes #72063 Change-Id: I5fb5723a819b1e2c5aadb57c1023ec84ca9fa53c Reviewed-on: https://go-review.googlesource.com/c/go/+/654195 Reviewed-by: David Chase <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 8279188 commit 4f45b2b

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

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

+22
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,28 @@ func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCa
10331033
return false, 0, false
10341034
}
10351035
}
1036+
do := func(fn *ir.Func) bool {
1037+
// Can't recursively inline a function if the function body contains
1038+
// a call to a function f, which the function f is one of the call arguments.
1039+
return ir.Any(fn, func(node ir.Node) bool {
1040+
if call, ok := node.(*ir.CallExpr); ok {
1041+
for _, arg := range call.Args {
1042+
if call.Fun == arg {
1043+
return true
1044+
}
1045+
}
1046+
}
1047+
return false
1048+
})
1049+
}
1050+
for _, fn := range []*ir.Func{callerfn, callee} {
1051+
if do(fn) {
1052+
if log && logopt.Enabled() {
1053+
logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to function: %s", ir.FuncName(fn)))
1054+
}
1055+
return false, 0, false
1056+
}
1057+
}
10361058

10371059
if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) {
10381060
// Runtime package must not be instrumented.

test/fixedbugs/issue72063.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// run
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 main
8+
9+
import "fmt"
10+
11+
// Y is the Y-combinator based on https://dreamsongs.com/Files/WhyOfY.pdf
12+
func Y[Endo ~func(RecFct) RecFct, RecFct ~func(T) R, T, R any](f Endo) RecFct {
13+
14+
type internal[RecFct ~func(T) R, T, R any] func(internal[RecFct, T, R]) RecFct
15+
16+
g := func(h internal[RecFct, T, R]) RecFct {
17+
return func(t T) R {
18+
return f(h(h))(t)
19+
}
20+
}
21+
return g(g)
22+
}
23+
24+
func main() {
25+
26+
fct := Y(func(r func(int) int) func(int) int {
27+
return func(n int) int {
28+
if n <= 0 {
29+
return 1
30+
}
31+
return n * r(n-1)
32+
}
33+
})
34+
35+
want := 3628800
36+
if got := fct(10); got != want {
37+
msg := fmt.Sprintf("unexpected result, got: %d, want: %d", got, want)
38+
panic(msg)
39+
}
40+
}

0 commit comments

Comments
 (0)