The problem with existing attempts to fix C, like Cyclone, are they're creating a new language, but what we really want is C, with improvements. The approach should not be to make a new language, but to augment C with optional new features, which can be incrementally applied to existing codebases to improve them.

You should start with a plain old C compiler, and add the features you want in ways that fully preserve backward compatibility. Code written with these new features should compile with existing C compilers without changing any semantics, and not only your own compiler. Using an existing compiler rather than yours would just mean they're not taking advantage of the features you add.

To give an example, lets say you want to augment pointers with some kind of ownership semantics that your compiler can statically check. We can add some type qualifiers in place of `restrict`.

void * _Owned foo;
void * _Shared foo;

We could make `_Owned` and `_Shared` keywords in the dialect compiled by your compiler, but we need the code to still work with an existing compiler. To fix this we can simply define them the mean nothing.

#if defined(__MY_DIALECT__)
#define _Owned __my_dialect_owned
#define _Shared __my_dialect_shared
#else
#define _Owned
#define _Shared
#endif

Now when you compile with your compiler, it can be checked that you are not performing use-after-move, but if you're compiling with an existing compiler, the code will still compile, but the checks will not be done.

An alternative syntactic representation from the above could use `[[attributes]]` which are now part of the C standard, but attributes can only appear in certain places, whereas symbols defined by the preprocessor can appear anywhere.

---

An example of good retrofitting is C#'s adding of non-nullable reference types. Using non-nullabiliy is optional, but can be made the default. When not enabled globally they can be used explicitly with `X!`. We can gradually annotate existing codebases to use non-nullable references, and then once we have updated the full translation unit we can enable them by default globally, so that `X` means `X!` instead of `X?`. The approach lets us gradually improve a codebase without having to rewrite it to use the new feature all at once.

Contrast this to Cyclone, which required you update the full translation unit for the Cyclone compiler to utilize non-nullable types.

If we were to add non-nullable pointers to C, we could take an approach like the above, where we have `void * _Nullable` and `void * _Notnull`, with the default setting for a translation unit provided with a `#pragma` - meaning `void *` without any annotation would default to nullable, but when the pragma is set, they become not-null by default. If, eventually you convert a whole codebase to using non-nullable pointers, you could enable it globally with a compiler switch and omit the pragmas, and from that point onward you would have to explicitly mark pointers that may be null with `_Nullable`.

---

An additional advantage of approaching it this way is that you can focus on the front-end facing features and leave the optimization to an existing compiler.

IMO this is the only sane approach to retrofit C. You need to be a compatible superset of C. You also need to have ABI compatibility because C is the lingua-franca for other languages to communicate with the OS.

I also think the C committee should stop trying to add new features into the standard until they've been proven in practice. While many of the proposals[1], such as to clean up various parts of the specification (Slay some earthly demons), are worthwhile, there are some contributors who propose adding X, Y, Z, without an actual implementation of them that can be experimented with, like they're competing with each other to get their pet feature into the standard.

What would be ideal would be if we could take some C26 code and compile it with a C23 compiler, because they added features in ways like the above, where they give additional meaning to the new compiler, but are just annotations that perform no function when compiled with an old compiler.

New features should be implemented and utilized before being considered for standardization. Let various ideas compete and let the best ones win, because prematurely adding features just piles more and more technical debt into the language, and makes it more difficult to add improvements further down the line.

[1]:https://www.open-std.org/jtc1/sc22/wg14/www/docs/?C=M;O=D