@trusted considered harmful

Jonathan M Davis jmdavisProg at gmx.com
Fri Jul 27 19:33:38 PDT 2012


On Saturday, July 28, 2012 04:05:14 Jesse Phillips wrote:
> On Saturday, 28 July 2012 at 00:08:30 UTC, David Nadlinger wrote:
> >  2) [...] The obvious solution is to add a "@trusted"
> > 
> > declaration/block, which would allow unsafe code in a certain
> > region. Putting @trusted in the function header would still be
> > allowed for backwards compatibility (but discouraged), and
> > would have the same effect as marking the function @safe and
> > wrapping its whole body in a @trusted block. It could e.g. look
> > something like this (the @ prefix definitely looks weird, but I
> > didn't want to introduce a new keyword):
> > 
> > ---
> > 
> >  void foo(T)(T t) {
> >  
> >    t.doSomething();
> >    @trusted {
> >    
> >      // Do something dirty.
> >    
> >    }
> >    t.doSomethingElse();
> >    @trusted phobosFunctionWhichHasNotBeenMarkedSafeYet();
> >  
> >  }
> > 
> > ---
> 
> I don't see flaw with 1.
> 
> However 2 doesn't sound right.
> 
>      @trusted {
>        // Do something dirty.
>      }
> 
> You aren't supposed to do dirty things in @trusted code. You're
> supposed to  safely wrap a system function to be usable by a safe
> function. The system function is supposed to be short and getting
> its hands dirty. Remember this is about memory safety and not
> lack of bugs safety.

The whole point of @trusted is to mark @system code as being @safe. The 
programmer is certifying that the code is @safe, because they know that what 
it's doing is actually @safe in spite of the fact that the compiler can't 
verify that. It's perfectly acceptable to put "dirty code" in an @trusted 
function. The difference between @trusted and @system is that with @trusted, 
the programmer is guaranteeing that it's actually @safe regardless of what 
arguments it's given, whereas @system is not only doing unsafe things, but 
whether they're ultimately @safe or not depends on arguments and/or other 
code, so the programmer _can't_ guarantee that it's @safe.

> The template issue needs fixed, but maybe it is the inference
> which needs expanded? Maybe a template is only inferred as safe
> or trusted and require explicitly system?
> 
> I think I was going to say more, but I'm not versed in the
> problems for this area, which I'm sure there are many, so this is
> probably good enough self butchering.

The main problem is when you have code that is @system in a templated function 
and while you can guarantee that that code is actually @safe and therefore 
mark it as @trusted, you can't guarantee that for the rest of the function.

With the current situation, you can't mark that function as @trusted, because 
that would be certifying that _everything_ in the function was @safe, which 
isn't necessarily true. Ideally, you would be able to mark the operations that 
are @system but you know are really @safe with @trusted and let whether the 
function as a whole is @safe or @system be inferred by the compiler. But right 
now, you either have to break up that @trusted code into a separate function 
(which isn't always reasonable) or play games with static if and having two 
versions of the same function where one is @safe and the other is @system. For 
instance, in the new std.range.RefRange, save is defined something like this:

private static void _testSave(R)(R* range)
{
    (*range).save;
}

static if(isSafe!(_testSave!R))
{
    @property auto save() @trusted
    {
        mixin(_genSave());
    }
}
else
{
    @property auto save()
    {
        mixin(_genSave());
    }
}

private static string _genSave() @safe pure nothrow
{
    return `import std.conv;` ~
           `alias typeof((*_range).save) S;` ~
           `static assert(isForwardRange!S, S.stringof ~ " is not a forward 
range.");` ~
           `auto mem = new void[S.sizeof];` ~
           `emplace!S(mem, cast(S)(*_range).save);` ~
           `return RefRange!S(cast(S*)mem.ptr);`;
}

The problem is that the emplace stuff is @system, and I know that it's really 
@safe and want to mark it with @trusted, but I _don't_ know that _range's save 
function is @safe, so I can't mark RefRange's save as @trusted. I'm stuck 
either doing nonsense like the code above or giving up on making it possible 
for save to be @safe. With David's suggestion, all of that can be reduced to 
this:

@property auto save()
{
    import std.conv;
    alias typeof((*_range).save) S;
    static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");

    @trusted
    {
        auto mem = new void[S.sizeof];`
        emplace!S(mem, cast(S)(*_range).save);`
        return RefRange!S(cast(S*)mem.ptr);
    }
}

That is _way_ cleaner.

Now, the actual code of in RefRange is actually even _more_ complicated 
because of the need to handle the possibility of making save const, but if 
enhancement request# 8407 were implemented, then that would be solved. I 
believe that there is at least _some_ buy in on Walter with regards to issue# 
8407 based on some comments by Andrei, but I have no idea what Walter will 
think of David's proposal. With both, functions like RefRange's save become 
reasonably small. Without them, you either give up on const and/or @safe, make 
your templated code require that the functions it uses be const and/or @safe 
(which can be way too restrictive), or you do what I did with RefRange, which 
works but is downright ugly.

- Jonathan M Davis


More information about the Digitalmars-d mailing list