Skip to content

Commit

Permalink
Merge pull request #5292 from 9rnsr/fix15296x
Browse files Browse the repository at this point in the history
Properly fix Issue 15296 - expand CallExp in ExpStatement as statements
  • Loading branch information
MartinNowak committed Feb 29, 2016
2 parents e5e8f3f + 25453b6 commit 06133fd
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 45 deletions.
11 changes: 8 additions & 3 deletions src/func.d
Expand Up @@ -2087,19 +2087,23 @@ public:
Statement s = new ReturnStatement(Loc(), new IntegerExp(0));
a.push(s);
}

Statement sbody = new CompoundStatement(Loc(), a);

/* Append destructor calls for parameters as finally blocks.
*/
if (parameters)
{
for (size_t i = 0; i < parameters.dim; i++)
foreach (v; *parameters)
{
VarDeclaration v = (*parameters)[i];
if (v.storage_class & (STCref | STCout | STClazy))
continue;
if (v.needsScopeDtor())
{
Statement s = new ExpStatement(Loc(), v.edtor);
// same with ExpStatement.scopeCode()
Statement s = new DtorExpStatement(Loc(), v.edtor, v);
v.noscope = true;

s = s.semantic(sc2);

uint nothrowErrors = global.errors;
Expand All @@ -2119,6 +2123,7 @@ public:
}
// from this point on all possible 'throwers' are checked
flags &= ~FUNCFLAGnothrowInprocess;

if (isSynchronized())
{
/* Wrap the entire function body in a synchronized statement
Expand Down
112 changes: 79 additions & 33 deletions src/inline.d
Expand Up @@ -47,11 +47,11 @@ enum EXPANDINLINE_LOG = false;
*/
enum COST_MAX = 250;
enum STATEMENT_COST = 0x1000;
enum STATEMENT_COST_MAX = 250 * 0x1000;
enum STATEMENT_COST_MAX = 250 * STATEMENT_COST;

// STATEMENT_COST be power of 2 and greater than COST_MAX
//static assert((STATEMENT_COST & (STATEMENT_COST - 1)) == 0);
//static assert(STATEMENT_COST > COST_MAX);
static assert((STATEMENT_COST & (STATEMENT_COST - 1)) == 0);
static assert(STATEMENT_COST > COST_MAX);

bool tooCostly(int cost)
{
Expand Down Expand Up @@ -1201,36 +1201,59 @@ public:
{
printf("ExpStatement.inlineScan(%s)\n", s.toChars());
}
if (s.exp)
if (!s.exp)
return;

Statement inlineScanExpAsStatement(ref Expression exp)
{
/* TODO: It's a problematic inlineScan call. If s.exp is a TOKcall,
* CallExp.inlineScan would try to expand the call as expression.
* If it's impossible, a false "cannot inline function" error
* would be reported.
/* If there's a TOKcall at the top, then it may fail to inline
* as an Expression. Try to inline as a Statement instead.
*/
inlineScan(s.exp); // inline as an expression
if (exp.op == TOKcall)
{
visitCallExp(cast(CallExp)exp, null, true);
if (eresult)
exp = eresult;
auto s = sresult;
sresult = null;
eresult = null;
return s;
}

/* If there's a TOKcall at the top, then it failed to inline
* as an Expression. Try to inline as a Statement instead.
* Note that inline scanning of s.exp.e1 and s.exp.arguments was already done.
/* If there's a CondExp or CommaExp at the top, then its
* sub-expressions may be inlined as statements.
*/
if (s.exp && s.exp.op == TOKcall)
if (exp.op == TOKquestion)
{
CallExp ce = cast(CallExp)s.exp;

/* Workaround for Bugzilla 15296.
*
* Before the PR#5121, here was inlined a function call only
* when ce.e1.op == TOKvar.
* After the PR, visitCallExp has started to handle TOKdotvar
* and TOKstar. However it was not good for the issue case.
*
* Revive a restriction which was in previous code to avoid regression.
*/
if (ce.e1.op == TOKvar)
visitCallExp(ce, null, true);
auto e = cast(CondExp)exp;
inlineScan(e.econd);
auto s1 = inlineScanExpAsStatement(e.e1);
auto s2 = inlineScanExpAsStatement(e.e2);
if (!s1 && !s2)
return null;
auto ifbody = !s1 ? new ExpStatement(e.e1.loc, e.e1) : s1;
auto elsebody = !s2 ? new ExpStatement(e.e2.loc, e.e2) : s2;
return new IfStatement(exp.loc, null, e.econd, ifbody, elsebody);
}
if (exp.op == TOKcomma)
{
auto e = cast(CommaExp)exp;
auto s1 = inlineScanExpAsStatement(e.e1);
auto s2 = inlineScanExpAsStatement(e.e2);
if (!s1 && !s2)
return null;
auto a = new Statements();
a.push(!s1 ? new ExpStatement(e.e1.loc, e.e1) : s1);
a.push(!s2 ? new ExpStatement(e.e2.loc, e.e2) : s2);
return new CompoundStatement(exp.loc, a);
}

// inline as an expression
inlineScan(exp);
return null;
}

sresult = inlineScanExpAsStatement(s.exp);
}

override void visit(CompoundStatement s)
Expand Down Expand Up @@ -1484,8 +1507,7 @@ public:
*/
inlineScan(e.e1);
}
inlineScan(ce.e1);
arrayInlineScan(ce.arguments);

visitCallExp(ce, e.e1, false);
if (eresult)
{
Expand All @@ -1501,8 +1523,6 @@ public:
override void visit(CallExp e)
{
//printf("CallExp.inlineScan() %s\n", e.toChars());
inlineScan(e.e1);
arrayInlineScan(e.arguments);
visitCallExp(e, null, false);
}

Expand All @@ -1513,11 +1533,16 @@ public:
* e = the function call
* eret = if !null, then this is the lvalue of the nrvo function result
* asStatements = if inline as statements rather than as an Expression
* Returns:
* this.eresult if asStatements == false
* this.sresult if asStatements == true
*/
void visitCallExp(CallExp e, Expression eret, bool asStatements)
{
//printf("visitCallExp() %s\n", e.toChars());
inlineScan(e.e1);
arrayInlineScan(e.arguments);

//printf("visitCallExp() %s\n", e.toChars());
FuncDeclaration fd;

void inlineFd()
Expand Down Expand Up @@ -2200,20 +2225,41 @@ void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration parent, Expre
{
/* Construct:
* { eret; ethis; eparams; fd.fbody; }
* or:
* { eret; ethis; try { eparams; fd.fbody; } finally { vthis.edtor; } }
*/

auto as = new Statements();
if (eret)
as.push(new ExpStatement(callLoc, eret));
if (ethis)
as.push(new ExpStatement(callLoc, ethis));

auto as2 = as;
if (vthis && !vthis.isDataseg())
{
if (vthis.needsScopeDtor())
{
// same with ExpStatement.scopeCode()
as2 = new Statements();
vthis.noscope = 1;
}
}

if (eparams)
as.push(new ExpStatement(callLoc, eparams));
as2.push(new ExpStatement(callLoc, eparams));

fd.inlineNest++;
Statement s = inlineAsStatement(fd.fbody, ids);
fd.inlineNest--;
as.push(s);
as2.push(s);

if (as2 != as)
{
as.push(new TryFinallyStatement(callLoc,
new CompoundStatement(callLoc, as2),
new DtorExpStatement(callLoc, vthis.edtor, vthis)));
}

sresult = new ScopeStatement(callLoc, new CompoundStatement(callLoc, as));

Expand Down
2 changes: 1 addition & 1 deletion src/statement.d
Expand Up @@ -3373,7 +3373,7 @@ public:

if (match.edtor)
{
Statement sdtor = new ExpStatement(loc, match.edtor);
Statement sdtor = new DtorExpStatement(loc, match.edtor, match);
sdtor = new OnScopeStatement(loc, TOKon_scope_exit, sdtor);
ifbody = new CompoundStatement(loc, sdtor, ifbody);
match.noscope = true;
Expand Down
5 changes: 4 additions & 1 deletion src/toir.c
Expand Up @@ -793,7 +793,10 @@ void buildClosure(FuncDeclaration *fd, IRState *irs)
VarDeclaration *v = fd->closureVars[i];
//printf("closure var %s\n", v->toChars());

if (v->needsScopeDtor())
// Hack for the case fail_compilation/fail10666.d,
// until proper issue 5730 fix will come.
bool isScopeDtorParam = v->edtor && (v->storage_class & STCparameter);
if (v->needsScopeDtor() || isScopeDtorParam)
{
/* Because the value needs to survive the end of the scope!
*/
Expand Down
14 changes: 7 additions & 7 deletions test/fail_compilation/pragmainline2.d
Expand Up @@ -16,15 +16,15 @@ void foo()
pragma(inline, false);
pragma(inline);
pragma(inline, true); // this last one will affect to the 'foo'
while (0) { }
asm { nop; }
}

pragma(inline, true) void f1t() { while (0) {} } // cannot inline
pragma(inline, false) void f1f() { while (0) {} }
pragma(inline) void f1d() { while (0) {} }
void f2t() { pragma(inline, true); while (0) {} } // cannot inline
void f2f() { pragma(inline, false); while (0) {} }
void f2d() { pragma(inline); while (0) {} }
pragma(inline, true) void f1t() { asm { nop; } } // cannot inline
pragma(inline, false) void f1f() { asm { nop; } }
pragma(inline) void f1d() { asm { nop; } }
void f2t() { pragma(inline, true); asm { nop; } } // cannot inline
void f2f() { pragma(inline, false); asm { nop; } }
void f2d() { pragma(inline); asm { nop; } }

void main()
{
Expand Down
91 changes: 91 additions & 0 deletions test/runnable/inline.d
Expand Up @@ -953,6 +953,7 @@ static int x15296;
struct S15296
{
// Can be expanded only as statements.
pragma(inline, true)
void bar(size_t , size_t )
{
for (size_t w = 0; w < 2; w++) { ++x15296; }
Expand All @@ -965,14 +966,102 @@ struct S15296
}
}

pragma(inline, true)
static void voidCall15296()
{
for (size_t w = 0; w < 3; w++) { ++x15296; }
}

void test15296()
{
bool cond = true;

S15296 s;

// CallExp at the top of ExpStatement
x15296 = 0;
s.foo(0, 0);
assert(x15296 == 2);

// CondExp at the top of ExpStatement
x15296 = 0;
(cond ? s.foo(0, 0) : voidCall15296());
assert(x15296 == 2);
(cond ? voidCall15296() : s.foo(0, 0));
assert(x15296 == 2 + 3);

// CommaExp at the top of ExpStatement
x15296 = 0;
(s.foo(0, 0), voidCall15296());
assert(x15296 == 3 + 2);
}

// ----

struct File15296
{
struct Impl {}
Impl* _p;

pragma(inline, true)
~this() { _p = null; }

struct LockingTextWriter
{
pragma(inline, true)
this(ref File15296 f)
{
assert(f._p, "Attempting to write to closed File");
}
}

pragma(inline, true)
auto lockingTextWriter() { return LockingTextWriter(this); }

pragma(inline, true)
void write() { auto w = lockingTextWriter(); }

//pragma(inline, true)
static uint formattedWrite(Writer)(Writer w) { return 0; }

pragma(inline, true)
void writef() { formattedWrite(lockingTextWriter()); }
}

__gshared File15296 stdout15296 = {new File15296.Impl()};

pragma(inline, true)
@property File15296 trustedStdout15296() { return stdout15296; }

// ----
// reduced case from runnable/test34.d test34()

void test15296b()
{
// trustedStdout() returns a temporary File object. Its dtor call
// should be deferred till the end of expanded writef body statements.
trustedStdout15296().writef();
}

// ----
// reduced case from runnable/xtest46.d test136()

struct Perm15296c
{
this(byte[] input)
{
foreach (elem; input)
{
// if vthis.isDataseg() is true in expandInline,
// its edtor should not be called.
stdout15296.write();
}
}
}

void test15296c()
{
auto perm2 = Perm15296c([0, 1, 2]);
}

/**********************************/
Expand Down Expand Up @@ -1009,6 +1098,8 @@ int main()
test9785_3();
test15207();
test15296();
test15296b();
test15296c();

printf("Success\n");
return 0;
Expand Down

0 comments on commit 06133fd

Please sign in to comment.