16 ms·
Lifetime Annotations for C++
- westurner 5y agoFrom https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377 https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/... : > We are designing, implementing, and evaluating an attribute-based annotation scheme for C++ that describes object lifetime contracts. It allows relatively cheap, scalable, local static analysis to find many common cases of heap-use-after-free and stack-use-after-return bugs. It allows other static analysis algorithms to be less conservative in their modeling of the C++ object graph and potential mutations done to it. Lifetime annotations also enable better C++/Rust and C++/Swift interoperability. > This annotation scheme is inspired by Rust lifetimes, but it is adapted to C++ so that it can be incrementally rolled out to existing C++ codebases. Furthermore, the annotations can be automatically added to an existing codebase by a tool that infers the annotations based on the current behavior of each function’s implementation. > Clang has existing features for detecting lifetime bugs [...]
- pornel 5y agoedit: Chrome evaluated previous [[clang::lifetimebound]], and that wasn't enough: https://docs.google.com/document/d/e/2PACX-1vRZr-HJcYmf2Y76DhewaiJOhRNpjGHCxliAQTBhFxzv1QTae9o8mhBmDl32CRIuaWZLt5kVeH9e9jXv/pub https://docs.google.com/document/d/e/2PACX-1vRZr-HJcYmf2Y76D...
- ameliaquining 5y agoIs this doc talking about the same feature that this post is about? It sounds like the doc is talking about a different, less-precise analysis that the post cites as prior art.
- c0balt 5y agoSounds awesome. After the initial hurdle of getting used to lifetimes in rust it was really an enjoyable time and I would love to see the same feature in c++.
- kjksf 5y agoIt's a bad sign that I can't tell if someone spent a lot of time on an elaborate April's Fool joke or if it's a serious C++ proposal.
- deleted 5y ago[deleted]
- SilasX 5y agoIt's a bad sign in general that adding long-needed safeguards to C++ can be considered a joke.
- qualudeheart 5y agoC++ should double down on high performance. Better support for inline assembly. More parallelism and concurrency. Specialization.
- Ar-Curunir 5y agocan't do that without these safeguards...
- flqn 5y agoPerformance without safety sounds to me like "I'm doing the wrong thing, but faster!"
- qualudeheart 5y agoWhere we’re going we won’t need the right thing.
- deleted 5y ago[deleted]
- verdagon 5y ago> To summarize, enforcing [Rust's] borrowing rule in C++ is unfortunately not so simple because there is a lot of existing code that creates multiple non-const pointers or references to the same object, intentionally violating the borrowing rule. At this point we don’t have a plan of how we could incrementally roll out the borrowing rule to existing C++ code, but it is a very interesting direction for future work. This is actually possible for C++, if we add a concept of pure functions: functions that don't modify anything indirectly reachable from arguments (or globals). Basically: * Annotate those parameters as "immutable" or as coming from the same immutable "region". Anything reached through them would also have that immutable annotation. * Anything created during the call would not have that annotation. The type system, basically a borrow checker, could keep them separate and make sure we don't modify anything that came from outside the function. We're currently adding this to Vale [0], It's a way to blend shared mutability with borrow checking, and it gives us the optimization power and memory safety of borrow checking without all the constraints and learning curve problems. [0]: https://verdagon.dev/blog/seamless-fearless-structured-concurrency https://verdagon.dev/blog/seamless-fearless-structured-concu...
- Rusky 5y agoThat's not the issue here- there are plenty of schemes that could be used to enforce the rule, including just copying Rust's if you wanted. The issue is that a bunch of existing code actively holds and uses multiple mutable references to the same objects. It would simply not be able to adopt the chosen scheme, regardless of how it's spelled.
- verdagon 5y agoThat's what this "region borrow checking" solves: if a pure function (or block) regards all previously existing memory as one immutable region, it doesn't matter if there was any mutable aliasing happening before, because everything inside that region is shared and immutable now. Don't get me wrong, it's not the only missing piece for C++; C++ lets pointers escape to other threads which might modify the objects while our thread considers them immutable. Vale solves this by isolating threads' memory from each other (except in the case of mutexes or Seamless Concurrency). Luckily, there are plenty of schemes that can solve that particular problem for C++. If I had infinite time I would love to figure out how to implement this into C++, after the proof-of-concept in Vale. It's a fascinating topic, and an exciting time in the memory safety field, full of possibilities =)
- lamp987 5y ago10 years from now, there will be nobody on earth able to say "i know c++" because each codebase will use a completely different set of bazillion optional features making each codebase look like an entirely different language from each other. but maybe thats how we'll finally get rid of c++.
- ben-schaaf 5y agoI'd argue we've already been there for a while with everyone using a different subset of C++. I don't think it's made the language less popular.
- pjmlp 5y agoIf anything other languages are catching up in complexity.
- pjmlp 5y agoI have to agree, but then we need replacements for everything that is written in C++, like LLVM.
- Mikeb85 5y ago> 10 years from now, there will be nobody on earth able to say "i know c++" because each codebase will use a completely different set of bazillion optional features making each codebase look like an entirely different language from each other. I mean, this is kind of why C++ is successful. It's a toolbox that'll do anything and you can write high level or low level code. > but maybe thats how we'll finally get rid of c++ Lol we're never getting rid of it. There's too much C++ software and will Rust replace it all? Probably not.
- MathCodeLove 5y agoThere's too much Flash content on the web we'll never deprecate it
- not2b 5y ago
- htfy96 5y agoThis reminds me of the -Wlifetime proposal, which provides similar checks but requires annotation of ownership at struct level (hence the check only applies to new structs): An example: https://github.com/llvm/llvm-project/blob/main/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp https://github.com/llvm/llvm-project/blob/main/clang/test/Se... More details can be found at https://herbsutter.com/2018/09/20/lifetime-profile-v1-0-posted/ https://herbsutter.com/2018/09/20/lifetime-profile-v1-0-post... Unfortunately it was never upstreamed according to https://github.com/mgehre/llvm-project/issues/98 https://github.com/mgehre/llvm-project/issues/98
- Rusky 5y agoThere's a lot of overlap between the authors and implementors, and this proposal has extensive comparisons with the -Wlifetime one!
- clock99 5y ago
- benreesman 5y agoI tend to think that the ROI for large legacy codebases is in static analysis and instrumented tooling like the sanitizers. I’m grudgingly coming around to the idea that Rust is probably a rock-solid FFI story away from being a serious part of my kit (pybind11 good, not like anything we have now). But there is this in-between place where first-class linear/affine typing could be bootstrapped off of move semantics.
- kevincox 5y agoFWIW the Rust-C FFI is very solid. Binding to more complex languages is in various degrees of progress. For C++ I have heard really good things about https://cxx.rs/ https://cxx.rs/ (but never had the need to try it). wasm_bindgen is already very good for binding to JS and I have heard people having lots of success writing Python and Ruby libraries in Rust (with some manual glue on the scripted side).
- benreesman 5y agoI agree that the language-level C-FFI is quite good, the `bindgen`/`cbindgen` stuff is ok, but not amazing. I have a fair amount of experience with `cxx`, and even with `autocxx`. These tools show a lot of promise but are nowhere in the ballpark of `pybind11` in features, maturity, flexibility, or ease-of-use. It's early days on the Rust <-> C++ journey, which I appreciate might not be a huge concern for a lot of folks, but I depend on a lot of existing C++. My in-house thing ("alloy") is a big mess of `bindgen`/`cbindgen` and pain. It works ok but it's super counterintuitive at best that I budget like 5x the time to hoist something core up into Rust and I do for Python. I'm excited to see what happens in the space!
- dureuill 5y agoThis is where I stand right now, too. I recently published an overview of Rust <-> C++ interoperability[0], and cxx is the most effective tool available IME, but OTOH even cxx is not there yet (mainly missing bindings for Option and iterators). However the space, especially with autocxx, is evolving rapidly. [0] https://blog.tetrane.com/2022/Rust-Cxx-interop.html https://blog.tetrane.com/2022/Rust-Cxx-interop.html
- tialaramex 5y agoMaybe a C++ person can help me out, I am staring at this C++ translation of Rust's elision rules: > If there are multiple input lifetimes but one of them applies to the implicit this parameter, that lifetime is assigned to all elided output lifetimes. In Rust we have self rather than this in methods, but importantly we sometimes don't take a reference here, and that's still a method, and you still have the self parameter, but the lifetime elision rules don't apply. They don't apply because if you've actually got self, not some reference to self, the lifetime of self is going to end when the function finishes, so returning things with that lifetime is nonsense. This can make sense in Rust for transformative methods. If there's a method on a God that turns them into a Mortal, the God doesn't exist any more when that method exits, the Mortal is the return type, and if you just drop it then I guess sucks to be them. (In Rust, and I think in C++ you can label something as must-use and cause a warning or error if the programmer forgets to use it). It seems though, as if this C++ rule would hit such methods too. Can you distinguish in C++ between "a reference to me" and "me" when it comes to methods? If you had a method on a God class in C++ that transforms the God into a Mortal and returns the Mortal, what does that look like? Does this elision rule make sense to you for such a scenario?
- Rusky 5y agoC++ does not currently have such methods- all `this` parameters are always by (some kind of) reference. (This doesn't stop you from moving out of `*this`, but C++ moves require the source object to remain valid, and I'm not sure I've ever seen that in the wild anyway!) This will change in C++23 with "explicit this", but then the `this` parameter will no longer be implicit and that translated elision rule will no longer apply either. So in today's C++ your example might look something like this: struct God { Mortal transform() && { // Take an rvalue reference for `*this`, I guess? return Mortal{ /* Move some fields out of `*this` probably? */ }; } }; The elision rule does apply here even though we're moving out of `*this`, but again we are still taking a reference and the God object remains valid afterward anyway. With "explicit this" you could instead write something like this: struct God { Mortal transform(this God self) { ... } }; Now there's no reference and the rule ceases to apply. (Though in most cases you are still going to be leaving a valid, but now at least unrelated, God object behind- the only way to avoid that is to construct it directly in argument position so it stays a prvalue.)
- 323 5y agoWhat happens if you make an annotation mistake? Could the compiler generated code then have a security vulnerability, just like when you use undefined behaviour and the compiler is then allowed to optimize safety checks away?
- Rusky 5y agoNo- the idea is that if you make an annotation mistake, you will get an error, because the program will not match its annotations. Lifetimes are not like an `unreachable` UB operation. Instead they are just a description of cross-function information, about what both the caller and callee are allowed to assume. You could technically (both in Rust and under this proposal) do the same checks without them, if you had access to the entire program at once. However, this would be more expensive, and probably give you less-localized error messages (a lot like C++ template errors, for similar reasons).
- netheril96 5y agoIt doesn't affect code generation, only static analysis tools.
- malkia 5y agoApril fools, right? Like std::weekend_ptr - holds the ptr only during the weekend....
- glouwbug 5y agoSounds as useful as most pointers to be honest
- glouwbug 5y agoDownvote all you like. Memory managed languages pay the bills
- arcticbull 5y agoMemory managed languages and pointers aren't a disjoint set.
- shamilatesoglu 5y ago:D. Is this really a thing, or like a joke between friends? I googled it but nothing came up.
- malkia 5y agoSomeone made it up, I think on twitter :)
- summerlight 5y agoLooks like at least some of the authors are working for Google (if not everyone), with compiler backgrounds. I wonder if this is a strategical investment from Google to make cxx like approach more robust? Chrome team showed some interests on using Rust but not sure if there's any significant code written in Rust from then. Benefit from using Rust beside well isolated library might be quite limited without this kind of lifetime annotation while interop itself is additional cognitive overheads for programmers.
- verdagon 5y agoI think it makes the most sense for Google, considering the ridiculous amount of C++ code. Even Spanner alone is larger than most company's codebases, and rewriting it in Rust would take decades, especially because of the paradigm mismatch (C++ embraces shared mutability, Rust rejects it). It also makes sense because in Spanner, doing any minor change required many months of review, because it was such critical infrastructure. Refactoring was a non-starter in a lot of cases. So, a more gradual approach, just adding annotations that don't themselves affect the behavior of the program (just assist in static analysis) makes much more sense.
- zozbot234 5y agoRust does not reject shared mutability. Rather, it is explicitly reified via the Cell<>, RefCell<>, etc. patterns. Even unsafe shared mutability ala idiomatic C++ is just an UnsafeCell<> away.
- verdagon 5y agoIn theory yes, but when you look at the average Rust program, those mechanisms are avoided in favor of more idiomatic approaches.
- hgomersall 5y agoInterior mutability is used all over the place. It's absolutely necessary. The thing is you are forced to be clear about the runtime safety mechanism you're using. In general, it gets abstracted away behind a safe API.
- aaaaaaaaaaab 5y agoNooo! If this gets implemented then Rust is basically killed… :(
- The_rationalist 5y agohaha C++ lifetimes go brrr
- roca 5y agoNot at all. This is all about retrofitting existing code with more static checking. Nothing here creates an incentive to write new code in C++ instead of Rust.
- TheDesolate0 5y ago
- queuebert 5y agoPardon the stupid question, but at this point why not just use Rust? It interoperates with C++ fairly well.
- Too 5y agoC++ developers tend to be very conservative. I still meet shops that genuinely are discussing if they should use c++98 or try this new modern thing c++11. Lately that’s getting more and more rare fortunately. Even if your team is more progressive, there is likely a huge mountain of still active legacy code that nobody wants to rewrite. There a more incremental approach is still the way to go. Otherwise yes, for a greenfield project, rust is likely superior in many other ways. Not just in this area of borrowing but also build, syntax and lots of other features.
- pjmlp 5y agoIt interoperates with a tiny subset of the C++ language, it is miles away to interoperate with libraries, SDKs, IDEs, GPGPU, FPGAs and tons of other tooling used in C++ ecosystem.
- StillBored 5y agoI think this is a good idea, mostly because I've been of the opinion that much of the rust "safety" could be done with a linter pass in C++. These annotations should help solve the edge cases that aren't deterministic. I've not generally been a big fan of where C++ has been going in the last couple revisions, mostly because it seems the newer features aren't fully thought out when it comes to how they interact with other features (reminds me of javascript in that regard) leaving a bunch of new footguns, looks at lambda's. I think i've said this here before, that C++ needs its own version of "use strict;" that basically kills off some of the syntactically odd corner cases that lead to those footguns.