Friends don't let friends use inout with scope and -dip1000

Atila Neves atila.neves at gmail.com
Fri Aug 17 07:36:27 UTC 2018


Here's a struct:

-----------------
struct MyStruct {
     import core.stdc.stdlib;
     int* ints;
     this(int size) @trusted { ints = cast(int*) malloc(size); }
     ~this() @trusted { free(ints); }
     scope int* ptr() { return ints; }
}
-----------------

Let's try and be evil with -dip1000:

-----------------
@safe:

// struct MyStruct ...

const(int) *gInt;

void main() {
     auto s = MyStruct(10);
     gInt = s.ptr;
}
-----------------

% dmd -dip1000 scope_inout.d
scope_inout.d(26): Error: scope variable this may not be returned


Yay!

What if instead of `auto` I write `const` instead (or immutable)? 
This is D we're talking about, so none of this boilerplate 
nonsense of writing two (or three) basically identical functions. 
So:

-----------------
// used to be scope int* ptr() { return ints; }
scope inout(int)* ptr() inout { return ints; }
-----------------

% dmd -dip1000 scope_inout.d
% echo $?
0
# nope, no error here

Wait, what? Turns out now it compiles. After some 
under-the-breath mumbling I go hit issues.dlang.org and realise 
that the issue already exists:


https://issues.dlang.org/show_bug.cgi?id=17935


For reasons unfathomable to me, this is considered the _correct_ 
behaviour. Weirder still, writing out the boilerplate that 
`inout` is supposed to save us (mutable, const and immutable 
versions) doesn't compile, which is what one would expect.

So: @safe + inout + scope + dip1000 + custom memory allocation in 
D gets us to the usability of C++ circa 1998. At least now we 
have valgrind and asan I guess.

"What about template this?", I hear you ask. It kinda works. 
Sorta. Kinda. Behold:

------------
scope auto ptr(this T)() { return ints; }
------------

After changing the definition of `ptr` this way the code compiles 
fine and `ints` is escaped. Huh. However, if you change `auto s` 
to `scope s`, it fails to compile as <insert deity> intended. 
Very weird.

If you change the destructor to `scope` then it also fails to 
compile even if it's `auto s`. Because, _obviously_, that's 
totally different.

I'd file an issue but given that the original one is considered 
not a bug for some reason, I have no idea about what I just wrote 
is right or not.

What I do know is I found multiple ways to do nasty things to 
memory under the guise of @safe and -dip1000, and my 
understanding was that the compiler would save me from myself. In 
the meanwhile I'm staying away from `inout` and putting `scope` 
on my destructors even if I don't quite understand when 
destructors should be `scope`. Probably always? I have no idea.




More information about the Digitalmars-d mailing list