On the subject of error messages

Stanislav Blinov via Digitalmars-d digitalmars-d at puremagic.com
Mon May 15 17:14:04 PDT 2017


On Monday, 15 May 2017 at 20:55:35 UTC, Steven Schveighoffer 
wrote:
> On 5/15/17 4:24 PM, Stanislav Blinov wrote:
>> On Monday, 15 May 2017 at 19:44:11 UTC, Steven Schveighoffer 
>> wrote:
>
>>> It has to know. It has to evaluate the boolean to see if it 
>>> should
>>> compile! The current situation would be like the compiler 
>>> saying
>>> there's an error in your code, but won't tell you the line 
>>> number.
>>> Surely it knows.
>>
>> It "knows" it evaluated false. It doesn't know how to give 
>> user a
>> digestible hint to make that false go away.
>
> I'm going to snip away pretty much everything else and focus on 
> this.
>
> The compiler absolutely 100% knows, and can demonstrate, 
> exactly why a template constraint failed. We don't have to go 
> any further, or make suggestions about how to fix it.

In complex constraints, that is not enough. When we have loops 
(i.e. over arguments, or over struct members), would it report 
the iteration/name? Would it know to report it if `false` came 
several levels deep in the loop body? Would it know that we 
actually *care* about that information? (*cough* C++ *cough* 
pages and pages of error text because of a typo...)

When we have nested static ifs, it's important to see, at a 
glance, which parts of the combination were false. Again, if 
they're several &&, || in a row, or nested, pointing to a single 
one wouldn't in any way be informative.

When we have tests using dummy lambdas, are we to expect users to 
immediately extract the lambda body, parse it, and figure out 
what's wrong?

> Just output what exactly is wrong, even if you have to recurse 
> into the depths of some obscure template isXXX, and all it's 
> recursively called templates, I can get the correct 
> determination of where either my type isn't right, or the 
> constraint isn't right.

Please look over my isMovable example (I'm not sure if you caught 
it, I posted it as a follow up to my other reply). Suppose the 
`false` is pointed at by the compiler:

>    else static if (is(T == struct) &&
>            (hasElaborateDestructor!T || 
> hasElaborateCopyConstructor!T)) {
>        foreach (m; T.init.tupleof) {
>            static if (!isMovable!(typeof(m)) && (m == m.init)) {
>                return false;
>                       ^
>                       |
>            }
>        }
>        return true;
>    } else

That is very, *very* uninformative. I don't know which member it 
was, I don't know which part of the conditional was false. I 
don't know which part of the conditional further up was true. 
Would the compiler know to tell me all that? Would it know to 
test further, to collect *all* information, so that I don't have 
to incrementally recompile fixing one thing at a time?

Most importantly, as a user who sees this for the first time, I'd 
have no idea *why* those checks are there. I'd have no context, 
no grounds to base my reasoning on, so I'd either have to jump 
back to docs to see if I missed a corner case, or start 
spelunking code that I didn't write, which is always so fun... 
Thing is, the compiler is exactly in that position. It doesn't 
read the docs, ever :) It's always spelunking code written by 
someone else. It can't tell what the constraint, as a unit, is 
*actually* testing for. It doesn't care that we shouldn't 
destructively move structs with const members. So it wouldn't be 
able to tell me either. All it will do is report me that that 
false was returned on that line, and (hopefully), some additional 
info, like member type and name.


More information about the Digitalmars-d mailing list