On Saturday, 19 November 2022 at 15:00:16 UTC, Paul Backus wrote:
> On Saturday, 19 November 2022 at 14:07:59 UTC, Nick Treleaven wrote:
> Hi,
The following seems like a bug to me (reduced code, FILE* changed to int*):
@safe:
struct LockedFile
{
private int* fps;
auto fp() return scope => fps;
}
void main()
{
int* p;
{
auto lf = LockedFile(new int);
p = lf.fp;
}
assert(p != null); // address escaped
}
There's no error with -dip1000.
I'll file this unless I overlooked something.
I think this is intended behavior, because you do get an error if you replace new int
with a pointer to a stack variable; e.g.,
int local;
auto lf = LockedFile(&local);
The return scope
qualifier on the method does not mean "the return value of this method is scope
". It means "this method may return one of this object's pointers, but does not allow them to escape anywhere else." In other words, it lets the compiler determine that the return value of lf.fp
has the same lifetime as lf
itself.
Since, in your example, lf
has global lifetime, the compiler deduces that lf.fp
also has global lifetime, and therefore there is nothing wrong with assigning it to p
.
I follow your rationale, but for the life of me I cannot see how lf
"has global lifetime".
Looks to me like lf
is a value instance of the LockedFile
struct (so on the stack) in a local scope inside main. I fully agree that the above code is not problematic, but isn't that because p
is declared outside this local scope, and the allocation that happens inside the local scope (in the lf
constructor) is on the heap, so the allocation (now assigned to p
) survives the end of the local scope (and the end of the life of lf
) since it is p
that has global lifetime?
I don't grok how lf
can survive the local scope. Or am I missing something?