http://dpaste.dzfl.pl/08d60e83
import std.exception : assertThrown;
import std.file : dirEntries, SpanMode;
// segfault does not occur with -O command line argument (exception is thrown as expected with -O)
void main() {
assertThrown(dirEntries("test", SpanMode.breadth)); // assert does not fail, as expected (ie, exception was thrown)
func("test");
}
void func(string suicide_variable) { // must be in a seperate function
auto ax = suicide_variable; // uncomment this for a segfault in the following statement
// not clear as to why, can't yet reproduce with other unpredictable exceptions (AFAIK)
foreach(s; dirEntries("test", SpanMode.breadth)) {} // exception is expected, not a segfault
}
----------
I tried to produce this behaviour for other functions (ie, without the dirEntries call), but I could not get at it; but this is all still very much undefined behaviour.
Further reduced:
struct Foo
{
int[100] i;
}
struct DirIterator
{
Foo* _store ;
~this()
{
if (_store)
{
_store.i = 0; // to segfault for sure
_store = null;
}
}
this(string pathname)
{
throw new Exception("");
}
void popFront() { }
bool empty() { return true; }
Foo front() { return *_store; }
}
auto dirEntries()
{
return DirIterator("path");
}
void main() {
foreach(s; dirEntries()) {}
}
This looks like issue 9438 (struct with pointer field and dtor) but now temporaries from function return values are involved.
Comment #3 by maxim — 2013-02-18T11:26:46Z
The reason of the issue seems to be in that struct temporary inside main() is not initialized. If, line
foreach(s; dirEntries()) {}
is replaced to directly accessing object
foreach(s; DirIterator("path")) {}
dmd emits some code to clear the stack before assigning $rdi.
Comment #4 by nilsbossung — 2013-06-26T19:07:23Z
*** Issue 10475 has been marked as a duplicate of this issue. ***
Comment #5 by nilsbossung — 2013-06-26T19:09:12Z
Some observations (includes the stuff form issue 10475):
struct DirIterator
{
int _store = 42;
this(string dummy) {throw new Exception("constructor");}
~this()
{
assert(_store == 42, "destructing garbage");
assert(false, "destructor");
}
}
DirIterator dirEntries() {return DirIterator("");}
void main()
{
/* This triggers only "constructor". The destructor is not called. */
version(A) DirIterator a = dirEntries();
/* This triggers "constructor" and "destructing garbage".
I.e. destructor is called on uninitialized data. Probably fine in this case
because of explicit void initialization. */
else version(B)
{
DirIterator b = void;
b = dirEntries();
}
/* This triggers "constructor" and "destructor".
Arguably, the destructor should not be called, since construction has
failed. */
else version(C) for(DirIterator c = DirIterator(""); true; ) {}
/* Just like B, this triggers "constructor" and "destructing garbage".
No explicit void initialization here, so that should not happen. */
else version(D) for(DirIterator c = dirEntries(); true;) {}
else static assert(false);
}
Comment #6 by k.hara.pg — 2013-07-02T07:05:14Z
I posted bug 10475 fix and now it's merged in git head.
Could you please confirm the actual case by using git head?
Comment #7 by maxim — 2013-07-02T10:12:24Z
(In reply to comment #6)
> I posted bug 10475 fix and now it's merged in git head.
> Could you please confirm the actual case by using git head?
This issue is also fixed. Thank you.
Comment #8 by dlang-bugzilla — 2014-08-15T18:15:57Z
*** Issue 6329 has been marked as a duplicate of this issue. ***