Jump to page: 1 2
Thread overview
__traits(compiles, ...) with syntax errors
Jan 14, 2019
Paul Backus
Jan 14, 2019
Jonathan M Davis
Jan 14, 2019
H. S. Teoh
Jan 15, 2019
Ali Çehreli
Jan 15, 2019
Olivier FAURE
Jan 14, 2019
Iain Buclaw
Jan 14, 2019
Nicholas Wilson
Jan 15, 2019
Kagamin
Jan 15, 2019
Atila Neves
January 14, 2019
How many times have you written something like:

void foo(T)(T t) if (__traits(compiles, t.bar())) // or is(typeof(t.bar()))
{
   t.bar();
}

And somehow, somewhere, this isn't called? Then you remove the constraint, and find that there's a syntax error in the bar template function (like a missing semicolon).

I often times, to avoid this, simply just use __traits(hasMember, T, "bar"), and live with the consequences.

I'd love to have something like __traits(compiles), but have it be an error if the syntax is incorrect.

In other words, the constraint above only fails if there is a missing symbol, or incorrect type conversion, not that there is a stupid syntax error that would NEVER compile.

Ironically, it would be something like compilesOrSyntaxError.

Any thoughts?

-Steve
January 14, 2019
On 1/14/19 4:47 PM, Steven Schveighoffer wrote:
> How many times have you written something like:
> 
> void foo(T)(T t) if (__traits(compiles, t.bar())) // or is(typeof(t.bar()))
> {
>     t.bar();
> }
> 
> And somehow, somewhere, this isn't called? Then you remove the constraint, and find that there's a syntax error in the bar template function (like a missing semicolon).
> 
> I often times, to avoid this, simply just use __traits(hasMember, T, "bar"), and live with the consequences.
> 
> I'd love to have something like __traits(compiles), but have it be an error if the syntax is incorrect.
> 
> In other words, the constraint above only fails if there is a missing symbol, or incorrect type conversion, not that there is a stupid syntax error that would NEVER compile.
> 
> Ironically, it would be something like compilesOrSyntaxError.

Another option is to have __traits(compiles) spit out an error type that has truthiness if it's not an error (for backwards compatibility), then you could something like:

if(__traits(compiles, ...) != COMPILER_ERROR.SYNTAX)

-Steve
January 14, 2019
On 1/14/19 4:52 PM, Steven Schveighoffer wrote:
> Another option is to have __traits(compiles) spit out an error type that has truthiness if it's not an error (for backwards compatibility), then you could something like:
> 
> if(__traits(compiles, ...) != COMPILER_ERROR.SYNTAX)

Oops, it would have to be success or syntax error, so something like:

if(successOrSyntaxError!(__traits(compiles, ...)))

successOrSyntaxError would be a simple template

-Steve

January 14, 2019
On Monday, 14 January 2019 at 21:47:40 UTC, Steven Schveighoffer wrote:
> How many times have you written something like:
>
> void foo(T)(T t) if (__traits(compiles, t.bar())) // or is(typeof(t.bar()))
> {
>    t.bar();
> }
>
> And somehow, somewhere, this isn't called? Then you remove the constraint, and find that there's a syntax error in the bar template function (like a missing semicolon).

Ideally, you would catch this in the unit tests for `bar`. You are writing unit tests, aren't you? ;)
January 14, 2019
On Mon, 14 Jan 2019 at 22:50, Steven Schveighoffer via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
>
> Any thoughts?
>

Compile with -verrors=spec (or -Wspeculative if using gdc).

-- 
Iain
January 14, 2019
On 1/14/19 4:57 PM, Paul Backus wrote:
> On Monday, 14 January 2019 at 21:47:40 UTC, Steven Schveighoffer wrote:
>> How many times have you written something like:
>>
>> void foo(T)(T t) if (__traits(compiles, t.bar())) // or is(typeof(t.bar()))
>> {
>>    t.bar();
>> }
>>
>> And somehow, somewhere, this isn't called? Then you remove the constraint, and find that there's a syntax error in the bar template function (like a missing semicolon).
> 
> Ideally, you would catch this in the unit tests for `bar`. You are writing unit tests, aren't you? ;)

It's not always that simple. Sometimes bar is part of a hook or something to a complex system, and you expect it to be called one way, and it's called another way (or the lack of ability to call it changes how the system behaves).

Do you unit test all your calls to front(), popFront or empty individually? I generally don't, I just put it in as a range, and see if that works.

Then I get an error like "this isn't a range", and it's not super-helpful.

Or worse, I get a "I can't call any of these 10 template overloads, each with a 5-term constraint"

-Steve
January 14, 2019
On 1/14/19 5:25 PM, Iain Buclaw wrote:
> On Mon, 14 Jan 2019 at 22:50, Steven Schveighoffer via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>>
>>
>> Any thoughts?
>>
> 
> Compile with -verrors=spec (or -Wspeculative if using gdc).
> 

But wouldn't that show all errors, not just syntax errors? That might be a bit difficult to wade through.

-Steve
January 14, 2019
On 1/14/19 5:32 PM, Steven Schveighoffer wrote:
> On 1/14/19 5:25 PM, Iain Buclaw wrote:
>> On Mon, 14 Jan 2019 at 22:50, Steven Schveighoffer via Digitalmars-d
>> <digitalmars-d@puremagic.com> wrote:
>>>
>>>
>>> Any thoughts?
>>>
>>
>> Compile with -verrors=spec (or -Wspeculative if using gdc).
>>
> 
> But wouldn't that show all errors, not just syntax errors? That might be a bit difficult to wade through.

BTW, I didn't know about this switch, thanks for pointing it out.

-Steve
January 14, 2019
On Monday, 14 January 2019 at 22:32:59 UTC, Steven Schveighoffer wrote:
> On 1/14/19 5:25 PM, Iain Buclaw wrote:
>> On Mon, 14 Jan 2019 at 22:50, Steven Schveighoffer via Digitalmars-d
>> <digitalmars-d@puremagic.com> wrote:
>>>
>>>
>>> Any thoughts?
>>>
>> 
>> Compile with -verrors=spec (or -Wspeculative if using gdc).
>> 
>
> But wouldn't that show all errors, not just syntax errors? That might be a bit difficult to wade through.
>
> -Steve

Yep, the SNR on that switch is abominable.
January 14, 2019
On Monday, January 14, 2019 2:57:28 PM MST Paul Backus via Digitalmars-d wrote:
> On Monday, 14 January 2019 at 21:47:40 UTC, Steven Schveighoffer
>
> wrote:
> > How many times have you written something like:
> >
> > void foo(T)(T t) if (__traits(compiles, t.bar())) // or
> > is(typeof(t.bar()))
> > {
> >
> >    t.bar();
> >
> > }
> >
> > And somehow, somewhere, this isn't called? Then you remove the constraint, and find that there's a syntax error in the bar template function (like a missing semicolon).
>
> Ideally, you would catch this in the unit tests for `bar`. You are writing unit tests, aren't you? ;)

Good unit tests catch a lot, but it's hard for them to catch everything, and having to test for stuff that the compiler can trivially tell you can get really annoying. That's not to say that the compiler should find everything for you, but sometimes, having it be able to tell you a bit more than currently it does could save a lot of trouble. In this particular case, it's not uncommon to have alternate implementations depending on compile time conditionals. e.g. stuff like

static if(isRandomAccessRange!R)
{
    ...
}
else
{
    ...
}

isn't all that uncommon in optimized range code. The function may work just fine whether the range is random-access or not, but the actual code that gets compiled in (and therefore its performance) could vary quite a bit depending on the range's capabilities.

Syntax errors are less likely in code like that than code that uses is expressions or __traits(compiles, ...) directly, but the problem that Steven brings up is very real - and in a way that it can actually be rather difficult to test for. In many cases, you're basically forced to put in pragmas or invalid code to see which branches are being compiled. Having a trait similar to __traits(compiles, ...) but which actually gives you an error for syntax errors would catch _some_ of the problems surrounding branching on compile-time conditionals. However, given that it's still not verifying that the correct branch was taken (just that the test is syntactically valid), it still wouldn't solve the entire problem. It would catch a certain class of bugs though. And they're bugs that wouldn't exist anywhere outside of an is expression or __traits(compiles), ...), because everywhere else, such syntax errors would be caught. It's just that with those constructs, those errors basically get shut up and treated simply as false (which is probably the wrong behavior in all cases except perhaps for those that involve checking code that's been mixed in).

- Jonathan M Davis



« First   ‹ Prev
1 2