Unfortunately those two are linked together. C compilers produce insecure C code because the semantics of the C language limit the options that the C compiler has for generating secure code. You could make a C compiler that generated more secure code, but doing so would make it non-compatible with the C standard and would most likely break a large swath of programs that compile and run on the other non-secure compilers. Worse still it would still generate insecure code even if less of it because things like address aliasing, and being able to cast ints to pointers are fundamentally insecure.
For an idea of what it would take to make C actually secure (and even then only in so much as you reasonably can) take a look at all the things Rust has had to do. Nearly everything that exists in Rust is there because they started with a C feature and asked themselves "what do we need to change to make this safe/secure?". Most other languages as a minimal first step towards improving security remove direct access to pointers, they're simply too powerful to do securely in the manner C does. Rust has (mostly) secure pointers, but only after putting an insane amount of work into bolting a massively complicated ownership and lifetime tracking system on top of it, and even then it's still not perfectly secure since they had to include unsafe blocks to provide a trap door for certain edge cases (also for C interop).
So yes, you could make a secure C compiler, it just wouldn't compile C code anymore, it would compile some "safe" subset of C which would probably end up looking like either Rust or C# (or maybe ObjectiveC).
> being able to cast ints to pointers are fundamentally insecure
Your compiler could track those ints used as pointers all the way throughout the whole program and prevent out of bound values either at compile time or at runtime for compatibility. There is nothing fundamental about C that makes it incompatible with memory safety.
Which was kind of my point. In order for the compiler to have the info it needs to properly track everything and prevent unsafe things from happening you need to extend the language to provide enough context for the compiler to work with. Once you do that you've basically arrived at Rust (or something very much like it).
The compiler has the info it needs already, you don't need to change C to make it memory safe. I even posted a link to a working memory safe C compiler, that preserves complete compatibility. The only downside, at least initially, is that you have to sacrifice some of the performance. And with enough resources pouring into the compiler you can get most of that performance back eventually.
Or Cyclone, Vault, Clay, SAFEcode, or Softbound+CETS that are either C variants or protect legacy C. Rust is a totally separate topic and style. I'd push Modula-2 with C-like syntax on C programmers before Rust.
For an idea of what it would take to make C actually secure (and even then only in so much as you reasonably can) take a look at all the things Rust has had to do. Nearly everything that exists in Rust is there because they started with a C feature and asked themselves "what do we need to change to make this safe/secure?". Most other languages as a minimal first step towards improving security remove direct access to pointers, they're simply too powerful to do securely in the manner C does. Rust has (mostly) secure pointers, but only after putting an insane amount of work into bolting a massively complicated ownership and lifetime tracking system on top of it, and even then it's still not perfectly secure since they had to include unsafe blocks to provide a trap door for certain edge cases (also for C interop).
So yes, you could make a secure C compiler, it just wouldn't compile C code anymore, it would compile some "safe" subset of C which would probably end up looking like either Rust or C# (or maybe ObjectiveC).