@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