D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 19966 - [DIP1000] DIP1000 with a template behaves differently
Summary: [DIP1000] DIP1000 with a template behaves differently
Status: RESOLVED INVALID
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P1 critical
Assignee: No Owner
URL:
Keywords: safe
Depends on:
Blocks:
 
Reported: 2019-06-14 12:17 UTC by Jacob Carlborg
Modified: 2020-03-13 10:21 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 Jacob Carlborg 2019-06-14 12:17:35 UTC
The following code compile successfully with DIP1000 enabled:

struct Foo()
{
    int* bar;

    int* foo() @safe return
    {
        return bar;
    }
}

int* a;

void main() @safe
{
    Foo!() f;
    a = f.foo;
}

But if `Foo` is not a template it fails to compile, complaining that the return value of `foo` outlives `f`.

I think there are two errors here:

1. `Foo` as a template and as a non-template struct behaves differently
2. I think the above code is actually valid because it's not provable that `foo` escapes a pointer to the internal state of `Foo`
Comment 1 Walter Bright 2020-03-13 09:18:06 UTC
> 2. I think the above code is actually valid because it's not provable that `foo` escapes a pointer to the internal state of `Foo`

It's not valid because foo() is defined as returning a pointer that is equivalent to foo()'s `this` pointer. Then, `a = f.foo;` is treated as assigning the address of `f` to `a`, a global, which is an error. The compiler is behaving correctly for this point.
Comment 2 Jacob Carlborg 2020-03-13 09:26:47 UTC
(In reply to Walter Bright from comment #1)
> > 2. I think the above code is actually valid because it's not provable that `foo` escapes a pointer to the internal state of `Foo`
> 
> It's not valid because foo() is defined as returning a pointer that is
> equivalent to foo()'s `this` pointer. Then, `a = f.foo;` is treated as
> assigning the address of `f` to `a`, a global, which is an error. The
> compiler is behaving correctly for this point.

So it doesn't matter if "foo" is returning something else, like a global variable?
Comment 3 Walter Bright 2020-03-13 09:28:12 UTC
> 1. `Foo` as a template and as a non-template struct behaves differently

What's happening is that when `Foo` is a template, then inference happens with `foo()`, which infers that `this.bar` is being returned, not `this`. You can verify this by removing the function body for `foo()` so inference cannot happen, and then the error appears, because without inference the `return` applies to `this`, not `this.bar`.

Not a bug.
Comment 4 Walter Bright 2020-03-13 09:29:47 UTC
> So it doesn't matter if "foo" is returning something else, like a global variable?

If inference is not happening, then the compiler believes you when you say it is returning the `this` pointer. This is a feature, not a bug, as it enables you to attach the checking to the `this` pointer.
Comment 5 Jacob Carlborg 2020-03-13 09:39:00 UTC
(In reply to Walter Bright from comment #3)
> > 1. `Foo` as a template and as a non-template struct behaves differently
> 
> What's happening is that when `Foo` is a template, then inference happens
> with `foo()`, which infers that `this.bar` is being returned, not `this`.
> You can verify this by removing the function body for `foo()` so inference
> cannot happen, and then the error appears, because without inference the
> `return` applies to `this`, not `this.bar`.
> 
> Not a bug.

Is it possible to somehow get the behavior I want? To make sure that some internal state of "f" cannot outlive "f" itself.
Comment 6 Jacob Carlborg 2020-03-13 09:47:36 UTC
(In reply to Walter Bright from comment #3)
> > 1. `Foo` as a template and as a non-template struct behaves differently
> 
> What's happening is that when `Foo` is a template, then inference happens
> with `foo()`, which infers that `this.bar` is being returned, not `this`.
> You can verify this by removing the function body for `foo()` so inference
> cannot happen, and then the error appears, because without inference the
> `return` applies to `this`, not `this.bar`.
> 
> Not a bug.

But is it possible to get what I want somehow? To make sure no internal state of "f" outlives "f" itself.
Comment 7 Walter Bright 2020-03-13 10:21:07 UTC
That's what @live is for.