Difference between "can call" and "can compile"
Paul Backus
snarwin at gmail.com
Tue Sep 8 19:48:36 UTC 2020
On Tuesday, 8 September 2020 at 18:59:00 UTC, Steven
Schveighoffer wrote:
> Now,
>
> writeln(S.init) => S()
>
> Wait, where's my specified message? I've literally spent hours
> on stuff like this, where I'm racking my brain trying to figure
> out why that function can't be called, only to realize that the
> function doesn't compile in the first place, when called with
> the specified constraints satisfied.
>
> Whereas if writeln checked using the proposed
> __traits(instantiates) instead of __traits(compiles) on the
> call to S.toString(lockedTextOutput) (or whatever it does),
> then instead of the wrong path, I get a compilation error,
> telling me that my toString doesn't compile.
writeln could just as easily use __traits(hasMember) to check for
the presence of toString, and print a warning message if it
exists but doesn't compile. In other words, the problem here is
that writeln assumes "toString doesn't compile" implies "toString
isn't supposed to compile," which in most cases is not actually
true. Adding a new language feature is neither necessary nor
sufficient to correct that assumption.
I agree that it would probably be a good idea to change writeln
(and other similar code) so that it does not make these kinds of
assumptions.
> The point is not to ungag errors during argument deduction or
> gag them afterwards, but to stop trying to compile after
> deduction is done for that call. Essentially, don't actually
> instantiate the template, just see whether there is a matching
> template you would instantate.
From the programmer's perspective, is there any difference?
Let me make this concrete. Here's a contrived example program
that illustrates the problem from your first post in this thread:
--- example.d
void use(alias fun)()
if (__traits(compiles, fun()))
{}
void bad()()
{
oops();
}
void irrelevant()()
{
oops();
}
void test()
{
enum _ = __traits(compiles, irrelevant());
use!bad();
}
---
Here's what the compiler currently prints when you compile it:
---
example.d(18): Error: template instance `example.use!(bad)` does
not match template declaration `use(alias fun)()`
with `fun = bad()()`
must satisfy the following constraint:
` __traits(compiles, fun())`
---
Here's what the compiler currently prints when you compile it
with `-verrors=spec`:
---
(spec:1) example.d(12): Error: undefined identifier `oops`
(spec:1) example.d(17): Error: template instance
`example.irrelevant!()` error instantiating
(spec:1) example.d(17): Error: template instance
`example.irrelevant!()` cannot resolve forward reference
(spec:1) example.d(17): Error: template `example.irrelevant`
cannot deduce function from argument types `!()()`, candidates
are:
(spec:1) example.d(10): `irrelevant()()`
(spec:1) example.d(7): Error: undefined identifier `oops`
(spec:1) example.d(2): Error: template instance `example.bad!()`
error instantiating
(spec:1) example.d(2): Error: template instance `example.bad!()`
cannot resolve forward reference
(spec:1) example.d(2): Error: template `example.bad` cannot
deduce function from argument types `!()()`, candidates are:
(spec:1) example.d(5): `bad()()`
(spec:1) example.d(7): Error: undefined identifier `oops`
(spec:1) example.d(2): Error: template instance `example.bad!()`
error instantiating
(spec:1) example.d(2): Error: template instance `example.bad!()`
cannot resolve forward reference
(spec:1) example.d(2): Error: template `example.bad` cannot
deduce function from argument types `!()()`, candidates are:
(spec:1) example.d(5): `bad()()`
example.d(18): Error: template instance `example.use!(bad)` does
not match template declaration `use(alias fun)()`
with `fun = bad()()`
must satisfy the following constraint:
` __traits(compiles, fun())`
---
And here's what I would like the compiler to print with my
proposed `-verrors=spec-but-only-stuff-i-care-about` flag:
---
(spec:1) example.d(7): Error: undefined identifier `oops`
(spec:1) example.d(2): Error: template instance `example.bad!()`
error instantiating
(spec:1) example.d(2): Error: template instance `example.bad!()`
cannot resolve forward reference
(spec:1) example.d(2): Error: template `example.bad` cannot
deduce function from argument types `!()()`, candidates are:
(spec:1) example.d(5): `bad()()`
(spec:1) example.d(7): Error: undefined identifier `oops`
(spec:1) example.d(2): Error: template instance `example.bad!()`
error instantiating
(spec:1) example.d(2): Error: template instance `example.bad!()`
cannot resolve forward reference
(spec:1) example.d(2): Error: template `example.bad` cannot
deduce function from argument types `!()()`, candidates are:
(spec:1) example.d(5): `bad()()`
example.d(18): Error: template instance `example.use!(bad)` does
not match template declaration `use(alias fun)()`
with `fun = bad()()`
must satisfy the following constraint:
` __traits(compiles, fun())`
---
Notice how the above includes messages about speculative errors
in `bad`, but does NOT include messages about speculative errors
in `irrelevant`.
More information about the Digitalmars-d
mailing list