hsutter / cppfront

A personal experimental C++ Syntax 2 -> Syntax 1 compiler
Other
5.48k stars 242 forks source link

[SUGGESTION] Be by default as strict as possible #110

Closed redradist closed 1 year ago

redradist commented 1 year ago

As was suggested on Can C++ be 10x Simpler & Safer? - Herb Sutter - CppCon 2022 in QA session, to use all strict features by default: 1) Do all bounds checking by default and allow alternative --allow-unbound-index-operator 2) Allow only by default cpp2 syntax and instead add flag --allow-mixed-mode 3) ... and others

It is not harder to use flags to allow current C++ behavior instead of allowing "safe" behavior. I mean if user want to use current C++ behavior then a new flag that relax all restriction could be introduced

Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code? Yes, because it will enforce users to write code "safe" code and to ask to write "unsafe" code. Also, whenever user will want to write "unsafe" code, it will appear in code review

Will your feature suggestion eliminate X% of current C++ guidance literature?" Should be calculated :)

aminya commented 1 year ago

I agree with this. I think the defaults should enable the pure mode and the bound checks. Only if someone needs mixed or unsafe code, they might opt out by passing a flag or using a contract.

Doing it the reverse reminds me of the compilers not enabling warnings and requiring manual passing of -Wall -Wextra -Wpedantic. /W4 /permissive-, and all the other Ws that a compiler might or might not support!

I can't count how many C++ developers/instructors I have seen that don't even enable these warnings. This can be easily proved and measured by compiling the C++ projects that exist on GitHub, vcpkg, Conan, etc. with -Wall -Wextra -Werror. If these warnings were enabled by default, it would have been "a measurable improvement."

Additionally, ensuring the safety of the language also requires checking the correctness of the whole build system because the build system might not pass -p somewhere mistakenly.

fluffychaos commented 1 year ago

This also increases confidence in the code compiling without warnings to behave correctly. By having all these safety checks enabled by default the mental burden is moved from the developer to the compiler. Once your code compiles a lot of potential sources of runtime errors are already eliminated or are no longer silent errors like out-of-bounds accesses can easily be.

As it got already pointed out, people can easily not think about many aspects of their code, like the actual lifetime of their objects, correct indices etc. If, however, you have to explicitly disable a safety feature you have ti think about it beforehand. Otherwise you would not come to the conclusion that you have to deactivate it.

It makes writing good code easier while potentially bad code becomes harder to write as you have to go this extra step asking you to be aware of what you are doing.

While you could have compiler flags to turn these safety checks off it may also be worth thinking about having a form of Rusts unsafe language construct so you can disable these checks just for a certain region of code. Tools can then detect these regions and apply a more relaxed set of rules there, like allowing pointer arithmetic. This way these regions without the safety checks become more localized.

JohelEGP commented 1 year ago

I mostly agree, but see https://github.com/hsutter/cppfront/wiki/Design-note:-Unsafe-code for the last part.

While you could have compiler flags to turn these safety checks off it may also be worth thinking about having a form of Rusts unsafe language construct so you can disable these checks just for a certain region of code. Tools can then detect these regions and apply a more relaxed set of rules there, like allowing pointer arithmetic. This way these regions without the safety checks become more localized.

fluffychaos commented 1 year ago

Ah, thanks of showing me that. His position certainly is a good one. Making the kind of safety-feature-suppression explicit should further help with forcing you to make your intent clear.

I mostly agree, but see https://github.com/hsutter/cppfront/wiki/Design-note:-Unsafe-code for the last part.

hsutter commented 1 year ago

Hmm... are you maybe wanting something like a single -strict-cpp2 switch that is equivalent to

-p[ure-cpp2]        Allow Cpp2 syntax only
-n[ull-checks]      Enable null safety contract checks
-s[ubscript-checks] Enable subscript bounds safety contract checks

?

One concern I have about making the null and subscript checks on by default and rely on opt-out is that, should there be some minor overhead, people will start a meme that "Cpp2 is slow" without context. That has happened with C++. Making them off by default and opt-in helps reduce the chances of that misunderstanding/misinformation, and also feels like it hews more closely to C++'s core philosophy that we shouldn't pay for something unless we use it.

aminya commented 1 year ago

One concern I have about making the null and subscript checks on by default and rely on opt-out is that, should there be some minor overhead, people will start a meme that "Cpp2 is slow" without context

Well, if Cpp2 is not as strict by default, some people will say "Cpp2 is not safe" or "Cpp2 doesn't solve anything". Such a minor overhead should not be a concern! Rust has runtime unwraps everywhere, and no one has ever said that Rust is slow.

Other examples are Zig and D. If disabling the checks is opt-in, it still allows getting that extra minor push. Zig and D have the release fast/nobounds profiles that allow disabling the bounds checking. But they are opt-in.

redradist commented 1 year ago

@hsutter

Hmm... are you maybe wanting something like a single -strict-cpp2 switch that is equivalent to

-p[ure-cpp2]        Allow Cpp2 syntax only
-n[ull-checks]      Enable null safety contract checks
-s[ubscript-checks] Enable subscript bounds safety contract checks

?

One concern I have about making the null and subscript checks on by default and rely on opt-out is that, should there be some minor overhead, people will start a meme that "Cpp2 is slow" without context. That has happened with C++. Making them off by default and opt-in helps reduce the chances of that misunderstanding/misinformation, and also feels like it hews more closely to C++'s core philosophy that we shouldn't pay for something unless we use it.

Yes, the suggestion is to solve fundamental C++ issue - "safety", like Rust did !! Rust has a bound checks all over the place as @aminya said !!

C++ - if you do not use something you do not pay for it, but if you use it you pay for it.

In Cpp2, we use a "safety" feature and we pay for it !! If we do not want to pay for it, we need to say compiler about it and Cpp2 will have the same "safety" as Cpp1

aminya commented 1 year ago

A survey on the actual effect of the Rust bounds checks in a real-world application. The conclusion is that the performance effect is negligible and sometimes it results in improvements by unlocking optimizations! https://blog.readyset.io/bounds-checks/

hsutter commented 1 year ago

Thanks! After thinking this over, I've decided to enable -null-checks and -subscript-checks by default, and enable opt-out with a - suffix (e.g., -null-checks- or -n-). This way the direction of the switches stays compatible with any existing command lines people may be using, plus it's easier to flip the default again in the future if that makes sense for some reason.

Closed by this commit: 1a0967408f7de880183a329b1bb5bad30b59cb65