Difference between "can call" and "can compile"
Steven Schveighoffer
schveiguy at gmail.com
Mon Sep 7 14:57:24 UTC 2020
Consider a template function like this:
void foo(T)(T v)
{
auto x = v.someProperty;
}
Now, I might create a constraint based on whether I can compile that
function:
void bar(alias x)() if (__traits(compiles, x(1)))
{
x(1);
}
now, if I try to call bar like:
bar!foo();
I get a compilation error. But it's not in foo, it's in bar. It says:
Error: template instance onlineapp.bar!(foo) does not match template
declaration bar(alias x)()
with x = foo(T)(T v)
must satisfy the following constraint:
__traits(compiles, x(1))
In this instance, the error message is straightforward. With a simple
case like this, I can figure it out and move on.
However, in a more complex case, perhaps the constraint is encapsulated
in a template. Perhaps that template basically checks if it can call it
in various ways, with various types. Perhaps it depends on the context
in which it was called.
And my alias I'm passing in is INTENDED to work. In fact, it might be
specifically DESIGNED to work with this exact function. However, I don't
get to see why it doesn't. I just get a "must satisfy the following
constraint: isSomeConstraint!foo"
The problem is, if the intention is for it to pass, but the
implementation is bugged, the error message is useless. If the thing I'm
passing is a type with 200 LOC, and there's an error buried in there
somewhere, the compiler is telling me "somewhere in this, it doesn't
work". And sometimes those function signatures and their constraints are
hairy themselves.
My usual steps taken at this point are to extract the constraint
template and actually try to call the functions in the constraints with
that type, and see what fails. This is somewhat annoying, and sometimes
difficult to do based on where the code that implements the constraint
actually is, and where the code actually calling it is (I may have to
duplicate a lot of stuff).
But the thing is -- we already HAVE a constraint system that says
whether the code should be callable or not -- the template constraint!
In fact, we can declare right on foo that it should only compile if I
can call someProperty on the value:
void foo(T)(T v) if (__traits(compiles, v.someProperty))
{
auto x = v.someProperty;
}
However, for this extra bit of forethought, I get no different error
messages.
I was wondering, would it be a useful addition to have a way to say "can
call" instead of "can compile"? In other words, this has a valid
template IFTI match, regardless of whether it compiles or not. In which
case, you can distinguish mismatch errors from implementation errors.
In the example above, foo without the template constraint instantiated
with int has a matched call, but the match doesn't compile. Whereas, the
one with the template constraint doesn't have a match, and so the call
itself will not compile. This difference could push the error down to
where it really belongs, and save me the time of having to perform
exploratory surgery on a library and its dependencies.
Thoughts?
-Steve
More information about the Digitalmars-d
mailing list