Skip to content

Commit

Permalink
fix Issue 15030 - ICE with recursive delegate, -unittest, and std.range
Browse files Browse the repository at this point in the history
Refer the detailed explanation in `test/runnable/ice15030.d`.
  • Loading branch information
9rnsr committed Sep 9, 2015
1 parent 334e291 commit d96a649
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 16 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
71 changes: 71 additions & 0 deletions test/runnable/ice15030.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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.
+/
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 d96a649

Please sign in to comment.