Source file test/escape_dynconst_closure.go
1 // errorcheck -0 -m -d=closure 2 //go:build !goexperiment.newinliner 3 4 // Copyright 2026 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 // Test that closures do not capture variables that hold a constant, and 9 // stop being closures if they capture nothing else. 10 11 package main 12 13 var sink func() 14 15 // The closure captures s only, which holds a constant. 16 func constOnly() { 17 s := "" 18 f := func() { println(s) } // ERROR "can inline constOnly.func1" "func literal does not escape" "closure converted to global" 19 g(f) 20 } 21 22 // Same, but the closure escapes, so its closure record would be heap 23 // allocated if it still captured anything. 24 func constOnlyEscapes() { 25 i := 42 26 sink = func() { println(i) } // ERROR "can inline constOnlyEscapes.func1" "func literal escapes to heap" "closure converted to global" 27 g(nil) 28 } 29 30 // Same as constOnlyEscapes, but without any call, so that no 31 // ReassignOracle has been built for the enclosing function yet. 32 func constOnlyNoCall() { // ERROR "can inline constOnlyNoCall" 33 i := 42 34 sink = func() { println(i) } // ERROR "can inline constOnlyNoCall.func1" "func literal escapes to heap" "closure converted to global" 35 } 36 37 // Variables that do not hold a constant are still captured. 38 func mixed(n int) { 39 s := "" 40 r := "" 41 f := func() { // ERROR "can inline mixed.func1" "func literal does not escape" "stack closure, captured vars = \[n r\]" 42 println(s) 43 println(n) 44 println(&r) 45 } 46 g(f) 47 } 48 49 // Nested closures capturing the same variable. 50 func nested() { // ERROR "can inline nested" 51 s := "" 52 g(func() { // ERROR "can inline nested.func1" "func literal does not escape" "closure converted to global" 53 g(func() { println(s) }) // ERROR "can inline nested.func1.1" "func literal does not escape" "closure converted to global" 54 }) 55 } 56 57 // The constant is still visible to the interface conversion below, which 58 // therefore does not need to allocate. See also test/fixedbugs/issue4085b.go, 59 // where replacing the captured variable with a literal instead would turn a 60 // run time panic into a compile time error. 61 func iface() { 62 i := 42 63 f := func() { h(i) } // ERROR "can inline iface.func1" "func literal does not escape" "closure converted to global" "42 does not escape" 64 g(f) 65 } 66 67 // A variable that is reassigned after being captured is captured by 68 // reference, so its value is not a constant. 69 func reassigned() { 70 s := "" 71 f := func() { println(s) } // ERROR "can inline reassigned.func1" "func literal does not escape" "stack closure, captured vars = \[s\]" 72 s = "x" 73 g(f) 74 } 75 76 // A variable whose address is taken is captured by reference. 77 func addrtaken() { 78 s := "" 79 f := func() { println(s) } // ERROR "can inline addrtaken.func1" "func literal does not escape" "stack closure, captured vars = \[s\]" 80 _ = &s 81 g(f) 82 } 83 84 //go:noinline 85 func g(func()) { 86 } 87 88 //go:noinline 89 func h(any) { 90 } 91