[HN Gopher] Circle C++ with memory safety
       ___________________________________________________________________
        
       Circle C++ with memory safety
        
       Author : davikr
       Score  : 103 points
       Date   : 2024-06-02 12:53 UTC (10 hours ago)
        
 (HTM) web link (www.circle-lang.org)
 (TXT) w3m dump (www.circle-lang.org)
        
       | KerrAvon wrote:
       | Haven't read through it in enough detail yet to fully understand
       | the language changes, but the authors are absolutely correct on
       | some basic background that other folks don't always understand:
       | 
       | > Memory-safe languages are predicated on a basic observation of
       | human nature: people would rather try something, and only then
       | ask for help if it doesn't work. For programming, this means
       | developers try to use a library, and only then read the docs if
       | they can't get it to work. This has proven very dangerous, since
       | appearing to work is not the same as working.
       | 
       | 100% 100% 100%
        
         | dataflow wrote:
         | It's an elegant sentence but it's incorrect to say memory save
         | languages are predicated on that? Even a room full of C++
         | experts who understand this completely and write their code
         | strictly based on formal contracts will still eventually write
         | memory bugs.
         | 
         | Memory safe languages are just predicated on the memory-safety
         | problem being difficult to avoid for humans, because nobody has
         | a 0% error rate. They would still be incredibly necessary and
         | relevant even if nobody relied on "appears to work" as the
         | measure of correctness.
        
           | pornel wrote:
           | I think the point is that Rust encodes more rules in its
           | interfaces (ownership, lifetimes, thread safety). If you
           | misunderstand how a Rust library works, your code most likely
           | won't compile instead of silently causing UB.
           | 
           | The rules for safe interfaces are the same for all Rust
           | programs, so users know what to expect. Whereas in C++ the
           | library author has more to say what usage is supported and
           | what isn't (e.g. Rust requires all globals to be thread-safe
           | unconditionally, but a C++ library may say it's thread safe
           | if you set a config option or it's your problem to
           | synchronize access).
        
         | tialaramex wrote:
         | You already wrote "100%" enough times, so I'll add that Rust's
         | technology, and Rust's culture, still aren't enough, you have
         | to really put the work in counteract this very powerful danger.
         | Rust's technology + culture should mean you won't blow your
         | foot off with this (entirely human) approach in their language,
         | but you can definitely give yourself a nasty splinter, destroy
         | your customer's data, and a million other dumb things.
         | 
         | For example Rust provides types like core::cmp::Ordering,
         | core::time::Duration and core::ops::ControlFlow so sometimes
         | your API will be harder to misuse than in might have been
         | because you know, your timeout parameter was a Duration, not an
         | integer count of seconds (or was it milliseconds?)
         | 
         | But, although eventually Clippy will express doubts, Rust won't
         | force you to rewrite that function which took N booleans and
         | now after modification takes N+1 booleans, even though all your
         | callers are probably a mess of true, false, true, true, false
         | undecipherable flag nonsense and a re-factor was called for.
         | 
         | It's surprisingly hard to give new programmers the right
         | instincts on this stuff. I'm pretty sure I was terrible (thirty
         | years ago) too, so this isn't a humble brag it's just an
         | observation.
        
       | maxloh wrote:
       | Also, check out Google's in-development Carbon Language, designed
       | to address the same issues with a Kotlin-like approach. It's an
       | entirely new language that could interoperate with existing C++
       | code/library.
       | 
       | https://github.com/carbon-language/carbon-lang
        
         | jjnoakes wrote:
         | Does Carbon have any undefined behavior? Some of the wording in
         | their goals document suggests that they do/will, but it isn't
         | clear to me.
        
           | gumby wrote:
           | Unless you solve the halting problem, every language has
           | undefined behavior.
        
             | tialaramex wrote:
             | Nope.
             | 
             | A language which has _semantic requirements_ and isn 't
             | willing to reject programs for which it has been unable to
             | determine whether they meet the requirements, will have
             | these cases by Rice's Theorem. This is why C++ is a
             | complete disaster. C++ _specifically_ does not allow the
             | compiler to reject such programs.
             | 
             | You can defuse this like (safe) Rust by just writing
             | conservative checking. You will reject some programs you
             | would like to have successfully compiled, but that's
             | actually OK, programmers who hate it are inspired to
             | improve the checking, that's what Rust's "Non-Lexical
             | Lifetimes" - mentioned by Sean, are about.
             | 
             | Even more radically, you can be a Special Purpose language
             | and just only allow a relatively small subset of possible
             | programs, all of which you know are correct, that's why
             | WUFFS gets to emit indexing with no runtime bounds miss
             | checks - it did all the bounds checks at compile time, all
             | source where that wouldn't be possible isn't legal WUFFS
             | code.
        
               | mgaunard wrote:
               | That is incorrect. A C++ compiler is perfectly allowed to
               | reject any program that contains undefined behaviour.
               | 
               | It's just not required to do so.
        
               | leni536 wrote:
               | It's allowed to reject programs that will evaluate an
               | undefined operation on every possible execution. This is
               | pretty much impossible, so no compiler even attempts
               | this.
        
               | mgaunard wrote:
               | It can replace any path with undefined behaviour to one
               | that does std::abort.
        
               | pjmlp wrote:
               | It can, but in practice no one does it.
        
               | majoe wrote:
               | When you think about it, that's what -fsanitize=undefined
               | is doing. It detects undefined behaviour at runtime and
               | aborts the program.
        
               | gumby wrote:
               | I suppose you can make that assertion by moving the
               | goalposts. But Rice's theorem specifically addresses such
               | behavior: all turing machines are inherently undecidable
               | for all but trivial cases.
               | 
               | You can well argue that Rust may be _better_ than C++ in
               | some dimension, just as others can argue that those
               | qualities have an unreasonable or intractable cost (note
               | that about 20% of all crates resort to `unsafe`). But
               | your blanket assertion is not even supported by the
               | reference you made.
               | 
               | Also the user is free to tell a C++ compiler to reject
               | programs that use many sorts of undefined behavior,
               | though indeed nobody would claim that any current
               | compiler can identify _all_ such cases. But Rice 's
               | theorem says that no Rust compiler could either.
        
               | mike_hock wrote:
               | Yes, but it seems that we can't satisfy all reasonable
               | real-world use cases this way, or else Rust wouldn't have
               | the "unsafe" escape hatch that's actually used by real
               | code.
        
             | umanwizard wrote:
             | That's not true. The halting problem / Rice's theorem only
             | mean that the behavior can't be statically determined by
             | the compiler, not that it's undefined.
        
             | Yoric wrote:
             | C++ has a specific definition of UB. Most languages don't
             | suffer from it, regardless of halting problem.
        
             | Asooka wrote:
             | No? The CPU doesn't have undefined behaviour, thus any code
             | running on it doesn't have undefined behaviour either.
             | There are certain operations whose result is not defined in
             | certain states, I think on Intel some SSE operations had
             | undefined results, but even then the same order of
             | operations will lead to the same result. C/C++ compilers
             | generally let you turn off almost all possible undefined
             | behaviour assumptions. I think the only one you can't turn
             | off is the assumption that a pointer to an object is
             | properly aligned, which is understandable - if every
             | pointer had to be checked for alignment before every
             | access, it would ruin performance.
             | 
             | In general I would be much happier if I could assert to the
             | compiler that my data fits certain parameters and it would
             | optimise based on these explicit assertions rather than on
             | implicit assumptions (undefined behaviour). Those
             | assertions could be turned off in the final build if we
             | determine they actually impact performance, but I expect
             | I'd be able to keep them on all the time.
        
               | fluoridation wrote:
               | CPUs do have undefined behavior.
        
           | summerlight wrote:
           | My understanding is that if you want a high level of semantic
           | compatibility with C++, a certain degree of UB is quite hard
           | to avoid. But at least unlike C++, they prefer not to leave
           | 100s of different possibilities on the table.
        
       | tialaramex wrote:
       | Sean (the author of Circle) is an impressive guy. He started
       | pursuing this work at about the same point several of the "C++
       | Successor Languages" were created, but although all of them
       | _claimed_ to be about solving this problem, especially when first
       | announced, they actually don 't have a solution unlike this
       | Circle work. Let me briefly enumerate:
       | 
       | Val (now HyLo) says it wants to solve this problem... but it
       | doesn't yet have the C++ interop stuff, so, it's just really a
       | completely different language nobody uses.
       | 
       | Carbon wants to ship a finished working Carbon language, then
       | bolt on safety (somehow) but, only for some things, not data
       | races for example, so, you still don't actually have Rust's
       | Memory Safety
       | 
       | Cpp2 explicitly says it isn't even interested in solving the
       | problem, Herb says if he can produced measurements saying it's
       | "safer" somehow that's good enough.
       | 
       | It's interesting how many good ideas from Rust just come along
       | free for the ride when you try to do this, do you like
       | Destructive Move? Good news, that's how you make this work in
       | Rust so that's how Circle does it. Exhaustive Pattern Matching?
       | Again, Circles does that for the same reason Rust needs it.
       | 
       | It is certainly true that this is not the Only Possible Way to
       | get Safety. It would be profoundly weird if Rust had stumbled
       | onto such a thing, but "Let's just copy the thing that worked in
       | Rust" is somehow at once the simplest possible plan that could
       | work _and_ an astonishing achievement for one man.
        
         | Vt71fcAqt7 wrote:
         | >Carbon wants to ship a finished working Carbon language, then
         | bolt on safety (somehow) but, only for some things, not data
         | races for example, so, you still don't actually have Rust's
         | Memory Safety
         | 
         | I'm not sure this is correct. As I understand it, Carbon's plan
         | is to add a borrow checker like Rust's.
         | 
         | From a recent talk[0][1] by one of the lead developers:
         | 
         | >Best candidate for C++ is likely similar to Rust's borrow
         | checker
         | 
         | [0] slides: https://chandlerc.blog/slides/2023-cppnow-carbon-
         | strategy/in...
         | 
         | [1] relevant timestamps:
         | 
         | https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h31m34s
         | 
         | https://youtube.com/watch?v=1ZTJ9omXOQ0&t=1h9m49s
        
           | tialaramex wrote:
           | Chandler has explicitly said that he doesn't see a reason to
           | solve data races.
           | 
           | The borrow checker isn't enough on its own to solve this in
           | Rust, Sean explains (probably deeper in Circle's
           | documentation) that you need to track a new property of types
           | which is infectious, Rust does this with the Send and Sync
           | traits.
        
             | Vt71fcAqt7 wrote:
             | You're right. In fact it was in the previous slide[0] from
             | that same talk. Thanks for pointing that out.
             | 
             | [0] https://youtube.com/watch?v=1ZTJ9omXOQ0?t=1h8m19s
        
         | pjmlp wrote:
         | Right now, Circle looks like the only Typescript like evolution
         | path for existing C++, with a production quality compiler.
         | 
         | Unfortunately WG21 seems to have some issues with any ideas
         | coming out from Circle, going back to its early days, and I
         | don't see them being willing to adopt Sean's work.
         | 
         | Which is really a pity, as he single handled managed to deliver
         | more than whole C++ compiler folks, stuck in endless
         | discussions about the philosophical meaning of the word safety.
         | 
         | Maybe at least some vendor in high integrity computing domain
         | does adopt his work.
        
           | jokoon wrote:
           | > Unfortunately WG21 seems to have some issues with any ideas
           | coming out from Circle, going back to its early days, and I
           | don't see them being willing to adopt Sean's work.
           | 
           | What reasons? Are those valid?
        
             | pjmlp wrote:
             | It was due to the metaprogramming capabilities, due to how
             | Circle enables to use full C++ at compile time instead of
             | constexpr/constinit/constval, David Sankel has a talk where
             | he jokes with the WG21 decision process that was behind it,
             | 
             | "Don't constexpr All the Things", from CppNow 2021,
             | 
             | https://youtu.be/NNU6cbG96M4?t=2045
        
               | fweimer wrote:
               | Well, GCC supports -fimplicit-constexpr these days:
               | https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-
               | Optio...
        
         | wrsh07 wrote:
         | I'm very confused by the love for circle - there's a GitHub
         | that hasn't been updated for 7 months that doesn't have a
         | license.
         | 
         | When I first heard about it, a lot of the ideas seemed
         | interesting, but are there users of it?
         | 
         | I think my biggest question is "what is the goal here?"
         | 
         | For carbon they're pretty explicit that it's a research
         | prototype. If anything is to come if it, it will need to be
         | usable at Google's scale (they have real issues with build
         | times, they build from head so abi compatibility isn't
         | required, etc)
         | 
         | Herb wasn't really designing cpp2 as a successor language so
         | much as a playground to understand what features might make
         | sense to propose for adoption in c++.
         | 
         | What is circle? It's more than just some project, but the ideas
         | haven't been adopted in cpp and the compiler repository isn't
         | being updated
        
           | steveklabnik wrote:
           | Circle is not open source, that's correct.
        
             | dwheeler wrote:
             | I think that's automatically a dead end, then. People have
             | inceasingly abandoned closed source compilers, they create
             | a huge risk if the maker decides to stop maintaining it.
             | Most languages that people pick up have an open source
             | software implementation.
        
               | steveklabnik wrote:
               | I think it's more complicated than that, but I agree that
               | it's a factor that would give some folks pause for
               | currently adopting Circle.
        
               | jay-barronville wrote:
               | In most cases, I'd probably agree with your point here,
               | but in this case, I think you're wrong. If Circle can
               | truly accomplish its stated goals, the value proposition
               | of a memory-safe superset of C++ is ginormous. Lots of
               | companies with critical software written in C++ won't
               | care that Circle isn't open-source as long as it ticks
               | all of their boxes (certifications, audits, etc.) and
               | they have a strong enterprise support story. This isn't
               | your average project.
        
       | reynmorris wrote:
       | A lot of coders already do this. My STL replacement has Vector
       | and VectorUnsafe. Vector is checked to the hilt for bounds
       | safety, stack safety, UB safety, etc. and is slower. But if I
       | have a tight loop, I can use VectorUnsafe and just make sure I'm
       | being careful, and it has no checks at all.
        
         | jjnoakes wrote:
         | Care to share your Vector implementation that is "stack safe"
         | and "UB safe"?
        
           | reynmorris wrote:
           | It's simple, whether the backing memory is heap or stack,
           | it's bounds checked. And overriding all the operators and
           | only returning safe types prevents many types of undefined
           | behavior.
           | 
           | Of course you can fuck with it enough to make it unsafe, but
           | at that point you know exactly what you're doing
        
             | tialaramex wrote:
             | Without seeing it, of course it's hard to write examples,
             | but typically for this type of thing it turns out that
             | "fucking with it" enough to be unsafe is easy to do by
             | mistake and so you end up basically saying resorting to
             | classic C++ "Nobody will make mistakes" safety which we
             | know doesn't work.
             | 
             | In some cases you can even "fuck with it" less than
             | std::vector and cause memory unsafety because std::vector
             | was implemented by people who've been fucked with before,
             | and this "safe" collection maybe was not. Pushing items
             | from the collection itself into the collection again when
             | it's full is often one way to cause this - the std::vector
             | promises this works correctly.
        
               | reynmorris wrote:
               | You can assume that the collection doesn't have good
               | coverage, but what I'm saying is the constructs in the
               | C++ language are there to make it have good coverage.
               | Pair this with some Clang sanitation (like banning raw
               | pointers) and you'd have to go out of your way to make it
               | unsafe.
        
             | umanwizard wrote:
             | Does it stop you from writing code like this?
             | Vector<int> v {1, 2, 3};         int *p = &v[0];
             | v.push_back(4);         printf("%d\n", *p); // this is UB
        
               | reynmorris wrote:
               | That is not an issue with the safety of the Vector, it is
               | an issue with the safety of 'int' and raw pointers. If
               | the Vector grows, that pointer points to freed memory.
               | 
               | But yes, in my implementation I have a safe version of
               | int called 'i32' which overrides the & operator and
               | doesn't allow it to return raw pointers.
        
       | rurban wrote:
       | > rust: rigorous memory safety
       | 
       | > circle : unsafe printf
       | 
       | Comeon people, if you allow unsafety, you cannot call your
       | language safe. There are safe system languages, but don't lie and
       | call unsafe languages safe. Partial safety is not full safety.
        
         | wild_pointer wrote:
         | > circle: rigorous memory safety
         | 
         | > rust: unsafe { println!("{}", *r1); }
         | 
         | Comeon people, if you allow unsafety, you cannot call your
         | language safe. There are safe system languages, but don't lie
         | and call unsafe languages safe. Partial safety is not full
         | safety.
        
           | lpapez wrote:
           | Unsafe in Rust is not unsafe in the same sense that C/C++ UB
           | is unsafe.
           | 
           | Unsafe in Rust means "soundness cannot be statically
           | verified, the language will insert runtime checks for you and
           | perform a clearly defined action (panic) if they are
           | violated".
           | 
           | Much ink has been spilled about "unsafe" in Rust being
           | unfortunately named.
        
             | j16sdiz wrote:
             | No. No. And no.
             | 
             | > It is the programmer's responsibility when writing unsafe
             | code to ensure that any safe code interacting with the
             | unsafe code cannot trigger these behaviors.
             | 
             | https://doc.rust-lang.org/reference/behavior-considered-
             | unde...
             | 
             | And they transverse
             | 
             | > However, violations of these constraints generally will
             | just transitively lead to one of the above problems.
             | 
             | https://doc.rust-lang.org/nomicon/what-unsafe-does.html
        
             | loeg wrote:
             | > Unsafe in Rust is not unsafe in the same sense that C/C++
             | UB is unsafe. Unsafe in Rust means "soundness cannot be
             | statically verified
             | 
             | Right.
             | 
             | But as sibling points out, the rest of your sentence is
             | incorrect. The language mostly does not insert additional,
             | runtime checks and you are allowed to create UB-level bad
             | behavior in unsafe blocks.
        
           | tialaramex wrote:
           | Notice that "unsafe" isn't a magic "off" switch, it's just
           | super powers, things you would otherwise be forbidden from
           | doing are now legal, but things you could have done before
           | have the same behaviour - and if everything in an unsafe
           | block doesn't need super powers you'll get a compiler warning
           | saying the unsafe block you wrote was futile.
           | 
           | So, this only does anything interesting _if_ r1 was a raw
           | pointer so that dereferencing it would be prohibited without
           | the unsafe block. If it 's just a reference or some smart
           | pointer type then that's fine anyway.
        
       | geertj wrote:
       | First came across this 2 days ago and found this extremely
       | impressive. There's also a YouTube presentation where Sean goes
       | over the main features of Circle's C++ memory:
       | https://www.youtube.com/watch?v=5Q1awoAwBgQ.
       | 
       | This seems to be adding Rust borrow semantics and borrow checking
       | to C++. The delivery vehicle seems to be a C++ compiler that the
       | author has been working on for a number of of years. I couldn't
       | find a ton more on the background of this.
       | 
       | From a technical perspective this looks promising as a C++
       | successor language. The project will have to attract other
       | members in the C++ community though.
        
         | secondcoming wrote:
         | He's active on the C++ Slack, regularly asking questions about
         | the minutiae of the C++ spec. It seems like a massive headache.
        
       | mgaunard wrote:
       | Memory-safe languages are IMHO a distraction from making people
       | write bug-free code.
       | 
       | There are much more important bugs than memory safety bugs,
       | including all sorts of performance bugs.
       | 
       | We need to address the whole spectrum instead of compromising all
       | areas in order to tackle niche bugs that only matter in security-
       | critical contexts.
        
         | umanwizard wrote:
         | It's not true that memory safety bugs only matter in security-
         | critical contexts. If a program segfaults, it's going to make
         | the user unhappy regardless of security implications.
        
           | Yoric wrote:
           | And don't forget that a segfault is the lucky case in matters
           | of memory safety bugs. I've worked on many memory safety bugs
           | that manifested themselves by silently corrupting data.
        
         | kiitos wrote:
         | Memory safety bugs are second only to segfaults in terms of
         | importance.
        
           | logicchains wrote:
           | In terms of the actual bugs encountered when developing
           | modern C++ applications, logic bugs vastly outnumber
           | segfaults and memory safety bugs, which are quite rare when
           | you're writing business logic.
        
           | erik_seaberg wrote:
           | With an MMU, segfaults cause pretty abrupt and obvious
           | failures for straying outside the heap or stack. Violating
           | memory safety may cause subtle and unpredictable errors even
           | in correct functions, which is a lot more dangerous.
        
         | Yoric wrote:
         | What kind of niche bugs are you speaking of?
         | 
         | For what it's worth, Rust (and other languages of ML heritage)
         | are really good at getting rid of huge categories of bugs, not
         | just memory. Not all bugs, of course.
        
         | sunshowers wrote:
         | This isn't true in practice.
         | 
         | When you don't have to worry about memory safety, your brain is
         | freed up to worry about other kinds of bugs.
         | 
         | The other bit of good news is that the sorts of things Rust
         | does to have memory safety also enable the systematic
         | elimination of many other kinds of bugs. For example, iterator
         | invalidation.
        
           | mgaunard wrote:
           | For example, the easiest way to make programs avoid use-
           | after-free errors is to never free things.
           | 
           | Would you rather get a program that constantly use all of
           | your RAM until you restart it, or one that consistently uses
           | very little, but someone crashes in edge cases?
        
         | panstromek wrote:
         | The term "Memory safety" also heavily undersells what it
         | actually implies in Rust.
        
         | djur wrote:
         | > all sorts of performance bugs
         | 
         | A lot of code is written in safe, slow languages that could be
         | written in a safe, fast language.
        
       | Guvante wrote:
       | This sounds a lot like C++/CLI which has Microsoft backing and
       | has a whole .NET sub language.
       | 
       | The problem was even when written together the impedance mismatch
       | existed.
        
       | logicchains wrote:
       | It's quite ironic that on a page about safety they use an if
       | statement without {} braces:                       if(x % 2)
       | vec^.push_back(x);                  unsafe printf("%d\n", x);
       | 
       | Skipping the braces for single line conditionals in C++ is a lazy
       | practice that almost inevitably leads to bugs, like Apple's
       | famous goto fail bug: https://www.synopsys.com/blogs/software-
       | security/understandi... . While memory bugs are difficult to
       | prevent, this particular class of logic bug can be eliminated
       | entirely just by always remembering to write two {} around the
       | statement, so a language concerned with correctness should
       | promote such good practices.                       if(x % 2){
       | vec^.push_back(x);             }
        
         | jandrewrogers wrote:
         | This is nitpicking style, there are no real safety
         | implications. Compilers will happily inform you if the parse is
         | ambiguous or code is unreachable.
        
           | logicchains wrote:
           | If you write in this style then there's the possibility that
           | someone else (or yourself in future) will add a second line,
           | like:                       if(x % 2)
           | vec^.push_back(x);               do_something_else();
           | 
           | under the mistaken assumption that the second line will also
           | only run in the x % 2 case. People make mistakes, this
           | particular one can happen, does happen, and has happened many
           | times, and the compiler absolutely will not inform or warn
           | you, because it doesn't know you intended the second line to
           | run in the same if statement as the first (because C++ isn't
           | whitespace sensitive). Is ruling out an entire class of bug
           | really not worth the effort of just typing two more
           | characters?
        
             | j16sdiz wrote:
             | Indentation can be fixed with a code formatter in pre
             | commit hook.
        
               | dragonwriter wrote:
               | Or even an on-save or (and more likely to prevent this
               | kind of error in a large file) as-you-type feature in
               | your IDE/editor.
        
               | dllthomas wrote:
               | Available autoformatting is great, but IIRC the Apple
               | issue happened in an automatic merge. Autoformatting
               | would still help it be noticed, when someone wondered why
               | the formatter changed some bit of the code they hadn't
               | touched, but a formatting check in with the automated
               | tests would catch it faster.
        
             | throwaway376512 wrote:
             | > and the compiler absolutely will not inform or warn you
             | 
             | False.
             | 
             | https://gcc.gnu.org/onlinedocs/gcc/Warning-
             | Options.html#inde...
             | 
             | https://clang.llvm.org/docs/DiagnosticsReference.html#wmisl
             | e...
        
         | Gibbon1 wrote:
         | That feels like a I was learning how to program and this bit me
         | and now I'm always scared of it type of bug. I've never seen it
         | myself. But then again some of my friends complain about OCD
         | coworkers that can't not obsessively remove blank lines from
         | code.
         | 
         | I have seen                  for(size_t i=0; i<n; i++);
         | foo(i);             if(a>b);          a=b;
         | 
         | The famous goto bug was because the assholes had no tests for
         | that module.
        
       | jandrewrogers wrote:
       | Many of the criticisms are of the C++ standard library design and
       | implementation rather than C++ the language per se, particularly
       | with respect to undefined behavior. Much of this, in turn, is
       | because the C++ standard library is very old and anything new
       | added to it must be compatible and interoperable with all of the
       | older parts, whether or not it is a good idea. Borrow checking is
       | a separate matter.
       | 
       | Modern C++ provides all the tools to build an alternative
       | standard library from the ground up with most behaviors and
       | interactions being defined and safe. This always seemed like
       | lower-hanging fruit for a first attempt than changing the
       | language.
       | 
       | C++ is commonly used in contexts where object lifetimes and
       | ownership are inherently unknowable at compile-time, safety can
       | only be determined at runtime. This is why memory safety
       | annotations like 'std::launder' exist in C++. Trying force these
       | cases into compile-time memory safety box is a source of much
       | friction when using languages like Rust. They handle the 80% case
       | where compile-time safety, destructive moves, etc make things
       | easy for the developer but then significantly worsen the
       | complexity, and therefore safety, of the other 20%. This isn't
       | necessarily a bad thing but it intrinsically creates a market for
       | C++ which explicitly allows you to handle these cases in a
       | reasonable way.
       | 
       | Systems programming is full of edge cases that break tidy
       | programming language models. This has to be accommodated
       | reasonably in languages that want to wear the mantle of "systems
       | language". Zig is an example of a new systems language that does
       | this well in my opinion.
        
         | pjmlp wrote:
         | Zig is basically Modula-2 with a revamped syntax for C folks,
         | plus compile time metaprogramming.
         | 
         | And yes, many of these issues were already solved by other
         | system programming languages outside the UNIX linage of
         | programming languages.
        
         | 112233 wrote:
         | > Modern C++ provides all the tools to build an alternative
         | standard library from the ground up with most behaviors and
         | interactions being defined and safe.
         | 
         | It also goes to a great lenght to embed standard library into
         | base language as deeply as it can, and forces unsafe
         | constructs. Just see how many magic types from std namespace
         | are emmited by compiler when you use range for, initializers,
         | or, worst of all, coroutines. Lambdas with capture by reference
         | replace old fashioned dangling pointers with much more modern
         | dangling references (you are free to copy lambdas that
         | reference local variables :smiling virus with thumbs-up
         | emoji:).
         | 
         | There is no way to get "another library", without making it
         | not-c++. Those guys cannot even make no-exceptions/no-rtti a
         | part of the standard.
        
         | mike_hearn wrote:
         | See for example https://suslib.cc/
        
         | pornel wrote:
         | I think the "unknowable" lifetimes are C++'s own making.
         | 
         | It's like types in statically vs dynamically typed languages.
         | Types are unknowable at compile time when the compiler doesn't
         | force them to be static.
         | 
         | And similarly ownership and lifetimes are "dynamically typed"
         | in C++, because the compiler doesn't force them to be rigidly
         | declared like in Rust.
        
         | orf wrote:
         | > C++ is commonly used in contexts where object lifetimes and
         | ownership are inherently unknowable at compile-time
         | 
         | What specific contexts are these?
        
         | roca wrote:
         | The claim that Rust makes the safety of 20% of code worse
         | requires some justification. It is utterly contradictory to my
         | experience. Having to do a dynamic check (an array bounds
         | check, or `Option::unwrap`) does not make code "less safe".
        
       | legobmw99 wrote:
       | I think a challenge to this evolution path is the same as what
       | motivated Sutter's cpp2: defaults
       | 
       | I think the premise that a lot of experienced programmers can
       | write good C++ is at least somewhat valid. They know to not use
       | raw pointers, which APIs to use for bound checking, whatever. The
       | issue is that new users don't, and the defaults are bad.
       | 
       | If I have to write #feature on safety in every file, it becomes
       | possible to forget. Opting in vs opting out
        
       | ljlolel wrote:
       | Who is using Circle C++ in production?
        
         | grumpyprole wrote:
         | There was once a time when nobody used C++ in production.
        
           | ljlolel wrote:
           | Sorry are you saying nobody is using Circle? Also it seems
           | closed source?
        
       | quietbritishjim wrote:
       | > There's only one systems-level/non-garbage collected language
       | that provides rigorous memory safety. That's the Rust language.
       | 
       | Honest question: what about Ada? It was specifically designed to
       | be a safe language for critical systems, and for a while was
       | mandated for some military systems. Did the author not consider
       | it, or are its protections just not as expansive as Rust's?
        
         | steveklabnik wrote:
         | I'm not the author, but there's a few reasons Ada tends to be
         | forgotten in these discussions:
         | 
         | Back when Ada was new, people just didn't actually like
         | programming in it much. Some did, of course, but many did not.
         | This is the reason the Ada Mandate was abandoned.
         | 
         | This led into a situation with a small, walled off community
         | that didn't really communicate with the outside world much.
         | This has a compounding effect over time.
         | 
         | Ada, while designed for safety critical systems, was not
         | actually memory safe until fairly recently. Deallocating memory
         | at runtime wasn't, and in my understanding, may only be in the
         | presence of SPARK? Hopefully someone can chime in here. Now,
         | that fine for the systems Ada tended to be used for, which
         | often have either no dynamic allocation or a singular
         | allocation at program startup, but for inspiration for other
         | language designs, given that it forgoes a hard problem, it's
         | not really as useful to those who are trying to solve those
         | problems. This doesn't mean Ada is useless for inspiration, but
         | for "how do I implement memory safety," it doesn't have many
         | unique uses things to offer.
         | 
         | None of this means Ada is a bad language, but these are the
         | main contributing factors that I see with regards to your
         | specific question.
        
           | pjmlp wrote:
           | That is the usual Ada outdated image, just like unsafe code
           | blocks in Rust, those Unchecked Deallocation are wrapped in
           | safe calls, and since Ada95 there are controlled types,
           | allowing for RAII patterns.
           | 
           | Additionally, it is common to use arenas, and many types can
           | be dynamically stack allocated with a retry operation in case
           | there is not enough space, so that the call can be redone
           | with a smaller size.
           | 
           | Memory Management with Ada 2012 from FOSDEM 2016,
           | 
           | https://archive.fosdem.org/2016/schedule/event/ada_memory/
           | 
           | Doesn't go much into SPARK related improvements though, given
           | its date.
        
             | steveklabnik wrote:
             | Sure, none of that is particularly novel though, so in
             | terms of citing it as something specifically to add to the
             | conversation, there isn't any real reason.
        
               | quietbritishjim wrote:
               | It was the article author's choice to specifically say
               | there is no safe systems programming language other than
               | Rust. If that is not true then it's worth citing in
               | discussion of the article.
        
               | steveklabnik wrote:
               | My parent confirmed that deallocating memory is still
               | unsafe, even though there are common usage patterns that
               | help make sure you're doing it correctly.
        
             | roca wrote:
             | How does "redo the call with a smaller size" possibly help
             | you? If you could get by with less memory, you should have
             | asked for less memory in the first place.
        
             | roca wrote:
             | Rust's affine types and borrow checking give safe Rust a
             | lot more power than the safe subset of Ada95. A trivial
             | getter method that returns a reference (i.e. pointer) to a
             | field of its object is safe in Rust, but in Ada95 (and C++
             | and most other non-GC languages) in general you can't
             | ensure the object outlives the reference.
             | 
             | It is true that Ada has a safe suitable-for-systems-
             | programming subset that's much better than most languages.
             | I think when people say "Rust is the first safe systems
             | programming language" they implicitly mean "that is
             | expressive enough for me to replace C++ with".
        
         | tialaramex wrote:
         | Ada itself doesn't provide you the same guarantees as Safe
         | Rust. You can use SPARK to grant Ada more memory safety
         | capabilities. However as a language (rather than comments which
         | may or may not be ignored by your Ada compiler) SPARK is from
         | 2014, so now we're close to Rust's age.
         | 
         | I assume that the totality of SPARK's guarantees would get you
         | to the same place as Rust but I don't know.
         | 
         | A big thing which counts against Ada (and SPARK) in practice is
         | that it's not popular. You'll trip over Rust programmers
         | everywhere, two of my friends are getting paid to write Rust,
         | for completely unrelated companies, unrelated reasons, Rust
         | seems like a reasonable fit so those companies picked Rust. You
         | don't see that with Ada and SPARK.
        
       | pizlonator wrote:
       | Nice to see Sean cite Fil-C, though he does it much more in
       | passing than it deserves, considering that Fil-C gives you
       | memory-safe C and C++ without requiring any annotations
       | whatsoever. He cites it as a sanitizer and references undefined
       | behavior, which isn't really how I would describe it (unlike a
       | sanitizer, it catches all memory safety bugs, and also unlike a
       | sanitizer, it turns a lot of what would have been UB in C++ into
       | defined-but-safe behavior). It's a very different approach from
       | Sean's.
       | 
       | For example, Circle implies broad overhaul to tooling and
       | libraries while Fil-C implies no such thing. Also, Circle is all
       | about not using existing compilers on the grounds that they are
       | hard to experiment with, while Fil-C is a surgical change to
       | clang/LLVM.
       | 
       | The main advantage of Circle over Fil-C is that if you pay the
       | cost of that overhaul, you'll get performance that is better than
       | what Fil-C could ever do even with a lot of optimization. Not
       | obvious to me if that makes it net better, though.
        
         | tialaramex wrote:
         | For the performance, there are a _bunch_ of people, some of
         | them probably wrong and others definitely right, who believe
         | they _need_ the best possible performance from software.
         | 
         | You can sell these people something like Rust because you can
         | very often either show why the "better performance" C++ they
         | have is wrong (and if they wanted a _wrong_ answer here 's zero
         | already, pay me) or sometimes actually worse performance. Not
         | every time, but often enough to make a real difference. The
         | Circle safety feature should be in the same ballpark.
         | 
         | You can't sell them anything that's just anyway going to have
         | worse performance, if you could they'd be writing Java already.
         | So that's counting against Fil-C.
        
           | pizlonator wrote:
           | Java is a totally different language, so it's not even
           | remotely a competitor in this space. Also Java is quite fast,
           | even compared to C or Rust.
           | 
           | Fil-C is all about being able to run existing C/C++ code that
           | nobody is going to rewrite, not even in a dialect like
           | Circle, since the burden of annotations will be too great.
        
             | mike_hearn wrote:
             | For existing C++ just using a checked std::vector and Boehm
             | GC can get you quite a long way.
        
               | pizlonator wrote:
               | Nowhere near to memory safety. There are so many exploits
               | left on the table if you do what you say.
               | 
               | Not to mention that Boehm isn't sound on modern C
               | compilers. Conservative stack scanning can be foiled by
               | optimizations that are valid from the compiler's
               | perspective.
        
       | Asooka wrote:
       | I'm not sure that last paragraph had to be so toxic. We've had
       | enough toxic egos in this industry, we don't need any more. I've
       | never heard of this person before, but I do not think I wish to
       | be part of his project.
        
       | roca wrote:
       | It's an impressive project. One of the key problems for any safe
       | language interoperating with C++ (even a safe C++ subset) is that
       | you really want to be able to interact with C++ standard library
       | types (string, vector etc) safely because they will appear at
       | interface boundaries. Circle introduces its own safe standard
       | library so I don't see how Circle fares here.
        
       ___________________________________________________________________
       (page generated 2024-06-02 23:00 UTC)