the traits trap

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Fri Nov 21 08:05:08 PST 2014


On 11/21/14 2:25 AM, Sergei Nosov wrote:
> On Friday, 21 November 2014 at 04:08:52 UTC, Steven Schveighoffer wrote:
>> Can anyone figure out a good solution to this problem? I like template
>> constraints, but they are just too black-boxy. Would we have to
>> signify that some enum is actually a trait and so the compiler would
>> know to spit out the junk of compiling? Would it make sense to add
>> some __traits function that allows one to signify that this is a
>> special trait thing?
>>
>> This is one area that D's templates are very user-unfriendly.
>>
>> -Steve
>
> I would second this. Personally, I have the same "not very pleasant"
> experience debugging template constraints.
>
> Since more often than not the constraints have the form of:
>
> if (clause1 && clause2 && clause3 ...)
>
> my naive proposal would be to show which clause was first to be false in
> the error message.

That helps, but you still have to figure out why that clause fails.

> However, I have no idea if this could be implemented easily.

I think it's a good idea in general. Inevitably, a template constraint 
breaks down into a decision tree, and knowing which decisions 
contributed to the false at the top is essential. Of course, this is 
only needed if it *doesn't* compile.

What I'd like to see is a combination of both determining at the highest 
level which part of the if expression caused the failure (this is only 
if the thing doesn't compile), and then add a new feature:

static assert(__traits(analyzeTrait, isSomeTrait!x));

The analyzeTrait directive would compile isSomeTrait!x, keeping track of 
the decision tree (or optionally, re-compiling it to print the tree), 
and whichever pieces caused it to be 0, including __traits(compiles, 
...) error messages, and if the result ends up being non-zero, it just 
discards that tree. If the result ends up being 0, then the static 
assert prints the decision tree.

An example:
enum isFoo(T) = (is(T == int) || is(T == long)) && __traits(compiles, (T 
t) { blah(t); });

void blah(int x);

static assert(__traits(analyzeTrait, isFoo!int)); // => no messages, 
compilation continues

static assert(__traits(analyzeTrait, isFoo!long)); // Compilation stops, 
output is:

Error: analyzeTrait returned false for isFoo!(T) where T == long:
is(T == int) => false [1]
is(T == long) => true [2]
[1] || [2] => true [3]
__traits(compiles, ...) => false [4]
    Error: no overload of blah for long
[3] && [4] => false

Note, the __traits(compiles, ...) line would show the entire compile 
expression for reference.

Something like this makes analysis of why the static assert failed so 
much better. You could just build this into static assert, but I think 
it might be too unwieldy to see all the messages. Sometimes, just 
knowing static assert fails some involved test is fine, you don't need 
the details.

-Steve


More information about the Digitalmars-d mailing list