Nesting in pure functions
Don
nospam at nospam.com
Mon Apr 6 04:04:50 PDT 2009
Don wrote:
> Don wrote:
>> bearophile wrote:
>>> This post was originally meant for digitalmars.D.learn, but maybe it
>>> can interest more people here.
>>>
>>> Now that the D1/D2 zips have a better internal structure and don't
>>> require DMC anymore I am more free to use D2 more, so I have tried to
>>> understand how the optimization of pure functions works.
>>>
>>> So I have written this toy program that computes:
>>> ((x*x)+(x*x)) + ((x*x)+(x*x))
>>>
>>>
>>> import std.c.stdio: printf;
>>> import std.conv: toInt;
>>>
>>> pure int double_sqr(int x) {
>>> int y, z;
>>> void do_sqr() { y *= y; }
>>> y = x;
>>> do_sqr();
>>> z += y;
>>> y = x;
>>> do_sqr();
>>> z += y;
>>> return z;
>>> }
>>>
>>> void main(string[] args) {
>>> int x = args.length == 2 ? toInt(args[1]) : 10;
>>> int y = double_sqr(x) + double_sqr(x);
>>> printf("4 * x * x = %d\n", y);
>>> }
>>>
>>
>>> double_sqr() is a pure function. do_sqr() isn't a pure function, but
>>> it has no side effects outside double_sqr(), so double_sqr() is
>>> globally pure still. But the compiler (dmd v2.027) doesn't accept it
>>> (notice the strange blank line in the middle):
>>
>> There's some compiler bugs. Try:
>>
>> void do_sqr() pure { y *= y; }
>>
>> and it compiles, but generates wrong code.
>> pure void do_sqr() doesn't compile. Nothrow behaves the same.
>> Compare with bugzilla 2694.
>>
>> But this one works:
>>
>> pure int double_sqr(int x) {
>> int z;
>> int do_sqr(int y) pure { return y*y; }
>> z = do_sqr(x);
>> z += do_sqr(x);
>> return z;
>> }
> In fact, the fact that it accepts 'pure' on nested functions is probably
> a bug. Patch:
>
> In expression.c, around line 1100, you can disable the purity check if
> it's a nested function:
>
> void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
> {
> if (sc->func && sc->func->isPure() && !sc->intypeof &&
> (!f->isNested() && !f->isPure()))
>
> -----
> But, this still isn't enough, because it doesn't check the nested
> functions for purity. Rather than checking if the function is pure,
> FuncDeclaration needs 'ultimatelyPure' and 'ultimatelyNothrow' members,
> which are assigned when the declaration is encountered. Almost all
> purity checks need to be made against the 'ultimatelyPure' member. (But
> things like purity of delegates would be made from the 'pure' member
> rather than the 'ultimately pure' member).
Actually, it's much easier than I thought. Here's a patch that prevents
inner functions from calling impure external ones, and accessing static
variables. (sorry that this isn't a proper patch, I've hacked my DMD so
much by now, the line numbers would be all wrong... <g>)
around line 1100:
void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
{
if (sc->func) {
FuncDeclaration *outerfunc=sc->func;
while (outerfunc->toParent2()
&& outerfunc->toParent2()->isFuncDeclaration())
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->isPure() && !sc->intypeof && (!f->isNested()
&& !f->isPure()))
error("pure function '%s' cannot call impure function '%s'\n",
sc->func->toChars(), f->toChars());
}
}
and around line 4000, change this code:
#if DMDV2
if (sc->func && sc->func->isPure() && !sc->intypeof)
{
if (v->isDataseg() && !v->isInvariant())
error("pure function '%s' cannot access mutable static data '%s'",
sc->func->toChars(), v->toChars());
}
#endif
into:
#if DMDV2
if (sc->func) {
FuncDeclaration *outerfunc=sc->func;
while (outerfunc->toParent2() &&
outerfunc->toParent2()->isFuncDeclaration()) {
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
}
if (outerfunc->isPure() && !sc->intypeof && v->isDataseg() &&
!v->isInvariant())
error("pure function '%s' cannot access mutable static data '%s'",
sc->func->toChars(), v->toChars());
}
#endif
--------------------
More information about the Digitalmars-d
mailing list