Structs have many limitations though and two different argument passing semantics in the same language mean more complexity. Also you may be lucky to eliminate allocations in your own code, but what about libraries? Existence of GC shepherds programmers and library creators into heap allocation. You don't need to write new explicitly to allocate on the heap.
Also high level languages with no GC make manual memory management much more usable and provide way better ergonomics in this area, just because they have to. E.g. automated reference counting, ownership/lifetime control, move semantics etc.
Automated reference counting is a garbage collection algorithm, and it is possible to have ownership/lifetime control without giving up on GC, Chapel, ParaSail, Haskell, OCaml, Swift, D are following up exactly this path.
Automated reference counting is technically a GC, but a kind of GC that can be enabled only for a subset of objects. In languages which force GC on everything (even reference counted GC) the incentive to provide abstractions that work without GC are much weaker. It is just hard to opt-out from GC once you have it and once all your stdlib relies on it. I thing D learned it the hard way.
Stack and register allocation in Haskell is under compiler control via escape analysis, however you can allocate native heap outside GC via mallocBytes/allocaBytes/free, and if they get integrated into mainstream GHC, linear types.
Besides, plenty of GC enabled languages offer the option to stack allocate, static global allocations, or native heap.
Some examples, including languages that for whatever reason failed on the mainstream market.
D, C#, Swift, Oberon, Oberon-2, Active Oberon, Component Pascal, Mesa/Cedar, Sing#, System C#, Nim, Modula-2+, Modula-3, VB, Xojo, C++ (via C++/CLI, C++/CX and Unreal C++), Common Lisp.