Go is a perfectly fine middleware language, but no C replacement. (Rust does much better in that regard).
Edit: formatting
* calloc would a;so accept a typename T and count and return T[count]*
* Generic containers could be implemented as something closer to C++ templates (or preprocessor macros on steroids, like T4, as you suggest).
IIRC, historically void* didn't exist, and it used char*. It added void as a unit type, and absent historical baggage could add... let's call it "noreturn" as a bottom type:
#define NULL ((noreturn*)0)
noreturn* malloc(size_t);
void free(void*);
noreturn exit(int); // never returns
int main() {
// where (T)... is any expression of type T
void x = (T)...; // throw away the value
T y = (noreturn)...; // a nonterminating expression
void* p = (T*)...; // pointers convertible *to* void*
T* q = (noreturn*)...; // pointers convertible *from* noreturn*
*(noreturn*)...; // notionally, this should always fault
*(void*)...; // read zero bytes, so always fine
}Rust is a sane C++: Zero-overhead abstractions, not afraid of language complexity.
Go is a modern C: Simplicity, stability.
They have some overlap in what they're best at, but they both take on unique and important missions that both need to be targeted, in our rapidly expanding universe of software engineering.
(edit: formatting)
Simplicity: yes, but in a senseful manner. Omitting generics is not senseful to me.