www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D Logic bug

reply James Japherson <JJ goolooking.com> writes:
Took me about an hour to track this one down!

A + (B == 0) ? 0 : C;

D is evaluating it as

(A + (B == 0)) ? 0 : C;


The whole point of the parenthesis was to associate.

I usually explicitly associate precisely because of this!

A + ((B == 0) ? 0 : C);

In the ternary operator it should treat parenthesis directly to 
the left as the argument.

Of course, I doubt this will get fixed but it should be noted so 
other don't step in the same poo.
Oct 11 2018
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 12/10/2018 3:35 AM, James Japherson wrote:
 Took me about an hour to track this one down!
 
 A + (B == 0) ? 0 : C;
 
 D is evaluating it as
 
 (A + (B == 0)) ? 0 : C;
 
 
 The whole point of the parenthesis was to associate.
 
 I usually explicitly associate precisely because of this!
 
 A + ((B == 0) ? 0 : C);
 
 In the ternary operator it should treat parenthesis directly to the left 
 as the argument.
 
 Of course, I doubt this will get fixed but it should be noted so other 
 don't step in the same poo.
The specification makes this clear (even if another example is needed). The conditional expression takes an OrOrExpression not an expression as the condition. In other words, it requires a boolean operation on the left, with Expression's for the branches (deprecated behavior related to assignment for else branch but won't go into that). So not a bug and is working in a reasonable way. https://dlang.org/spec/expression.html#conditional_expressions
Oct 11 2018
prev sibling next sibling parent krzaq <dlangmailinglist krzaq.cc> writes:
On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson 
wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;


 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly to 
 the left as the argument.

 Of course, I doubt this will get fixed but it should be noted 
 so other don't step in the same poo.
Are any languages evaluating it differently? For example, is this
Oct 11 2018
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson 
wrote:
 In the ternary operator it should treat parenthesis directly to 
 the left as the argument.
I don't think parentheses are ever treated like that. They are self-contained and don't affect operators outside them.
Oct 11 2018
parent Shachar Shemesh <shachar weka.io> writes:
On 11/10/18 20:16, Kagamin wrote:
 On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson wrote:
 In the ternary operator it should treat parenthesis directly to the 
 left as the argument.
I don't think parentheses are ever treated like that. They are self-contained and don't affect operators outside them.
Almost. const(int) *
Oct 11 2018
prev sibling next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson 
wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;
As it should.
 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly to 
 the left as the argument.

 Of course, I doubt this will get fixed but it should be noted 
 so other don't step in the same poo.
No. Except for assignement and assignment operators, ternary operator has the lowest precedence of any operator in D (and C,
Oct 11 2018
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson 
wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;


 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly to 
 the left as the argument.

 Of course, I doubt this will get fixed but it should be noted 
 so other don't step in the same poo.
In c++ the ternary operator is the second most lowest precedence operator, just above the comma. You can see a table of each operator and their precendence here, I refer to it every so often: https://en.cppreference.com/w/cpp/language/operator_precedence Learning that the ternary operator has such a low precedence is one of those things that all programmers eventually run into...welcome to the club :) It looks like D has a similar table here (https://wiki.dlang.org/Operator_precedence). However, it doesn't appear to have the ternary operator in there. On that note, D would take it's precedence order from C/C++ unless there's a VERY good reason to change it.
Oct 11 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, October 11, 2018 1:09:14 PM MDT Jonathan Marler via 
Digitalmars-d wrote:
 On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson

 wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;


 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly to
 the left as the argument.

 Of course, I doubt this will get fixed but it should be noted
 so other don't step in the same poo.
In c++ the ternary operator is the second most lowest precedence operator, just above the comma. You can see a table of each operator and their precendence here, I refer to it every so often: https://en.cppreference.com/w/cpp/language/operator_precedence Learning that the ternary operator has such a low precedence is one of those things that all programmers eventually run into...welcome to the club :) It looks like D has a similar table here (https://wiki.dlang.org/Operator_precedence). However, it doesn't appear to have the ternary operator in there. On that note, D would take it's precedence order from C/C++ unless there's a VERY good reason to change it.
The operator precedence matches in D. Because in principle, C code should either be valid D code with the same semantics as it had in C, or it shouldn't compile as D code, changing operator precedence isn't something that D is going to do (though clearly, the ternary operator needs to be added to the table). It would be a disaster for porting code if we did. - Jonathan M Davis
Oct 11 2018
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 11 October 2018 at 21:57:00 UTC, Jonathan M Davis 
wrote:
 On Thursday, October 11, 2018 1:09:14 PM MDT Jonathan Marler 
 via Digitalmars-d wrote:
 On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson

 wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;


 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly 
 to the left as the argument.

 Of course, I doubt this will get fixed but it should be 
 noted so other don't step in the same poo.
In c++ the ternary operator is the second most lowest precedence operator, just above the comma. You can see a table of each operator and their precendence here, I refer to it every so often: https://en.cppreference.com/w/cpp/language/operator_precedence Learning that the ternary operator has such a low precedence is one of those things that all programmers eventually run into...welcome to the club :) It looks like D has a similar table here (https://wiki.dlang.org/Operator_precedence). However, it doesn't appear to have the ternary operator in there. On that note, D would take it's precedence order from C/C++ unless there's a VERY good reason to change it.
The operator precedence matches in D. Because in principle, C code should either be valid D code with the same semantics as it had in C, or it shouldn't compile as D code, changing operator precedence isn't something that D is going to do (though clearly, the ternary operator needs to be added to the table). It would be a disaster for porting code if we did. - Jonathan M Davis
I had a look at the table again, looks like the ternary operator is on there, just called the "conditional operator". And to clarify, D's operator precedence is close to C/C++ but doesn't match exactly. This is likely a result of the grammar differences rather than an intention one. For example, the "Conditional operator" in D actually has a higher priority than an assignment, but in C++ it's the same and is evaluated right-to-left. So this expression would be different in C++ and D: a ? b : c = d In D it would be: (a ? b : c ) = d And in C++ would be: a ? b : (c = d) Check it out: --- import core.stdc.stdio; void main() { int a = 2, b = 3; printf("expr = %d\n", 1 ? a : b = 4); } --- prints "expr = 4" it evaluates the conditional (1 ? a : b) into the expression a, which is actually an rvalue! and then assigns it to 4 because of the "= 4" and then returns the value 4 to printf. Here's the C++ version: #include <stdio.h> int main(int argc, char *argv[]) { int a = 2, b = 3; printf("expr = %d\n", 1 ? a : b = 4); } This one prints "expr = 2" It simply returns the value of `a` because the "b = 4" at the end is all part of the "else" contition in the ternary operator.
Oct 11 2018
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/11/18 7:17 PM, Jonathan Marler wrote:

 I had a look at the table again, looks like the ternary operator is on 
 there, just called the "conditional operator".  And to clarify, D's 
 operator precedence is close to C/C++ but doesn't match exactly.  This 
 is likely a result of the grammar differences rather than an intention 
 one.  For example, the "Conditional operator" in D actually has a higher 
 priority than an assignment, but in C++ it's the same and is evaluated 
 right-to-left.  So this expression would be different in C++ and D:
 
Not in my C/D code. It would have copious parentheses everywhere :) That case is actually very strange, I don't know if it's something that's really common. -Steve
Oct 11 2018
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 11 October 2018 at 23:29:05 UTC, Steven 
Schveighoffer wrote:
 On 10/11/18 7:17 PM, Jonathan Marler wrote:

 I had a look at the table again, looks like the ternary 
 operator is on there, just called the "conditional operator".  
 And to clarify, D's operator precedence is close to C/C++ but 
 doesn't match exactly.  This is likely a result of the grammar 
 differences rather than an intention one.  For example, the 
 "Conditional operator" in D actually has a higher priority 
 than an assignment, but in C++ it's the same and is evaluated 
 right-to-left.  So this expression would be different in C++ 
 and D:
 
Not in my C/D code. It would have copious parentheses everywhere :)
Good :)
 That case is actually very strange, I don't know if it's 
 something that's really common.
Yes, that explains why myself, Jonathan Davis and certainly others didn't know there were actually differences between C++ and D Operator precedence :) I wasn't sure myself but having a quick look at each's operator precedence table made it easy to find an expression that behaves differently in both.
Oct 11 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/11/18 9:16 PM, Jonathan Marler wrote:
 On Thursday, 11 October 2018 at 23:29:05 UTC, Steven Schveighoffer wrote:
 On 10/11/18 7:17 PM, Jonathan Marler wrote:

 I had a look at the table again, looks like the ternary operator is 
 on there, just called the "conditional operator". And to clarify, D's 
 operator precedence is close to C/C++ but doesn't match exactly.  
 This is likely a result of the grammar differences rather than an 
 intention one.  For example, the "Conditional operator" in D actually 
 has a higher priority than an assignment, but in C++ it's the same 
 and is evaluated right-to-left.  So this expression would be 
 different in C++ and D:
Not in my C/D code. It would have copious parentheses everywhere :)
Good :)
Yep. General rule of thumb for me after having been burned many many times -- Always use parentheses to define order of operations when dealing with bitwise operations (and, or, xor) and for the ternary operator. I think I do make an exception when it's a simple assignment. i.e.: a = cond ? 1 : 2;
 
 That case is actually very strange, I don't know if it's something 
 that's really common.
Yes, that explains why myself, Jonathan Davis and certainly others didn't know there were actually differences between C++ and D Operator precedence :)  I wasn't sure myself but having a quick look at each's operator precedence table made it easy to find an expression that behaves differently in both.
I actually was curious whether DMC followed the rules (hey, maybe Walter just copied his existing code!), but it does follow C's rules. -Steve
Oct 11 2018
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Thursday, 11 October 2018 at 23:17:15 UTC, Jonathan Marler 
wrote:
 I had a look at the table again, looks like the ternary 
 operator is on there, just called the "conditional operator".  
 And to clarify, D's operator precedence is close to C/C++ but 
 doesn't match exactly.  This is likely a result of the grammar 
 differences rather than an intention one.  For example, the 
 "Conditional operator" in D actually has a higher priority than 
 an assignment, but in C++ it's the same and is evaluated 
 right-to-left.  So this expression would be different in C++ 
 and D:

 a ? b : c = d

 In D it would be:

 (a ? b : c ) = d

 And in C++ would be:

 a ? b : (c = d)
That's https://issues.dlang.org/show_bug.cgi?id=14186
Oct 12 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/12/18 6:06 AM, Kagamin wrote:
 On Thursday, 11 October 2018 at 23:17:15 UTC, Jonathan Marler wrote:
 I had a look at the table again, looks like the ternary operator is on 
 there, just called the "conditional operator". And to clarify, D's 
 operator precedence is close to C/C++ but doesn't match exactly.  This 
 is likely a result of the grammar differences rather than an intention 
 one.  For example, the "Conditional operator" in D actually has a 
 higher priority than an assignment, but in C++ it's the same and is 
 evaluated right-to-left.  So this expression would be different in C++ 
 and D:

 a ? b : c = d

 In D it would be:

 (a ? b : c ) = d

 And in C++ would be:

 a ? b : (c = d)
That's https://issues.dlang.org/show_bug.cgi?id=14186
Wow, interesting that C precedence is different from C++ here. -Steve
Oct 12 2018
parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Friday, 12 October 2018 at 13:15:22 UTC, Steven Schveighoffer 
wrote:
 On 10/12/18 6:06 AM, Kagamin wrote:
 On Thursday, 11 October 2018 at 23:17:15 UTC, Jonathan Marler 
 wrote:
 [...]
That's https://issues.dlang.org/show_bug.cgi?id=14186
Wow, interesting that C precedence is different from C++ here.
It's C++ which the anormal one.
Oct 12 2018
prev sibling next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 11 October 2018 at 23:17:15 UTC, Jonathan Marler 
wrote:
 On Thursday, 11 October 2018 at 21:57:00 UTC, Jonathan M Davis 
 wrote:
 On Thursday, October 11, 2018 1:09:14 PM MDT Jonathan Marler 
 via Digitalmars-d wrote:
 On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson

 wrote:
 [...]
In c++ the ternary operator is the second most lowest precedence operator, just above the comma. You can see a table of each operator and their precendence here, I refer to it every so often: https://en.cppreference.com/w/cpp/language/operator_precedence Learning that the ternary operator has such a low precedence is one of those things that all programmers eventually run into...welcome to the club :) It looks like D has a similar table here (https://wiki.dlang.org/Operator_precedence). However, it doesn't appear to have the ternary operator in there. On that note, D would take it's precedence order from C/C++ unless there's a VERY good reason to change it.
The operator precedence matches in D. Because in principle, C code should either be valid D code with the same semantics as it had in C, or it shouldn't compile as D code, changing operator precedence isn't something that D is going to do (though clearly, the ternary operator needs to be added to the table). It would be a disaster for porting code if we did. - Jonathan M Davis
I had a look at the table again, looks like the ternary operator is on there, just called the "conditional operator". And to clarify, D's operator precedence is close to C/C++ but doesn't match exactly.
Please do not conflate C and C++. It is specifically on order of precedence of the ternary that the 2 languages differ. It is C++ and only C++ which has the unconventionnal order of precedence where the ternary has the same priority as the assign operators. ALL other C derived languages have a higher priority for the ternary than the assignments.
Oct 12 2018
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 11 October 2018 at 23:17:15 UTC, Jonathan Marler 
wrote:
 For example, the "Conditional operator" in D actually has a 
 higher priority than an assignment, but in C++ it's the same 
 and is evaluated right-to-left.  So this expression would be 
 different in C++ and D:

 a ? b : c = d

 In D it would be:

 (a ? b : c ) = d

 And in C++ would be:

 a ? b : (c = d)
This is now deprecated: int b = 1, c = 1; 1 ? b : c = 0; Deprecation: `1 ? b : c` must be surrounded by parentheses when next to operator `=` https://dlang.org/changelog/2.082.0.html#cond_assign
Oct 15 2018
prev sibling next sibling parent Neia Neutuladh <neia ikeran.org> writes:
On 10/11/2018 07:35 AM, James Japherson wrote:
 Took me about an hour to track this one down!
 
 A + (B == 0) ? 0 : C;
 
 D is evaluating it as
 
 (A + (B == 0)) ? 0 : C;
Friends don't let friends use the ternary operator except in trivial cases. This would be a good thing for a linter to check.
 The whole point of the parenthesis was to associate.
Yes. The expression would otherwise have been parsed as: ((A + B) == 0) ? 0 : C It might be a good idea to deprecate arithmetic with booleans, which would have caught this specific error.
Oct 11 2018
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, October 11, 2018 8:35:34 AM MDT James Japherson via 
Digitalmars-d wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;


 The whole point of the parenthesis was to associate.

 I usually explicitly associate precisely because of this!

 A + ((B == 0) ? 0 : C);

 In the ternary operator it should treat parenthesis directly to
 the left as the argument.

 Of course, I doubt this will get fixed but it should be noted so
 other don't step in the same poo.
When parens are used to affect operator precedence, it's _always_ by enclosing the expression. There may be a language out there that does it a different way, but if so, I've never heard of it. Certainly, major languages the same kind of precedence for the ternary operator that D does. I honestly have no clue how you could ever have gotten the idea that putting parens in front of something could affect operator precedence, since I don't think that I've ever seen anything work that way in any language ever. By putting the parens around (B == 0), you've told the compiler to treat that as a single expression regardless of what operator precendence would otherwise do. You haven't told it to do anything to any other operators. So, A + B == 0 has gone from being equivalent to (A + B) == 0 to A + (B == 0) but that has no effect whatsoever on the ternary operator or any other operator that has lower precedence. So, really, it seems to me that your misunderstanding of operator precedence and parens goes well beyond the ternary operator. Normally, if someone were going to be confused by the operator precedence of the ternary operator, I'd expect them to be confused about the precendence of stuff that happens to the right of the ternary operator when it's in a more complicated expression, not about what happens with parens used in the condition. That stuff is the same as you'd get in any if statement or while loop condition. - Jonathan M Davis
Oct 11 2018
parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 11 October 2018 at 23:17:57 UTC, Jonathan M Davis 
wrote:
 On Thursday, October 11, 2018 8:35:34 AM MDT James Japherson 
 via Digitalmars-d wrote:

 the way that D does, and they all have the same kind of 
 precedence for the ternary operator that D does.
No, the off man out is C++. it's the only one with the priority of the ternary equal to assignments. All other languages do it like C, i.e. with a higher priority for ?: C++ is the annoying one (as always) here.
Oct 12 2018
prev sibling parent Trass3r <un known.com> writes:
On Thursday, 11 October 2018 at 14:35:34 UTC, James Japherson 
wrote:
 Took me about an hour to track this one down!

 A + (B == 0) ? 0 : C;

 D is evaluating it as

 (A + (B == 0)) ? 0 : C;
That's why shouldn't compose it like that. It's been a constant source of bugs in C/C++ code: https://www.viva64.com/en/b/0583/#ID0E1CAC
Oct 11 2018