What exactly does "@safe" mean?

Jonathan M Davis jmdavisProg at gmx.com
Sat Jun 1 16:32:32 PDT 2013


On Sunday, June 02, 2013 01:12:53 Piotr Szturmaj wrote:
> W dniu 01.06.2013 23:55, Jonathan M Davis pisze:
> > The guarantees of @safe hold only so long as there are no holes in it, but
> > any and all holes we find get fixed. Making ref be truly @safe has been a
> > large part of the recent ref discussions, as you can currently get away
> > with doing something like
> > 
> > ref int id(ref int i) { return i; }
> > 
> > ref int foo()
> > {
> > 
> >      int j;
> >      return id(j);
> > 
> > }
> 
> I know that introducing another type qualifier may complicate things
> but this would be a compile time solution.
> 
> I mean _scope_ type qualifier, so your example could be rewritten as:
> 
> ref int id(ref int i) { return i; }
> 
> ref int foo()
> {
>      int j;
>      return id(j); // error: could not pass scope(int) as ref int parameter
> }
> 
> Taking an address of local variable would always yield a scope
> qualified type, scope(int) in this example.
> 
> Obviously, scope qualified type could be passed to functions taking
> scope parameters:
> 
> void bar(scope ref int i) { i = 10; }
> 
> void foo()
> {
>      int j = 0;
>      bar(j); // ok
>      assert(i == 10);
> }
> 
> I think this could fill the @safety holes.

Except that that makes it so that you can't return a ref argument, which is 
completely unacceptable. You need to be able to pass local variables to 
functions which accept ref in @safe code, and you need functions which ref 
arguments to be able to return those arguments by ref. The only thing that we 
want to prevent is a local variable from escaping its original scope. It's 
perfectly valid that the id function accept a local variable by ref and 
returns it by ref. What's invalid is that the function that the local variable 
was declared in then returns it by ref.

Manu suggested something similar to what you're suggesting with the addition 
of having making it so that you can then return variables as scope ref, in 
which case, the caller would see that the function was accepting by scope ref 
and returning by scope ref and that none of the variables that it accepted 
were scope ref in the caller. But this requires having having yet another 
annotation - scope ref - which Andrei was completely against (and Walter too 
IIRC), and it actually would end up making something like this illegal as 
well, went it shouldn't:

scope ref int foo(scope ref int i, scope ref int j)
{
    return j;
}

scope ref bar(scope ref int q)
{
    int i;
    return foo(i, q);
}

The compiler can't know whether it's i or q that's being returned from foo, so 
it would have to given a compilation error, which is more restrictive than the 
runtime solution that has been proposed. So, you can do less, and you have to 
have mark up your functions with even more attributes, and it's yet another 
attribute for those learning the language to have to learn.

Contrast this with simply inserting a very cheap runtime check in the rare 
cases where the compiler detects that a local variable might escape. No 
additional attributes are needed. So, the code is simpler, and there's less 
for people to learn. There's almost no performance hit (and if you want it to 
be zero, then use -noboundscheck). And we lose _zero_ functionality. None of 
that is the case with the scope ref proposal.

Walter and Andrei do not like the idea of introducing even more attributes to 
solve this problem and were very excited to have this solution proposed 
(unfortunately, I'm not sure who proposed it though, since I missed that part 
of the conversation). And I'm inclined to agree with them. It's very simple 
and cheap. The only real downside is that it's caught at runtime rather than 
compile time, but it's quickly and easily caught at runtime, and the 
simplicity of it makes it seem like a great solution.

- Jonathan M Davis


More information about the Digitalmars-d mailing list