Skip to content

Commit dfb683f

Browse files
committed
add purity inference
1 parent 5363aed commit dfb683f

File tree

6 files changed

+156
-28
lines changed

6 files changed

+156
-28
lines changed

src/constfold.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,13 @@ Expression *Cast(Type *type, Type *to, Expression *e1)
10771077
to->implicitConvTo(e1->type) >= MATCHconst)
10781078
return expType(to, e1);
10791079

1080+
// Allow covariant converions of delegates
1081+
// (Perhaps implicit conversion from pure to impure should be a MATCHconst,
1082+
// then we wouldn't need this extra check.)
1083+
if (e1->type->toBasetype()->ty == Tdelegate &&
1084+
e1->type->implicitConvTo(to) == MATCHconvert)
1085+
return expType(to, e1);
1086+
10801087
Type *tb = to->toBasetype();
10811088
Type *typeb = type->toBasetype();
10821089

src/declaration.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,6 @@ struct FuncDeclaration : Declaration
542542
Loc endloc; // location of closing curly bracket
543543
int vtblIndex; // for member functions, index into vtbl[]
544544
int naked; // !=0 if naked
545-
int inlineAsm; // !=0 if has inline assembler
546545
ILS inlineStatus;
547546
int inlineNest; // !=0 if nested inline
548547
int cantInterpret; // !=0 if cannot interpret function
@@ -577,6 +576,9 @@ struct FuncDeclaration : Declaration
577576
Dsymbols closureVars; // local variables in this function
578577
// which are referenced by nested
579578
// functions
579+
580+
unsigned flags;
581+
#define FUNCFLAGpurityInprocess 1 // working on determining purity
580582
#else
581583
int nestedFrameRef; // !=0 if nested variables referenced
582584
#endif
@@ -616,6 +618,7 @@ struct FuncDeclaration : Declaration
616618
int isCodeseg();
617619
int isOverloadable();
618620
enum PURE isPure();
621+
bool setImpure();
619622
int isSafe();
620623
int isTrusted();
621624
virtual int isNested();

src/expression.c

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,11 @@ void Expression::checkDeprecated(Scope *sc, Dsymbol *s)
12211221
}
12221222

12231223
#if DMDV2
1224+
/*********************************************
1225+
* Calling function f.
1226+
* Check the purity, i.e. if we're in a pure function
1227+
* we can only call other pure functions.
1228+
*/
12241229
void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
12251230
{
12261231
#if 1
@@ -1256,12 +1261,14 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
12561261
}
12571262
// If the caller has a pure parent, then either the called func must be pure,
12581263
// OR, they must have the same pure parent.
1259-
if (outerfunc->isPure() && !sc->intypeof &&
1264+
if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now
1265+
!sc->intypeof &&
12601266
!(sc->flags & SCOPEdebug) &&
12611267
!(f->isPure() || (calledparent == outerfunc)))
12621268
{
1263-
error("pure function '%s' cannot call impure function '%s'",
1264-
outerfunc->toChars(), f->toChars());
1269+
if (outerfunc->setImpure())
1270+
error("pure function '%s' cannot call impure function '%s'",
1271+
outerfunc->toChars(), f->toChars());
12651272
}
12661273
}
12671274
#else
@@ -4405,21 +4412,83 @@ Expression *VarExp::semantic(Scope *sc)
44054412
VarDeclaration *v = var->isVarDeclaration();
44064413
if (v)
44074414
{
4408-
#if 0
4409-
if ((v->isConst() || v->isImmutable()) &&
4410-
type->toBasetype()->ty != Tsarray && v->init)
4415+
v->checkNestedReference(sc, loc);
4416+
#if DMDV2
4417+
#if 1
4418+
/* Look for purity and safety violations when accessing variable v
4419+
* from current function.
4420+
*/
4421+
if (sc->func &&
4422+
!sc->intypeof && // allow violations inside typeof(expression)
4423+
!(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals
4424+
v->ident != Id::ctfe && // magic variable never violates pure and safe
4425+
!v->isImmutable() && // always safe and pure to access immutables...
4426+
!(v->storage_class & STCmanifest) // ...or manifest constants
4427+
)
44114428
{
4412-
ExpInitializer *ei = v->init->isExpInitializer();
4413-
if (ei)
4429+
if (v->isDataseg())
4430+
{
4431+
/* Accessing global mutable state.
4432+
* Therefore, this function and all its immediately enclosing
4433+
* functions must be pure.
4434+
*/
4435+
bool msg = FALSE;
4436+
for (Dsymbol *s = sc->func; s; s = s->toParent2())
4437+
{
4438+
FuncDeclaration *ff = s->isFuncDeclaration();
4439+
if (!ff)
4440+
break;
4441+
if (ff->setImpure() && !msg)
4442+
{ error("pure function '%s' cannot access mutable static data '%s'",
4443+
sc->func->toChars(), v->toChars());
4444+
printf("test1\n");
4445+
msg = TRUE; // only need the innermost message
4446+
}
4447+
}
4448+
}
4449+
else
44144450
{
4415-
//ei->exp->implicitCastTo(sc, type)->print();
4416-
return ei->exp->implicitCastTo(sc, type);
4451+
/* Given:
4452+
* void f()
4453+
* { int fx;
4454+
* pure void g()
4455+
* { int gx;
4456+
* void h()
4457+
* { int hx;
4458+
* void i() { }
4459+
* }
4460+
* }
4461+
* }
4462+
* i() can modify hx and gx but not fx
4463+
*/
4464+
4465+
/* Back up until we find the parent function of v,
4466+
* requiring each function in between to be impure.
4467+
*/
4468+
Dsymbol *vparent = v->toParent2();
4469+
for (Dsymbol *s = sc->func; s; s = s->toParent2())
4470+
{
4471+
if (s == vparent)
4472+
break;
4473+
FuncDeclaration *ff = s->isFuncDeclaration();
4474+
if (!ff)
4475+
break;
4476+
if (ff->setImpure())
4477+
{ error("pure nested function '%s' cannot access mutable data '%s'",
4478+
ff->toChars(), v->toChars());
4479+
printf("test2\n");
4480+
break;
4481+
}
4482+
}
44174483
}
4484+
4485+
/* Do not allow safe functions to access __gshared data
4486+
*/
4487+
if (sc->func->isSafe() && v->storage_class & STCgshared)
4488+
error("safe function '%s' cannot access __gshared data '%s'",
4489+
sc->func->toChars(), v->toChars());
44184490
}
4419-
#endif
4420-
v->checkNestedReference(sc, loc);
4421-
#if DMDV2
4422-
#if 1
4491+
#else
44234492
if (sc->func && !sc->intypeof && !(sc->flags & SCOPEdebug))
44244493
{
44254494
/* Given:
@@ -4486,12 +4555,6 @@ Expression *VarExp::semantic(Scope *sc)
44864555
error("safe function '%s' cannot access __gshared data '%s'",
44874556
sc->func->toChars(), v->toChars());
44884557
}
4489-
#else
4490-
if (sc->func && sc->func->isPure() && !sc->intypeof)
4491-
{
4492-
if (v->isDataseg() && !v->isImmutable())
4493-
error("pure function '%s' cannot access mutable static data '%s'", sc->func->toChars(), v->toChars());
4494-
}
44954558
#endif
44964559
#endif
44974560
}
@@ -7365,9 +7428,10 @@ Expression *CallExp::semantic(Scope *sc)
73657428
{ TypeDelegate *td = (TypeDelegate *)t1;
73667429
assert(td->next->ty == Tfunction);
73677430
tf = (TypeFunction *)(td->next);
7368-
if (sc->func && sc->func->isPure() && !tf->purity && !(sc->flags & SCOPEdebug))
7431+
if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug))
73697432
{
7370-
error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars());
7433+
if (sc->func->setImpure())
7434+
error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars());
73717435
}
73727436
if (sc->func && sc->func->isSafe() && tf->trust <= TRUSTsystem)
73737437
{
@@ -7379,9 +7443,10 @@ Expression *CallExp::semantic(Scope *sc)
73797443
{
73807444
Expression *e = new PtrExp(loc, e1);
73817445
t1 = ((TypePointer *)t1)->next;
7382-
if (sc->func && sc->func->isPure() && !((TypeFunction *)t1)->purity && !(sc->flags & SCOPEdebug))
7446+
if (sc->func && !((TypeFunction *)t1)->purity && !(sc->flags & SCOPEdebug))
73837447
{
7384-
error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars());
7448+
if (sc->func->setImpure())
7449+
error("pure function '%s' cannot call impure function pointer '%s'", sc->func->toChars(), e1->toChars());
73857450
}
73867451
if (sc->func && sc->func->isSafe() && !((TypeFunction *)t1)->trust <= TRUSTsystem)
73877452
{
@@ -9332,6 +9397,8 @@ Expression *AssignExp::semantic(Scope *sc)
93329397
VarDeclaration *v = new VarDeclaration(loc, aaValueType,
93339398
id, new VoidInitializer(NULL));
93349399
v->storage_class |= STCctfe;
9400+
v->semantic(sc);
9401+
v->parent = sc->parent;
93359402

93369403
Expression *de = new DeclarationExp(loc, v);
93379404
VarExp *ve = new VarExp(loc, v);

src/func.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla
6464
naked = 0;
6565
inlineStatus = ILSuninitialized;
6666
inlineNest = 0;
67-
inlineAsm = 0;
6867
cantInterpret = 0;
6968
isArrayOp = 0;
7069
semanticRun = PASSinit;
@@ -85,6 +84,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla
8584
#if DMDV2
8685
builtin = BUILTINunknown;
8786
tookAddressOf = 0;
87+
flags = 0;
8888
#endif
8989
}
9090

@@ -249,6 +249,18 @@ void FuncDeclaration::semantic(Scope *sc)
249249
linkage = sc->linkage;
250250
protection = sc->protection;
251251

252+
/* Purity for literals and function templates is determined by examining the body
253+
* of the function, unless overridden with the keyword.
254+
*/
255+
if (fbody &&
256+
f->purity == PUREimpure && // purity not specified
257+
(isFuncLiteralDeclaration() || parent->isTemplateInstance()) &&
258+
!f->hasLazyParameters()
259+
)
260+
{
261+
flags |= FUNCFLAGpurityInprocess;
262+
}
263+
252264
if (storage_class & STCscope)
253265
error("functions cannot be scope");
254266

@@ -1315,7 +1327,7 @@ void FuncDeclaration::semantic3(Scope *sc)
13151327
}
13161328
else if (!hasReturnExp && type->nextOf()->ty != Tvoid)
13171329
error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars());
1318-
else if (!inlineAsm)
1330+
else if (!(hasReturnExp & 8)) // if no inline asm
13191331
{
13201332
#if DMDV2
13211333
// Check for errors related to 'nothrow'.
@@ -1614,6 +1626,14 @@ void FuncDeclaration::semantic3(Scope *sc)
16141626
sc2->pop();
16151627
}
16161628

1629+
/* If function survived being marked as impure, then it is pure
1630+
*/
1631+
if (flags & FUNCFLAGpurityInprocess)
1632+
{
1633+
flags &= ~FUNCFLAGpurityInprocess;
1634+
f->purity = PUREfwdref;
1635+
}
1636+
16171637
if (global.gag && global.errors != nerrors)
16181638
semanticRun = PASSsemanticdone; // Ensure errors get reported again
16191639
else
@@ -2634,6 +2654,22 @@ enum PURE FuncDeclaration::isPure()
26342654
return purity;
26352655
}
26362656

2657+
/**************************************
2658+
* The function is doing something impure,
2659+
* so mark it as impure.
2660+
* If there's a purity error, return TRUE.
2661+
*/
2662+
bool FuncDeclaration::setImpure()
2663+
{
2664+
if (flags & FUNCFLAGpurityInprocess)
2665+
{
2666+
flags &= ~FUNCFLAGpurityInprocess;
2667+
}
2668+
else if (isPure())
2669+
return TRUE;
2670+
return FALSE;
2671+
}
2672+
26372673
int FuncDeclaration::isSafe()
26382674
{
26392675
assert(type->ty == Tfunction);

src/mtype.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5348,6 +5348,20 @@ Type *TypeFunction::reliesOnTident()
53485348
return next ? next->reliesOnTident() : NULL;
53495349
}
53505350

5351+
/********************************************
5352+
* Return TRUE if there are lazy parameters.
5353+
*/
5354+
bool TypeFunction::hasLazyParameters()
5355+
{
5356+
size_t dim = Parameter::dim(parameters);
5357+
for (size_t i = 0; i < dim; i++)
5358+
{ Parameter *fparam = Parameter::getNth(parameters, i);
5359+
if (fparam->storageClass & STClazy)
5360+
return TRUE;
5361+
}
5362+
return FALSE;
5363+
}
5364+
53515365
/***************************
53525366
* Examine function signature for parameter p and see if
53535367
* p can 'escape' the scope of the function.
@@ -5446,7 +5460,7 @@ MATCH TypeDelegate::implicitConvTo(Type *to)
54465460
//printf("to : %s\n", to->toChars());
54475461
if (this == to)
54485462
return MATCHexact;
5449-
#if 0 // not allowing covariant conversions because it interferes with overriding
5463+
#if 1 // not allowing covariant conversions because it interferes with overriding
54505464
if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1)
54515465
return MATCHconvert;
54525466
#endif

src/mtype.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ struct TypeFunction : TypeNext
589589
MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes);
590590
TypeInfoDeclaration *getTypeInfoDeclaration();
591591
Type *reliesOnTident();
592+
bool hasLazyParameters();
592593
#if CPP_MANGLE
593594
void toCppMangle(OutBuffer *buf, CppMangleState *cms);
594595
#endif

0 commit comments

Comments
 (0)