Can we actually return a problematic pointer this way? As it is, the test case only returns `null`s.
It seems that without attribute inference, DMD assumes `get` might return a problematic pointer. But with attribute inference, DMD looks at the actual expression that is being returned.
Comment #2 by stanislav.blinov — 2020-06-03T18:40:48Z
Of course we can. What ptr points to and how its value came to be is irrelevant for this test case, thus omitted.
Comment #3 by ag0aep6g — 2020-06-03T21:35:22Z
(In reply to Stanislav Blinov from comment #2)
> Of course we can. What ptr points to and how its value came to be is
> irrelevant for this test case, thus omitted.
Please show an example. When I tried returning a problematic pointer, DMD showed the same error as in the `Correct` case.
Comment #4 by stanislav.blinov — 2020-06-07T11:42:26Z
(In reply to ag0aep6g from comment #3)
> Please show an example. When I tried returning a problematic pointer, DMD
> showed the same error as in the `Correct` case.
? malloc it in a constructor, free it in a destructor, disable copy and assignment
Comment #5 by ag0aep6g — 2020-06-07T19:27:27Z
(In reply to Stanislav Blinov from comment #4)
> ? malloc it in a constructor, free it in a destructor, disable copy and
> assignment
Makes sense. When I think about DIP 1000, I tend to think of pointers to the stack. But yeah, it's also supposed to enable malloc/free, isn't it.
For future readers, a full example:
----
import core.stdc.stdlib: free, malloc;
struct Faulty
{
private int* ptr;
this(int v) @trusted
{
ptr = cast(int*) malloc(int.sizeof);
*ptr = v;
}
~this() @trusted { if (ptr !is null) free(ptr); }
@disable this(this);
@disable void opAssign(Faulty);
int* get1() @safe return { return ptr; }
int* get2()() @safe return { return ptr; }
}
void main() @safe
{
int* outlive;
{
auto f1 = Faulty(42);
outlive = f1.get1(); /* error */
outlive = f1.get2(); /* should be error */
}
/* `outlive` is invalid from here on */
}
----
Comment #6 by ag0aep6g — 2020-06-08T11:58:40Z
The method is being inferred as `return scope`. Oddly enough, that doesn't show up when printing with `pragma(msg, ...)`.
With this information, we can construct a test case that shows memory corruption even without malloc/free and @trusted:
----
struct Faulty
{
private int x;
private int* unused;
int* get1() @safe return scope { return &x; }
}
int* f(int x) @safe
{
auto f = Faulty(x);
return f.get1(); /* Returns a pointer to the stack. */
}
void main() @safe
{
int* p1 = f(42);
int* p2 = f(13);
import std.stdio;
writeln(*p1); /* Prints "13". Whoopsie. */
}
----
Comment #7 by dkorpel — 2021-11-24T15:00:06Z
*** Issue 22528 has been marked as a duplicate of this issue. ***
Comment #8 by dkorpel — 2021-11-24T15:18:51Z
There's multiple things going on, so I'm going to limit this issue to "scope inference turns return-ref into return-scope", the issue that the compiler confuses `return scope` and `return ref` internally should be covered by issue 22108 and issue 22541.
Comment #9 by dlang-bot — 2022-03-09T16:36:01Z
@dkorpel updated dlang/dmd pull request #13693 "Make consistent use of `STC.returnScope`" fixing this issue:
- Fix issue 20881, 22790 - make ref-return-scope consistent
https://github.com/dlang/dmd/pull/13693
Comment #10 by dlang-bot — 2022-03-21T17:27:29Z
dlang/dmd pull request #13693 "Fix issue 20881, 22790 - make ref-return-scope consistent" was merged into master:
- fce2d6529550931dc7ed35f904bbc7a6ac0cd8f0 by dkorpel:
Fix issue 20881, 22790 - make ref-return-scope consistent
https://github.com/dlang/dmd/pull/13693