// errorcheck -0 -m // Copyright 2025 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. package escape type hashIface interface { Sum() []byte } type cloneableHashIface interface { hashIface Clone() hashIface } type hash struct{ state [32]byte } func (h *hash) Sum() []byte { // ERROR "can inline \(\*hash\).Sum$" "h does not escape$" return make([]byte, 32) // ERROR "make\(\[\]byte, 32\) escapes to heap$" } func (h *hash) Clone() hashIface { // ERROR "can inline \(\*hash\).Clone$" "h does not escape$" c := *h // ERROR "moved to heap: c$" return &c } type hash2 struct{ state [32]byte } func (h *hash2) Sum() []byte { // ERROR "can inline \(\*hash2\).Sum$" "h does not escape$" return make([]byte, 32) // ERROR "make\(\[\]byte, 32\) escapes to heap$" } func (h *hash2) Clone() hashIface { // ERROR "can inline \(\*hash2\).Clone$" "h does not escape$" c := *h // ERROR "moved to heap: c$" return &c } func newHash() hashIface { // ERROR "can inline newHash$" return &hash{} // ERROR "&hash{} escapes to heap$" } func cloneHash1(h hashIface) hashIface { // ERROR "can inline cloneHash1$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone() } return &hash{} // ERROR "&hash{} escapes to heap$" } func cloneHash2(h hashIface) hashIface { // ERROR "can inline cloneHash2$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone() } return nil } func cloneHash3(h hashIface) hashIface { // ERROR "can inline cloneHash3$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone() } return &hash2{} // ERROR "&hash2{} escapes to heap$" } func cloneHashWithBool1(h hashIface) (hashIface, bool) { // ERROR "can inline cloneHashWithBool1$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone(), true } return &hash{}, false // ERROR "&hash{} escapes to heap$" } func cloneHashWithBool2(h hashIface) (hashIface, bool) { // ERROR "can inline cloneHashWithBool2$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone(), true } return nil, false } func cloneHashWithBool3(h hashIface) (hashIface, bool) { // ERROR "can inline cloneHashWithBool3$" "leaking param: h$" if h, ok := h.(cloneableHashIface); ok { return h.Clone(), true } return &hash2{}, false // ERROR "&hash2{} escapes to heap$" } func interleavedWithTypeAssertions() { h1 := newHash() // ERROR "&hash{} does not escape$" "inlining call to newHash" _ = h1.Sum() // ERROR "devirtualizing h1.Sum to \*hash$" "inlining call to \(\*hash\).Sum" "make\(\[\]byte, 32\) does not escape$" h2 := cloneHash1(h1) // ERROR "&hash{} does not escape$" "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHash1" _ = h2.Sum() // ERROR "devirtualizing h2.Sum to \*hash$" "inlining call to \(\*hash\).Sum" "make\(\[\]byte, 32\) does not escape$" h3 := cloneHash2(h1) // ERROR "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHash2" _ = h3.Sum() // ERROR "devirtualizing h3.Sum to \*hash$" "inlining call to \(\*hash\).Sum" "make\(\[\]byte, 32\) does not escape$" h4 := cloneHash3(h1) // ERROR "&hash2{} escapes to heap$" "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHash3" "moved to heap: c$" _ = h4.Sum() h5, _ := cloneHashWithBool1(h1) // ERROR "&hash{} does not escape$" "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHashWithBool1" _ = h5.Sum() // ERROR "devirtualizing h5.Sum to \*hash$" "inlining call to \(\*hash\).Sum" "make\(\[\]byte, 32\) does not escape$" h6, _ := cloneHashWithBool2(h1) // ERROR "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHashWithBool2" _ = h6.Sum() // ERROR "devirtualizing h6.Sum to \*hash$" "inlining call to \(\*hash\).Sum" "make\(\[\]byte, 32\) does not escape$" h7, _ := cloneHashWithBool3(h1) // ERROR "&hash2{} escapes to heap$" "devirtualizing h.Clone to \*hash$" "inlining call to \(\*hash\).Clone" "inlining call to cloneHashWithBool3" "moved to heap: c$" _ = h7.Sum() } type cloneableHashError interface { hashIface Clone() (hashIface, error) } type hash3 struct{ state [32]byte } func newHash3() hashIface { // ERROR "can inline newHash3$" return &hash3{} // ERROR "&hash3{} escapes to heap$" } func (h *hash3) Sum() []byte { // ERROR "can inline \(\*hash3\).Sum$" "h does not escape$" return make([]byte, 32) // ERROR "make\(\[\]byte, 32\) escapes to heap$" } func (h *hash3) Clone() (hashIface, error) { // ERROR "can inline \(\*hash3\).Clone$" "h does not escape$" c := *h // ERROR "moved to heap: c$" return &c, nil } func interleavedCloneableHashError() { h1 := newHash3() // ERROR "&hash3{} does not escape$" "inlining call to newHash3" _ = h1.Sum() // ERROR "devirtualizing h1.Sum to \*hash3$" "inlining call to \(\*hash3\).Sum" "make\(\[\]byte, 32\) does not escape$" if h1, ok := h1.(cloneableHashError); ok { h2, err := h1.Clone() // ERROR "devirtualizing h1.Clone to \*hash3$" "inlining call to \(\*hash3\).Clone" if err == nil { _ = h2.Sum() // ERROR "devirtualizing h2.Sum to \*hash3$" "inlining call to \(\*hash3\).Sum" "make\(\[\]byte, 32\) does not escape$" } } }