Skip to content

Commit

Permalink
Merge pull request #3625 from 9rnsr/fix6359
Browse files Browse the repository at this point in the history
Issue 6359 - Pure/@safe-inference should not be affected by __traits(compiles)
  • Loading branch information
WalterBright committed Jun 6, 2014
2 parents d2347f6 + 94c11fd commit e051d11
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 70 deletions.
1 change: 1 addition & 0 deletions src/declaration.h
Expand Up @@ -674,6 +674,7 @@ class FuncDeclaration : public Declaration
bool hasOverloads();
PURE isPure();
PURE isPureBypassingInference();
bool isPureBypassingInferenceX();
bool setImpure();
bool isSafe();
bool isSafeBypassingInference();
Expand Down
2 changes: 1 addition & 1 deletion src/doc.c
Expand Up @@ -2165,7 +2165,7 @@ bool isReservedName(utf8_t *str, size_t len)
static const char *table[] = {
"__ctor", "__dtor", "__cpctor", "__postblit", "__invariant", "__unitTest",
"__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result",
"__returnLabel", "__vptr", "__monitor", "__xopEquals", "__xopCmp",
"__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp",
"__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
"__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
"__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", "__va_argsave_t",
Expand Down
142 changes: 83 additions & 59 deletions src/expression.c
Expand Up @@ -2256,50 +2256,58 @@ void Expression::checkDeprecated(Scope *sc, Dsymbol *s)
*/
void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
{
if (sc->func && sc->func != f && sc->intypeof != 1 && !(sc->flags & (SCOPEctfe | SCOPEdebug)))
{
/* Given:
* void f()
* { pure void g()
* {
* void h()
* {
* void i() { }
* }
* }
* }
* g() can call h() but not f()
* i() can call h() and g() but not f()
*/
if (!sc->func)
return;
if (sc->func == f)
return;
if (sc->intypeof == 1)
return;
if (sc->flags & (SCOPEctfe | SCOPEdebug))
return;

// Find the closest pure parent of the calling function
FuncDeclaration *outerfunc = sc->func;
while ( outerfunc->toParent2() &&
!outerfunc->isPureBypassingInference() &&
outerfunc->toParent2()->isFuncDeclaration())
{
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->type->ty == Terror)
return;
}
/* Given:
* void f()
* { pure void g()
* {
* void h()
* {
* void i() { }
* }
* }
* }
* g() can call h() but not f()
* i() can call h() and g() but not f()
*/

FuncDeclaration *calledparent = f;
while ( calledparent->toParent2() &&
!calledparent->isPureBypassingInference() &&
calledparent->toParent2()->isFuncDeclaration())
{
calledparent = calledparent->toParent2()->isFuncDeclaration();
if (calledparent->type->ty == Terror)
return;
}
// Find the closest pure parent of the calling function
FuncDeclaration *outerfunc = sc->func;
while ( outerfunc->toParent2() &&
!outerfunc->isPureBypassingInference() &&
outerfunc->toParent2()->isFuncDeclaration())
{
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->type->ty == Terror)
return;
}

// If the caller has a pure parent, then either the called func must be pure,
// OR, they must have the same pure parent.
if (!f->isPure() && calledparent != outerfunc)
FuncDeclaration *calledparent = f;
while ( calledparent->toParent2() &&
!calledparent->isPureBypassingInference() &&
calledparent->toParent2()->isFuncDeclaration())
{
calledparent = calledparent->toParent2()->isFuncDeclaration();
if (calledparent->type->ty == Terror)
return;
}

// If the caller has a pure parent, then either the called func must be pure,
// OR, they must have the same pure parent.
if (!f->isPure() && calledparent != outerfunc)
{
if (sc->flags & SCOPEcompile ? outerfunc->isPureBypassingInferenceX() : outerfunc->setImpure())
{
if (outerfunc->setImpure())
error("pure function '%s' cannot call impure function '%s'",
outerfunc->toPrettyChars(), f->toPrettyChars());
error("pure function '%s' cannot call impure function '%s'",
outerfunc->toPrettyChars(), f->toPrettyChars());
}
}
}
Expand All @@ -2318,10 +2326,8 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)
return;
if (sc->intypeof == 1)
return; // allow violations inside typeof(expression)
if (sc->flags & SCOPEdebug)
return; // allow violations inside debug conditionals
if (sc->flags & SCOPEctfe)
return; // allow violations inside compile-time evaluated expressions
if (sc->flags & (SCOPEctfe | SCOPEdebug))
return; // allow violations inside compile-time evaluated expressions and debug conditionals
if (v->ident == Id::ctfe)
return; // magic variable never violates pure and safe
if (v->isImmutable())
Expand All @@ -2334,21 +2340,24 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)

if (v->isDataseg())
{
// Bugzilla 7533: Accessing implicit generated __gate is pure.
if (v->ident == Id::gate)
return;

/* Accessing global mutable state.
* Therefore, this function and all its immediately enclosing
* functions must be pure.
*/
bool msg = false;
for (Dsymbol *s = sc->func; s; s = s->toParent2())
{
FuncDeclaration *ff = s->isFuncDeclaration();
if (!ff)
break;
// Accessing implicit generated __gate is pure.
if (ff->setImpure() && !msg && strcmp(v->ident->toChars(), "__gate"))
{ error("pure function '%s' cannot access mutable static data '%s'",
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure())
{
error("pure function '%s' cannot access mutable static data '%s'",
sc->func->toPrettyChars(), v->toChars());
msg = true; // only need the innermost message
break;
}
}
}
Expand Down Expand Up @@ -2396,8 +2405,9 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)
FuncDeclaration *ff = s->isFuncDeclaration();
if (!ff)
break;
if (ff->setImpure())
{ error("pure nested function '%s' cannot access mutable data '%s'",
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure())
{
error("pure nested function '%s' cannot access mutable data '%s'",
ff->toChars(), v->toChars());
break;
}
Expand All @@ -2416,11 +2426,18 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)

void Expression::checkSafety(Scope *sc, FuncDeclaration *f)
{
if (sc->func && sc->func != f && sc->intypeof != 1 &&
!(sc->flags & SCOPEctfe) &&
!f->isSafe() && !f->isTrusted())
if (!sc->func)
return;
if (sc->func == f)
return;
if (sc->intypeof == 1)
return;
if (sc->flags & SCOPEctfe)
return;

if (!f->isSafe() && !f->isTrusted())
{
if (sc->func->setUnsafe())
if (sc->flags & SCOPEcompile ? sc->func->isSafeBypassingInference() : sc->func->setUnsafe())
{
if (loc.linnum == 0) // e.g. implicitly generated dtor
loc = sc->func->loc;
Expand All @@ -2433,11 +2450,18 @@ void Expression::checkSafety(Scope *sc, FuncDeclaration *f)

void Expression::checkNogc(Scope *sc, FuncDeclaration *f)
{
if (sc->func && sc->func != f && sc->intypeof != 1 &&
!(sc->flags & SCOPEctfe) &&
!f->isNogc())
if (!sc->func)
return;
if (sc->func == f)
return;
if (sc->intypeof == 1)
return;
if (sc->flags & SCOPEctfe)
return;

if (!f->isNogc())
{
if (sc->func->setGC())
if (sc->flags & SCOPEcompile ? sc->func->isNogcBypassingInference() : sc->func->setGC())
{
if (loc.linnum == 0) // e.g. implicitly generated dtor
loc = sc->func->loc;
Expand Down
16 changes: 10 additions & 6 deletions src/func.c
Expand Up @@ -1291,6 +1291,7 @@ void FuncDeclaration::semantic3(Scope *sc)
sc2->structalign = STRUCTALIGN_DEFAULT;
if (this->ident != Id::require && this->ident != Id::ensure)
sc2->flags = sc->flags & ~SCOPEcontract;
sc2->flags &= ~SCOPEcompile;
sc2->tf = NULL;
sc2->os = NULL;
sc2->noctor = 0;
Expand Down Expand Up @@ -3659,6 +3660,11 @@ PURE FuncDeclaration::isPureBypassingInference()
return isPure();
}

bool FuncDeclaration::isPureBypassingInferenceX()
{
return !(flags & FUNCFLAGpurityInprocess) && isPure() != PUREimpure;
}

/**************************************
* The function is doing something impure,
* so mark it as impure.
Expand Down Expand Up @@ -4849,13 +4855,12 @@ void StaticCtorDeclaration::semantic(Scope *sc)
* Note that this is not thread safe; should not have threads
* during static construction.
*/
Identifier *id = Lexer::idPool("__gate");
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
v->storage_class = STCtemp | (isSharedStaticCtorDeclaration() ? STCstatic : STCtls);
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), id);
Expression *e = new IdentifierExp(Loc(), v->ident);
e = new AddAssignExp(Loc(), e, new IntegerExp(1));
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1));
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL);
Expand Down Expand Up @@ -4981,13 +4986,12 @@ void StaticDtorDeclaration::semantic(Scope *sc)
* Note that this is not thread safe; should not have threads
* during static destruction.
*/
Identifier *id = Lexer::idPool("__gate");
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL);
VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL);
v->storage_class = STCtemp | (isSharedStaticDtorDeclaration() ? STCstatic : STCtls);
Statements *sa = new Statements();
Statement *s = new ExpStatement(Loc(), v);
sa->push(s);
Expression *e = new IdentifierExp(Loc(), id);
Expression *e = new IdentifierExp(Loc(), v->ident);
e = new AddAssignExp(Loc(), e, new IntegerExp(-1));
e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0));
s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL);
Expand Down
1 change: 1 addition & 0 deletions src/idgen.c
Expand Up @@ -75,6 +75,7 @@ Msgtable msgtable[] =
{ "q" },
{ "__vptr" },
{ "__monitor" },
{ "gate", "__gate" },

{ "TypeInfo" },
{ "TypeInfo_Class" },
Expand Down
5 changes: 5 additions & 0 deletions src/traits.c
Expand Up @@ -901,6 +901,11 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc)
ex = ex->semantic(sc2);
ex = resolvePropertiesOnly(sc2, ex);
ex = ex->optimize(WANTvalue);
if (sc2->func && sc2->func->type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)sc2->func->type;
canThrow(ex, sc2->func, tf->isnothrow);
}
ex = checkGC(sc2, ex);
if (ex->op == TOKerror)
err = true;
Expand Down
63 changes: 63 additions & 0 deletions test/compilable/testInference.d
Expand Up @@ -84,6 +84,69 @@ void test6351()
alias bug6351!(deleg6351) baz6531;
}

/***************************************************/
// 6359

void impure6359() nothrow @safe @nogc {}
void throwable6359() pure @safe @nogc {}
void system6359() pure nothrow @nogc {}
void gcable6359() pure nothrow @safe {}

int global6359;

void f6359() pure nothrow @safe @nogc
{
static assert(!__traits(compiles, impure6359()));
static assert(!__traits(compiles, throwable6359()));
static assert(!__traits(compiles, system6359()));
static assert(!__traits(compiles, gcable6359()));
static assert(!__traits(compiles, global6359++));

//static assert(!__traits(compiles, { impure6359(); }())); // BUG: blocked by issue 9148.
static assert(!__traits(compiles, { throwable6359(); }()));
static assert(!__traits(compiles, { system6359(); }()));
static assert(!__traits(compiles, { gcable6359(); }()));
static assert(!__traits(compiles, { global6359++; }()));
}

void g6359()() pure nothrow @safe @nogc
{
static assert(!__traits(compiles, impure6359()));
static assert(!__traits(compiles, throwable6359()));
static assert(!__traits(compiles, system6359()));
static assert(!__traits(compiles, gcable6359()));
static assert(!__traits(compiles, global6359++));

//static assert(!__traits(compiles, { impure6359(); }())); // BUG: blocked by issue 9148.
static assert(!__traits(compiles, { throwable6359(); }()));
static assert(!__traits(compiles, { system6359(); }()));
static assert(!__traits(compiles, { gcable6359(); }()));
static assert(!__traits(compiles, { global6359++; }()));
}

// attribute inference is not affected by the expressions inside __traits(compiles)
void h6359()()
{
static assert( __traits(compiles, impure6359()));
static assert( __traits(compiles, throwable6359()));
static assert( __traits(compiles, system6359()));
static assert( __traits(compiles, gcable6359()));
static assert( __traits(compiles, global6359++));

static assert( __traits(compiles, { impure6359(); }()));
static assert( __traits(compiles, { throwable6359(); }()));
static assert( __traits(compiles, { system6359(); }()));
static assert( __traits(compiles, { gcable6359(); }()));
//static assert( __traits(compiles, { global6359++; }())); // BUG: blocked by issue 9148.
}

void test6359() pure nothrow @safe @nogc
{
f6359();
g6359();
h6359();
}

/***************************************************/
// 7017

Expand Down
6 changes: 2 additions & 4 deletions test/runnable/statictor.d
Expand Up @@ -38,10 +38,8 @@ static this() nothrow pure @safe
int* p;
static assert(!__traits(compiles, ++p));
static assert(!__traits(compiles, ++global6677));
// BUG (not related to this):
//auto throwit = { throw new Exception("sup"); };
//static assert(!__traits(compiles, throwit() )); // Should pass
//throwit() // This fails, but __traits(compiles, ...) claims it compiles
auto throwit = { throw new Exception("sup"); };
static assert(!__traits(compiles, throwit() ));
}

shared static this() nothrow pure @safe
Expand Down

0 comments on commit e051d11

Please sign in to comment.