Skip to content

Commit

Permalink
Merge pull request #5061 from MartinNowak/merge_stable
Browse files Browse the repository at this point in the history
Merge branch 'merge_stable_convert' into merge_stable
  • Loading branch information
9rnsr committed Sep 9, 2015
2 parents e9808b5 + b1cea21 commit f407129
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 22 deletions.
42 changes: 38 additions & 4 deletions src/dtemplate.d
Expand Up @@ -6379,7 +6379,18 @@ public:
// Elide codegen because this is really speculative.
return false;
}

/* Even when this is reached to the codegen pass,
* a non-root nested template should not generate code,
* due to avoid ODR violation.
*/
if (enclosing && enclosing.inNonRoot())
{
if (tinst)
return tinst.needsCodegen();
if (tnext)
return tnext.needsCodegen();
return false;
}
/* The issue is that if the importee is compiled with a different -debug
* setting than the importer, the importer may believe it exists
* in the compiled importee when it does not, when the instantiation
Expand Down Expand Up @@ -7266,6 +7277,14 @@ public:
{
int nested = 0;
//printf("TemplateInstance::hasNestedArgs('%s')\n", tempdecl->ident->toChars());
version (none)
{
if (!enclosing)
{
if (TemplateInstance ti = tempdecl.isInstantiated())
enclosing = ti.enclosing;
}
}
/* A nested instance happens when an argument references a local
* symbol that is on the stack.
*/
Expand Down Expand Up @@ -7379,18 +7398,33 @@ public:
if (mi && !mi.isRoot())
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
* member of a root module. Then:
* - semantic3 pass will get called on the instance members.
* - codegen pass will get a selection chance to do/skip it.
*/
struct N
{
extern (C++) 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 @@ -7403,7 +7437,7 @@ public:
* - 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
42 changes: 26 additions & 16 deletions src/glue.c
Expand Up @@ -735,6 +735,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 @@ -764,6 +778,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 @@ -802,23 +819,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 @@ -881,6 +890,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
4 changes: 2 additions & 2 deletions src/inline.d
Expand Up @@ -1812,9 +1812,9 @@ extern (C++) static Expression expandInline(FuncDeclaration fd, FuncDeclaration
// When the function is actually expanded
if (TemplateInstance ti = fd.isInstantiated())
{
// change ti to non-speculative instance
// change ti to non-speculative root instance
if (!ti.minst)
ti.minst = ti.tempdecl.getModule();
ti.minst = ti.tempdecl.getModule().importedFrom;
}

if (ps)
Expand Down
92 changes: 92 additions & 0 deletions test/runnable/ice15030.d
@@ -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
@@ -0,0 +1,3 @@
module a;

import imports.std15030algo;
17 changes: 17 additions & 0 deletions test/runnable/imports/b15030.d
@@ -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;
}
13 changes: 13 additions & 0 deletions test/runnable/imports/std15021conv.d
@@ -0,0 +1,13 @@
module imports.std15021conv;

T to(T, A...)(A args)
{
return toImpl!T(args);
}

T toImpl(T, S)(S value)
{
import imports.std15021format;
enforceValidFormatSpec!(S, char)('c');
return "";
}
12 changes: 12 additions & 0 deletions test/runnable/imports/std15021format.d
@@ -0,0 +1,12 @@
module imports.std15021format;

T enforceEx(T)(T value, lazy string msg = "")
{
if (!value) throw new Exception(msg);
return value;
}

void enforceValidFormatSpec(T, Char)(int spec)
{
enforceEx(spec == 's');
}
35 changes: 35 additions & 0 deletions test/runnable/imports/std15030algo.d
@@ -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]));
}
}
18 changes: 18 additions & 0 deletions test/runnable/link15021.d
@@ -0,0 +1,18 @@
// PERMUTE_ARGS: -inline -g -debug -unittest

import imports.std15021conv;

class AliasDecl {}

void aliasDecl(AliasDecl ad)
{
AliasDecl* zis;

static if (is(typeof(to!string(*zis))))
{
pragma(msg, "hit!");
to!string(*zis);
}
}

void main() {}

0 comments on commit f407129

Please sign in to comment.