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  

View as plain text