1. 13
  1.  

  2. 3

    Really interesting article. It feels like C++ is not far off the target, as long as certain features, and compiler behaviors around invocation of destructors – are reconsidered.

    Putting in in another way. Although, there are, probably, more areas in C++ that prevent borrow checking at compile time, I hope there can be other, more optimistic conclusions than ‘given the existing C++ features, it is not possible’.

    May be another conclusion could be: as a suggestion to C++ standards committee, to introduce a compiler flag that prohibits the ‘incompatible syntax and assumptions’, and let the new code bases in C++ not be inhibited by the old.
    May be make that flag also part of the packaging/module system such that I would get a compile time warning that a module I am not trying to include is incompatible with the flag.

    1. 3

      Putting in in another way. Although, there are, probably, more areas in C++ that prevent borrow checking at compile time, I hope there can be other, more optimistic conclusions than ‘given the existing C++ features, it is not possible’.

      I think the real problem is the breadth of possible C++ APIs. In Rust, if you write an API that isn’t amenable to borrow checking then you get compile failures and have to fix it. In C++, if you write an API that doesn’t have clearly defined ownership semantics then consumers of your API suffer but you don’t (until you try to use your own API). It would be quite easy to write a borrow checker for C++ code written in the style of Rust (or even something a bit less restrictive), but writing one for arbitrary C++ code is not computable.

      May be another conclusion could be: as a suggestion to C++ standards committee, to introduce a compiler flag that prohibits the ‘incompatible syntax and assumptions’, and let the new code bases in C++ not be inhibited by the old.

      This might become possible with modules but in general it’s a hard problem. C++’s module system, pre-C++20, is cat with some syntactic sugar. You concatenate a whole bunch of C++ source files (some of which are referred to as ‘headers’, to pretend that they’re not full of C++ implementation code) together and then pass that to your compiler. This means that any restriction of what’s allowed in the syntax breaks your ability to consume existing libraries.

      With modules, it’s (theoretically) possible to restrict the syntax because you’re consuming modules by importing ASTs that were parsed separately. That doesn’t help with the semantics though: a C++ API can still return a bare pointer whose lifetime is bounded by something unrelated. POSIX, for example, has a bunch of APIs that return a pointer to some internal buffer whose contents are invalidated after every call and POSIX is one of the less-bad C APIs that C++ codebases import directly.

      1. 1

        Thank you for the follow up.

        I am speculating, that new code bases for end products, as well as libraries could be written with

        --error-undeciadable-and-unsafe=true

        (that underneath enables –enforce-borrow-checker and other flags),

        will be very welcomed by a larger portion C++ developer community.

        I realize, of course, that most of the existing source code will not compile (without updates) with that flag on, and existing precompiled shared libraries that are supplied without source code, and cannot be ‘recompiled’ at link time would not be compatible with this ‘undecidable’ compilation flag.

        But reworking the existing code to compile with this type of flag, is significantly more acceptable – than moving to a new language…

        My current, very subjective view, of course – is that ‘compatibility’ with the old code base, in leu of new safety and productivity features, is not a sustainable path forward for the language.

    2. 2

      Interesting that they ran into the need for destructive move semantics. That would open a world of typestate programming – type level state machines, like the one that was attempted here, that can be used to ensure that an API is used correctly.

      I hope that comes to C++ one day (and we can deprecate nondestructive moves). In a way, it seems inevitable: If zero-cost abstractions are a goal at all, then this would be a broader one (in that classes don’t need a moved-from state).

      But in case it’s too late to add to C++, what about adding ownership and destructive move semantics to C? Imagine a type attribute, “alive”, that means that the program won’t compile if you forget to end the lifetime of such an object – you have to pass it to a function that destroys it. You get the safety of implicit destructors, but explicit, which is even better: The destructor function would be any normal function: It could take other arguments, so you don’t need to keep references in the object for use by the destructor, and you can enforce correct destruction order.