From 17fed5980b17e06fdf1dab2437eefae266e5c46b Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 18 Mar 2026 11:27:00 -0700 Subject: [PATCH] internal/abi: add EscapeNonString, EscapeToResultNonString --- src/internal/abi/escape.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/internal/abi/escape.go b/src/internal/abi/escape.go index 1f4c76331..5dcdd636b 100644 --- a/src/internal/abi/escape.go +++ b/src/internal/abi/escape.go @@ -15,3 +15,35 @@ func Escape[T any](x T) T { // as-is. panic("internal/abi.Escape: unreachable (implemented in the compiler)") } + +// EscapeNonString forces v to be on the heap, if v contains a +// non-string pointer. +// +// This is used in hash/maphash.Comparable. We cannot hash pointers +// to local variables on stack, as their addresses might change on +// stack growth. Strings are okay as the hash depends on only the +// content, not the pointer. +// +// This is essentially +// +// if hasNonStringPointers(T) { Escape(v) } +// +// Implemented as a compiler intrinsic. +func EscapeNonString[T any](v T) { panic("intrinsic") } + +// EscapeToResultNonString models a data flow edge from v to the result, +// if v contains a non-string pointer. If v contains only string pointers, +// it returns a copy of v, but is not modeled as a data flow edge +// from the escape analysis's perspective. +// +// This is used in unique.clone, to model the data flow edge on the +// value with strings excluded, because strings are cloned (by +// content). +// +// TODO: probably we should define this as a intrinsic and EscapeNonString +// could just be "heap = EscapeToResultNonString(v)". This way we can model +// an edge to the result but not necessarily heap. +func EscapeToResultNonString[T any](v T) T { + EscapeNonString(v) + return *(*T)(NoEscape(unsafe.Pointer(&v))) +}