How can overloads be distinguished on attributes alone?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon Jul 31 18:15:25 UTC 2023


On Monday, July 31, 2023 4:55:44 AM MDT Quirin Schroll via Digitalmars-d-learn 
wrote:
> Apparently, functions can be overloaded solely distinguished by
> attributes:
> ```d
> void f(ref int x) pure { x = 1; }
> void f(ref int x)      { x = 2; static int s; ++s; }
> ```
>
> I thought that, maybe, a `pure` context calls the `pure` function
> and an impure context calls the impure function, but no: Calling
> `f` leads to an ambiguity error in both contexts. Even if that
> worked, what about inferred contexts, i.e. templates? In simple
> cases, they could forward the contexts in which they are called,
> but you can instantiate a template without calling it.
>
> What am I missing here?

As things stand, the context in which a function is called is irrelevant.
All that matters is the arguments.

And actually, allowing it would complicate any functions that infer
attributes, potentially in a way that wouldn't work. For instance, if you
have a templated function that's trying to infer purity, which one should it
call? If it calls the pure one, it could be pure, but if it doesn't, it
can't be. Either way, because the context isn't yet pure or not, the context
can't be used to determine which should be called. Potentially, the compiler
could just choose the pure function in that case, but the problem gets worse
as you add more attributes.

For instance, what happens when you have a function that's pure but not
@safe and one that's @safe but not pure?

void f() pure {...}
void f() @safe {...}

Should the compiler favor calling the pure one or the @safe one? And what if
you then add something to the function that isn't @safe? If it was calling
the @safe version before, should it switch to the pure one? And if the
functions were @safe pure and @system and not pure instead

void f() @safe pure {...}
void f() @system {...}

then changing the @safety or purity of some of the other code in the
templated function could result in the loss of both attributes. And the more
attributes are involved, the more complex the situation gets.

In effect, we'd be making the attribute inference process have to go in two
directions instead of just going from the bottom up, with the added
complication that it would potentially need to choose between sets of
attributes when choosing which function overload to call.

It's not necessarily the case that we couldn't sort all of this out and come
up with a clean set of rules that allowed functions that infer their
attributes to call the correct function, but it does get pretty complicated,
and it comes with the serious downside that there's no guarantee that the
overloads even do something similar to one another. And when you consider
that it's pretty easy for a change in one part of the code to change which
attributes are inferred in another part of the code, you could easily end up
having a change in one part of your program resulting in drastically
different behavior in a seemingly unrelated part of your program. And even
worse, that change could be because of a library update, making it that much
less obvious which parts of your program could suddenly change behavior due
to a change in attributes.

And I'm probably forgetting other issues that this would add to the mix. So,
while it may very well be possible to do something along the lines of what
you're looking for, I strongly suspect that it's simply not worth it.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list