Dlist and dip1000 challenge
Steven Schveighoffer
schveiguy at gmail.com
Tue Oct 23 15:10:24 UTC 2018
I've implemented a mergesort for both slist and dlist in phobos. I found
it easier to start with slist, since the algorithm and operation is
basically the same, dlist will simply have a post-sort operation that
reconnects all the prev pointers.
I went to apply this to dlist, and found a disturbing thing -- all
accesses to data in dlist are done via *casting*. The idea behind it is,
we only store non-templated nodes inside a list, and those nodes only
have prev and next pointers. Then you cast to the correct "PayNode"
type, giving you the actual data. Things are allocated properly, so it
works, but holy crap, what a mess. For those who are interested, here is
the original PR that introduced this:
https://github.com/dlang/phobos/pull/2457.
I decided, I'll make a PR to undo all that (the savings from doing this
can't be worth it, the only thing that's non-templated is the range
popFront and popBack), but I've now run into an issue with dip1000.
Apparently, some of the casting confuses the compiler sufficiently
enough that it can't track lifetimes, and it just rubber-stamps it (at
least, that's my theory).
I've removed the "BaseNode" type, and just use "PayNode" everywhere. All
of a sudden, things become un- at safe.
So I started slapping @safe tags on to see where the issue is. It starts
with something I've known is an issue for a long time, and that is
having a scoped array of strings (or other reference types). You get
this with a type-safe variadic function.
The compiler treats all the array items as if they were scope, meaning
you can't assign a string, either allocated from the heap or a literal,
to things that aren't scope, which doesn't make any sense. Sure the
array is scope, but not what the elements point at!
In any case, I created a mock-up of the parts that are not working, can
anyone find either an issue we can file for dip1000 or a way to solve
this properly? I found I can mark stuff trusted, but I *really* don't
like that. It's not much worse than casting everything, however.
Here is the mockup code:
https://run.dlang.io/is/6xDFnr
I've marked main @safe, and the problematic function @safe (if you don't
mark the problematic function @safe, it just complains that main can't
do anything).
Note that in reality everything here is perfectly safe, I'm not escaping
anything.
---------
Begin rant
So, here is one other thing I want to say. This took me HOURS to find,
and narrow down. Not because I don't understand the concepts behind
dip1000, but because the compiler has fully inserted so many hidden
scopes, I don't know what the actual code it's compiling is. One big
problem I think with dip1000 is simply that it's nearly impossible to
understand where the issues are. Like I said at the end of the post
above, the result of allowing compiler inference of dip1000 is that your
whole program is simply marked unsafe, and you have absolutely no idea
where it is. You can't even guess, because scope just shows up where you
never typed it. Given that you NEED this functionality on templates,
it's going to result, IMO, in people either not using dip1000, or giving
up and adding @trusted: to the top of their file. This is going to be
horrible if we can't find a way to either require scope instead of
inferring it in some cases, or create a way to diagnose where the
blasted problem actually is. Maybe something to say "I expected this
call to be @safe, why isn't it".
End rant.
-Steve
More information about the Digitalmars-d
mailing list