// errorcheck -0 -m -d=closure //go:build !goexperiment.newinliner // Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Test that closures do not capture variables that hold a constant, and // stop being closures if they capture nothing else. package main var sink func() // The closure captures s only, which holds a constant. func constOnly() { s := "" f := func() { println(s) } // ERROR "can inline constOnly.func1" "func literal does not escape" "closure converted to global" g(f) } // Same, but the closure escapes, so its closure record would be heap // allocated if it still captured anything. func constOnlyEscapes() { i := 42 sink = func() { println(i) } // ERROR "can inline constOnlyEscapes.func1" "func literal escapes to heap" "closure converted to global" g(nil) } // Same as constOnlyEscapes, but without any call, so that no // ReassignOracle has been built for the enclosing function yet. func constOnlyNoCall() { // ERROR "can inline constOnlyNoCall" i := 42 sink = func() { println(i) } // ERROR "can inline constOnlyNoCall.func1" "func literal escapes to heap" "closure converted to global" } // Variables that do not hold a constant are still captured. func mixed(n int) { s := "" r := "" f := func() { // ERROR "can inline mixed.func1" "func literal does not escape" "stack closure, captured vars = \[n r\]" println(s) println(n) println(&r) } g(f) } // Nested closures capturing the same variable. func nested() { // ERROR "can inline nested" s := "" g(func() { // ERROR "can inline nested.func1" "func literal does not escape" "closure converted to global" g(func() { println(s) }) // ERROR "can inline nested.func1.1" "func literal does not escape" "closure converted to global" }) } // The constant is still visible to the interface conversion below, which // therefore does not need to allocate. See also test/fixedbugs/issue4085b.go, // where replacing the captured variable with a literal instead would turn a // run time panic into a compile time error. func iface() { i := 42 f := func() { h(i) } // ERROR "can inline iface.func1" "func literal does not escape" "closure converted to global" "42 does not escape" g(f) } // A variable that is reassigned after being captured is captured by // reference, so its value is not a constant. func reassigned() { s := "" f := func() { println(s) } // ERROR "can inline reassigned.func1" "func literal does not escape" "stack closure, captured vars = \[s\]" s = "x" g(f) } // A variable whose address is taken is captured by reference. func addrtaken() { s := "" f := func() { println(s) } // ERROR "can inline addrtaken.func1" "func literal does not escape" "stack closure, captured vars = \[s\]" _ = &s g(f) } //go:noinline func g(func()) { } //go:noinline func h(any) { }