D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 19371 - Taking address of ref return in @safe code: compile-time checks fail
Summary: Taking address of ref return in @safe code: compile-time checks fail
Status: RESOLVED INVALID
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P1 normal
Assignee: No Owner
URL:
Keywords: safe
Depends on:
Blocks:
 
Reported: 2018-11-07 00:09 UTC by Stanislav Blinov
Modified: 2018-11-28 17:58 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Stanislav Blinov 2018-11-07 00:09:56 UTC
Static asserts in the snippet below shouldn't trigger:

```
void main() @safe {
    int x;
    ref int get() { return x; }

    static assert(!is(typeof(&get())));
    static assert(!is(typeof(() @safe { return &get(); })));
    static assert(!__traits(compiles, { auto p = &get(); }));

    //auto p = &get(); // cannot take address of ref return in @safe
}
```
Comment 1 Nick Treleaven 2018-11-10 20:56:17 UTC
Using __traits(compiles) works for the first two asserts:

    static assert(!__traits(compiles, &get()));
    static assert(!__traits(compiles, () @safe { return &get(); }));

is(typeof()) is not as strict, it is looking for a type rather than compiling.

>     static assert(!__traits(compiles, { auto p = &get(); }));

Here, the function literal is inferred as @system, and can even be assigned to a variable in @safe code (but not called):

    auto f = { auto p = &get(); }; //ok
    f(); // Error: `@safe` function cannot call `@system` function pointer `f`

You can fix it by forcing @safe for the literal, or calling the literal:

    static assert(!__traits(compiles, () @safe { auto p = &get(); }));
    static assert(!__traits(compiles, { auto p = &get(); }()));
Comment 2 Stanislav Blinov 2018-11-10 21:15:55 UTC
Thanks for the pointers about __traits(compiles), I somehow lost the distinction of braces. However, the point about is(typeof()) still stands though:

    pragma(msg, typeof(() @safe { return &get(); }));

will print _error_.

In other words:

```
void main() @safe {
    int x;
    ref int get() { return x; }
    // since this doesn't compile:
    int* getExplicit() @safe { return &get(); }
    // ...then it has no type, therefore this should pass:
    static assert(!is(typeof(() @safe { return &get(); })));
}
```
Comment 3 Nick Treleaven 2018-11-13 16:40:48 UTC
(In reply to Stanislav Blinov from comment #2)
>     pragma(msg, typeof(() @safe { return &get(); }));
> 
> will print _error_.

What compiler are you using? With run.dlang.io the above (currently) prints:

int* delegate() pure nothrow @nogc @safe

It's the same even if I remove the @safe attribute, safety inference (or safe consistency checking) is not happening inside typeof alone. 

BTW if I do this:

    auto f = { return &get(); };
    pragma(msg, typeof(f));

I get `int* delegate() pure nothrow @nogc @system`. The compiler does safety inference for `f` before typeof(f) is analysed.

The same happens if I replace the literal with `{ return cast(int*)7; }`, it's reported as @safe inside pragma(msg, typeof(...)), but @system when assigning it to a variable `f` and doing pragma(msg, typeof(f)).

>     // since this doesn't compile:
>     int* getExplicit() @safe { return &get(); }
>     // ...then it has no type, therefore this should pass:
>     static assert(!is(typeof(() @safe { return &get(); })));

It depends if `typeof` is supposed to do @safe checking, or if it is just a tool to extract a type from an expression (that appears to have a valid type), even if that expression might not actually compile with full compiler checks. I think it's the latter, which is why __traits(compiles) was invented.
Comment 4 Nick Treleaven 2018-11-13 16:47:20 UTC
See:
https://forum.dlang.org/post/k6o04g$259s$1@digitalmars.com
Comment 5 Stanislav Blinov 2018-11-14 00:02:53 UTC
Add -dip1000 to the command line. With it, I don't understand how these two lines are not contradicting:

    pragma(msg, typeof(() @safe { return &get(); }));         // _error_
    static assert(!is(typeof(() @safe { return &get(); })));  // fails
Comment 6 Nick Treleaven 2018-11-28 17:45:28 UTC
(In reply to Stanislav Blinov from comment #5)

Adding -dip1000, the pragma line does error, but the static assert passes.
Comment 7 Stanislav Blinov 2018-11-28 17:58:23 UTC
Hmm, I must've done something wrong initially.