DMD Symbol Reference Analysis Pass

via Digitalmars-d digitalmars-d at puremagic.com
Wed May 27 02:54:31 PDT 2015


On Wednesday, 27 May 2015 at 08:38:48 UTC, Per Nordlöw wrote:
> On Wednesday, 27 May 2015 at 08:30:33 UTC, Marc Schütz wrote:
>> See above. Conceptually, you can of course treat it as if it 
>> were marked with `scope`, but an actual annotation should not 
>> be necessary.
>
> But now you're talking about an upcoming feature in DMD, right?
>

Well, obviously, nothing of what we're talking about works with 
current DMD. Even scope doesn't do anything (except for 
delegates).

> AFAIK, in current DMD, I can't get any help in avoiding 
> patterns such as
>
>     char[] saved_line;
>     foreach (line; File("foo.txt").byLine)
>     {
>         saved_line = line; // should give error
>     }
>
> Right?

Yes.

>
> Are you saying that adding DMD support for qualifying `line` as 
> `scope` is not the right way to solve this problem?

Yes. First of all, `File.byLine.front` is the function that needs 
to get annotated, like this:

     char[] front() return {
         // ...
         return buffer;
     }

The `return` keyword here means the same thing as in DIP25, 
namely that the returned value is owned by `this`. In your 
example, the owner is the temporary returned by 
`File("foo.txt").byLine`, which means that the returned buffer 
must no longer be used when that temporary gets destroyed, i.e. 
the read strings must not escape the foreach (without being 
copied), which allows the buffer to be safely released then. This 
is the original problem that `scope` was meant to address.

Conceptually, you're right that now `line` needs to be annotated 
with `scope`, because otherwise you wouldn't be allowed to store 
the scoped slices there. But there really isn't any point in 
actually adding that annotation, because it can always be 
inferred by the compiler (it can add the annotation for you when 
it sees that you assign a scoped value to it).

This alone is then already enough to prevent the "volatility" 
problem in your example, because it is longer possible for the 
individual lines to outlive the byLine() temporary that gets 
iterated over.

However, it is not enough in the general case:

     auto lines = stdin.ByLine;
     auto line1 = lines.front;
     lines.popFront();
     // line1 now changes
     auto line2 = lines.front;

In this case, the rule that `line1` must not outlive `lines` is 
fulfilled, but still it gets invalidated. With byLine(), this 
just leads to unexpected behaviour, but with e.g. reference 
counting, it can cause memory corruption (use after free). 
Therefore, any complete scope proposal needs to address this 
problem, too.

What I propose is the following: The compiler keeps track of 
outstanding "loans" to owned objects. As long as any such "loan" 
exists (in the above example, `line1` and `line2`), the owner 
(i.e. `lines`) will either become read-only (const), or 
alternatively, it will stay mutable, but mutating it will become 
@system. This effectively addresses both the safety problems as 
well as volatile ranges, because they are actually the same 
problem.

I hope it is now clear what I want to say. It is unfortunately a 
complicated topic...


More information about the Digitalmars-d mailing list