On the subject of error messages

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Mon May 15 08:30:38 PDT 2017


On 5/13/17 10:41 AM, Stanislav Blinov wrote:
> Let's suppose I wrote the following template function:
>
>> import std.meta;
>>
>> enum bool isString(T) = is(T == string);
>>
>> void foo(Args...)(auto ref Args args)
>> if (!anySatisfy!(isString, Args)) {
>>    // ...
>> }
>
> This one is variadic, but it could as well have been non-variadic. The
> important
> aspect is that it has a constraint. In this case, the constraint is that
> it should
> accept any argument types *but* strings.
>
> Now, if I call it with a string argument:
>
>> foo(1, "a");
>
> I get the following error:
>
>> file(line): Error: template foo cannot deduce function from argument
>> types !()(int, string), candidates are:
>> file(line): foo(Args...)(auto ref Args arg) if (!anySatisfy!(isString,
>> Args))
>
> Ok, so the call does not compile, but the message is rather vague: it
> doesn't
> tell me which argument(s) failed to satisfy the constraint.
> In this simple example it's easy to see where the error is, but if foo()
> was
> called in a generic way (i.e. arguments come from somewhere else, their
> type
> determined by inference, etc.), or if the constraint was more complex, it
> wouldn't be as easy to spot.
>
> So, to help with this, let me write a checker and modify foo's
> signature, thanks
> to CTFE:
>
>> template types(args...) {
>>    static if (args.length)
>>        alias types = AliasSeq!(typeof(args[0]), types!(args[1..$]));
>>    else
>>        alias types = AliasSeq!();
>> }
>>
>> auto noStringArgs(args...)() {
>>    import std.format;
>>    // use types, as otherwise iterating over args may not compile
>>    foreach(i, T; types!args) {
>>        static if (is(T == string)) {
>>            pragma(msg, format!"Argument %d is a string, which is not
>> supported"
>>                    (i+1));
>>            return false;
>>        }
>>    }
>>    return true;
>> }
>>
>> void foo(Args...)(auto ref Args args)
>> if (noStringArgs!args) {
>>    // ...
>> }
>
>
> Now if I call foo() with a string argument, I get this:
>
>> foo(1, "a");
>>
>>
>> Argument 2 is a string, which is not supported
>> file(line): Error: template foo cannot deduce function from argument
>> types !()(int, string), candidates are:
>> file(line): foo(Args...)(auto ref Args arg) if (noStringArgs!args)

I think the compiler should be able to figure this out, and report it. 
The if constraint is a boolean expression, and so it can be divided into 
the portions that pass or fail.

What I'd love to see is the constraint colorized to show green segments 
that evaluate to true, and red segments that evaluate to false. And then 
recursively show each piece when asked.

I think any time spent making a user-level solution will not scale. The 
compiler knows the information, can ascertain why it fails, and print a 
much nicer error message. Plus it makes compile-time much longer to get 
information that is already available.

Imagine also a constraint like isInputRange!R. This basically attempts 
to compile a dummy lambda. How would one handle this in user-code?

I think there are several forum threads about diagnosing constraint 
issues, haven't got the time right now to look for them.

-Steve



More information about the Digitalmars-d mailing list