Live data from Hacker News

Cake – C23 and Beyond (2023)

thradams.com

81–90 of 128 posts

Re: Cake – C23 and Beyond (2023)

#81
I think this is a really interesting direction.

That it can translate C23 to C89 means it has most of the work in place to translate C23 to C23, or C99 to C99 etc. If that is done in a (mostly) reversible fashion - successfully re-encode back to the original, where you `preprocess -> parse -> unparse -> re-preprocess` which is a nuisance but possible, then it opens the door to much more aggressive type systems.

In particular, the input can be C with the ownership annotations, and if they're valid, the output can be C with those annotations dropped to be fed into some other compiler. Or whatever other invariant systems the compiler dev is interested in.

Or the input could be C extended with namespace {} syntax, C++ style lambdas, contract checking - whatever you wish really, and the output can be the extensions desugared into C. Templates (possibly the D style ones) can be implemented as instantiating normal functions from said template.

That the output is C means this is usable in all the pipelines that already work with C.

Good stuff, thanks for posting.

Re: Cake – C23 and Beyond (2023)

#82

I think this is a really interesting direction. That it can translate C23 to C89 means it has most of the work in place to translate C23 to C23, or C99 to C99 etc. If that is done in a (mostly) reversible fashion - successfully re-encode back to the original, where you `preprocess -> parse -> unparse -> re-preprocess` which is a nuisance but possible, then it opens the door to much more aggressive type systems. In pa…

The idea is to keep cake aligned with C, not a language fork. But Cake itself could have a fork to Cake++. :D

Re: Cake – C23 and Beyond (2023)

#83

I've been dabbling in embedded programming. Everything is written in C. I just don't understand why. C++ solves pretty much all problems if you want it too (RAII, smart pointers, move semantics) and the frameworks writers wouldn't need to implement their bespoke OOP system on top of opaque pointers and callbacks. Maybe it was bad luck on my part, and other embedded frameworks are better; but I got into both ESP32 and…

C doesn't need to look like this. Some of it does, because it comes from the days where function inlining and dead code elimination were aspirational, but your C compiler is probably derived from clang or gcc now and totally capable of folding away branches on constant data.

An abstract class is a struct with function pointers in it. Mark the fields const and the instance const and it'll be devirtualised and optimised away. If you miss overloading, `static inline __attribute__((overloadable))` wrappers in a header will bring it back.

Code generators can be better for debugging than built in templates. At the source level they look the same, but if it's behaving weirdly, you can look at the generated C instead of the templated layer.

C code can look rather like modern C++. If you're up for feeding it to a custom preprocessor to implement templates, or especially if you've gone as hardcore as the compiler front end under discussion here, C++ starts to look a lot like a syntactic obfuscation over C.

[it's not quite syntax over C, the languages play divergent games with semantics as well, but picking a different set of syntax abstractions over C to the C++ one is an interesting way to go]

Re: Cake – C23 and Beyond (2023)

#84

I think this is a really interesting direction. That it can translate C23 to C89 means it has most of the work in place to translate C23 to C23, or C99 to C99 etc. If that is done in a (mostly) reversible fashion - successfully re-encode back to the original, where you `preprocess -> parse -> unparse -> re-preprocess` which is a nuisance but possible, then it opens the door to much more aggressive type systems. In pa…

The idea is to keep cake aligned with C, not a language fork. But Cake itself could have a fork to Cake++. :D

A 'C' -> C compiler which preserves most source code unchanged (i.e. would be the identity transform on some input) and which implements something like constexpr on functions (by running the interpreter during the transform) could be argued to be a forward looking C implementation. Specifically C23 has constexpr, but in an extremely limited form, and aspires to extend that to be more useful later.

Equally one which replaces 'auto' with the name of the type (and similar desugaring games) is still a C to C compiler, just running as a C23 to C99 or whatever. Resolve the branch in _Generic before emitting code as part of downgrading C11.

The lifetime annotations are an interesting one because they're a different language which, if it typechecks, can be losslessly converted into C (by dropping the annotations on the way out).

I'm not sure where in that design space the current implementation lies. In particular folding preprocessed code back into code that has the #defines and #includes in is a massive pain and only really valuable if you want to lean into the round trip capability.

Re: Cake – C23 and Beyond (2023)

#85
post #27

C safety addons like this (there have been many) is that they don't prevent extracting raw pointers from controlled pointers. Optional memory safety isn't. > If this can be reasonably retrofitted to existing libraries and projects That's the problem. If you want to fool around in this space, consider revisiting C++ to Rust conversion. There's something called Corrode, which compiles C to a weird subset of Rust full o…

>Can you ask Github Co-pilot to look at C code and answer the question "What is >the length of the array 'buf' passed to this function"? That tells you how to >express the array in a language where arrays have enforced lengths, whicn >includes both C++ and Rust this is the way you tell C what is the size of array. void f(int n, int a[n]) { }

you're missing the word "static" to have that work as intended. Option (2) at https://en.cppreference.com/w/c/language/array

Parameters like `const double b[static restrict 10]` for at least 10 long and doesn't alias other parameters.

Syntactically this is pretty weird.

Re: Cake – C23 and Beyond (2023)

#86

Earlier quoted context omitted.

The idea is to keep cake aligned with C, not a language fork. But Cake itself could have a fork to Cake++. :D

A 'C' -> C compiler which preserves most source code unchanged (i.e. would be the identity transform on some input) and which implements something like constexpr on functions (by running the interpreter during the transform) could be argued to be a forward looking C implementation. Specifically C23 has constexpr, but in an extremely limited form, and aspires to extend that to be more useful later. Equally one which r…

auto, typeof, _Generic are implemented in cake. Sometimes when they are used inside macros the macros needs to be expanded. Then cake has #pragma expand MACRO. for this task.

Sample macro NEW using c23 typeof.

    #include 
    #include 

    static inline void* allocate_and_copy(void* s, size_t n) {
        void* p = malloc(n);
        if (p) {
            memcpy(p, s, n);
        }
        return p;
    }

    #define NEW(...) (typeof(__VA_ARGS__)*) allocate_and_copy(&(__VA_ARGS__), sizeof(__VA_ARGS__))
    #pragma expand NEW

    struct X {
        const int i;
    };

    int main() { 
        auto p = NEW((struct X) {});     
    }
The generated code is

    #include 
    #include 

    static inline void* allocate_and_copy(void* s, size_t n) {
        void* p = malloc(n);
        if (p) {
            memcpy(p, s, n);
        }
        return p;
    }

    #define NEW(...) (typeof(__VA_ARGS__)*) allocate_and_copy(&(__VA_ARGS__), sizeof(__VA_ARGS__))
    #pragma expand NEW

    struct X {
        const int i;
    };

    int main() { 
        struct X  * p =  (struct X*) allocate_and_copy(&((struct X) {0}), sizeof((struct X) {0}));     
    }

Re: Cake – C23 and Beyond (2023)

#87

I've been dabbling in embedded programming. Everything is written in C. I just don't understand why. C++ solves pretty much all problems if you want it too (RAII, smart pointers, move semantics) and the frameworks writers wouldn't need to implement their bespoke OOP system on top of opaque pointers and callbacks. Maybe it was bad luck on my part, and other embedded frameworks are better; but I got into both ESP32 and…

C doesn't need to look like this. Some of it does, because it comes from the days where function inlining and dead code elimination were aspirational, but your C compiler is probably derived from clang or gcc now and totally capable of folding away branches on constant data. An abstract class is a struct with function pointers in it. Mark the fields const and the instance const and it'll be devirtualised and optimise…

I can't say I understand the overall point you're trying to make.

Re: Cake – C23 and Beyond (2023)

#88

Earlier quoted context omitted.

let p: Box = Box::new(X { ... }); let x2 = X { ... }; // Moves x2 into the same memory as the first X. // The first X is automatically dropped as part of this assignment. // Also consumes x2 so x2 is not available any more. *p = x2; // Drops the X that was originally assigned to x2 and then moved into p. drop(p); // No need, nor is it possible, to destroy x2.

Thanks for the rust sample. It looks very similar. Can the allocator be customized? As I said I am not Rust specialist. Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved. In cake ownership this needs to me explicit ( and the destructor is not generated) I also had a look at Rust in lifetime annotations. This concept may be necessary but I am avoidi…

> Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved.

Yes?

    if cond {
        drop(p)
    }
    // p may or may not be dropped here
or

    let p;
    if cond {
        p = something();
    }
    // p may or may not be set here
These trigger dynamic drop semantics, in which case the stackframe has a hidden set of drop flags going alongside any variable with dynamic drop semantics, to know if they do or don’t need to be dropped. The flags are automatically updated when the corresponding variables are set or moved-from.

Re: Cake – C23 and Beyond (2023)

#89

Earlier quoted context omitted.

Thanks for the rust sample. It looks very similar. Can the allocator be customized? As I said I am not Rust specialist. Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved. In cake ownership this needs to me explicit ( and the destructor is not generated) I also had a look at Rust in lifetime annotations. This concept may be necessary but I am avoidi…

> Also, in my understanding is that in Rust, sometimes a dynamic state is created when the object may or may not be moved. Yes? if cond { drop(p) } // p may or may not be dropped here or let p; if cond { p = something(); } // p may or may not be set here These trigger dynamic drop semantics , in which case the stackframe has a hidden set of drop flags going alongside any variable with dynamic drop semantics, to know…

This "dynamic drop semantics" does not exist in cake.

    int * owner p = malloc(sizeof(int));
    if (condition) 
       free(p);
    free(p); // error p may be initialized/moved.
to fix

    int * owner p = malloc(sizeof(int));
    if (condition) 
    { 
      free(p);
      p = 0;
    }
    free(p);

Re: Cake – C23 and Beyond (2023)

#90
post #58

Earlier quoted context omitted.

> The whole point of a good mempool is that you malloc once, and only call free when you exit the program So you're describing fork() and _exit(). That's my favorite memory manager. For example, chibicc never calls free() and instead just forks a process for each item of work in the compile pipeline. It makes the codebase infinitely simpler. Rui literally solved memory leaks! No idea what you're talking about.

One issue I see with this approach (compiler leaking memory) is, for instance, if the requirements change and you need to utilize the compiler as a lib or service. For example, if the Cake source is used within a web browser compiled with Emscripten, leaking memory with each compilation would lead to a continuous increase in memory usage. Additionally, compilers often offer the option to compile multiple files. There…

True, but with some stuff you just ain't gonna need it. For example, chibicc forks a process for each input file. They're all ephemeral. So the fork/_exit model does work well for chibicc. You could compile a thousand files and all its subprocesses would just clean things up. Now needless to say, I have compiled some juicy files with chibicc. Memory does get a bit high. It's manageable though. I imagine it'd be more of an issue if it were a c++ compiler.
Post reply on HN