Bug 19371 – Taking address of ref return in @safe code: compile-time checks fail
Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-11-07T00:09:56Z
Last change time
2018-11-28T17:58:23Z
Keywords
safe
Assigned to
No Owner
Creator
Stanislav Blinov
Comments
Comment #0 by stanislav.blinov — 2018-11-07T00:09:56Z
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 by nick — 2018-11-10T20:56:17Z
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 by stanislav.blinov — 2018-11-10T21:15:55Z
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 by nick — 2018-11-13T16:40:48Z
(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 #5 by stanislav.blinov — 2018-11-14T00:02:53Z
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 by nick — 2018-11-28T17:45:28Z
(In reply to Stanislav Blinov from comment #5)
Adding -dip1000, the pragma line does error, but the static assert passes.
Comment #7 by stanislav.blinov — 2018-11-28T17:58:23Z