Bug 19966 – [DIP1000] DIP1000 with a template behaves differently
Status
RESOLVED
Resolution
INVALID
Severity
critical
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2019-06-14T12:17:35Z
Last change time
2020-03-13T10:21:07Z
Keywords
safe
Assigned to
No Owner
Creator
Jacob Carlborg
Comments
Comment #0 by doob — 2019-06-14T12:17:35Z
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 by bugzilla — 2020-03-13T09:18:06Z
> 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 by doob — 2020-03-13T09:26:47Z
(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 by bugzilla — 2020-03-13T09:28:12Z
> 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 by bugzilla — 2020-03-13T09:29:47Z
> 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 by doob — 2020-03-13T09:39:00Z
(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 by doob — 2020-03-13T09:47:36Z
(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.