I dont think the interpretation of heap allocation if and only if escapes is correct. The stack is only used for small and fixed size allocations currently. Therefore some variables/expressions might not escape but are still allocated on the heap. Examples I can think of in the current go gc implementation are big fixed size slice literals, maps past the initial small size, string concatenations larger than 32 bytes. Even with escape analysis not changing the compiler might choose to do heap allocations for different situations and cut offs. Stack allocation is an optimisation and escape analysis is just one factor to decide if stack allocation should be done but the final decision depends on compiler implementation outside escape analysis. The compiler might choose to allocate everything on the heap in a very simplistic version.
I thought it again, and now think the message should be "a + a does not escape" instead. The accurate meaning of it is "the header part of a + a does not escape". As you have explained, it is hard (impossible actually) for the compiler to determine whether or not the underlying part of "a + a" will escape to heap.