On Saturday, 28 July 2012 at 20:22:27 UTC, Jonathan M Davis wrote:
@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];}

    static if(isSafelyCallable!((){(*_range).save;}))
    {
        @trusted { emplace!S(mem, cast(S)(*_range).save); }
    }
    else
    {
        emplace!S(mem, cast(S)(*_range).save);
    }

    @trusted {return RefRange!S(cast(S*)mem.ptr);}
}

It's still a bit ugly, but it's far better than what we can currently do. As it stands, you can't even put the emplace call in a separate function and mark it @trusted or @system depending on save, because the only way to mark the rest of the function as @trusted is to mark the whole thing as @trusted. You'd have to specifically put _everything else_ in its own function _and_ emplace in its own function (one @trusted, one @system) and have the outer function call both. It's a mess. Being able to mark statements as @trusted rather than just
entire functons would be _huge_.

For the specific case of calling unsafe functions from otherwise safe code, which occurs quite frequently due to parts of Phobos not being properly annotated yet, I've been experimenting with an alternative solution in my code. It allows you to »apply trusted« at a function call level, and is easily implemented in the library with today's D – using it RefRange.save would look like this using it:

---
@property auto save()
{
    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];
    TRUSTED!(emplace!S)(mem, cast(S)(*_range).save);
    @trusted return RefRange!S(cast(S*)mem.ptr);
}
---

TRUSTED is just a @trusted template function which accepts a callable via an alias parameter and invokes it with the given parameters, adding @trusted to the function type via a cast (an application of std.traits.SetFunctionAttributes, by the way).

It seems to work fine, but there is an obvious catch: Just like @trusted, TRUSTED circumvents safety, and all of its uses must be carefully examined. But in contrast to the former, it is not part of the language, so anyone working on the code base, especially reviewers, must be aware of implications. The all-caps name is meant to help drawing attention to .

Maybe it would be a good idea to also allow `@trusted(emplace!S)(mem, cast(S)(*_range).save)`, with semantics similar to TRUSTED? Or even applying @trusted to arbitrary expressions, similar to `checked` in C#?

David

Reply via email to