digitalmars.D - Feedback Thread: DIP 1028--Make safe the Default--Final Review
- Mike Parker (47/47) Mar 25 2020 This is the feedback thread for the Final Review of DIP 1028,
- ag0aep6g (9/13) Mar 25 2020 It does not say clearly what happens with other `extern` functions.
- Walter Bright (4/16) Mar 25 2020 Since @safe will now be the default, all extern functions will be @safe....
- Jonathan M Davis (41/43) Mar 25 2020 The line "Interfaces to extern C and C++ functions should always be
- Walter Bright (24/58) Apr 11 2020 The DIP isn't entirely a spec. When it says "should" it's a recommendati...
- Dukc (21/22) Mar 31 2020 There is still this issue, that was brought up at previous review
- Walter Bright (4/26) Apr 11 2020 The DIP says: "Functions such as template functions, nested functions, a...
- John Colvin (38/41) Apr 06 2020 I think one detail of the DIP should be amended:
- Walter Bright (1/1) Apr 11 2020 See my reply to Jonathan M Davis.
- jeckel (67/67) Apr 11 2020 The extern(C) and friends should be @system by default. It is a
- Mike Parker (6/19) Apr 11 2020 This review round ended on April 8. Even so, the rules for the
This is the feedback thread for the Final Review of DIP 1028, "Make safe the Default". =================================== **THIS IS NOT A DISCUSSION THREAD** Posts in this thread must adhere to the feedback thread rules outlined in the Reviewer Guidelines (and listed at the bottom of this post). https://github.com/dlang/DIPs/blob/master/docs/guidelines-reviewers.md That document also provides guidelines on contributing feedback to a DIP review. Please read it before posting here. If you would like to discuss this DIP, please do so in the discussion thread: https://forum.dlang.org/post/jelbtgegkwcjhzwzesig forum.dlang.org ================================== You can find DIP 1028 here: https://github.com/dlang/DIPs/blob/5afe088809bed47e45e14c9a90d7e78910ac4054/DIPs/DIP1028.md The review period will end at 11:59 PM ET on April 8, or when I make a post declaring it complete. Feedback posted to this thread after that point may be ignored. At the end of the Final Review, the DIP will be forwarded to the language maintainers for Formal Assessment. ================================== Posts in this thread that do not adhere to the following rules will be deleted at the DIP author's discretion: * All posts must be a direct reply to the DIP manager's initial post, with only two exceptions: - Any commenter may reply to their own posts to retract feedback contained in the original post - The DIP author may (and is encouraged to) reply to any feedback solely to acknowledge the feedback with agreement or disagreement (preferably with supporting reasons in the latter case) * Feedback must be actionable, i.e., there must be some action the DIP author can choose to take in response to the feedback, such as changing details, adding new information, or even retracting the proposal. * Feedback related to the merits of the proposal rather than to the contents of the DIP (e.g., "I'm against this DIP.") is allowed in Community Review (not Final Review), but must be backed by supporting arguments (e.g., "I'm against this DIP because..."). The supporting arguments must be reasonable. Obviously frivolous arguments waste everyone's time. * Feedback should be clear and concise, preferably listed as bullet points (those who take the time to do an in-depth review and provide feedback in the form of answers to the questions in this document will receive much gratitude). Information irrelevant to the DIP or is not provided in service of clarifying the feedback is unwelcome.
Mar 25 2020
On 25.03.20 08:03, Mike Parker wrote:You can find DIP 1028 here: https://github.com/dlang/DIPs/blob/5afe088809bed47e45e14c9a90d7e78910ac 054/DIPs/DIP1028.mdThe DIP says about `extern` functions:An unmarked extern (D) function will now be considered safe.It does not say clearly what happens with other `extern` functions. For unmarked `extern (C)` and `extern (C++)` functions, the DIP should either say that they will still be considered system, or it should say that they become errors. The DIP should also mention `extern (Windows)` and `extern (Objective-C)`, or just have a catch-all sentence about "other `extern` functions".
Mar 25 2020
On 3/25/2020 12:29 AM, ag0aep6g wrote:The DIP says about `extern` functions:Since safe will now be the default, all extern functions will be safe. It shouldn't be necessary to enumerate what declarations are covered by this. It would only be necessary to enumerate declarations which would not be.An unmarked extern (D) function will now be considered safe.It does not say clearly what happens with other `extern` functions. For unmarked `extern (C)` and `extern (C++)` functions, the DIP should either say that they will still be considered system, or it should say that they become errors. The DIP should also mention `extern (Windows)` and `extern (Objective-C)`, or just have a catch-all sentence about "other `extern` functions".
Mar 25 2020
On Wednesday, March 25, 2020 1:03:16 AM MDT Mike Parker via Digitalmars-d wrote:This is the feedback thread for the Final Review of DIP 1028, "Make safe the Default".The line "Interfaces to extern C and C++ functions should always be explicitly marked" is pretty vague from the perspective of a spec. Does it mean that the compiler will require that the programmer mark extern(C) functions as system, trusted, or safe and that extern(C) functions without any of those attributes are illegal? Or does it have a default if not provided, and it's just that it's best practice for the programmer to explicitly specify it? And if it has a default, what is the default? Based on the paragraph above stating that "An unmarked extern (D) function will now be considered safe," I would guess that that means that any functions which aren't extern(D) (be they extern(C), extern(C++), extern(ObjC) or whatever) would be treated as system if not marked otherwise (just like happens now), but I think that the DIP should be explicit about it. There's also the question of declaration vs definition for functions which aren't extern(D). It makes no sense for a function declaration which is not extern(D) to be treated as safe by default, because the compiler can't guarantee its safety. However, I don't think that it would be a problem for non-extern(D) function definitions, because then the compiler _can_ check their safety. Either way, the DIP should be explicit about what happens with declarations and definitions which are not extern(D) - regardless of whether declarations and definitions are treated differently. I'd also argue that safe should not be allowed on function declarations which are not extern(D), because the compiler can't guarantee their safety (meaning that if an explicit safety attribute is supplied, it must either be system or trusted), and allowing safe on non-extern(D) declarations makes it much harder for programmers to grep for trusted code that could be the source of safety problems. In practice, right now, I'm unaware of anyone marking extern(C) declarations as safe, but if someone did, it could be very hard to track down the problem if it were hiding a memory safety bug, and given the ease of marking all declarations and definitions in a module with safe via safe: or safe {}, it wouldn't surprise me if some code bases are accidentally marking extern(C) declarations with safe right now and thus hiding memory safety issues. However, allowing safe on non-extern(D) function _definitions_ should be fine, since then the compiler actually is verifying the code. Regardless, much as I think that the DIP _should_ make it illegal to mark non-extern(D) declarations as safe, that would arguably be an improvement over what the DIP currently does rather than being required for the DIP to actually make sense or be acceptable. - Jonathan M Davis
Mar 25 2020
On 3/25/2020 9:57 PM, Jonathan M Davis wrote:The line "Interfaces to extern C and C++ functions should always be explicitly marked" is pretty vague from the perspective of a spec.The DIP isn't entirely a spec. When it says "should" it's a recommendation for best practice.Based on the paragraph above stating that "An unmarked extern (D) function will now be considered safe," I would guess that that means that any functions which aren't extern(D) (be they extern(C), extern(C++), extern(ObjC) or whatever) would be treated as system if not marked otherwise (just like happens now), but I think that the DIP should be explicit about it.It's pretty clear that declarations would default to safe. All of them. The discussion feedback indicated that this point wasn't confusing.There's also the question of declaration vs definition for functions which aren't extern(D). It makes no sense for a function declaration which is not extern(D) to be treated as safe by default, because the compiler can't guarantee its safety. However, I don't think that it would be a problem for non-extern(D) function definitions, because then the compiler _can_ check their safety. Either way, the DIP should be explicit about what happens with declarations and definitions which are not extern(D) - regardless of whether declarations and definitions are treated differently. I'd also argue that safe should not be allowed on function declarations which are not extern(D), because the compiler can't guarantee their safety (meaning that if an explicit safety attribute is supplied, it must either be system or trusted), and allowing safe on non-extern(D) declarations makes it much harder for programmers to grep for trusted code that could be the source of safety problems. In practice, right now, I'm unaware of anyone marking extern(C) declarations as safe, but if someone did, it could be very hard to track down the problem if it were hiding a memory safety bug, and given the ease of marking all declarations and definitions in a module with safe via safe: or safe {}, it wouldn't surprise me if some code bases are accidentally marking extern(C) declarations with safe right now and thus hiding memory safety issues. However, allowing safe on non-extern(D) function _definitions_ should be fine, since then the compiler actually is verifying the code. Regardless, much as I think that the DIP _should_ make it illegal to mark non-extern(D) declarations as safe, that would arguably be an improvement over what the DIP currently does rather than being required for the DIP to actually make sense or be acceptable.On the other hand, 1. it's a special case inconsistency, which has its own costs and confusion. 2. the compiler cannot verify any extern declarations as being safe, even D ones. It's always going to be up to the user to annotate them correctly. 3. the extern(C) specifies an ABI, it doesn't say anything about how the function is implemented, or even which language it is implemented in. A pretty big chunk of the dmd implementation (80-90%?) is extern(C++) 4. it's trivial to mark a block of C function declarations with system and trivial to audit it. I've done it already to a bunch of druntime headers 5. D's separate compilation model relies on extern declarations where source is not available and safety cannot be machine checked. It's inherent 6. We're just talking about the default. The whole point of safe being the default is that it is far and away the most common case, even for C functions. 7. A function having different attributes depending on whether or not a body is present is surprising behavior 8. annotating "extern(C) void free(void*);" as safe doesn't make it safe, either, again relying on the user 9. what do we do with "nothrow" by default? Say this doesn't apply to extern(C++) functions? Is anyone going to remember all these special cases?
Apr 11 2020
On Wednesday, 25 March 2020 at 07:03:16 UTC, Mike Parker wrote:[snip]There is still this issue, that was brought up at previous review also: Consider large amounts of legacy code modules that mix a lot of templated and non-templated functions. Majority of the non-templated functions are not ` safe`, but a many templates rely on inferring safety based on their arguments. The code is already tested to be reliable, and refactoring it to be safe would not be worth it. This DIP, as it currently stands, would force the maintainers to add ` system` function by function to the codebase, which can be somewhat large effort. If the maintainers could just add ` system:` to top of each module, the migration effort would be greatly reduced. However, they can't because they rely on the templates in the modules inferring ` safe` based on the arguments. I recommend that the DIP proposes to avoid this problem somehow. One possibility is proposing an alternative to ` system:` that does not apply to templates. Another is proposing that by default, regular functions would have their safety inferred the same way it's inferred for templated functions (thanks to Vladimir Panteleev for this idea -wouldn't have occured to me).
Mar 31 2020
On 3/31/2020 4:08 PM, Dukc wrote:On Wednesday, 25 March 2020 at 07:03:16 UTC, Mike Parker wrote:The DIP says: "Functions such as template functions, nested functions, and lambdas that are not annotated currently have their safe / system attribute inferred. This behavior will not change." which takes care of this.[snip]There is still this issue, that was brought up at previous review also: Consider large amounts of legacy code modules that mix a lot of templated and non-templated functions. Majority of the non-templated functions are not ` safe`, but a many templates rely on inferring safety based on their arguments. The code is already tested to be reliable, and refactoring it to be safe would not be worth it. This DIP, as it currently stands, would force the maintainers to add ` system` function by function to the codebase, which can be somewhat large effort. If the maintainers could just add ` system:` to top of each module, the migration effort would be greatly reduced. However, they can't because they rely on the templates in the modules inferring ` safe` based on the arguments. I recommend that the DIP proposes to avoid this problem somehow. One possibility is proposing an alternative to ` system:` that does not apply to templates. Another is proposing that by default, regular functions would have their safety inferred the same way it's inferred for templated functions (thanks to Vladimir Panteleev for this idea -wouldn't have occured to me).
Apr 11 2020
On Wednesday, 25 March 2020 at 07:03:16 UTC, Mike Parker wrote:This is the feedback thread for the Final Review of DIP 1028, "Make safe the Default".[snip]https://github.com/dlang/DIPs/blob/5afe088809bed47e45e14c9a90d7e78910ac4054/DIPs/DIP1028.mdI think one detail of the DIP should be amended: The section making non-D-mangled - e.g. extern(C) - functions safe by default. Points against: 1) existing extern(C) declarations - including bindings in libraries in transitive dependencies etc. - will likely be silently wrong in a way that breaks the guarantees of safe. Without careful review and updates, all that code can't be used if safe is a requirement, or nasty memory safety bugs will occur that would otherwise not have happened (because safe without this DIP would have caught them).* 2) It now means that one now has to search for extern(C) as well as trusted to find code that might be breaking safe. Linter rules to support correct usage of the language will be created to ensure all extern(C) declarations have explicit attributes, to avoid the inevitable mistakes that will occur otherwise. This is pretty sad, because the compiler can do a better job. 3) It can be quite hard to ascertain whether it's reasonable to mark a C function as trusted or not. The path of least resistance shouldn't be towards assuming safe without consideration. In summary, this invites new bugs and creates new work to avoid them. Suggestions to improve the DIP: A) non-D-mangled functions should be system by default, or B) the recommendation in the DIP "Interfaces to extern C and C++ functions should always be explicitly marked." should be enforced by the compiler. * yes, I know there are many cases where it will be totally fine, because no new safe (inferred or explicit) code is being written calling those functions (or any functions relying on attribute inference that do call those functions), or because existing complicated meta-programming won't spit out new calls in unexpected places and accidentally be inferred as safe. But those cases aren't all the important cases.
Apr 06 2020
The extern(C) and friends should be system by default. It is a special case, the intention is to make the unsafe code harder to use. Just like with () trusted { ... }(). While all extern declaration can't be verified as being safe, this is especially true for extern(C) and extern(C++). As the mangling does not have whether it was safe or not, unlike extern(D). Yes you can still use an extern(D) declaration that is unsafe, but that is exactly why this is an unsafe feature that shouldn't be allowed in the first place. For a language that prioritizes safety. Take Rust for example. It has no header files, there are no "declarations", other than C. Cargos are a first class citizen, and is much safer as a result not relying on user error with declarations. Declarations to C are unsafe by default. Rust doesn't have a "safe"/"trusted" keyword, so they cannot be implied to be "safe". They are *always* unsafe. Pointing to extern(D) and saying that's unsafe as well, so we should make extern(C) unsafe too. That's a very bad rationale. We should fix extern(D) as well to actually be safe, like with what Rust has done. We shouldn't introduce more unsafe code because something in D is already unsafe. The declaration itself is what is unsafe. That's why Rust has done away with them with Cargo, other than C declarations which default to unsafe. This is where a separe compilation model is inherently unsafe. That doesn't mean because one thing that is already unsafe, should continue to be unsafe. I can guarantee you there's more extern(C) unsafe code out there than there is extern(C) safe code. So it makes sense to make extern(C) unsafe by default, as that is most definitely the common case. If that's the kind of rationale you are trying to impose onto extern(C) of why it should be safe by default. It large majority of extern(C) code is most definitely not safe. Otherwise citation required. It's not surprising if a declaration has different attributes than it's definition. If it was, it'll be extremely easy to figure out. If you use an extern(C) declaration in safe code, you'll get an error saying it isn't safe and needs to be explicitly annotated. There's no gotcha, no surprise down the line when a bug shows itself. In comparison if there's an unsafe extern(C) function being used somewhere because extern(C) declarations can be defined *everywhere* including within the body of a function. Even though currently ` safe:` "flows through", it doesn't for extern(C) declarations. safe: void test() { extern(C) void foo(); foo(); // error calling system function extern(C) void foo2() { // this is safe } foo2(); // ok foo2 is safe } https://godbolt.org/z/ppsXMG Even Walter of old knew better than to make extern(C) declarations implicitly safe. The case with nothrow is pretty simple. C doesn't have exceptions so it can always be nothrow. C++ has a nothrow equivalent and is included as part of the mangling, so that can be used for C++. So nothrow can simply be nothrow by default. It isn't that big of an issue as there isn't a case of calling a nothrow function and it actually being able to throw an exception. Even if there was, if an exception is thrown in a C++ nothrow function it simply terminates the application. Yes extern declarations are still terrible. It's something that was inherited from C and is something we should strive to remove, as Rust has done. Possibly in a future DIP.
Apr 11 2020
On Wednesday, 25 March 2020 at 07:03:16 UTC, Mike Parker wrote:The review period will end at 11:59 PM ET on April 8, or when I make a post declaring it complete. Feedback posted to this thread after that point may be ignored.Posts in this thread that do not adhere to the following rules will be deleted at the DIP author's discretion: * All posts must be a direct reply to the DIP manager's initial post, with only two exceptions: - Any commenter may reply to their own posts to retract feedback contained in the original post - The DIP author may (and is encouraged to) reply to any feedback solely to acknowledge the feedback with agreement or disagreement (preferably with supporting reasons in the latter case)This review round ended on April 8. Even so, the rules for the Feedback Thread are still in place. If you'd like to discuss any of the DIP author's comments, please post in the Discussion Thread: https://forum.dlang.org/post/ovllntpiebixbtrbiuxj forum.dlang.org
Apr 11 2020