On 5/15/17 8:14 PM, Stanislav Blinov wrote:
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?
Yes.
Would it know to report it if `false` came several
levels deep in the loop body?
Yes.
Would it know that we actually *care*
about that information? (*cough* C++ *cough* pages and pages of error
text because of a typo...)
The idea is to give a preliminary rough report of which part of a
boolean expression caused it to evaluate to false. Then you recompile
with a flag to tell the compiler to do a deeper dive.
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.
Why not?
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?
This is what you have to do today. The task has already been tried by
the compiler, and the result is known by the compiler. Just have the
compiler tell you.
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?
The compiler, and by extension your hand-written error checking, cannot
know the true intention of the user. All it knows is you tried to do
something that isn't supported. You have to figure out what is wrong and
fix it. If that takes several iterations, that's what it takes. There is
no solution that will give you all the answers.
In your example, the compiler would point at isMovable!S as the issue.
Not super-informative, but is all it gives to prevent huge outputs. Then
you tell it to print more information, and it would say that false was
returned when the m member of type T is being checked, at which point
you could get a stack trace of what values were at each level of
recursion. Everywhere a boolean evaluated to true in order to get to the
point where false is returned would be colored green, every time it was
false, it would be colored red, and every time a short circuit happened,
it wouldn't be colored.
For checking to see that "something compiles", it could identify the
code that fails to compile. Again, the compiler has all this information.
This isn't any harder than debugging, in fact it should be easier, as
all the compiler metadata is available in memory, and most of the
information can be conveyed without having to poke around.
And it should be just about as good as your hand-written message. But
always accurate, and instantly available to all existing constraints.
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.
We can't hand-hold everyone. At some point you have to learn programming
and debugging :)
-Steve