dip1000 + pure is a DEADLY COMBO
12345swordy
alexanderheistermann at gmail.com
Wed May 12 14:58:23 UTC 2021
On Wednesday, 12 May 2021 at 13:14:30 UTC, Dennis wrote:
> Sorry for the attention-grabbing title, but I think it's
> warranted, because the gist of it is this:
>
> **With `-preview=dip1000` enabled, the compiler will happily
> compile valid, `@safe` D code into memory corrupting machine
> code.**
>
> The root cause is:
> [Issue 20150 - -dip1000 defeated by
> pure](https://issues.dlang.org/show_bug.cgi?id=20150)
>
> The compiler ignores "reference to local variable `x` assigned
> to non-scope parameter `y`" errors when the function is
> annotated or inferred `pure`. The idea is, presumably, that
> `pure` functions can't escape references because they have no
> interaction with global variables. This is false of course,
> since they can still return them or assign them to other
> parameters.
>
> The deadly part it that using this flawed logic, the compiler
> sometimes turns GC allocations into stack allocations too
> eagerly. Here I got memory corruption because the compiler
> allocated an array literal on the stack instead of the heap:
>
> [Issue 21291 - Array literal that escapes scope is allocated on
> stack](https://issues.dlang.org/show_bug.cgi?id=21291)
>
> Later I encountered another instance of it where a closure was
> not heap-allocated, which looked something like this:
>
> ```D
> import core.thread;
> @safe:
> void main() {
> S s;
> s.memberFunc();
> }
>
> struct S {
> int a;
> auto memberFunc() {
> auto t = new Thread({
> auto pa = &a; // pointer to stack frame of main!
> });
> }
> }
> ```
>
> I'm not the only one who encountered memory corruption bugs
> this way, user Dechcaudron commented on my issue: "This has
> also happened to me, no idea it could be due to -dip1000".
>
> And most recently:
> [Issue 21912 - Invalid stack closure when calling delegate
> inside lambda](https://issues.dlang.org/show_bug.cgi?id=21912)
>
> ### Why is this not fixed?
>
> Walter made a PR for fixing the behavior in dmd: (March 2020)
> https://github.com/dlang/dmd/pull/10924
>
> Later, aG0aep6G made a better fix: (November 2020)
> https://github.com/dlang/dmd/pull/12010
>
> But they're both blocked by the fact that Phobos relies on the
> bug to compile with -dip1000. This makes sense, because the
> conversion process was mostly "add `scope` and `return`
> annotations until the compile errors go away". `pure` functions
> did not give error messages, so they did not get those
> annotations.
>
> Regarding this extra work, [aG0aep6G
> commented:](https://github.com/dlang/dmd/pull/12010#issuecomment-759070687) (January 2021)
>
>> I had started on it, but it's tedious work tracking down the
>> errors through templates and overloads. If I remember
>> correctly, dup gave me some trouble, too.
>>
>> So I've put it on ice for the time being. If someone else
>> wants to give it a shot, that would be great.
>
> And that's where we are now.
>
> ### Future of dip1000
>
> Matthias asked "Is there a plan to enable DIP1000 by default?"
> during [DConf Online 2020 Day One Q \& A Livestream, at
> 4:50:11](https://youtu.be/o-2_mxaCL9w?t=17411).
> Walter mentioned "we can do it now" and Atila mentioned how the
> first step would be to change -dip1000 errors into equivalent
> deprecation warnings.
>
> Clearly, issue 20150 is a blocker for dip1000 by default.
>
> In the meantime, since I absolutely don't want another
> unfortunate soul debugging memory corruption bugs that dip1000
> introduces, this post is meant to raise awareness, and discuss
> intermediate solutions.
>
> Maybe the compiler can defensively heap-allocate for now,
> though that would break `@nogc` code. Or maybe we can add
> another switch, `-preview=dip1000proper`, since the fix is a
> breaking change. What do you think?
Should it be a bug with Pure rather than dip1000?
-Alex
More information about the Digitalmars-d
mailing list