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