On Monday, 31 July 2023 at 18:15:25 UTC, Jonathan M Davis wrote:
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.
I reasoned like this up about this point.
For instance, what happens when you have a function that's pure
but not @safe and one that's @safe but not pure?
```d
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
```d
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.
I didn’t even consider multiple attributes “in competition”.
At this point, it’s obvious that this can’t work.
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.
I tried assigning the address to a function pointer to
disambiguate which overload I want. Didn’t work.
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.
Actually, I do think it’s impossible to do the right thing. The
spec can only make guesses on what a programmer might want.
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.
Before looking into this, I thought that maybe this was in fact
intended.
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.
You might have gotten me wrong. I don’t want to do something with
it, I wondered if overloading based on attributes is a thing one
has to consider when writing templates or something like that. A
simple test was: Can I define those? If so, what happens on a
function call? The spec doesn’t say anything about it.
As you say, overloads should essentially do the same. Overloads
differing in attributes would differ in implementation details
such that one can make guarantees and the other might give you
better performance or other guarantees. Maybe that’s enough such
that, if both implementations have value, they should differ in
name (or a kind of tag parameter for overload selection).
Filed as https://issues.dlang.org/show_bug.cgi?id=24063