6 ms·
At this point I'm wondering if the purpose of safety profiles is simply to serve as a distraction. In other words, safety profiles are just something people can
by SubjectToChange 2y ago
At this point I'm wondering if the purpose of safety profiles is simply to serve as a distraction. In other words, safety profiles are just something people can point to when the topic of memory safety comes up, that’s it. The objectives of the initiative always seemed hopelessly optimistic, if not absurd. In particular, I don't understand why littering a codebase with auto, const, constexpr, inline, [[nodiscard]], noexcept, etc is wonderful, yet lifetime annotations are somehow an intolerable tyranny.
- ameliaquining 2y agoI think maybe it's because lifetime annotations can get arbitrarily complicated. If you look at enough Rust code you'll definitely see some function signatures that make your head hurt, even if they're vastly outnumbered by simple ones. A guarantee that the comprehension complexity of that part of your code will always be below some low ceiling is tempting.
- whimsicalism 2y agoRust has nothing on template meta programming and the type signatures you get there, though
- nickitolas 2y agoNot to mention the error messages when you get something slightly wrong
- crest 2y agoGive the proc macro fans a little more time...
- jerf 2y agoI understand the point you are making, but C++ templates really are a uniquely disastrous programming model. They can be used to pull off neat tricks, but the way those neat tricks are done is terrible.
- duped 2y agoWhen a proc macro fails you get an error at the site where the macro is used, and a stack trace into the proc macro crate. You can even use tools to expand the proc macro to see what went wrong (although those aren't built in, yet). Debugging a proc macro failure is miles and above easier than debugging template errors.
- consteval 2y agoThis isn't really true since concepts were introduced. Granted, you have to use them, but it makes the debugging/error messages MUCH better.
- jimbob45 2y agoI’ve spent a fair amount of time writing C++ but F12’ing any of the std data structures makes me feel like I’ve never seen C++ before in my life.
- NekkoDroid 2y agoto be fair, a major cause of the pain of looking at the std is because of the naming and being semi-required to use reserved names for implementation details (either double underscore or starting underscore uppercase) and also for keeping backwards compat for older standard versions.
- thadt 2y agoIt's deceptively easy to look at a number of examples and think: "If I can see that aliasing would be a problem in this function, then a computer should be able to see that too." The article states "A C++ compiler can infer nothing about aliasing from a function declaration." Which is true, but assumes that the compiler only looks at the function declaration. In the examples given, an analyzer could look at the function bodies and propagate the aliasing requirements upward, attaching them to the function declaration in some internal data structure. Then the analyzer ensures that those functions are used correctly at every call site. Start at leaf functions and walk your way back up the program until you're done. If you run into a situation where there is an ambiguity, you throw an error and let the developer know. Do the same for lifetimes. Heck, we just got 'auto' type inference working in C++11, shouldn't we be able to do this too? I like not having to see and think about lifetimes and aliasing problems most of the time, and it would be nice if the compiler (or borrow checker) just kept track of those without requiring me to explicitly annotate them everywhere.
- seanbax 2y agoFrom P3465: "why this is a scalable compile-time solution, because it requires only function-local analysis" From P1179: "This paper ... shows how to efficiently diagnose many common cases of dangling (use-after-free) in C++ code, using only local analysis to report them as deterministic readable errors at compile time." Local analysis only. It's not looking in function definitions. Whole program analysis is extremely complicated and costly to compute. It's not comparable to return type deduction or something like that.
- account42 2y agoMaking programmers manually annoate every single function is infinitely more costly.
- dwattttt 2y agoThat rather depends. Compile time certainly wouldn't scale linearly with the size of a function, you could well reach a scenario where adding in a line to a function results in a year being added to the compile time.
- estebank 2y agoThe thing is, if you were to make the same design in C++ the code might look "cleaner" because there is less code/fewer annotations, but the other side of that coin is that the developer also has less information about how things are meant to fit together. You not only lose the compiler having your back, you also don't have useful documentation, even if that documentation would be too complicated to grasp at once. Without that documentation you might be fooled into thinking that you do understand what's going on even if you don't in reality.
- yellow_lead 2y agoThat's a good point. There's many times in a C++ codebase, where I'd see or write a seemingly innocuous function, but it has so many assumptions about lifetimes, threads, etc that it would make your brain hurt. Of course we try to remove those or add a comment, but it's still difficult to deal with.
- bluGill 2y agoThere are reasonably good c++11 conventions for lifetimes - if it is a unique_ptr you own it, otherwise you don't and shouldn't save a copy. Almost nobody follows them, but they are good conventions and you should, and write up a bug if someone else isn't. Similar, for threads, keep your data confined to one thread, but explicit where you move/copy it to a different thread (note I said move or copy - the first thread should lose access in some way) - with the only exception of data explicitly marked as thread safe. The above is forced by Rust, which would be nice, but the conventions are easy enough if you try at all. But most developers refuse to write anything more than C++98.
- duped 2y ago> But most developers refuse to write anything more than C++98. I think the bigger mistake is equating memory safety with C++11 smart pointers. They buy you a little, but not the whole buffet. There are a lot of C++ developers that think memory safety is a skill issue and if you just use "best practices with C++11 or higher" then you get it - when evidence proves to the contrary.
- myworkinisgood 2y ago[flagged]
- pjmlp 2y agoExcept those kind of annotations already exist, but have proven not to be enough withough language semantic changes, SAL is a requirement in Microsoft's own code since Windows XP SP2. https://learn.microsoft.com/en-us/cpp/code-quality/understanding-sal?view=msvc-170 https://learn.microsoft.com/en-us/cpp/code-quality/understan...
- fmbb 2y agoYes. Lifetimes are complicated. Complicated codes make them even harder. Not annotating is not making anything easier.
- HelloNurse 2y agoWhat are the "arbitrarily complicated" cases of lifetime annotations? They cannot grow beyond one lifetime (and up to one compilation error) per variable or parameter or function return value.
- ameliaquining 2y agoMostly involving structs. Someone at work once posted the following, as a slightly-modified example of real code that they'd actually written: pub struct Step<'a, 'b> { pub name: &'a str, pub stage: &'b str, pub is_last: bool, } struct Request<'a, 'b, 'c, 'd, 'e> { step: &'a Step<'d, 'e>, destination: &'c mut [u8], size: &'b Cell<Option<usize>>, } To be sure, they were seeking advice on how to simplify it, but I imagine those with a more worse-is-better technical sensibility arguing that a language simply should not allow code like that to ever be written. I also hear that higher-ranked trait bounds can get scary even within a single function signature, but I haven't had cause to actually work with them.
- steveklabnik 2y agoIn general, you can usually simplify the first one to have one lifetime for both, and in the second, you’d probably want two lifetimes, one for destination and the others all shared. Defaulting to the same lifetime for everything and then introducing more of them when needed is better than starting with a unique lifetime for each reference. I think you two are ultimately talking about slightly different things, your parent is trying to point out that, even if this signature is complex, it can’t get more complex than this: one lifetime per reference means the complexity has an upper bound.
- HelloNurse 2y agoBut you are specifying that all members of Request except step.is_last have arbitrary unrelated lifetimes (shouldn't some of them be unified?) and you are simply exposing these lifetime parameters to Request client code like you would expose C++ template parameters: a trivial repetition that is easy to read, write and reason about.
- myworkinisgood 2y ago[flagged]
- CoastalCoder 2y ago> You are more correct than you think you are!!! Your comment will be more interesting if you expand upon it.
- rswail 2y agoIt's a tick-the-box-for-compliance item like when Microsoft had a POSIX layer for Windows NT.
- pjmlp 2y agoMicrosoft eventually learned that keeping full POSIX support would have been a better outcome in today's server room if they had done it properly instead. Likewise, pushing half solutions like profiles that are still pretty much a paper idea, other than what already exists in static analysers, might decrease C++'s relevance in some domains, and eventually those pushing for them might find themselves in the position that adopting Safe C++ (circle's design) would have been a much better decision. The problem with ISO driven languages, is who's around in the room when voting takes place.
- badmintonbaseba 2y agoAdopting what static analyzers do is a no-go, as they rely on non-local reasoning, even across translation units for lifetime and aliasing analysis. Their output highly depend on what they can see, and they generally can't see the source code for the whole program. I also doubt that they promise any kind of stability in their output across versions. This is a not a jab against static analyzers, by all means use them, but I don't think they are a good fit as part of the language.
- pjmlp 2y agoYeah, yet that is exactly the approach being pushed by those on the profiles camp. Further, the clang tidy and VC++ analysis based on some of the previous work, e.g. lifetime analysis paper from 2015, barely work, full of false positives. I was looking forward to it in VC++, and to this day in VC++ latest, it still leaves too much on the table.
- klodolph 2y agoWe can dream of what it would be like with full POSIX support on Windows, but it was a pipe dream to begin with. There are some major differences between Windows and POSIX semantics for things like processes and files. The differences are severe enough that Windows and POSIX processes can’t coexist. The big issue with files is that on POSIX, you can conceptually think of a file as an inode, with zero or more paths pointing to it. On Windows, you conceptually think of a file as the path itself, and you can create mandatory locks. There are other differences. Maybe you could address these given enough time, but WSL’s solution is to basically isolate Windows and Linux, which makes a ton of sense.
- WalterBright 2y agoSince const can be cast away, it's useless for checking.
- SubjectToChange 2y agoconst can be cast away, auto can have some really nasty behavior, constexpr doesn't have to do anything, inline can be ignored, [[nodiscard]] can be discarded, exceptions can be thrown in noexcept functions, etc. Almost everything in C++ can be bypassed in one way or another.
- WalterBright 2y agoD can cast away const, but not in @safe code. Though we are considering revising this so it can only be done in @system code.