Non-techincal brain, is @safe by default good or not?

Mathias LANG geod24 at gmail.com
Thu May 28 13:04:15 UTC 2020


On Thursday, 28 May 2020 at 11:07:16 UTC, Johannes Loher wrote:
>
> Again, please file a bug report. This is either a bug with 
> @safe or there is some @trusted in `move` (or one of the 
> functions it calls) that calls `opPostMove`.

Oh I know exactly how it happened. I just went over the code 
quickly, saw `@trusted`, and saw it calling a user-supplied hook, 
and crafting a test case was trivial.
This is because, at the top level, there is a function which 
checks if the destruction is `@safe` (well, it checks quite a few 
other things actually, but that's not the point here), and if it 
is, just blindly trust a lower function, which itself happens to 
be more correct in its usage of @trusted.

That's the technical explanation. The higher level explanation is 
that we had code that was written correctly, audited, then a 
language feature was added, and support for it was added by 
different contributors over time. And the *whole* code was not 
re-audited for `@trusted`.

> Both of the examples you provide are bugs but I realize that 
> there are real examples of what you are trying to show. One 
> situation where this happens is if libraries have non-template 
> functions that take a callback as parameter. The library author 
> has to make a decision whether to make his function @safe, 
> which requires all callbacks passed to it also to be @safe, or 
> to make it @system, which makes it unusable in @safe code, even 
> if it actually is safe because the callback that was passed is 
> @safe. @trusted allows both kinds of usages but is a really bad 
> idea because it allows unchecked @system code to be called from 
> @safe code.

Yes, that's a point I tried to make over and over, but it seems 
no one was interested.
And as mentioned, it also affects OOP code.
Slight correction though: Accepting a `@trusted` delegate will 
**not** allow you to pass a `@system` delegate. I tried.

> This is why some people in the community have suggested to 
> allow attribute inference also for non-template functions. That 
> would solve the issue might might be a better default than both 
> @system and @safe. However, I don't think anybody has thought 
> that through completely yet. Some people have voiced concerns 
> regarding inheritance and .di files (and probably more) but I 
> think it might be possible to do this.

That's a terrible idea:
- It will be a massive slowdown for anyone not compiling 
all-at-once: at the moment the frontend knows about "root 
module", which are module for which the compiler will do codegen. 
As such, it doesn't do any semantic analysis on functions that 
are not in root modules (except for the prototype). Inference for 
everything means you have to perform full semantic analysis for 
everything. Compiling by packages means performing full semantic 
for your whole program multiple times over. It also means 
whatever function in Phobos, every D program will also need to 
perform semantic analysis on them if it imports it. The compiler 
could (should) be smarter and have lazier semantic, but we need 
to implement it, and at the core, this suggestion does not scale.

- You would have to either disable that for OOP code, which would 
be a weird special case, or find a way to negate it, or ... ? I 
can't even picture a way to make this work. If you have a class, 
and your base implementation of `toString` just returns a 
literal, it has all attributes available, however derived code 
might want to use `format`.

- It doesn't solve the actual problem: You still will write your 
function prototype as `void requestHTTP(scope void delegate(scope 
ref HTTPRequest) /* @safe */ req)`.
You need to express a way to tie `requestHTTP`'s attributes to 
`req`'s attributes , not just infer `req`'s attributes. And the 
way you do this needs to be subtle, because you might want to 
express: "I am `@safe` is this is `@safe`" and "I am `nothrow` no 
matter what" as well as "I am not `@nogc` even if `req` is". At 
the moment we can express the second and the third variant but 
not the first.

IMO the real answer is to come up with something like `inout` for 
attributes. `inout` is a wildcard. It's exactly what you want for 
delegates. It doesn't solve the issue with OOP though, although 
it makes it easier to mitigate it (see `Throwable.toString` for 
how you could have a conditionally `@nogc` / `nothrow` / etc... 
method).


More information about the Digitalmars-d mailing list