Skip to content

Commit

Permalink
Merge pull request #5058 from 9rnsr/fix15030
Browse files Browse the repository at this point in the history
[REG2.068.1] Issue 15030 - ICE with recursive delegate, -unittest, and std.range
  • Loading branch information
MartinNowak committed Sep 9, 2015
2 parents 334e291 + a77a2e3 commit ea5d85a
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 17 deletions.
42 changes: 26 additions & 16 deletions src/glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,20 @@ bool isDruntimeArrayOp(Identifier *ident)

/* ================================================================== */

UnitTestDeclaration *needsDeferredNested(FuncDeclaration *fd)
{
while (fd && fd->isNested())
{
FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration();
if (!fdp)
break;
if (UnitTestDeclaration *udp = fdp->isUnitTestDeclaration())
return udp->semanticRun < PASSobj ? udp : NULL;
fd = fdp;
}
return NULL;
}

void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj)
{
ClassDeclaration *cd = fd->parent->isClassDeclaration();
Expand Down Expand Up @@ -766,6 +780,9 @@ void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj)
if (fd->type && fd->type->ty == Tfunction && ((TypeFunction *)fd->type)->next->ty == Terror)
return;

if (fd->semantic3Errors)
return;

if (global.errors)
return;

Expand Down Expand Up @@ -804,23 +821,15 @@ void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj)
break;
}

FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration();
if (fd->isNested())
if (UnitTestDeclaration *udp = needsDeferredNested(fd))
{
if (fdp && fdp->semanticRun < PASSobj)
{
if (fdp->semantic3Errors)
return;

/* Can't do unittest's out of order, they are order dependent in that their
* execution is done in lexical order.
*/
if (UnitTestDeclaration *udp = fdp->isUnitTestDeclaration())
{
udp->deferredNested.push(fd);
return;
}
}
/* Can't do unittest's out of order, they are order dependent in that their
* execution is done in lexical order.
*/
udp->deferredNested.push(fd);
//printf("%s @[%s]\n\t--> pushed to unittest @[%s]\n",
// fd->toPrettyChars(), fd->loc.toChars(), udp->loc.toChars());
return;
}

if (fd->isArrayOp && isDruntimeArrayOp(fd->ident))
Expand Down Expand Up @@ -885,6 +894,7 @@ void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj)
/* The enclosing function must have its code generated first,
* in order to calculate correct frame pointer offset.
*/
FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration();
if (fdp && fdp->semanticRun < PASSobj)
{
toObjFile(fdp, multiobj);
Expand Down
28 changes: 27 additions & 1 deletion src/template.c
Original file line number Diff line number Diff line change
Expand Up @@ -7167,6 +7167,14 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
int nested = 0;
//printf("TemplateInstance::hasNestedArgs('%s')\n", tempdecl->ident->toChars());

#if 0
if (!enclosing)
{
if (TemplateInstance *ti = tempdecl->isInstantiated())
enclosing = ti->enclosing;
}
#endif

/* A nested instance happens when an argument references a local
* symbol that is on the stack.
*/
Expand Down Expand Up @@ -7294,6 +7302,10 @@ Dsymbols *TemplateInstance::appendToModuleMember()
mi = NULL;
}

//printf("%s->appendToModuleMember() enclosing = %s mi = %s\n",
// toPrettyChars(),
// enclosing ? enclosing->toPrettyChars() : NULL,
// mi ? mi->toPrettyChars() : NULL);
if (!mi || mi->isRoot())
{
/* If the instantiated module is speculative or root, insert to the
Expand All @@ -7302,9 +7314,22 @@ Dsymbols *TemplateInstance::appendToModuleMember()
* - codegen pass will get a selection chance to do/skip it.
*/

struct N
{
static Dsymbol *getStrictEnclosing(TemplateInstance *ti)
{
if (ti->enclosing)
return ti->enclosing;
if (TemplateInstance *tix = ti->tempdecl->isInstantiated())
return getStrictEnclosing(tix);
return NULL;
}
};
Dsymbol *enc = N::getStrictEnclosing(this);

// insert target is made stable by using the module
// where tempdecl is declared.
mi = tempdecl->getModule();
mi = (enc ? enc : tempdecl)->getModule();
if (!mi->isRoot())
mi = mi->importedFrom;
assert(mi->isRoot());
Expand All @@ -7317,6 +7342,7 @@ Dsymbols *TemplateInstance::appendToModuleMember()
* - codegen pass won't reach to the instance.
*/
}
//printf("\t--> mi = %s\n", mi->toPrettyChars());

Dsymbols *a = mi->members;
for (size_t i = 0; 1; i++)
Expand Down
92 changes: 92 additions & 0 deletions test/runnable/ice15030.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// REQUIRED_ARGS: -unittest -boundscheck=off
// PERMUTE_ARGS:
// EXTRA_SOURCES: imports/a15030.d imports/b15030.d

void main() {}

/+
Compiler output with -v switch.
With 2.068.0:
--------
code a
code b
function b.__unittestL5_2
function b.__unittestL5_2.__lambda1
function b.__unittestL5_2.__lambda1.__lambda2
function b.__unittestL5_2.__lambda1.__lambda2.filter!((a) => a).filter!(int[]).filter
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.this
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.empty
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.front
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.popFront
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xopEquals
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xtoHash
function b.__unittestL5_2.__lambda1.__lambda2.__lambda2
The nested functions '__lambda1', '__lambda2', and 'filter' are
(fortunately) generated from outer to inner.
With 2.068.1:
--------
code a
function b.__unittestL5_2.__lambda1.__lambda2.filter!((a) => a).filter!(int[]).filter
function b.__unittestL5_2.__lambda1.__lambda2
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.this
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.empty
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.front
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.popFront
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xopEquals
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xtoHash
code b
function b.__unittestL5_2
function b.__unittestL5_2.__lambda1
Assertion failure: '!v->csym' on line 1060 in file 'glue.c'
abnormal program termination
'filer' is generated before its ancestor functions '__lambda1' and '__lambda2' - it's a bug.
Fixed (contains debug prints):
--------
code a
b.__unittestL5_2.__lambda1.__lambda2.filter!((a) => a).filter!(int[]).filter @[algorithm.d(5)]
--> pushed to unittest @[b.d(5)]
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.this
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.empty
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.front
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.popFront
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xopEquals
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xtoHash
code b
function b.__unittestL5_2
function b.__unittestL5_2.__lambda1
function b.__unittestL5_2.__lambda1.__lambda2
function b.__unittestL5_2.__lambda1.__lambda2.filter!((a) => a).filter!(int[]).filter
function b.__unittestL5_2.__lambda1.__lambda2.__lambda2
By using `deferredNested` correctly, those nested function generations are ordered again.
Fixed more:
--------
function D main
code a
code b
function b.__unittestL5_2
function b.__unittestL5_2.__lambda1
function b.__unittestL5_2.__lambda1.__lambda2
function b.__unittestL5_2.__lambda1.__lambda2.filter!(int[]).filter
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.this
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.empty
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.front
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.popFront
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xopEquals
function b.__unittestL5_2.__lambda1.__lambda2.FilterResult!(__lambda2, int[]).FilterResult.__xtoHash
function b.__unittestL5_2.__lambda1.__lambda2.__lambda2
By the tweak in TemplateInstance::appendToModuleMember(), all the code of
(implicitly) nested instances are stored into corresponding module object file.
+/
3 changes: 3 additions & 0 deletions test/runnable/imports/a15030.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module a;

import imports.std15030algo;
17 changes: 17 additions & 0 deletions test/runnable/imports/b15030.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module b;

import imports.std15030algo;

unittest
{
auto dg =
// __lambda1
(int[] delegate(int[]) self) =>
// __lambda2
(int[] arr) => arr
? self([arr.filter!(
// __lambda2.__lambda2
a => a
).front])
: null;
}
35 changes: 35 additions & 0 deletions test/runnable/imports/std15030algo.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module imports.std15030algo;

template filter(alias pred)
{
auto filter(R)(R r)
{
return FilterResult!(pred, R)(r);
}
}

private struct FilterResult(alias pred, R)
{
R _input;

this(R r)
{
_input = r;
while (_input.length != 0 && !pred(_input[0]))
{
_input = _input[1..$];
}
}

@property bool empty() { return _input.length == 0; }

@property auto ref front() { return _input[0]; }

void popFront()
{
do
{
_input = _input[1..$];
} while (_input.length != 0 && !pred(_input[0]));
}
}

0 comments on commit ea5d85a

Please sign in to comment.