Skip to content

Commit

Permalink
Fix Issue 6169 - [CTFE] pure functions cannot compute constants using…
Browse files Browse the repository at this point in the history
… functions not marked as pure

When running semantic on an expression used anywhere that forces compile time evaluation, use a scope flag to prevent purity and safety checks on function calls.
This allows better purity/safety inferrence as well.
  • Loading branch information
yebblies committed May 2, 2013
1 parent 2c5df8d commit 42d1af1
Show file tree
Hide file tree
Showing 21 changed files with 193 additions and 73 deletions.
10 changes: 5 additions & 5 deletions src/attrib.c
Expand Up @@ -971,7 +971,7 @@ void PragmaDeclaration::semantic(Scope *sc)
{
Expression *e = (*args)[i];

e = e->semantic(sc);
e = e->ctfeSemantic(sc);
e = resolveProperties(sc, e);
if (e->op != TOKerror && e->op != TOKtype)
e = e->ctfeInterpret();
Expand Down Expand Up @@ -999,7 +999,7 @@ void PragmaDeclaration::semantic(Scope *sc)
{
Expression *e = (*args)[0];

e = e->semantic(sc);
e = e->ctfeSemantic(sc);
e = resolveProperties(sc, e);
e = e->ctfeInterpret();
(*args)[0] = e;
Expand Down Expand Up @@ -1027,7 +1027,7 @@ void PragmaDeclaration::semantic(Scope *sc)
else
{
Expression *e = (*args)[0];
e = e->semantic(sc);
e = e->ctfeSemantic(sc);
e = resolveProperties(sc, e);
e = e->ctfeInterpret();
(*args)[0] = e;
Expand All @@ -1050,7 +1050,7 @@ void PragmaDeclaration::semantic(Scope *sc)
for (size_t i = 0; i < args->dim; i++)
{
Expression *e = (*args)[i];
e = e->semantic(sc);
e = e->ctfeSemantic(sc);
e = resolveProperties(sc, e);
e = e->ctfeInterpret();
if (i == 0)
Expand Down Expand Up @@ -1428,7 +1428,7 @@ int CompileDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum)
void CompileDeclaration::compileIt(Scope *sc)
{
//printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars());
exp = exp->semantic(sc);
exp = exp->ctfeSemantic(sc);
exp = resolveProperties(sc, exp);
exp = exp->ctfeInterpret();
StringExp *se = exp->toString();
Expand Down
2 changes: 1 addition & 1 deletion src/cond.c
Expand Up @@ -259,7 +259,7 @@ int StaticIfCondition::include(Scope *sc, ScopeDsymbol *s)
sc = sc->push(sc->scopesym);
sc->sd = s; // s gets any addMember()
sc->flags |= SCOPEstaticif;
Expression *e = exp->semantic(sc);
Expression *e = exp->ctfeSemantic(sc);
e = resolveProperties(sc, e);
sc->pop();
if (!e->type->checkBoolean())
Expand Down
12 changes: 11 additions & 1 deletion src/declaration.c
Expand Up @@ -851,6 +851,11 @@ void VarDeclaration::semantic(Scope *sc)
if (!type)
{ inuse++;

// Infering the type requires running semantic,
// so mark the scope as ctfe if required
if (storage_class & (STCmanifest | STCstatic))
sc->needctfe++;

//printf("inferring type for %s with init %s\n", toChars(), init->toChars());
ArrayInitializer *ai = init->isArrayInitializer();
if (ai)
Expand All @@ -872,6 +877,8 @@ void VarDeclaration::semantic(Scope *sc)
else
type = init->inferType(sc);

if (storage_class & (STCmanifest | STCstatic))
sc->needctfe--;
// type = type->semantic(loc, sc);

inuse--;
Expand Down Expand Up @@ -1585,7 +1592,10 @@ void VarDeclaration::semantic(Scope *sc)
{
Expression *exp;
exp = ei->exp->syntaxCopy();
exp = exp->semantic(sc);
if (isDataseg() || (storage_class & STCmanifest))
exp = exp->ctfeSemantic(sc);
else
exp = exp->semantic(sc);
exp = resolveProperties(sc, exp);
Type *tb = type->toBasetype();
Type *ti = exp->type->toBasetype();
Expand Down
14 changes: 7 additions & 7 deletions src/enum.c
Expand Up @@ -252,7 +252,7 @@ void EnumDeclaration::semantic(Scope *sc)
if (e)
{
assert(e->dyncast() == DYNCAST_EXPRESSION);
e = e->semantic(sce);
e = e->ctfeSemantic(sce);
e = e->ctfeInterpret();
if (memtype)
{
Expand Down Expand Up @@ -299,30 +299,30 @@ void EnumDeclaration::semantic(Scope *sc)
if (!emax)
{
emax = t->getProperty(0, Id::max, 0);
emax = emax->semantic(sce);
emax = emax->ctfeSemantic(sce);
emax = emax->ctfeInterpret();
}

// Set value to (elast + 1).
// But first check that (elast != t.max)
assert(elast);
e = new EqualExp(TOKequal, em->loc, elast, emax);
e = e->semantic(sce);
e = e->ctfeSemantic(sce);
e = e->ctfeInterpret();
if (e->toInteger())
error("overflow of enum value %s", elast->toChars());

// Now set e to (elast + 1)
e = new AddExp(em->loc, elast, new IntegerExp(em->loc, 1, Type::tint32));
e = e->semantic(sce);
e = e->ctfeSemantic(sce);
e = e->castTo(sce, elast->type);
e = e->ctfeInterpret();

if (t->isfloating())
{
// Check that e != elast (not always true for floats)
Expression *etest = new EqualExp(TOKequal, em->loc, e, elast);
etest = etest->semantic(sce);
etest = etest->ctfeSemantic(sce);
etest = etest->ctfeInterpret();
if (etest->toInteger())
error("enum member %s has inexact value, due to loss of precision", em->toChars());
Expand Down Expand Up @@ -361,13 +361,13 @@ void EnumDeclaration::semantic(Scope *sc)

// Compute if(e < minval)
ec = new CmpExp(TOKlt, em->loc, e, minval);
ec = ec->semantic(sce);
ec = ec->ctfeSemantic(sce);
ec = ec->ctfeInterpret();
if (ec->toInteger())
minval = e;

ec = new CmpExp(TOKgt, em->loc, e, maxval);
ec = ec->semantic(sce);
ec = ec->ctfeSemantic(sce);
ec = ec->ctfeInterpret();
if (ec->toInteger())
maxval = e;
Expand Down
71 changes: 58 additions & 13 deletions src/expression.c
Expand Up @@ -1537,6 +1537,29 @@ Expression *Expression::trySemantic(Scope *sc)
return e;
}

/**********************************
* Shortcut to run semantic with purity and
* safety checking disabled for the immediate
* expressions
*/

Expression *Expression::ctfeSemantic(Scope *sc)
{
if (sc)
{
assert(sc->needctfe >= 0);
sc->needctfe++;
Expression *e = semantic(sc);
sc->needctfe--;
assert(sc->needctfe >= 0);
return e;
}
else
{
return semantic(sc);
}
}

void Expression::print()
{
fprintf(stdmsg, "%s\n", toChars());
Expand Down Expand Up @@ -1876,7 +1899,8 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
// If the caller has a pure parent, then either the called func must be pure,
// OR, they must have the same pure parent.
if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now
!f->isPure() && calledparent != outerfunc)
!f->isPure() && calledparent != outerfunc &&
!sc->needctfe)
{
if (outerfunc->setImpure())
error("pure function '%s' cannot call impure function '%s'",
Expand Down Expand Up @@ -1977,6 +2001,7 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v, Expression *ethis)
void Expression::checkSafety(Scope *sc, FuncDeclaration *f)
{
if (sc->func && !sc->intypeof &&
!(sc->needctfe) &&
!f->isSafe() && !f->isTrusted())
{
if (sc->func->setUnsafe())
Expand Down Expand Up @@ -4968,7 +4993,10 @@ Expression *NewAnonClassExp::semantic(Scope *sc)
#endif

Expression *d = new DeclarationExp(loc, cd);
int needctfe = sc->needctfe;
sc->needctfe = 0;
d = d->semantic(sc);
sc->needctfe = needctfe;

Expression *n = new NewExp(loc, thisexp, newargs, cd->type, arguments);

Expand Down Expand Up @@ -5392,6 +5420,11 @@ Expression *FuncExp::semantic(Scope *sc)
printf("FuncExp::semantic(%s)\n", toChars());
if (fd->treq) printf(" treq = %s\n", fd->treq->toChars());
#endif
Expression *e = this;

int needctfe = sc->needctfe;
sc->needctfe = 0;

if (!type || type == Type::tvoid)
{
/* fd->treq might be incomplete type,
Expand Down Expand Up @@ -5421,10 +5454,9 @@ Expression *FuncExp::semantic(Scope *sc)
td->semantic(sc);
type = Type::tvoid; // temporary type

if (!fd->treq) // defer type determination
return this;

return inferType(fd->treq);
if (fd->treq) // defer type determination
e = inferType(fd->treq);
goto Ldone;
}

unsigned olderrors = global.errors;
Expand Down Expand Up @@ -5479,7 +5511,9 @@ Expression *FuncExp::semantic(Scope *sc)
}
fd->tookAddressOf++;
}
return this;
Ldone:
sc->needctfe = needctfe;
return e;
}

// used from CallExp::semantic()
Expand Down Expand Up @@ -6563,7 +6597,7 @@ Expression *CompileExp::semantic(Scope *sc)
#if LOGSEMANTIC
printf("CompileExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = e1->ctfeSemantic(sc);
e1 = resolveProperties(sc, e1);
if (e1->op == TOKerror)
return e1;
Expand Down Expand Up @@ -6616,7 +6650,7 @@ Expression *FileExp::semantic(Scope *sc)
#if LOGSEMANTIC
printf("FileExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = e1->ctfeSemantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->ctfeInterpret();
if (e1->op != TOKstring)
Expand Down Expand Up @@ -8371,12 +8405,12 @@ Expression *CallExp::semantic(Scope *sc)
return new ErrorExp();
}

if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug))
if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug) && !sc->needctfe)
{
if (sc->func->setImpure())
error("pure function '%s' cannot call impure %s '%s'", sc->func->toPrettyChars(), p, e1->toChars());
}
if (sc->func && tf->trust <= TRUSTsystem)
if (sc->func && tf->trust <= TRUSTsystem && !sc->needctfe)
{
if (sc->func->setUnsafe())
error("safe function '%s' cannot call system %s '%s'", sc->func->toPrettyChars(), p, e1->toChars());
Expand Down Expand Up @@ -9557,14 +9591,22 @@ Expression *SliceExp::semantic(Scope *sc)
}

if (lwr)
{ lwr = lwr->semantic(sc2);
{
if (t->ty == Ttuple)
lwr = lwr->ctfeSemantic(sc2);
else
lwr = lwr->semantic(sc2);
lwr = resolveProperties(sc2, lwr);
lwr = lwr->implicitCastTo(sc2, Type::tsize_t);
if (lwr->type == Type::terror)
goto Lerr;
}
if (upr)
{ upr = upr->semantic(sc2);
{
if (t->ty == Ttuple)
upr = upr->ctfeSemantic(sc2);
else
upr = upr->semantic(sc2);
upr = resolveProperties(sc2, upr);
upr = upr->implicitCastTo(sc2, Type::tsize_t);
if (upr->type == Type::terror)
Expand Down Expand Up @@ -10038,7 +10080,10 @@ Expression *IndexExp::semantic(Scope *sc)
sc = sc->push(sym);
}

e2 = e2->semantic(sc);
if (t1->ty == Ttuple)
e2 = e2->ctfeSemantic(sc);
else
e2 = e2->semantic(sc);
e2 = resolveProperties(sc, e2);
if (e2->type == Type::terror)
goto Lerr;
Expand Down
1 change: 1 addition & 0 deletions src/expression.h
Expand Up @@ -113,6 +113,7 @@ struct Expression : Object
virtual int apply(apply_fp_t fp, void *param);
virtual Expression *semantic(Scope *sc);
Expression *trySemantic(Scope *sc);
Expression *ctfeSemantic(Scope *sc);

int dyncast() { return DYNCAST_EXPRESSION; } // kludge for template.isExpression()

Expand Down
6 changes: 3 additions & 3 deletions src/iasm.c
Expand Up @@ -3671,7 +3671,7 @@ STATIC code *asm_db_parse(OP *pop)

case TOKidentifier:
{ Expression *e = new IdentifierExp(asmstate.loc, asmtok->ident);
e = e->semantic(asmstate.sc);
e = e->ctfeSemantic(asmstate.sc);
e = e->ctfeInterpret();
if (e->op == TOKint64)
{ dt.ul = e->toInteger();
Expand Down Expand Up @@ -3747,7 +3747,7 @@ int asm_getnum()
Expression *e;

e = new IdentifierExp(asmstate.loc, asmtok->ident);
e = e->semantic(asmstate.sc);
e = e->ctfeSemantic(asmstate.sc);
e = e->ctfeInterpret();
i = e->toInteger();
v = (int) i;
Expand Down Expand Up @@ -4458,7 +4458,7 @@ STATIC OPND *asm_primary_exp()
break;
}
}
e = e->semantic(asmstate.sc);
e = e->ctfeSemantic(asmstate.sc);
e = e->ctfeInterpret();
if (e->isConst())
{
Expand Down
7 changes: 5 additions & 2 deletions src/init.c
Expand Up @@ -558,7 +558,7 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn
{
Expression *idx = index[i];
if (idx)
{ idx = idx->semantic(sc);
{ idx = idx->ctfeSemantic(sc);
idx = idx->ctfeInterpret();
index[i] = idx;
length = idx->toInteger();
Expand Down Expand Up @@ -942,7 +942,10 @@ bool arrayHasNonConstPointers(Expressions *elems)
Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret)
{
//printf("ExpInitializer::semantic(%s), type = %s\n", exp->toChars(), t->toChars());
exp = exp->semantic(sc);
if (needInterpret)
exp = exp->ctfeSemantic(sc);
else
exp = exp->semantic(sc);
exp = resolveProperties(sc, exp);
if (exp->op == TOKerror)
return this;
Expand Down

0 comments on commit 42d1af1

Please sign in to comment.