Showing with 89 additions and 14 deletions.
  1. +30 −6 src/dinterpret.d
  2. +24 −6 src/func.d
  3. +2 −2 test/fail_compilation/ice11850.d
  4. +33 −0 test/fail_compilation/ice15172.d
36 changes: 30 additions & 6 deletions src/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,13 @@ extern (C++) void ctfeCompile(FuncDeclaration fd)
}

/*************************************
*
* Entry point for CTFE.
* A compile-time result is required. Give an error if not possible
* A compile-time result is required. Give an error if not possible.
*
* `e` must be semantically valid expression. In other words, it should not
* contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
* functions and may invoke a function that contains `ErrorStatement` in its body.
* If that, the "CTFE failed because of previous errors" error is raised.
*/
extern (C++) Expression ctfeInterpret(Expression e)
{
Expand All @@ -710,21 +714,21 @@ extern (C++) Expression ctfeInterpret(Expression e)
//assert(e->type->ty != Terror); // FIXME
if (e.type.ty == Terror)
return new ErrorExp();
uint olderrors = global.errors;

// This code is outside a function, but still needs to be compiled
// (there are compiler-generated temporary variables such as __dollar).
// However, this will only be run once and can then be discarded.
auto ctfeCodeGlobal = CompiledCtfeFunction(null);
ctfeCodeGlobal.callingloc = e.loc;
ctfeCodeGlobal.onExpression(e);

Expression result = interpret(e, null);

if (!CTFEExp.isCantExp(result))
result = scrubReturnValue(e.loc, result);
if (CTFEExp.isCantExp(result))
{
assert(global.errors != olderrors);
result = new ErrorExp();
}

return result;
}

Expand Down Expand Up @@ -799,9 +803,11 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
return CTFEExp.cantexp;
if (fd.semanticRun < PASSsemantic3done)
return CTFEExp.cantexp;

// CTFE-compile the function
if (!fd.ctfeCode)
ctfeCompile(fd);

Type tb = fd.type.toBasetype();
assert(tb.ty == Tfunction);
TypeFunction tf = cast(TypeFunction)tb;
Expand All @@ -810,24 +816,28 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
fd.error("C-style variadic functions are not yet implemented in CTFE");
return CTFEExp.cantexp;
}

// Nested functions always inherit the 'this' pointer from the parent,
// except for delegates. (Note that the 'this' pointer may be null).
// Func literals report isNested() even if they are in global scope,
// so we need to check that the parent is a function.
if (fd.isNested() && fd.toParent2().isFuncDeclaration() && !thisarg && istate)
thisarg = ctfeStack.getThis();

if (fd.needThis() && !thisarg)
{
// error, no this. Prevent segfault.
// Here should be unreachable by the strict 'this' check in front-end.
fd.error("need 'this' to access member %s", fd.toChars());
return CTFEExp.cantexp;
}

// Place to hold all the arguments to the function while
// we are evaluating them.
Expressions eargs;
size_t dim = arguments ? arguments.dim : 0;
assert((fd.parameters ? fd.parameters.dim : 0) == dim);

/* Evaluate all the arguments to the function,
* store the results in eargs[]
*/
Expand Down Expand Up @@ -882,6 +892,7 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
}
eargs[i] = earg;
}

// Now that we've evaluated all the arguments, we can start the frame
// (this is the moment when the 'call' actually takes place).
InterState istatex;
Expand All @@ -893,6 +904,7 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
ctfeStack.push(fd.vthis);
setValue(fd.vthis, thisarg);
}

for (size_t i = 0; i < dim; i++)
{
Expression earg = eargs[i];
Expand All @@ -903,6 +915,7 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
printf("arg[%d] = %s\n", i, earg.toChars());
}
ctfeStack.push(v);

if ((fparam.storageClass & (STCout | STCref)) && earg.op == TOKvar && (cast(VarExp)earg).var.toParent2() == fd)
{
VarDeclaration vx = (cast(VarExp)earg).var.isVarDeclaration();
Expand All @@ -911,6 +924,7 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
fd.error("cannot interpret %s as a ref parameter", earg.toChars());
return CTFEExp.cantexp;
}

/* vx is a variable that is declared in fd.
* It means that fd is recursively called. e.g.
*
Expand All @@ -924,6 +938,7 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
* should be saved at the start of fd(2, vx) call.
*/
int oldadr = vx.ctfeAdrOnStack;

ctfeStack.push(vx);
assert(!hasValue(vx)); // vx is made uninitialized
// Bugzilla 14299: v->ctfeAdrOnStack should be saved already
Expand All @@ -942,12 +957,15 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
showCtfeExpr(earg);
}
}

if (fd.vresult)
ctfeStack.push(fd.vresult);

// Enter the function
++CtfeStatus.callDepth;
if (CtfeStatus.callDepth > CtfeStatus.maxCallDepth)
CtfeStatus.maxCallDepth = CtfeStatus.callDepth;

Expression e = null;
while (1)
{
Expand All @@ -967,11 +985,13 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
printf("function body failed to interpret\n");
}
}

if (istatex.start)
{
fd.error("CTFE internal error: failed to resume at statement %s", istatex.start.toChars());
return CTFEExp.cantexp;
}

/* This is how we deal with a recursive statement AST
* that has arbitrary goto statements in it.
* Bubble up a 'result' which is the target of the goto
Expand All @@ -995,15 +1015,19 @@ extern (C++) Expression interpret(FuncDeclaration fd, InterState* istate, Expres
if (tf.isref && e.op == TOKvar && (cast(VarExp)e).var == fd.vthis)
e = thisarg;
assert(e !is null);

// Leave the function
--CtfeStatus.callDepth;

ctfeStack.endFrame();

// If it generated an uncaught exception, report error.
if (!istate && e.op == TOKthrownexception)
{
(cast(ThrownExceptionExp)e).generateUncaughtError();
e = CTFEExp.cantexp;
}

return e;
}

Expand Down
30 changes: 24 additions & 6 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -2200,10 +2200,16 @@ public:
//fflush(stdout);
}

/****************************************************
* Resolve forward reference of function signature -
* parameter types, return type, and attributes.
* Returns false if any errors exist in the signature.
*/
final bool functionSemantic()
{
if (!_scope)
return true;
return !errors;

if (!originalType) // semantic not yet run
{
TemplateInstance spec = isSpeculative();
Expand All @@ -2218,11 +2224,17 @@ public:
if (olderrs != global.errors) // if errors compiling this function
return false;
}

// if inferring return type, sematic3 needs to be run
// - When the function body contains any errors, we cannot assume
// the inferred return type is valid.
// So, the body errors should become the function signature error.
if (inferRetType && type && !type.nextOf())
return functionSemantic3();

TemplateInstance ti;
if (isInstantiated() && !isVirtualMethod() && !(ti = parent.isTemplateInstance(), ti && !ti.isTemplateMixin() && ti.tempdecl.ident != ident))
if (isInstantiated() && !isVirtualMethod() &&
!(ti = parent.isTemplateInstance(), ti && !ti.isTemplateMixin() && ti.tempdecl.ident != ident))
{
AggregateDeclaration ad = isMember2();
if (ad && ad.sizeok != SIZEOKdone)
Expand All @@ -2234,15 +2246,19 @@ public:
//ad->sizeok = SIZEOKfwd;
}
else
return functionSemantic3();
return functionSemantic3() || !errors;
}

if (storage_class & STCinference)
return functionSemantic3();
return functionSemantic3() || !errors;

return true;
return !errors;
}

/****************************************************
* Resolve forward reference of function body.
* Returns false if any errors exist in the body.
*/
final bool functionSemantic3()
{
if (semanticRun < PASSsemantic3 && _scope)
Expand All @@ -2258,14 +2274,16 @@ public:
global.gag = 0;
semantic3(_scope);
global.gag = oldgag;

// If it is a speculatively-instantiated template, and errors occur,
// we need to mark the template as having errors.
if (spec && global.errors != olderrs)
spec.errors = (global.errors - olderrs != 0);
if (olderrs != global.errors) // if errors compiling this function
return false;
}
return true;

return !errors && !semantic3Errors;
}

// called from semantic3
Expand Down
4 changes: 2 additions & 2 deletions test/fail_compilation/ice11850.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ TEST_OUTPUT:
fail_compilation/ice11850.d(16): Error: incompatible types for ((a) < ([0])): 'uint[]' and 'int[]'
fail_compilation/imports/a11850.d(9): instantiated from here: FilterResult!(__lambda1, uint[][])
fail_compilation/ice11850.d(16): instantiated from here: filter!(uint[][])
fail_compilation/ice11850.d(16): Error: template imports.a11850.filter cannot deduce function from argument types !((a) => a < [0])(uint[][]), candidates are:
fail_compilation/imports/a11850.d(5): imports.a11850.filter(alias pred)
---
*/



import imports.a11850 : filter;

void main()
Expand Down
33 changes: 33 additions & 0 deletions test/fail_compilation/ice15172.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice15172.d(14): Error: constructor ice15172.ThreadError.this no match for implicit super() call in constructor
---
*/

// 1. ClassDeclaration.semantic
class ThreadError : Error
{
// 2. FuncDeclaration.semantic
// 4. FuncDeclaration.semantic3
// --> error happens
this(string)
{
}
}

// 3. FuncDeclaration.semantic
// 5. FuncDeclaration.semantic3
void onThreadError()
{
// 6. NewExp.semantic
// --> cd.members.errors == false, cd.members.semantic3Errors == true
// BUT, The ThreadError class constructor is not a template function, so
// the errors inside its function body won't be associated with the validness of this NewExp.
// Therefore, converting the semantic3Error to ErrorExp is not correct.
// 7. ctfeInterpret
// Finally, FuncDeclaration::interpret may encounter a function which is semantic3Errors == true. So
// 7a. functionSemantic3() should return false if semantic3Errors is true.
// 7b. the function body errors may not happen during ctfeInterpret call and global.errors could be unincremented.
__gshared auto ThreadError = new ThreadError(null);
}