Live data from Hacker News

Making C++ safe without borrow checking, reference counting, or tracing GC

verdagon.dev

31–40 of 226 posts

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#31
post #14

> Tracing GC is the simplest model for the user, and helps with time management and development velocity, two very important aspects of software engineering. > Borrow checking is very fast, and helps avoid data races. One thing many people seem to assume is that not having to care about memory means you can program faster and get to your goal faster. As the author here seems to do. However as it turns out, if your pr…

I don't write Rust. But here is what you said and what the author said don't conflict with each other, and it has been on my mind for a while. People who write similar code, or work on things for decades usually don't really think through what "sketch out some code" looks like. They spend most of their time on refactoring things that has clear use-cases, but not well-defined API boundaries within the component, or be…

In my experience even in those "sketching" areas static types and strict checking is the better trade-off.

I think the real criteria for "will static types and stricter checks help?" is "how long will this thing last for?".

E.g. for a shell REPL you definitely don't want to have to write our types, but for a shell script you definitely do.

Something like using MATLAB for exploratory research is probably another decent example. Or maybe hackathon games.

But for most games, data analysis, machine learning etc. then being stricter pays for itself almost immediately.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#32

The reason why safety in C++ is difficult to achieve is due to the memory model used by C and C++. The memory model is a flat space provided by the OS that can be addressed by pointers. In this sense, C++ is similar to assembly code. A language like Java, on the other hand, assumes a different model where you can only access objects with well defined behavior. To change this, one needs to disallow the use of native p…

One issue is that the memory model isn't just a flat space that can be addresses by any pointer value - it may look similar to one if your compiler and OS let you, but doing things like accessing memory allocated as a different type or outside (an array of) objects is invalid, and the compiler is perfectly allowed by the standard to assume that never happens and happily "optimize" everything that may be a result of that away.

A lot of bugs have been caused by programmers assuming any access to the 'linear address space' is fine, but that has never been reliable as it's not allowed by the standard. The worse thing is when it looks like it works for a while, but you're relying on stuff not allowed by the standard so may change at any time (like a compiler version or option change, or even a change to a different part of the code that happens to tickle the compiler's analysis stages a slightly different way). See the "Time traveling NULL-check removal" - as the compiler "knows" that no pointer can ever have the value of NULL during deference, any path that does that can be completely removed - even if there's something like a NULL check and a logging output before said deference, if compiler decides that deference will eventually happen in that path unconditionally, that path and logging before the deference Can Never Happen so can be removed.

Or type punning and pointer aliasing - objects are created with a type, and so the compiler Knows if you convert a pointer type to another type that isn't compatible with the first type, they somehow magically point to different memory, and all the assumptions that implies for the following code.

A lot of these restrictions are pretty similar to things like Java have - the difference is that the JVM checks and flags violations and/or straight up disallows them when compiling - not just allowing the compiler to (silently) optimize based on those assumptions, and throwing the result at hardware to see what happens.

There may be a few platform/compiler-specific behavior used to implement super low-level stuff like OSs, but that's platform-specific stuff outside the C++ (or C) spec itself.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#33

The reason why safety in C++ is difficult to achieve is due to the memory model used by C and C++. The memory model is a flat space provided by the OS that can be addressed by pointers. In this sense, C++ is similar to assembly code. A language like Java, on the other hand, assumes a different model where you can only access objects with well defined behavior. To change this, one needs to disallow the use of native p…

It depends what you mean exactly. The C and C++ official memory model is very much not a flat space, but exactly what you describe for Java - you can only (validly) access objects. For example, the operation x
  int x = 0;
  int y = 0;

  if(&x 
Now of course the implementation of C and C++ actually assumes without checking that you only access objects and not raw memory, and thus will happily read raw memory directly.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#36
post #13

Earlier quoted context omitted.

> It will never be perfect, but every little improvement helps. Or it might convince people to stay longer on a plane with a provably [0] terrible safety record. [0] https://alexgaynor.net/2020/may/27/science-on-memory-unsafet...

To put matters into perspective, Rust reference implementations depend on C++ toolchains. Same applies to all major Ada, Java, .NET, Swift, Ocaml and Haskell implementations. And any GPGPU toolchain. Which kind of shows it isn't going anywhere and those planes have to be improved no matter what.

I agree these planes are important and deserve care. At the same time pretty much all suggestions on how to meaningfully improve the safety of those planes boil down to successor languages Cpp2, Carbon etc. or require some other complex manual rewrite of components of said plane. There is an argument to be made for having good out-of-the-box interoperability, however even in some of the most complex and important code-bases in existence, namely browsers Firefox and Chrome, have demonstrated that you can do that part replacement in Rust. I'm not saying there is no other way. But these suggested and yet unproven improvements to C++ will not automatically make those planes safer. They will require replacing parts with new code, and if we are writing new code there is a serious question we should ask ourselves, building on what foundation do we want to improve those "planes".

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#38
You could also just isoheap according to type, where the type is whatever you come up with to make C++ casts sound. It could literally be C++ types or something looser (like if you want to say that bitcasting a int ptr to a float ptr is ok).

Then you don’t need any language changes to make UAF type safe.

Re: Making C++ safe without borrow checking, reference counting, or tracing GC

#39

“Rule 4: When you want a raw pointer as a field, use an index or an ID instead.” literally just woke up but: wouldn’t it be simpler to use a pointer to a pointer, or am I missing something

You might like: "Handles are the better pointers (2018) (floooh.github.io)"

https://news.ycombinator.com/item?id=36419739

Post reply on HN