Description
Consider the following program:
package pkg
import (
"iter"
)
func trivialIterator() iter.Seq[int] {
return func(yield func(int) bool) {
yield(0)
}
}
func consumer() {
for range trivialIterator() {
foo()
foo()
}
}
//go:noinline
func foo() {}
Building it with GOEXPERIMENT=newinliner
with go1.24-cbdb3545ad
reports:
./foo.go:8:9: can inline trivialIterator.func1 with cost 60 as: func(func(int) bool) { yield(0) }
./foo.go:7:6: can inline trivialIterator with cost 17 as: func() iter.Seq[int] { return func literal }
./foo.go:21:6: cannot inline foo: marked go:noinline
./foo.go:13:6: cannot inline consumer: function too complex: cost 175 exceeds budget 160
./foo.go:14:2: cannot inline consumer-range1: function too complex: cost 190 exceeds budget 160
./foo.go:14:27: inlining call to trivialIterator with score -23
./foo.go:8:9: can inline consumer.trivialIterator.func1 with cost 60 as: func(func(int) bool) { yield(0) }
./foo.go:14:2: inlining call to consumer.trivialIterator.func1 with score 60
./foo.go:8:14: yield does not escape
./foo.go:8:9: func literal escapes to heap:
./foo.go:8:9: flow: ~r0 = &{storage for func literal}:
./foo.go:8:9: from func literal (spill) at ./foo.go:8:9
./foo.go:8:9: from return func literal (return) at ./foo.go:8:2
./foo.go:8:9: func literal escapes to heap
./foo.go:8:14: yield does not escape
./foo.go:14:2: consumer capturing by ref: #state1 (addr=false assign=true width=8)
./foo.go:14:2: func literal does not escape
./foo.go:14:27: func literal does not escape
Note that despite the utter trivialness of the loop body (consumer-range1
), it cannot be inlined.
I think that inlining of range bodies deserves a special case and that the cost of the synthetic function is unimportant. If this were a "normal" loop, the body would inherently be part of the consumer
function, no matter how complex it'd be. If we know that yield
is only called in a single place syntactically, then we should be able to unconditionally inline the range func and produce code no more complex than for a normal loop (excluding the state tracking for range funcs, but that's fixed overhead).
I'm sure I'm missing something.
(The bot will find many related issues, but I don't think any of them directly mention this general case.)
Metadata
Metadata
Assignees
Labels
Type
Projects
Status