Add __traits(canCall) and __traits(resolve)
Steven Schveighoffer
schveiguy at gmail.com
Sat Jul 26 02:27:08 UTC 2025
Just having run into the problem of `__traits(compiles)`
swallowing unexpected errors for the umpteenth time, I'm
wondering if we can formally get this feature into the language.
This was a similar post by me a while back:
https://forum.dlang.org/post/rj5hok$c6q$1@digitalmars.com
What I'd like to see are 2 traits calls.
## `__traits(canCall, expression)`
This trait should return 0 if the expression is not a function
call, or if the function call expression does not match any
in-scope symbols.
It should return the number of matching symbols at the highest
matching level. If this number is 1, then the call should be
expected to resolve to a single correct function call.
If the number is greater than 1, then the code can expect that
calling the symbol in this manner will result in an ambiguity
error (as long as all functions are valid).
This does NOT compile the function, it just uses all existing
compiler mechanisms to find the matches and select the correct
option. Semantic errors in the function should not cause this to
return 0 (the point is to avoid the issue with
`__traits(compiles)`).
### Rationale
`__traits(compiles)` is often used to find whether a match to a
call exists, but often times ends up with the confusing result of
just ignoring the function altogether.
Everyone who has overloaded `toString` with an output range has
had this experience:
```d
import std.range;
struct S
{
int x;
void toString(Out)(ref Out outputRange) if
(isOutputRange!(Out, char))
{
// forgot to import std.format;
outputRange.formattedWrite("x is %s", x);
}
}
void main()
{
import std.stdio;
writeln(S(3)); // S(3), but expected x is 3
}
```
Debugging such things is difficult, because the "best effort"
function `writeln` decides that `S.toString` doesn't exist, so it
just doesn't bother calling it, and makes up its own formatting.
If instead, `writeln` used this trait, it could see that the
`toString` call matches, and try to use it, producing an error
the user can see and fix.
This does not fix errors in signature, or template constraints.
The experience of trying to implement hooks in D is significantly
degraded due to this limitation, and I'm hoping this would
improve the situation.
## `__traits(resolve, expression)`
IFTI and other calls are tricky to predict. This traits should
take a valid compilable call expression (no errors this time),
and give you a symbol that the compiler would use to resolve the
expression.
I'm expecting a result here for expressions that are call
expressions only. For other expressions, I would maybe expect an
error? I don't know. Are there other tricky situations that would
benefit from this?
The result would be an alias to the resolving symbol determined
by the IFTI or overload resolution algorithm.
I'm not 100% sure how to specify this. But something like:
```d
void foo(size_t opt = 42, K, V, T : K[V])(T val) {}
alias x = __traits(resolve, foo(int[int].init));
// equivalent to:
// alias x = foo!(42, int, int, int[int]);
```
Another example:
```d
void foo(long x) { }
void foo(string s) { }
auto getHandler(T)() {
void function(T) x = &foo;
return x;
}
void main()
{
auto x = getHandler!int;
x(1);
}
```
This produces an error, because there is no `foo` overload that
exactly matches `int` as the parameter. What you really want
inside `getHandler` is, give me the address of the foo overload
that would be called if I passed in the argument of type `T`.
```d
// I'd like to write:
auto getHandler(T) {
return &__traits(resolve, foo(T.init));
}
```
### Rationale
This feature unlocks introspection capabilities that are nearly
impossible today. You can write the alias as above, but you can't
tap into the machinery of IFTI or overload resolution (with
conversions) without actually trying to call these items.
What this is saying is "give me the thing you would call if I
wrote this expression".
These kinds of questions can be answered by the compiler, but we
have no way of asking them in a satisfactory way.
-Steve
More information about the dip.ideas
mailing list