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

Steven Schveighoffer schveiguy at gmail.com
Fri Aug 17 13:39:29 UTC 2018


On 8/17/18 3:36 AM, Atila Neves wrote:
> 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; }

Does scope apply to the return value or the `this` reference?

What happens if you remove the return type? (i.e. scope auto)

> -----------------
> 
> % 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

I don't see what this bug report has to do with the given case.

> 
> 
> 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.

This seems like a straight up bug.

> 
> 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.
> 
> 

This doesn't surprise me. I'm beginning to question whether scope 
shouldn't have been a type constructor instead of a storage class. It's 
treated almost like a type constructor in most places, but the language 
grammar makes it difficult to be specific as to what part it applies.

-Steve


More information about the Digitalmars-d mailing list