www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - safe by default

reply Atila Neves <atila.neves gmail.com> writes:
https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

Destroy!
May 30
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
Looks good.

This is everything I have argued /needs/ to happen (although I'd prefer 
inferred instead).
May 30
prev sibling next sibling parent reply bachmeier <no spam.net> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
How do you turn off safe by default? With a compiler switch? I know some folks think it's good to punish people for writing unsafe code, as became clear in the earlier discussions, but safe by default is of no benefit to me, and I don't want to have to clutter my code with a bunch of boilerplate for no reason.
May 30
parent Atila Neves <atila.neves gmail.com> writes:
On Thursday, 30 May 2024 at 19:41:26 UTC, bachmeier wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
How do you turn off safe by default?
``` system: ```
May 30
prev sibling next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
 All declarations (i.e. functions with no body) must have 
 exactly one of the  safe/ trusted/ system annotations or the 
 module that contains them will fail to compile
please no
May 30
prev sibling next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
 If there is no body, the compiler cannot verify the  safety of 
 the function and in those cases this DIP proposes that there 
 will be no default. All declarations (i.e. functions with no 
 body) must have exactly one of the  safe/ trusted/ system 
 annotations
For extern(D), there will be a linker error if a protoype is safe but the implementation is system. For other linkage, that case would silently link with no error. That breaks the principle of being able to grep for trusted to find an *accidental* safety violation. (Deliberate violation with pragma(mangle) is less serious as that is intentional and stands out more to a reviewer). Module 1: ```d import core.stdc.stdio; system extern(C) void ext(int* p) { p++; printf("%d\n", *p); } ``` Module 2: ```d safe extern(C) void ext(int* p); void main() // implicitly safe { int i; ext(&i); // boom } ``` safe should mean mechanically checked for accidental memory-safety violations - that is a more useful definition. Allowing non-extern(D) linkage prototypes to be safe breaks that principle and makes safe prototypes a minefield.
 This DIP makes no distinction between extern(D), extern(C), and 
 extern(C++) functions. Although the latter two usually apply to 
 functions written in C or C++ respectively, that is not 
 necessarily the case
A non-extern(D) prototype can be trusted, and its implementation in D can be safe, so the implementation is mechanically checked.
May 31
next sibling parent Nick Treleaven <nick geany.org> writes:
On Friday, 31 May 2024 at 09:18:17 UTC, Nick Treleaven wrote:
 Module 2:
 ```d
  safe extern(C) void ext(int* p);

 void main() // implicitly  safe
 {
     int i;
     ext(&i); // boom
 }
 ```
Sorry, that doesn't compile even with -dip1000. But this does: ```d auto p = new int; ext(p); // boom ```
May 31
prev sibling parent reply Dukc <ajieskola gmail.com> writes:
Nick Treleaven kirjoitti 31.5.2024 klo 12.18:
  safe should mean mechanically checked for accidental memory-safety 
 violations - that is a more useful definition. Allowing non-extern(D) 
 linkage prototypes to be  safe breaks that principle and makes  safe 
 prototypes a minefield.
I agree in principle, but note this is orthogonal to the DIP. D allows declaring external C functions as ` safe` right now. The DIP should not make it any worse, and it also doesn't prevent a separate proposal that would say that external non-D linked functions must be either ` trusted` or ` system`.
May 31
parent reply Atila Neves <atila.neves gmail.com> writes:
On Friday, 31 May 2024 at 11:42:13 UTC, Dukc wrote:
 Nick Treleaven kirjoitti 31.5.2024 klo 12.18:
  safe should mean mechanically checked for accidental 
 memory-safety violations - that is a more useful definition. 
 Allowing non-extern(D) linkage prototypes to be  safe breaks 
 that principle and makes  safe prototypes a minefield.
I agree in principle, but note this is orthogonal to the DIP. D allows declaring external C functions as ` safe` right now. The DIP should not make it any worse, and it also doesn't prevent a separate proposal that would say that external non-D linked functions must be either ` trusted` or ` system`.
This. I don't think we can stop programmers intent on lying to the compiler. As mentioned there's already `pragma(mangle)`, they can write assembly, ... I think that if there's a body written in D somewhere, it's unlikely someone will manually write a declaration and use the wrong attribute by mistake. And if there isn't, then they will have had to deliberately have picked an attribute.
May 31
parent Nick Treleaven <nick geany.org> writes:
On Friday, 31 May 2024 at 16:32:37 UTC, Atila Neves wrote:
 This.
I agree my argument is not necessarily an impediment to this DIP, but it is closely related.
 I don't think we can stop programmers intent on lying to the 
 compiler. As mentioned there's already `pragma(mangle)`,
Which sticks out in a review, and is clearly intentional not accidental (as I already said).
 they can write assembly, ...
Actually that supports my case, ` safe` annotated `asm` is deprecated: https://dlang.org/deprecate.html#unannotated%20asm%20blocks You have to write ` trusted` instead, *because it's not mechanically checked*.
 I think that if there's a body written in D somewhere, it's 
 unlikely someone will manually write a declaration and use the 
 wrong attribute by mistake. And if there isn't, then they will 
 have had to deliberately have picked an attribute.
Not convinced it's unlikely - the implementation may be safe when the prototype is written, then the implementation is changed to system and the prototype is forgotten. No reason not to require trusted for prototypes.
May 31
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
I was glad to see some compromise on this. I'm still not 100% behind it, but it is less objectionable than before, IMO. In terms of qualms, here is an example of a module that contains a mix of code, some of which is bringing in compiler intrinsics that don't have function bodies and part of it is additional functions: https://github.com/libmir/mir-core/blob/master/source/mir/math/common.d Under this DIP, this would no longer compile, which is fine it isn't hard to fix. My problem is with your recommendation elsewhere on the thread to add ` system:` at the top. Since the module mixes the intrinsics and functions together, that alone is not sufficient. The templates then would become system instead of having their attributes inferred. One way forward is to move everything without a body into a separate module with ` system:` at the top and publicly import that here. That's ok, although it separates the documentation. The other option is to go function by function to add the system to each function. I think some people have proposed solutions to this issue in the past when this comes up. I think it can't hurt to give some more thought to any improvements that can be brought to bear before this is adopted. You're still potentially in for a lot of code breakage that could keep a lot of people on the old edition for a while. Hopefully the edition approach is able to make ease this transition, although it's hard to ignore the risk of bifurcation.
May 31
parent reply Atila Neves <atila.neves gmail.com> writes:
On Friday, 31 May 2024 at 12:50:55 UTC, jmh530 wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
I was glad to see some compromise on this. I'm still not 100% behind it, but it is less objectionable than before, IMO. In terms of qualms, here is an example of a module that contains a mix of code, some of which is bringing in compiler intrinsics that don't have function bodies and part of it is additional functions: https://github.com/libmir/mir-core/blob/master/source/mir/math/common.d Under this DIP, this would no longer compile,
It wouldn't compile because of the intrinsics? Wouldn't the fix then be to add attributes to them?
May 31
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 31 May 2024 at 16:36:08 UTC, Atila Neves wrote:
 [snip]

 It wouldn't compile because of the intrinsics? Wouldn't the fix 
 then be to add attributes to them?
Actually it may compile in this case since it looks like they already have attributes at the top of the version block for them. I was trying to use this as an example of a file that has a mix of functions without bodies and templated functions that would have inference. It was meant as a criticism of your idea of putting ` system:` at the top of files as solution to the problem. As I said later in that post, putting ` system:` at the top would force the templated functions to be ` system` too instead of getting inferred. One solution is going function by function as I said in my post, but just putting it at the top isn't a solution.
May 31
parent Atila Neves <atila.neves gmail.com> writes:
On Friday, 31 May 2024 at 17:45:44 UTC, jmh530 wrote:
 On Friday, 31 May 2024 at 16:36:08 UTC, Atila Neves wrote:
 [...]
Actually it may compile in this case since it looks like they already have attributes at the top of the version block for them. I was trying to use this as an example of a file that has a mix of functions without bodies and templated functions that would have inference. It was meant as a criticism of your idea of putting ` system:` at the top of files as solution to the problem. As I said later in that post, putting ` system:` at the top would force the templated functions to be ` system` too instead of getting inferred. One solution is going function by function as I said in my post, but just putting it at the top isn't a solution.
Fair enough.
Jun 03
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
I agree completely that "having [ safe] be opt-in makes it hard to use in practice" and that "defaults matter". However, I think that implementing [Universal Function Attribute Inference][1] would be a better way to address the shortcomings of the current defaults. In ["Thoughts on Backward Compatibility"][2], I tried to figure out why some breaking changes succeed and others fail. The conclusion I came to was that breaking changes are most successful when: 1. Migration is easy, or 2. The change serves a goal that has strong buy-in from users While there is a vocal minority in the D community who strongly support safe (a group in which I include myself), **I do not believe that safe has strong buy-in from D programmers as a whole.** In other words, this proposal cannot satisfy condition (2). It follows that, if we would like it to succeed, it *must* satisfy condition (1). Sadly, I do not think migration to `-preview=safedefault` will be particularly easy. By the DIP's own admission, it "will likely break a lot of code". Even if editions allow us to mix pre-migration and post-migration code in the same project, the burden of migrating to `-preview=safedefault` will likely hurt the adoption of new editions, and cause projects that would otherwise upgrade to get "stuck" in the system-by-default edition. (It is also conceivable that new projects started by D programmers who don't care about safe will choose the old, system-by-default edition specifically to avoid the hassle of adding explicit system attributes to their code.) Universal inference avoids these problems. In most cases, it requires zero manual intervention to migrate existing code (that is, it Just Works™). The only major obstacle it faces is build performance when using separate compilation, and this obstacle can be overcome with toolchain improvements. Inference also solves the wrong-default problem for nogc, nothrow, and pure, in addition to safe, without the need for any additional -preview switches or migrations. Asking users to migrate from system-by-default to safe-by-default may be doable, but can we really ask them to do the same thing *three more times* for the other attributes? [1]: https://forum.dlang.org/thread/pfawiqhppkearetcrkno forum.dlang.org [2]: https://forum.dlang.org/post/axvuuknmtxpmuirbbgax forum.dlang.org
May 31
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, May 31, 2024 7:06:16 AM MDT Paul Backus via dip.ideas wrote:
 Inference also solves the wrong-default problem for  nogc,
 nothrow, and pure, in addition to  safe, without the need for any
 additional -preview switches or migrations. Asking users to
 migrate from  system-by-default to  safe-by-default may be
 doable, but can we really ask them to do the same thing *three
 more times* for the other attributes?
I think that you will have a hard time finding a consensus on the idea that any of those should be the default. It may be that most folks would agree that safe should be the default, but I'd be _very_ surprised to see a consensus on the others (especially nogc). Personally, I'm increasingly of the opinion that most attributes are actively detrimental rather than beneficial, and I don't want to see them be forced or for them become the default. I think that there's a good argument for making safe the default given that you usually do want most code to be safe, and trusted makes it fairly easy to have system code be used by safe code, but the others often simply do not work with code and get in the way of making changes - especially with larger code bases. So, I very much hope that we don't change the default attributes beyond possibly safe. And considering that DIP 1000 is a thing, I'm definitely concerned about what the repercussions of making safe the default are, since DIP 1000 makes life _way_ too complicated IMHO, and inference will trigger it in a number of cases (to the point that I would be tempted to slap system on everything and give up on any benefits from safe just to escape having to deal with DIP 1000). If DIP 1000 weren't a thing, then I'd probably be okay with safe being the default, but I think that all of these attempts to make malloc safe instead of just saying that if you want safe without trusted, you use the GC, are adding way too much complexity to the language, and making safe the default potentially forces you into that mess - either that or forces you to slap system all over the place. Having more attribute inference may help with some of these issues, but personally, I just want to avoid most attributes in general and not have to deal with stuff like pure, because inevitably, at some point, you have to figure out how to strip it from your code to get something to work. - Jonathan M Davis
May 31
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Friday, 31 May 2024 at 13:06:16 UTC, Paul Backus wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
I agree completely that "having [ safe] be opt-in makes it hard to use in practice" and that "defaults matter". However, I think that implementing [Universal Function Attribute Inference][1] would be a better way to address the shortcomings of the current defaults.
It might be; it's something Walter and I have discussed before, and the reason that didn't go forward is due to the potential compile-time performance penalties. If it's doable in a fast way it would kill two birds with one stone, for sure.
 In ["Thoughts on Backward Compatibility"][2], I tried to figure 
 out why some breaking changes succeed and others fail. The 
 conclusion I came to was that breaking changes are most 
 successful when:

 1. Migration is easy, or
 2. The change serves a goal that has strong buy-in from users

 While there is a vocal minority in the D community who strongly 
 support  safe (a group in which I include myself), **I do not 
 believe that  safe has strong buy-in from D programmers as a 
 whole.** In other words, this proposal cannot satisfy condition 
 (2). It follows that, if we would like it to succeed, it *must* 
 satisfy condition (1).

 Sadly, I do not think migration to `-preview=safedefault` will 
 be particularly easy. By the DIP's own admission, it "will 
 likely break a lot of code". Even if editions allow us to mix 
 pre-migration and post-migration code in the same project, the 
 burden of migrating to `-preview=safedefault` will likely hurt 
 the adoption of new editions, and cause projects that would 
 otherwise upgrade to get "stuck" in the  system-by-default 
 edition.
 (It is also conceivable that new projects started by D 
 programmers who don't care about  safe will choose the old, 
  system-by-default edition specifically to avoid the hassle of 
 adding explicit  system attributes to their code.)
I was going to write that I don't understand why anyone would want system-by-default until I realised that calling legacy code, including dub packages, might be a pain. Hmm.
 Universal inference avoids these problems. In most cases, it 
 requires zero manual intervention to migrate existing code 
 (that is, it Just Works™). The only major obstacle it faces is 
 build performance when using separate compilation, and this 
 obstacle can be overcome with toolchain improvements.
I'm all for not having to write attributes the compiler can infer itself and having them "only" show up in .di files. As long as build speeds aren't affected, since it's too slow as it is for me. Do I like writing ` safe nogc pure nothrow scope const`? No, but that's what I have to do right now.
May 31
prev sibling next sibling parent Guillaume Piolat <first.name gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
Neutral. I think safe by default is of no benefit to me. However I'd support this change (as an edition) since there are easy ways out and default do matter, I've remarked if you don't write safe right from the start it's not going to happen afterwards (but this is also a testament to how relatively useless it is to be safe in the first place imo).
May 31
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 5/30/24 20:35, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md
 
 Destroy!
- I think even more important than the default is the ability to change the default (e.g. `default( safe):`). This does not exist currently, but it would be required for easy migration. - There is not really any value in being able to write ` safe extern(C)/extern(C++)` prototypes. It's wrong and any linter would need to have a warning for it. I would just require an explicit ` system` or ` trusted` annotation. Note that for `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` have _the same semantics and interpretation_, but only one of them looks adequately dangerous and is easy to grep. - The DIP should clarify whether annotations like ` safe:` apply to prototypes or whether prototypes always need an individual annotation. - N.B.: OpenD has been experimenting with changing the default safety level behavior to be a distinct category from the other three. (It enables checks on pointer arithmetic, but is not transitive and does not include DIP1000 checks.) It does not guarantee memory safety but can catch bugs.
Jun 01
parent reply Atila Neves <atila.neves gmail.com> writes:
On Saturday, 1 June 2024 at 21:06:05 UTC, Timon Gehr wrote:
 On 5/30/24 20:35, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md
 
 Destroy!
- I think even more important than the default is the ability to change the default (e.g. `default( safe):`). This does not exist currently, but it would be required for easy migration.
That's a good point, but: do we only do it for this attribute or for the others as well?
 - There is not really any value in being able to write ` safe 
 extern(C)/extern(C++)` prototypes. It's wrong and any linter 
 would need to have a warning for it. I would just require an 
 explicit ` system` or ` trusted` annotation. Note that for 
 `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` have 
 _the same semantics and interpretation_, but only one of them 
 looks adequately dangerous and is easy to grep.
I get this, but the issue is that those functions might actually be written in D.
 - The DIP should clarify whether annotations like ` safe:` 
 apply to prototypes or whether prototypes always need an 
 individual annotation.
Added.
Jun 03
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 6/3/24 17:55, Atila Neves wrote:
 - There is not really any value in being able to write ` safe 
 extern(C)/extern(C++)` prototypes. It's wrong and any linter would 
 need to have a warning for it. I would just require an explicit 
 ` system` or ` trusted` annotation. Note that for 
 `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` have _the 
 same semantics and interpretation_, but only one of them looks 
 adequately dangerous and is easy to grep.
I get this, but the issue is that those functions might actually be written in D.
Why is this an issue?
Jun 06
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 6 June 2024 at 17:01:05 UTC, Timon Gehr wrote:
 On 6/3/24 17:55, Atila Neves wrote:
 - There is not really any value in being able to write ` safe 
 extern(C)/extern(C++)` prototypes. It's wrong and any linter 
 would need to have a warning for it. I would just require an 
 explicit ` system` or ` trusted` annotation. Note that for 
 `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` 
 have _the same semantics and interpretation_, but only one of 
 them looks adequately dangerous and is easy to grep.
I get this, but the issue is that those functions might actually be written in D.
Why is this an issue?
The compiler would have access to the body and so would verify its safe-ness.
Jun 06
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 6/7/24 02:58, Atila Neves wrote:
 On Thursday, 6 June 2024 at 17:01:05 UTC, Timon Gehr wrote:
 On 6/3/24 17:55, Atila Neves wrote:
 - There is not really any value in being able to write ` safe 
 extern(C)/extern(C++)` prototypes. It's wrong and any linter would 
 need to have a warning for it. I would just require an explicit 
 ` system` or ` trusted` annotation. Note that for 
 `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` have _the 
 same semantics and interpretation_, but only one of them looks 
 adequately dangerous and is easy to grep.
I get this, but the issue is that those functions might actually be written in D.
Why is this an issue?
The compiler would have access to the body and so would verify its safe-ness.
A function prototype does not have a body and the actual body may be in another compilation unit. For `extern(D)` the mangled name gives some assurances, but not for `extern(C)` and `extern(C++)`. I guess maybe an additional alternative name can be given to ` safe` `extern(C)` functions, but I doubt that linker errors are a great UX for safety checks.
Jun 07
parent Dukc <ajieskola gmail.com> writes:
Timon Gehr kirjoitti 7.6.2024 klo 17.25:
 On 6/7/24 02:58, Atila Neves wrote:
 On Thursday, 6 June 2024 at 17:01:05 UTC, Timon Gehr wrote:
 On 6/3/24 17:55, Atila Neves wrote:
 - There is not really any value in being able to write ` safe 
 extern(C)/extern(C++)` prototypes. It's wrong and any linter would 
 need to have a warning for it. I would just require an explicit 
 ` system` or ` trusted` annotation. Note that for 
 `extern(C)/extern(C++)` prototypes, ` safe` and ` trusted` have 
 _the same semantics and interpretation_, but only one of them looks 
 adequately dangerous and is easy to grep.
I get this, but the issue is that those functions might actually be written in D.
Why is this an issue?
The compiler would have access to the body and so would verify its safe-ness.
A function prototype does not have a body and the actual body may be in another compilation unit. For `extern(D)` the mangled name gives some assurances, but not for `extern(C)` and `extern(C++)`. I guess maybe an additional alternative name can be given to ` safe` `extern(C)` functions, but I doubt that linker errors are a great UX for safety checks.
When Átila wrote his last reply I was still agreeing with him. Why? I missed that you, in your earlier post, wrote specifically about function prototypes, not any C/C++ linked functions. I guess Átila did too - I can't see why else he would have thought the compiler has access to the function body. If I'm assessing this right, you're actually in agreement with each other. The only disagreement you maybe have is whether it's this DIP or some later alternative that should change the rules for those functions.
Jun 07
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. The right way: ```d module a; extern(C) int f(int) trusted; extern(C) int f(int) safe; // compile error: `extern(C)` function cannot be verified ` safe`. Hint: If the implementation is written in D in another module, use ` trusted`. ``` ```d module b; extern(C) int f(int x) safe => x; // okay: implementation present ``` In this case, unfortunately, ` trusted` can mean two things: * The implementation is ` system`, but the developer verified it is fool-proof to use UB-free. * The developer verified the implementation is annotated ` safe`. In this case, a comment can be added to indicate this case.
Jun 03
parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 3 June 2024 at 11:08:36 UTC, Quirin Schroll wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 [...]
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. [...]
I could see changing it to this; but what if it's writen in D and someone wants to use a .di file?
Jun 03
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 3 June 2024 at 15:56:05 UTC, Atila Neves wrote:
 On Monday, 3 June 2024 at 11:08:36 UTC, Quirin Schroll wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 [...]
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. [...]
I could see changing it to this; but what if it's writen in D and someone wants to use a .di file?
TBH, I don’t know what .di files really are. AFAIK, they’re basically what a header file in C/C++ is. They contain declarations. The biggest difference to C/C++ is that .di files are compiler generated and a programmer might occasionally read them, but essentially never write or edit one. Correct me if I’m wrong. For `extern(C/C++)` functions implemented in D, whether the .di file contains contains ` safe` or ` trusted` declarations doesn’t really make a difference. For callers, they’re the same. They also mangle the same. There may be a difference what reflection sees, though. I notice one issue, that ` trusted` is not the same as ` safe` from the caller perspective. In `extern(D)`, they also mangle differently, which is fine but weird. I noticed that this does not compile: ```d extern(C) void f() trusted; extern(C) void f() safe { } void main() { f(); } // error: `f` ambiguous ``` This could be an issue for the D module that defines the function, maybe it isn’t. If it is, I guess that could be changed, i.e. for `extern(C/C++)` make the compiler realize that function declarations only differing in attributes refer to the same function. I cannot imagine someone relies on this being an error.
Jun 04
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 05/06/2024 12:13 AM, Quirin Schroll wrote:
 On Monday, 3 June 2024 at 15:56:05 UTC, Atila Neves wrote:
 On Monday, 3 June 2024 at 11:08:36 UTC, Quirin Schroll wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 [...]
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. [...]
I could see changing it to this; but what if it's writen in D and someone wants to use a .di file?
TBH, I don’t know what .di files really are. AFAIK, they’re basically what a header file in C/C++ is. They contain declarations. The biggest difference to C/C++ is that .di files are compiler generated and a programmer might occasionally read them, but essentially never write or edit one. Correct me if I’m wrong.
.di files are currently all hand written. The only thing special about them is the convention that they should have methods discarded and that they get priority over .d files. In all other ways the are the same as .d files. The .di generator currently runs _before_ semantic, this means no inferred attributes get emitted. If you move it to after semantic (assuming no other issues lol) scope and return will be emitted in the wrong order messing up mangling. Basically right now the .di generator can't be used. I am working on fixing most of the blocking issues, but unfortunately I am going to hand it off due to some semantic rewrites that I cannot fix. A quick look on ABI page, both `` safe`` and `` trusted`` get name mangled differently. https://dlang.org/spec/abi.html#FuncAttrTrusted So yes we'd have to allow `` safe`` on function declarations without bodies unfortunately.
Jun 04
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 4 June 2024 at 20:33:07 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 05/06/2024 12:13 AM, Quirin Schroll wrote:
 On Monday, 3 June 2024 at 15:56:05 UTC, Atila Neves wrote:
 On Monday, 3 June 2024 at 11:08:36 UTC, Quirin Schroll wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 [...]
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. [...]
I could see changing it to this; but what if it's writen in D and someone wants to use a .di file?
TBH, I don’t know what .di files really are. AFAIK, they’re basically what a header file in C/C++ is. They contain declarations. The biggest difference to C/C++ is that .di files are compiler generated and a programmer might occasionally read them, but essentially never write or edit one. Correct me if I’m wrong.
.di files are currently all hand written. […] A quick look on ABI page, both `` safe`` and `` trusted`` get name mangled differently. https://dlang.org/spec/abi.html#FuncAttrTrusted So yes we'd have to allow `` safe`` on function declarations without bodies unfortunately.
I’m not referring to `extern(D)` functions. I knew those have attributes mangled into them, so you’ll get linker errors on an attribute mismatch between a function’s prototype and its definition. The issue is, for `extern(C/C++)` functions, attributes aren’t mangled into it, so the linker is blind to the mismatch. Of course, linker errors aren’t great, but still a lot better than UB at runtime. If .di files are hand-written, we must absolutely not allow ` safe` annotations on `extern(C/C++)` function declarations. Those must all be ` trusted`, even if the validation by the programmer only requires grepping the function name and observing that the function definition (the implementation) is annotated ` safe`, so the actual, difficult validation is done by the compiler. However, ` trusted` is warranted, as when the implementation isn’t annotated ` safe` (either because it’s not annotated or is in another language entirely), it simply may not be safe, and therefore, a ` safe` function declaration in a .di file would be a lie, and the bad thing about that is that it’s not going to be caught by the compiler or linker. The ` trusted` attribute only means that *some* programmer-side verification established the function is actually safe. It does not mean the verification can’t be as simple as a grep. In fact, if .di files are hand written, changing ` safe` to ` trusted` after copying a function signature isn’t too wild to ask. The simple fact is, on non-D functions, ` safe` can’t be verified except on function definitions, and therefore can’t be used. Átila: Making exceptions to ` safe` so that it isn’t fool-proof would be a disaster to D’s marketability.
Jun 10
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 10 June 2024 at 15:12:28 UTC, Quirin Schroll wrote:
 On Tuesday, 4 June 2024 at 20:33:07 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 05/06/2024 12:13 AM, Quirin Schroll wrote:
 On Monday, 3 June 2024 at 15:56:05 UTC, Atila Neves wrote:
 On Monday, 3 June 2024 at 11:08:36 UTC, Quirin Schroll wrote:
 On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 [...]
Because of a lack of mangling, `extern(X)` where `X` isn’t `D` shouldn’t ever be allowed to be annotated ` safe` unless it’s a definition. [...]
I could see changing it to this; but what if it's writen in D and someone wants to use a .di file?
TBH, I don’t know what .di files really are. AFAIK, they’re basically what a header file in C/C++ is. They contain declarations. The biggest difference to C/C++ is that .di files are compiler generated and a programmer might occasionally read them, but essentially never write or edit one. Correct me if I’m wrong.
.di files are currently all hand written. […] A quick look on ABI page, both `` safe`` and `` trusted`` get name mangled differently. https://dlang.org/spec/abi.html#FuncAttrTrusted So yes we'd have to allow `` safe`` on function declarations without bodies unfortunately.
Addendum to:
 If .di files are hand-written, we must absolutely not allow 
 ` safe` annotations on `extern(C/C++)` function declarations. 
 Those must all be ` trusted`, even if the validation by the 
 programmer only requires grepping the function name and 
 observing that the function definition (the implementation) is 
 annotated ` safe`, so the actual, difficult validation is done 
 by the compiler.
Missing bits: This affects all attributes, not just ` safe`. The compiler might be able to do the grepping. By that I mean not actual grepping, it must be fool-proof, i.e. use the D parser and make sure the symbols are really the same. Other than that, I see no way to allow non-D prototypes be annotated ` safe`.
Jun 10
prev sibling parent Mathias Lang <geod24 gmail.com> writes:
On Thursday, 30 May 2024 at 18:35:36 UTC, Atila Neves wrote:
 https://github.com/atilaneves/DIPs/blob/safe-by-default/safe-by-default.md

 Destroy!
I think it should eventually happen, but we are not ready for it. The real problem is that it is currently hard to compose with attributes. The problem is most evident with ` safe`, but happens with `nothrow`, `pure`, etc... Until we solve this problem, ` safe`-by-default would just be trading one pain for another, and breaking the ecosystem in the process. We saw what happened with DIP1000... Provided the issue with composition of attribute is fixed, I would make a suggestion to the DIP: Remove ` safe` entirely. Others have hinted at it, and it seems the solution is simple: Un-annotated definitions are ` safe`, definitions can be annotated to be ` system` or ` trusted`, and `extern` definitions MUST be annotated. It is also changing the perception - ` safe` is not an add-on anymore.
Jun 06