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