Define 'separate address spaces', you have linear address space (virtual) and kernel/user 'address spaces' which are address spaces but not in the way linear address spaces are.
It would be easier to simply move almost everything not needed by the kernel into 'userspace' and have some sort of capability system to enforce access rights.
There is a missing word; I meant to say "we wouldn't even need separate address spaces" ("separate" was meant as an adjective, not as a verb).
And by that I meant that we would need no hardware support for separate address spaces. The kernel and all processes would share a single address space. We could look at memory as just another resource, and readable or writeable chunks of memory ("buffers") would be only accessible through authorized "memory arbitrator" entities. Of course some mechanism is required to prevent programs from making up buffers on their own; this can be done in hardware of course, but alternatively also by software mechanism, e.g. enforcing a higher-level programming language like Lisp.
So one could even say that each such buffer was their own address space; however I'm convinced the approach is much simpler and less arbitrary (no coupling process<->address space).
"Virtual address spaces" are a required abstraction, just as filesystems need to provide the illusion of contiguous available storage for files. This could of course be done in software, just as filesystems do it in software. However it would be a lot slower since we don't batch memory accesses like we batch file accesses.
The key thing here is the separation of memory protection from memory addressing. Virtual memory addressing is a required abstraction to deal with stuff like paging. Memory protection does not need to be tied to addressing, and once you do that you can basically get this buffer abstraction that you are describing.
The buffers are just chunks of the global address space that this process has been allowed to access. Processes already deal with a similar abstraction, what does mmap return but a buffer of memory? The only difference from the point of view of writing software is that pointers in shared memory start to work better.
IIRC, Microsoft tried to build something along those lines with the Singularity research operating system - it did not use virtual memory to isolate processes from each other, but it strongly restricted how processes could exchange data, thus getting pretty much the same isolation level conventional operating systems get by using virtual memory.
I even think the source code to Singularity has been released under an open source-ish license, if you want to look into it.
It would be easier to simply move almost everything not needed by the kernel into 'userspace' and have some sort of capability system to enforce access rights.