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