Java has had AOT compilers since 2000, although they were commercial only, and nowadays there are some free beer ones via GraalVM and OpenJ9.
C# has always supported AOT compilation via NGEN, with the caveat that it needs to be done on-device and only dynamic linking is supported.
Besides NGEN, in the 20 years of .NET history, we had CosmOS AOT compiler, Singularity (whose tech landed on Windows 8 MDIL), Midori (whose tech landed on UWP .NET Native), Mono AOT (still going at it), Unity's IL2CPP, and now Native AOT.
Why do people select c? I assume it is because it is the only compiler they have for some target hardware, or that they are working in a codebase like the Linux kernel.
For the former go won't help. Tinygo may help some day, but for any weird new chip the first thing they make is a c compiler.
The linux kernel accepts rust, or will next year. It does not accept go. So Torvalds at least disagrees it fits there.
Go has plenty of uses, but writing microcontroller firmware and device drivers are not among them. And why else would you pick c?
I default to C. It's fast, runs everywhere, works with everything, and has few surprises. It's standardized and not captured by any individual or organization.
I can't think of a language that comes close to any of that.
C++ has few surprises? This is from Scott Meyers, author of several books on the intricacies of C++:
> I no longer plan to update my books to fix technical errors.It's not that I'm too lazy to do it. It's that in order to fix errors, I have to be able to identify them. That's something I no longer trust myself to do.
He then goes on to explain how the language is so complex, that in two years he has already forgotten enough of the gotchas to trust himself. This is far short of a language with no surprises.
I write in C only when I need to; in my case I have a limited runtime environment available, so I can't use some other language (which I might otherwise prefer) with a larger runtime requirement, and I also already know C, but not C++. Also, none of the libraries I have to use have C++ APIs. Had I already known C++, and if there were C++ APIs available for the libraries I need, then sure, I might have used C++, but as it is, the results (a C++ program but full of C-style programming because of the APIs) would not be worth the effort of learning C++. C++ also imposes a cost; that of reducing possible contributors to those people who already know C++ – knowledge of C is simply more common than C++.
A C++ program which calls nothing but C libraries will not have the “shape” of a C++ program, but essentially the shape and structure of a regular C program. And the small pieces that remain C++-shaped might not be large enough or provide enough architectural benefit for the drawbacks of C++ to be worth it.
If C++ had no operator overloading, and if it had no exceptions, and if the C++ standard specified name-mangling so FFI could work right, then maybe it would come close.
You are not obliged to use all of that in C++ code.
And if lack of standard specified name-mangling disqualifies C++, so it also does to C, because there is no such thing as a C ABI, only OS ABIs that happen to be written in C, and the C compilers of the platform follow the OS ABI, which you can get in C++ with extern "C".
People keep forgetting that before 1990, C didn't had that market, others did.
Macro Assemblers and other systems languages lost to C.
Heck even using Basic, Pascal, C++ instead of C would already be an improvement, and many don't because of religion not lack of tooling, there are enough vendors.
F-Secure decided they wanted to use Go for writing firmware, and so they did, TamaGo unikernel was born and is shipping into USB security keys all over the world.
That isn't exactly standard go or even tiny go, it is their custom go from one vendor that supports one SOC family from one vendor.
As you say, it isn't hard to design a better systems language than c, but I don't think it is hard to design one better than go either.
The key is that isn't enough to be better. The amount of inertia behind c is so much greater than any of its predecessors just by virtue of time and the growth of the industry.
ADA was better, but had bad timing and bad marketing and was encumbered by price. It still did pretty well just on its merits. But a lot of ada domains got reverted to c or c++ for developer supply and interest. Right or wrong, ada wasn't "cool".
I see rust much farther ahead on the adoption path. And while it isn't perfect or even revolutionary, it is an improvement, and the cool kids like it so management lets me use it.
Just like you won't fit standard ISO C into many embeeded scenarios, and have to deal with a crap of vendor specific extensions but it gets called "C", the usual two weights and two measures, when arguing for C.
No, that is fair, the "c" for TI fixed-point dsps is pretty odd. Andy Tiny go is better than I was first thinking after I dug more. It uses llvm backend to target lots of archs. I'm not clear if you get to keep gc or not. Or what other features you lose.
I'm not defending c, and if my only choices for a new project were a weird flavor c or a weird flavor of go, I can't imagine I'd pick c. Or even c99 vs a weird flavor of go.
But I don't forsee that ever being the choice. What does tinygo offer over other llvm backended languages targeting the same archs? GC seems like that might be the answer if you don't lose it? Channels and greenthreads? Library support if it compiles? I really don't find rust too complicated after using for real work. Both nim and zig seem like better choices than go as well. Even julia if you do some nonstandard stuff. And ada would be the other obvious choice.
Edit: sorry D should probably get a mention here too with new targets having been added.
Have a look at tinygo for embedded and system progamming in Go.
Keep in mind Go has an unsafe statement for manual memory management and pointer arithmetic. You can also import C-style malloc/calloc should you need them.
I mentioned tinygo in my post saying it wasn't quite there yet. Before I retorted I went back to dig in and find out why it didn't seem mature yet. I am changing my mind. It supports lots of architectures through llvm and the features it is missing are mostly the ones that should be.
That said, there are at least a couple languages that let you do manual memory management in safe code (ada and rust). While go jas nice syntax, if I'm giving up gc and the other bits missing from tinygo, what does it offer over D or zig?
Even K from K&R uses it!
https://youtu.be/VVpRj3Po6K4