DIP 1028---Make @safe the Default---Community Review Round 1

Steven Schveighoffer schveiguy at gmail.com
Thu Jan 9 20:33:00 UTC 2020


On 1/9/20 3:08 PM, ag0aep6g wrote:
> On Thursday, 9 January 2020 at 19:35:36 UTC, Steven Schveighoffer wrote:
>> If I could mark the whole thing trusted, and turn on the mechanical 
>> checking for everything except line X, then I'd do that instead.
> 
> Well, you can do that:
> 
>      void foo() @trusted
>      {
>          () @safe { /* ... stuff ... */ } ();
>          bar(1, 2, 3); /* line X */
>          () @safe { /* ... stuff ... */ } ();
>      }
> 
> The problem is, of course, that you can't have `foo`'s safety inferred 
> based on "stuff". Which is what we usually want.

This turns on its head the lines you have to pay most attention to though :)

Basically, your job of manually checking such a function is to 1. 
establish which parts are not mechanically checked, and 2. verify they 
are ALWAYS correct (unlikely) or that they are correct based on the 
mechanically checked parts of the function.

In order to do that efficiently, I want to tag where the trouble spots 
may be. And these are likely to be MUCH fewer than the checked ones.

Put it another way, a safe function with no trusted escapes needs no 
checking. A safe function with one trusted escape needs each safe line 
checked in reference to the trusted escape. With 2 escapes, you need to 
check each line against 2 blocks, etc.

e.g. (to build on the previous example):

@safe void someFunction()
{
     int[4] data;
     foo("argument");
     bar();
     @trusted
     {
         data.ptr[3] = 42;
     }
}

Looking at line 2 and 3, I can verify that no parts touch anything that 
might be affected by the trusted block. I don't need to check foo to see 
if it's safe when called with "argument", nor do I need to investigate 
bar(), I just need to know, it doesn't use anything that is currently 
affected by the trusted block. It makes my job easier as a manual 
checker. I only need to investigate the declaration of `data`, and the 
trusted block.

> 
> I have actually used that pattern once in Phobos:
> 
> https://github.com/dlang/phobos/blob/master/std/range/package.d#L1550-L1581
> 
> I had to duplicate the code and use `__traits(compiles, ...)` to get 
> inference. So the result isn't exactly pretty. But the use of @trusted 
> is watertight, as far as I can tell.

Hm... this is an odd thing for sure. I probably would have done it this 
way though:

ref getR1() @trusted { return r1; }
ref getR2() @trusted { return r2; }

return r1Chosen ? ChooseResult(r1Chosen, getR1().save, getR2()) : 
ChooseResult(r1Chosen, getR1(), getR2().save);

But actually, is that right? Even if it's safe, it's a bad idea to copy 
the non-tagged item, as its contents are the contents for the other 
item. Shouldn't it be:

return r1Chosen ? ChooseResult(r1Chosen, getR1().save, R2.init) : 
ChooseResult(r1Chosen, R1.init, getR2().save);

-Steve


More information about the Digitalmars-d mailing list