Difference between "can call" and "can compile"

Steven Schveighoffer schveiguy at gmail.com
Tue Sep 8 18:59:00 UTC 2020


On 9/8/20 2:24 PM, Paul Backus wrote:
> On Tuesday, 8 September 2020 at 17:49:08 UTC, Steven Schveighoffer wrote:
>> The error could cause subtle differences. The code could actually 
>> COMPILE, but not take the path intended. In which case the error is 
>> useless, even if it's more verbose.
> 
> I guess I've failed to communicate clearly. When I talk about "errors" 
> here, unless I specifically say otherwise, I am referring exclusively to 
> "top-level" non-speculative errors--the kind that DMD prints with red 
> letters and that cause compilation to fail.
> 
> By definition, any code that contains such an error cannot compile, so 
> the hypothetical you raise here is impossible.

Like for instance:

struct S
{
    import std.range : isOutputRange;
    void toString(Output)(Output output) if (isOutputRange!(Output, dchar))
    {
       put(output, "hello, this is an S!"); // no import of std.range.put
    }
}

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.

It's also possible that a compilation error happens in a path that isn't 
chosen because __traits(compiles) returns false in the path we expect, 
but the path taken instead doesn't have a __traits(compiles) guarding 
it. In which case, a weirder unexpected error message occurs far away 
from the true problem.

> Well, the compiler is already showing you where your code failed to 
> compile: it failed in the template constraint, because 
> `__traits(compiles, whatever)` evaluated to false. :)

In simple cases, this is enough. In complex cases, the error can be very 
far away from where the problem is.

> What you and I really want is for the compiler to show us the *root 
> cause* of that failure (which `-verrors=spec` currently succeeds at), 
> and to do so without overwhelming us with irrelevant information (which 
> `-verrors=spec` currently fails at).

I essentially want to know why it made the decisions it made. So yes, in 
some cases, this might involve seeing ALL the speculative compilation 
output, and sifting through it. In the majority of cases, I want to know 
why it couldn't call my function foo()(int) with an int parameter, which 
is what it's telling me.

This would not solve all template compilation problems. Just most of them.

>> If you can solve that problem, then I'd be all for that.
> 
> If it's possible to selectively un-gag errors based on whether they 
> happened during template argument deduction, it should also be possible 
> to selectively un-gag errors based on whether they happened during 
> template instantiation in general.

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.

-Steve


More information about the Digitalmars-d mailing list