Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions src/declaration.c
Original file line number Diff line number Diff line change
Expand Up @@ -1728,7 +1728,7 @@ void VarDeclaration::checkCtorConstInit()
* rather than the current one.
*/

void VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
bool VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
//printf("VarDeclaration::checkNestedReference() %s\n", toChars());
if (parent && !isDataseg() && parent != sc->parent &&
Expand Down Expand Up @@ -1763,7 +1763,41 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
//printf("\tfdthis = %s\n", fdthis->toChars());

if (loc.filename)
fdthis->getLevel(loc, sc, fdv);
{
int lv = fdthis->getLevel(loc, sc, fdv);
if (lv == -2) // error
return false;
if (lv > 0 &&
fdv->isPureBypassingInference() >= PUREweak &&
fdthis->isPureBypassingInference() == PUREfwdref &&
fdthis->isInstantiated())
{
/* Bugzilla 9148 and 14039:
* void foo() pure {
* int x;
* void bar()() { // default is impure
* x = 1; // access to enclosing pure function context
* // means that bar should have weak purity.
* }
* }
*/
fdthis->flags &= ~FUNCFLAGpurityInprocess;
if (fdthis->type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)fdthis->type;
if (tf->deco)
{
tf = (TypeFunction *)tf->copy();
tf->purity = PUREfwdref;
tf->deco = NULL;
tf->deco = tf->merge()->deco;
}
else
tf->purity = PUREfwdref;
fdthis->type = tf;
}
}
}

// Function literals from fdthis to fdv must be delegates
for (Dsymbol *s = fdthis; s && s != fdv; s = s->toParent2())
Expand All @@ -1772,20 +1806,6 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
{
fld->tok = TOKdelegate;
#if 0
/* This is necessary to avoid breaking tests for 8751 & 8793.
* See: compilable/testInference.d
*/
// if is a mutable variable or
// has any mutable indirections or
// does not belong to pure function
if (type->isMutable() ||
!type->implicitConvTo(type->immutableOf()) ||
!fdv->isPureBypassingInference())
{
fld->setImpure(); // Bugzilla 9415
}
#endif
}
}

Expand All @@ -1806,10 +1826,14 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
//printf("var %s in function %s is nested ref\n", toChars(), fdv->toChars());
// __dollar creates problems because it isn't a real variable Bugzilla 3326
if (ident == Id::dollar)
{
::error(loc, "cannnot use $ inside a function literal");
return false;
}
}
}
}
return true;
}

/****************************
Expand Down
5 changes: 2 additions & 3 deletions src/declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class VarDeclaration : public Declaration
ExpInitializer *getExpInitializer();
Expression *getConstInitializer(bool needFullType = true);
void checkCtorConstInit();
void checkNestedReference(Scope *sc, Loc loc);
bool checkNestedReference(Scope *sc, Loc loc);
Dsymbol *toAlias();
// Eliminate need for dynamic_cast
VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; }
Expand Down Expand Up @@ -626,7 +626,6 @@ class FuncDeclaration : public Declaration
bool hasOverloads();
PURE isPure();
PURE isPureBypassingInference();
bool isPureBypassingInferenceX();
bool setImpure();
bool isSafe();
bool isSafeBypassingInference();
Expand All @@ -649,7 +648,7 @@ class FuncDeclaration : public Declaration
virtual bool addPostInvariant();
const char *kind();
FuncDeclaration *isUnique();
void checkNestedReference(Scope *sc, Loc loc);
bool checkNestedReference(Scope *sc, Loc loc);
bool needsClosure();
bool hasNestedFrameRefs();
void buildResultVar(Scope *sc, Type *tret);
Expand Down
36 changes: 24 additions & 12 deletions src/delegatize.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

bool walkPostorder(Expression *e, StoppableVisitor *v);
void lambdaSetParent(Expression *e, Scope *sc);
void lambdaCheckForNestedRef(Expression *e, Scope *sc);
bool lambdaCheckForNestedRef(Expression *e, Scope *sc);

Expression *toDelegate(Expression *e, Scope *sc)
{
Expand All @@ -50,9 +50,12 @@ Expression *toDelegate(Expression *e, Scope *sc)
sc = sc->push();
sc->parent = fld; // set current function to be the delegate
lambdaSetParent(e, sc);
lambdaCheckForNestedRef(e, sc);
bool r = lambdaCheckForNestedRef(e, sc);
sc = sc->pop();

if (!r)
return new ErrorExp();

Statement *s;
if (t->ty == Tvoid)
s = new ExpStatement(loc, e);
Expand Down Expand Up @@ -110,14 +113,20 @@ void lambdaSetParent(Expression *e, Scope *sc)

/*******************************************
* Look for references to variables in a scope enclosing the new function literal.
* Returns false if error occurs.
*/
void lambdaCheckForNestedRef(Expression *e, Scope *sc)
bool lambdaCheckForNestedRef(Expression *e, Scope *sc)
{
class LambdaCheckForNestedRef : public StoppableVisitor
{
Scope *sc;
public:
LambdaCheckForNestedRef(Scope *sc) : sc(sc) {}
Scope *sc;
bool result;

LambdaCheckForNestedRef(Scope *sc)
: sc(sc), result(true)
{
}

void visit(Expression *)
{
Expand All @@ -127,29 +136,31 @@ void lambdaCheckForNestedRef(Expression *e, Scope *sc)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
v->checkNestedReference(sc, Loc());
result = v->checkNestedReference(sc, Loc());
}

void visit(VarExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
v->checkNestedReference(sc, Loc());
result = v->checkNestedReference(sc, Loc());
}

void visit(ThisExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
v->checkNestedReference(sc, Loc());
result = v->checkNestedReference(sc, Loc());
}

void visit(DeclarationExp *e)
{
VarDeclaration *v = e->declaration->isVarDeclaration();
if (v)
{
v->checkNestedReference(sc, Loc());
result = v->checkNestedReference(sc, Loc());
if (!result)
return;

/* Some expressions cause the frontend to create a temporary.
* For example, structs with cpctors replace the original
Expand All @@ -163,13 +174,14 @@ void lambdaCheckForNestedRef(Expression *e, Scope *sc)
if (v->init && v->init->isExpInitializer())
{
Expression *ie = v->init->toExpression();
lambdaCheckForNestedRef(ie, sc);
result = lambdaCheckForNestedRef(ie, sc);
}
}
}
};

LambdaCheckForNestedRef lcnr(sc);
walkPostorder(e, &lcnr);
LambdaCheckForNestedRef v(sc);
walkPostorder(e, &v);
return v.result;
}

101 changes: 73 additions & 28 deletions src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -2384,31 +2384,58 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f)

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

if (outerfunc->isInstantiated())
{
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->type->ty == Terror)
return;
// The attributes of outerfunc should be inferred from the call of f.
}

FuncDeclaration *calledparent = f;
while ( calledparent->toParent2() &&
!calledparent->isPureBypassingInference() &&
calledparent->toParent2()->isFuncDeclaration())
else if (f->isFuncLiteralDeclaration())
{
calledparent = calledparent->toParent2()->isFuncDeclaration();
if (calledparent->type->ty == Terror)
return;
// The attributes of f is always inferred in its declared place.
}
else
{
/* Today, static local functions are impure by default, but they cannot
* violate purity of enclosing functions.
*
* auto foo() pure { // non instantiated funciton
* static auto bar() { // static, without pure attribute
* impureFunc(); // impure call
* // Although impureFunc is called inside bar, f(= impureFunc)
* // is not callable inside pure outerfunc(= foo <- bar).
* }
*
* bar();
* // Although bar is called inside foo, f(= bar) is callable
* // bacause calledparent(= foo) is same with outerfunc(= foo).
* }
*/

while (outerfunc->toParent2() &&
outerfunc->isPureBypassingInference() == PUREimpure &&
outerfunc->toParent2()->isFuncDeclaration())
{
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->type->ty == Terror)
return;
}
while (calledparent->toParent2() &&
calledparent->isPureBypassingInference() == PUREimpure &&
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)
{
FuncDeclaration *ff = outerfunc;
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure())
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
{
error("pure function '%s' cannot call impure function '%s'",
ff->toPrettyChars(), f->toPrettyChars());
Expand Down Expand Up @@ -2453,7 +2480,7 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)
* functions must be pure.
*/
FuncDeclaration *ff = sc->func;
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure())
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
{
error("pure function '%s' cannot access mutable static data '%s'",
ff->toPrettyChars(), v->toChars());
Expand All @@ -2476,6 +2503,14 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v)
*/

Dsymbol *vparent = v->toParent2();
if (FuncDeclaration *fdp = vparent->isFuncDeclaration())
{
if (!sc->func->isPureBypassingInference() && fdp->setImpure())
{
error("impure function '%s' cannot access variable '%s' declared in enclosing pure function '%s'",
sc->func->toChars(), v->toChars(), fdp->toPrettyChars());
}
}
for (Dsymbol *s = sc->func; s; s = s->toParent2())
{
if (s == vparent)
Expand Down Expand Up @@ -3439,7 +3474,8 @@ Expression *ThisExp::semantic(Scope *sc)
var = fd->vthis;
assert(var->parent);
type = var->type;
var->isVarDeclaration()->checkNestedReference(sc, loc);
if (!var->isVarDeclaration()->checkNestedReference(sc, loc))
return new ErrorExp();
if (!sc->intypeof)
sc->callSuper |= CSXthis;
return this;
Expand Down Expand Up @@ -3544,7 +3580,8 @@ Expression *SuperExp::semantic(Scope *sc)
type = type->castMod(var->type->mod);
}

var->isVarDeclaration()->checkNestedReference(sc, loc);
if (!var->isVarDeclaration()->checkNestedReference(sc, loc))
return new ErrorExp();

if (!sc->intypeof)
sc->callSuper |= CSXsuper;
Expand Down Expand Up @@ -5053,12 +5090,16 @@ Expression *SymOffExp::semantic(Scope *sc)
//var->semantic(sc);
if (!type)
type = var->type->pointerTo();
VarDeclaration *v = var->isVarDeclaration();
if (v)
v->checkNestedReference(sc, loc);
FuncDeclaration *f = var->isFuncDeclaration();
if (f)
f->checkNestedReference(sc, loc);
if (VarDeclaration *v = var->isVarDeclaration())
{
if (!v->checkNestedReference(sc, loc))
return new ErrorExp();
}
else if (FuncDeclaration *f = var->isFuncDeclaration())
{
if (!f->checkNestedReference(sc, loc))
return new ErrorExp();
}
return this;
}

Expand Down Expand Up @@ -5125,11 +5166,13 @@ Expression *VarExp::semantic(Scope *sc)
if (VarDeclaration *vd = var->isVarDeclaration())
{
hasOverloads = 0;
vd->checkNestedReference(sc, loc);
if (!vd->checkNestedReference(sc, loc))
return new ErrorExp();
}
else if (FuncDeclaration *fd = var->isFuncDeclaration())
{
fd->checkNestedReference(sc, loc);
if (!fd->checkNestedReference(sc, loc))
return new ErrorExp();
}
else if (OverDeclaration *od = var->isOverDeclaration())
{
Expand Down Expand Up @@ -8733,7 +8776,8 @@ Expression *CallExp::semantic(Scope *sc)
checkPurity(sc, f);
checkSafety(sc, f);
checkNogc(sc, f);
f->checkNestedReference(sc, loc);
if (!f->checkNestedReference(sc, loc))
return new ErrorExp();
}
else if (sc->func && sc->intypeof != 1 && !(sc->flags & SCOPEctfe))
{
Expand Down Expand Up @@ -8820,7 +8864,8 @@ Expression *CallExp::semantic(Scope *sc)
checkPurity(sc, f);
checkSafety(sc, f);
checkNogc(sc, f);
f->checkNestedReference(sc, loc);
if (!f->checkNestedReference(sc, loc))
return new ErrorExp();
accessCheck(loc, sc, NULL, f);

ethis = NULL;
Expand Down
Loading