If a type's alias this is used as the parameter to a function, and that instance is a temporary, it should not be destroyed before the call occurs.
However, if that type is part of ternary operator, the dtor is called FIRST. Example:
import std.stdio;
struct S
{
void *get() {return null;}
~this() { writeln("S.dtor");}
alias get this;
}
S makes() { return S();}
void foo(void *x) { writeln("foo"); }
void main(string[] args)
{
foo(args.length ? makes() : null);
}
The output:
S.dtor
foo
If, for example, S's dtor destroys the memory that is returned by get(), then this will result in dangling pointer to destroyed memory being passed to foo.
Real life example: https://github.com/D-Programming-Language/phobos/pull/3404#issuecomment-111704941
I'm uncertain what is required to make this happen. If I remove the ternary expression, then the destructor is properly called after foo. But I don't know if alias this is required.
Comment #1 by ketmar — 2015-06-14T04:40:46Z
the same bug on HEAD without an alias:
//alias get this;
...
foo(args.length ? makes().get : null);
Comment #2 by schveiguy — 2015-06-15T12:40:09Z
(In reply to Ketmar Dark from comment #1)
> the same bug on HEAD without an alias:
>
> //alias get this;
> ...
> foo(args.length ? makes().get : null);
And the obvious smacks me in the face :) Thanks.
Comment #3 by bugzilla — 2015-06-16T20:19:58Z
A simpler demonstration:
import core.stdc.stdio;
struct S {
this(int i) {
c = 's';
p = &c;
}
~this() {
printf("S.dtor\n");
c = 'd';
}
char *p;
char c = 's';
}
int main() {
char t = 't';
char *q = &t;
int x = 1;
char *p = x ? S(1).p : q;
printf("*p = %c\n", *p);
assert(*p == 's');
return 0;
}
Comment #4 by schveiguy — 2015-06-16T21:08:15Z
(In reply to Walter Bright from comment #3)
> struct S {
> this(int i) {
> c = 's';
> p = &c;
This is not allowed, and I'm not sure it's a valid test case. You could fix by doing this:
struct S
{
this(ref char x){
p = &x;
*p = 's';
}
~this() {
if(p) *p = 'd';
p = null;
}
}
void main()
{
char c = 'c';
char o = 'o';
int i = 1;
writeln(*(i ? S(c).p : &o));
}
prints: d
(In reply to Walter Bright from comment #3)
> this(int i) {
> c = 's';
> p = &c;
> }
compiler should play a crybaby here, as storing pointer to struct field is not valid for non-heap-allocated structs. considering that most structs are non-heap, i prefer to see at least a warning. which, for example, can be silenced with explicit cast: `p = cast(char*)&c;`. or even with some library function like `p = (&c).assumeCorrect;`
Comment #9 by ketmar — 2015-06-17T12:49:49Z
(In reply to Yuxuan Shui from comment #5)
> I think this one might be related: #14653
it is related in the sense that it doesn't do struct dtor in the right place, but the exact place where it is failed is different. ;-)
Comment #10 by github-bugzilla — 2015-09-02T08:56:37Z
Comment #12 by sahmi.soulaimane — 2019-11-28T14:15:45Z
The following case is still affected:
```
extern(C) void puts(const char*);
struct S
{
int i;
~this() { puts("D"); }
}
S makeS(int i) { return S(i); }
void main()
{
int var = true ? 1 : makeS(makeS(1).i - 1).i;
}
```
The program prints "D" at runtime which means the destructor is called when it shouldn't.
Comment #13 by dlang-bot — 2019-11-28T15:11:59Z
@SSoulaimane created dlang/dmd pull request #10628 "Fix issue 14696 - Fix a remaining case where the destructor is still called on a cold branch" fixing this issue:
- Fix issue 14696 - Fix a remaining case where the destructor is still called on a cold branch
Temporary variables should not be destroyed when the branch that initializes them is not taken.
https://github.com/dlang/dmd/pull/10628
Comment #14 by dlang-bot — 2019-11-29T00:33:50Z
dlang/dmd pull request #10628 "Fix issue 14696 - Fix a remaining case where the destructor is still called on a cold branch" was merged into master:
- 2ec3f1090a11c5b6fe0a9507ba47f7081161ba7f by سليمان السهمي (Suleyman Sahmi):
Fix issue 14696 - Fix a remaining case where the destructor is still called on a cold branch
Temporary variables should not be destroyed when the branch that initializes them is not taken.
https://github.com/dlang/dmd/pull/10628