HNHacker News
TopNewBestAskShowJobs

lerno

1,425 karma · joined April 1, 2014

submissionscomments
lerno··on C3 solved memory lifetimes with scopes
> You can claim that C3 does not aim to solve memory safety for these reasons, _and they can be understandable_,

This seems to be where we speak past each other. What the blog post talks is how C3 handles the problem of memory lifetimes for temporary data, which is a major lack of ergonomics in C (and arguably also C-likes, such as Zig).

The title refers to how C3 does this is in userland without having to add any of the common solutions, such GC, ARC, RAII. Recently a superset of C called "Cake" added ownership annotations exactly to solve such problems.

C3 doesn't have anything like Rust memory safety. Nor is the blog post about memory safety, but on memory lifetimes.

lerno··on C3 solved memory lifetimes with scopes
It doesn't zero on free, that's not what the code does. But if you're looking for something to prevent exploits, then no, this is not it, nor does it try to be.

How would you want that implemented?

lerno··on C3 solved memory lifetimes with scopes
Well, we've already tried that, and no one used it.
lerno··on C3 solved memory lifetimes with scopes
It is a problem for manual memory management. I am not quite sure where you're coming from. In Modern C++ this is managed by RAII, but if you instead look at C there is no solution that doesn't involve various additional code.

You seem the same in "C alternatives" such as Zig.

But obviously if you're coming from a language which already has some form of automatic memory management it won't seem like a big thing.

But in the context of manual memory management it is solving a very real problem.

lerno··on C3 solved memory lifetimes with scopes
> So the issue is clearly not solved.

You can use --sanitize=address to get this today, or use the Vmem-based temp allocator (which is only in the 0.7.4 prerelease and only for 64 bit POSIX) if you're curious how it feels and works in practice.

> I can see multiple issues with this:

There is a constant trade-off, and being as safe as possible is obviously great, but there is also the question of performance.

The context matters though, it's a C-like language, an evolution of C. So it doesn't try to be a completely new language with new semantics, and that creates a lot of constraints.

The "safe-C" C-dialects usually add a lot of additional annotations that doesn't seem particularly palatable to most developers.

> Am I still misreading the title if I read it as "C3 solves the same issues that the borrow checker solves"?

Yes I am afraid you do. But that's my fault (since I suggested the title, even though I didn't write the article), and not yours.

lerno··on C3 solved memory lifetimes with scopes
> The article makes no mention of this, so in the context of the article the title remains very wrong

The temp allocator implementation isn't guaranteed to detect it, and the article doesn't go into implementation details and guarantees (which is good, because capabilities will be added on the road to 1.0).

> I tries searching how these contracts work in the C3 website [1] and these seems to be no guaranteed static checking of such contracts.

No, there is no guarantee at the language level because doing so would make a conforming implementation of the compiler harder than it needs to be. In addition, setting exact limits may hamper innovation of compilers that wish to add more analysis but will hesitate to reject code that can be statically know to violate contracts.

At higher optimizations, the compiler is allowed to assume that the contracts evaluate to true. This means that code like `assert(i == 1); if (i != 1) return false;` can be reduced to a no-op.

So the danger here is then if you rely on the function giving you a valid result even if the indata is not one that the function should work with.

And yes, it will be optional to have those "assumes" inserted.

Already today in current compiler, doing something trivial like writing `foo(0)` to a function that requires that the parameter > 1 is caught at compile time. And it's not doing any real analysis yet, but it will definitely happen.

lerno··on Zig's New Async I/O
> care more about binary size than speed

That does not seem to be true if you look at how string formatting is implemented.

lerno··on C3 solved memory lifetimes with scopes
Memory safety as in the full toolset that Rust provides? C3 clearly doesn't, I fully agree.
lerno··on C3 solved memory lifetimes with scopes
Well, the title (which is poorly worded as has been pointed out) refers to C3 being able to implement good handling of lifetimes for temporary allocations by baking it into the stdlib. And so it doesn't need to reach for any additional language features. (There is for example a C superset that implements borrowing, but C3 doesn't take that route)

What the C3 solution DOES to provide a way to detect at runtime when already freed temporary allocation is used. That's of course not the level of compile time checking that Rust does. But then Rust has a lot more in the language in order to support this.

Conversely C3 does have contracts as a language feature, which Rust doesn't have, so C3 is able to do static checking with the contracts to reject contract violations at compile time, which runtime contracts like some Rust creates provides, can't do.

lerno··on C3 solved memory lifetimes with scopes
No, that is quite possible. You will not be able to use that memory you just returned though. What actually happens is an implementation issue, but it ranges from having the memory overwritten (but still being writable) on platforms with the least support, to being neither read or writable, to throwing an exact error with ASAN on. Crashing on every use is often a good sign that there is a bug.
lerno··on C3 solved memory lifetimes with scopes
This is about tracking allocated memory, which is different. I know V claimed it could solve this with static analysis, but in practice it didn't work and had to fallback to a GC.

This is true for all similar schemes, that they have something for easy for simple-to-track allocations, and then have to fallback on something generic.

But even that is usually assuming that the language is somehow having a built-in notion of memory allocation and freeing.

lerno··on Forget borrow checkers: C3 solved memory lifetimes with scopes
Well the latter is covered: you can make temp allocations out of order when having nested "@pool"s. There are examples in the blog post.

It doesn't solve the case when lifetimes are indeterminate. But often they are well know. Consider "foo(bar())" where "bar()" returns an allocated object that we wish to free after "foo" has used it. In something like C it's easy to accidentally leak such a temporary object, and doing it properly means several lines of code, which might be bad if it's intended for an `if` statement or `while`.

lerno··on C3 solved memory lifetimes with scopes
You can certainly do it with RAII. However, what if a language lacks RAII because it prioritizes explicit code execution? Or simply want to retain simple C semantics?

Because that is the context. It is the constraint that C3, C, Odin, Zig etc maintains, where RAII is out of the question.

lerno··on C3 solved memory lifetimes with scopes
Well, there are no objects, no constructors and no destructors.
lerno··on C3 solved memory lifetimes with scopes
I would say that the title is easily misread. If you open the blog post and just read the title and a few lines into the intro, I think it's clear it's about C3 not having to implement any recently popular language features in order to solve the problem of memory lifetimes for temporary objects as they arise in a language with C-like semantics.

Now clearly people are misreading the title when it stands on its own as "borrow checkers suck, C3 has a way of handling memory safety that is much better". That is very unfortunate, but chance to fix that title already passed.

It should also be clear from the rest of the blog post that it doesn't try to make any claims that it's a novel technique (it's something that has been around for a long time). What's novel is that it's well integrated into the stdlib.

lerno··on C3 solved memory lifetimes with scopes
NSAutoreleasePool keeps a list of autoreleased objects, that are given a "release" message when the pool goes out of scope.

`@pool` flushes the temp allocator and all allocations made by the temp allocator are freed when the pool goes out of scope.

There are similarities, but NSAutoreleasePool is for refcounting and an object released by the autoreleasepool might have other objects retaining it, so it's not necessarily freed.

lerno··on C3 solved memory lifetimes with scopes
No, I don't think they do.

Given a function `foo` that is allocating an object "o" and returns it to the upper scope, how would you do "escape analysis" to determine it should be freed and HOW it should be freed? What is the mechanism if you do not have RAII, ARC or GC?

lerno··on C3 solved memory lifetimes with scopes
The benefit is that it: (a) works in a language without RAII, and C-like languages usually does not have that (b) there are no individual heap allocations and frees (c) allocations are grouped together.
lerno··on C3 solved memory lifetimes with scopes
I am not sure how this would be a problem. Certainly the resource manager should manage the memory itself in some manner.

It has very little to do with trying to manage temporary memory lifetimes.

lerno··on C3 solved memory lifetimes with scopes
Imagine we have a function "foo" which returns an allocated object Bar, we want to pass this to a function "bar" and then have it released.

Now we usually cannot do "bar(foo())" because it then leaks. We could allocate a buffer on the stack, and then do "bar(foo(&buffer))", but this relies on us safely knowing that the buffer does not overflow.

If the language has RAII, we can use that to return an object which will release itself after going out of scope e.g. std::unique_ptr, but this relies on said RAII and preferably move semantics.

If the context is RAII-less semantics, this is not trivial to solve. Languages that run into this is C3, Zig, C and Odin.

With the temp allocator solution, we can write `bar(foo())` if `foo` always allocates a temp variable, or `bar(foo(tmem))` if it takes an allocator.

lerno··on C3 solved memory lifetimes with scopes
A dangling pointer will generally still possible to dereference (this is an implementation detail, that might get improved – temp allocators aren't using virtual memory on supporting platforms yet), but in safe more that data will be scratched out with a value, I believe we use 0xAA by default. So as soon as this data is used out of scope you'll find out.

This is of course not as good as ASAN or a borrow checker, but it interacts very nicely with C.

lerno··on [dead]
C3 is a C-like programming language seeking to evolve from C.

The 0.7.3 release doesn't have any major changes, but ends up with some gradual improvements such as:

- type / typeid equivalence: it's possible to use a constant typeid instead of type in a lot more places now, requiring fewer typeid -> type conversions, which improves readability.

- $evaltype which turned a string into a type now merged into $typefrom which is the one that turns a typeid into a type.

- Type inference through && (taking a reference to a temporary), allowing Foo* f = &&{ 1, 2 }.

- Compile time "sprintf" for formating strings at compile time.

- Arithmetic operator overloading now accepts macros with untyped "wildcard" arguments.

The full change list can be found in the article.

Some other links that might be interesting follows:

- https://news.ycombinator.com/item?id=24108980

- https://news.ycombinator.com/item?id=27876570

- https://news.ycombinator.com/item?id=32005678

Here are some interviews on C3:

- https://www.youtube.com/watch?v=UC8VDRJqXfc

- https://www.youtube.com/watch?v=9rS8MVZH-vA

Here is a series doing various tasks in C3:

- https://ebn.codeberg.page/programming/c3/c3-file-io/

Some projects:

- Gameboy emulator https://github.com/OdnetninI/Gameboy-Emulator/

- RISCV Bare metal Hello World: https://www.youtube.com/watch?v=0iAJxx6Ok4E

- "Depths of Daemonheim" roguelike https://github.com/TechnicalFowl/7DRL-2025

For more projects written in C3, look at: https://github.com/c3lang/c3-showcase

lerno··on Programming Language Switching Politics: A Rigged Game of Power
Something you somewhat touch upon: early adopters are invested in the success of their chosen language because they are relying on its success to make their investment in early adoption pay off.

If you are an early adopter of language X and invest time and effort in learning it, then that will be a complete waste unless X actually is successful! That thinking will encourage all sorts of behaviour (some unethical) to promote their chosen language.

lerno··on Learning C3
In this case, how would you prevent the user from seeing an invalid foo_vec before initialization? This is either "oh, it's in an illegal state", in which case it's just an annotation without deeper enforcement or you need to somehow enforce that a non-null pointer is never seen, am I not right?
lerno··on Learning C3
A C backend is planned.
lerno··on Learning C3
C++ is just slow to compile. With the standard library it is much worse. The problem is that with C++ you're not getting as much encapsulation as you would in C unless you do extra work that also has a performance hit (pimpl). This means that C++ code often has to recompile a whole lot more than C code does when doing incremental compilation in my experience.
lerno··on Learning C3
Jai has operator overloading yes.
lerno··on Learning C3
Hasn't this been done already? C with classes I mean. eC and others.
lerno··on Learning C3
It's very simple, the latter describes an `if-else` chain. No more complicated than that. Can you explain what your concern is?
lerno··on Learning C3
C3 has case ranges, like

    case 'a'..'z':
It's from the GCC C extension (except GCC uses ...)
← PreviousPage 4 of 13Next →