ref is unsafe

Jonathan M Davis jmdavisProg at gmx.com
Sun Dec 30 14:01:22 PST 2012


On Sunday, December 30, 2012 17:32:40 Nick Treleaven wrote:
> I think the compiler needs to be able to mark foo as a function that
> returns its input reference. Then, any arguments to foo that are locals
> should cause an error at the call site (e.g. in baz). So legal calls to
> foo can always be @safe.
> 
> To extend the above code:
> 
> ref int quux(ref int i)
> {
>      return foo(i);
> }
> 
> Here the compiler already knows that foo returns its input reference. So
> it checks whether foo is being passed a local - no; but it also has to
> check if foo is passed any ref parameters of quux, which it is. The
> compiler now has to mark quux as a function that returns its input
> reference.
> 
> Works?

No. There's no guarantee that the compiler has access to the function's body, 
and the function being called could be compiled after the function which calls 
it. There's a reason that attribute inferrence only works with templated 
functions. In every other case, the programmer has to mark it. We're _not_ 
going to get any kind inferrence without templates. D's compilation model 
doesn't allow it.

The closest that we could get to what you suggest would be to add a new 
attribute similar to nothrow but which guarantees that the function does not 
return a ref to a parameter. So, you'd have to mark your functions that way 
(e.g. with @norefparamreturn). Maybe the compiler could infer it for templated 
ones, but this attribute would basically have to work like other inferred 
attributes and be marked manually in all other cases. Certainly, you can't 
have the compiler figuring it out for you in general, because D's compilation 
model allows the function being called to be compiled separately from (and 
potentially after) the function calling it.

And when you think about what this attribute would be needed for, it gets a 
bit bizarre to have it. The _only_ time that it's applicable is when a 
function takes an argument by ref and returns the same type by ref. In all 
other cases, the compiler can guarantee it just based on the type system.

I suppose that we could have an attribute that indicated that a function _did_ 
return a ref to one of its params and then have the compiler give an error if 
it were missing, which means that the foo function

ref int foo(ref int i)
{
    return i;
}

would end up with an error for not having the attribute, whereas a function 
like baz

ref int baz(int i)
{
    return foo(i);
}

would not end up with the error unless foo had the attribute on it. But that's 
very different from any attribute that we currently have. It would be like 
having a throw attribute instead of a nothrow attribute. I suppose that it is 
a possible solution though. I could also see an argument that the attribute 
should go on the parameter rather than the function, in which case you could 
have more fine-grained control over it, but it does complicate things further.

Honestly though, I'm inclined to argue that functions which return by ref and 
have a ref parameter of that same type just be considered @system. It's just 
way simpler. It's also more in line with how pointers to locals are handled, 
though because ref is far more restrictive, it should be possible to come up 
with a different solution (like the attribute), whereas the fact that you can 
squirrel away pointers to things makes it rather complicated (if not 
impossible) to have a solution other than simply make taking the address of a 
local variable @system. You can't squirrel away ref.

- Jonathan M Davis


More information about the Digitalmars-d mailing list