We need a way to make functions pure and/or nothrow based on the purity and/or nothrowability of the functions that they call

Jonathan M Davis jmdavisProg at gmx.com
Sun Nov 14 01:56:02 PST 2010


That is, there are plenty of cases where template code may or may not be able to 
pure or nothrow and that whether it can or not depends on what it's templatized 
on. For instance, if you had a range which iterated over a range of characters 
in some manner (other than simply iterating over it as std.array would). Whether 
or not empty could be pure or nothrow would depend on whether the range of 
characters that it holds has a empty which is pure or nothrow. At present, 
std.array's front is neither pure or nothrow, but it will hopefully become both 
later, and you could have a range of characters which isn't actually an array 
and whose empty is both pure and nothrow. So, whether or not the empty on the 
range type that you're instantiating can be pure or nothrow depends on what it's 
iterating over. The only way that I know how to do that at present is to use 
static ifs and get 4 different implementations. e.g.

    static if((isArray!R && (functionAttributes!(std.array.empty!
(ElementType!R)) & FunctionAttribute.PURE)) ||
              (!isArray!R && (functionAttributes!(R.empty) & 
FunctionAttribute.PURE)))
    {
        static if((isArray!R && (functionAttributes!(std.array.empty!
(ElementType!R)) & FunctionAttribute.NOTHROW)) ||
                  (!isArray!R && (functionAttributes!(R.empty) & 
FunctionAttribute.NOTHROW)))
        {
            @property bool empty() pure nothrow
            {
                return innerRange.empty;
            }
        }
        else
        {
            @property bool empty() pure
            {
                return innerRange.empty;
            }
        }
    }
    else
    {
        static if((isArray!R && (functionAttributes!(std.array.empty!
(ElementType!R)) & FunctionAttribute.NOTHROW)) ||
                  (!isArray!R && (functionAttributes!(R.empty) & 
FunctionAttribute.NOTHROW)))
        {
            @property bool empty() nothrow
            {
                return innerRange.empty;
            }
        }
        else
        {
            @property bool empty()
            {
                return innerRange.empty;
            }
        }
    }


That is _not_ a pretty way to do this. And if the function that you're declaring 
gets very complicated at all, you're going to need to find a way to get the 4 
versions to share code (probably via string mixin). The bodies are _identical_ 
and have no need to be different. _All_ you're changing is the function 
signature. And whether the function signature has pure and/or nothrow depends 
entirely on empty in this case. A more complicated function could depend on half 
a dozen or a dozen different functions, and because you're dealing with a 
templated type or function, whether or not the function can be pure and/or 
nothrow varies with the type which is used to instantiate the template.

We really need to add a way to have a function marked as nothrow and/or pure 
based on whether the functions that it calls are nothrow and/or pure. Whether 
that should require listing the functions that need to be pure and/or nothrow or 
whether the compiler should be able to figure it out on its own, I don't know. 
But the current situation is ugly.

The one problem I see is that if the compiler has to determine whether a given 
function can be pure and/or nothrow, it's going to potentially have to go 
arbitrarily deep into the call hierarchy to figure it out (which stinks of the 
halting problem), and it could require the source code of all of the functions 
that it has to check (or require that they've all already determined whether 
they can be pure and/or nothrow). I don't know whether that problem can be 
gotten around or what the best solution is if there is one, but the current 
situation is ugly.

As it stands, functions are likely to either be impure and be allowed to throw 
simply because doing otherwise would require declaring the function 4 times. 
std.algorithm and std.range are prime targets for places where functions 
_should_ be pure and nothrow if they can, but whether they can or not depends on 
their template arguments. I really think that we need to find a solution to this 
problem and relatively soon.

Does anyone have some good suggestions on how to solve this issue?

- Jonathan M Davis


More information about the Digitalmars-d mailing list