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*):
```d
@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?