www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fun surprising things

reply bauss <jj_1337 live.dk> writes:
What's the output of this program? (Try to figure it out without 
running it.)

```
int c = 0;

int a()
{
     return c++;
}

int b()
{
     return c--;
}

void main()
{
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
}
```

If your answer is "10" then you're wrong.

You can get the result and answer here: 
https://run.dlang.io/is/idZurR
Oct 31 2018
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
There isn't really any reason for it to be like this.

Assembly:

.text._Dmain	segment
	assume	CS:.text._Dmain
_Dmain:
		push	RBP
		mov	RBP,RSP
		sub	RSP,010h
		call	  int onlineapp.b() PLT32
		mov	RDI,RAX
		call	  pure nothrow  safe immutable(char)[] 
std.conv.to!(immutable(char)[]).to!(int).to(int) PLT32
		mov	RCX,RAX
		mov	R8,RDX
		mov	-010h[RBP],RCX
		mov	-8[RBP],R8
		call	  int onlineapp.a() PLT32
		mov	RDI,RAX
		call	  pure nothrow  safe immutable(char)[] 
std.conv.to!(immutable(char)[]).to!(int).to(int) PLT32
		mov	RSI,RAX
		mov	R8,-8[RBP]
		mov	RCX,-010h[RBP]
		mov	RDI,TypeInfo_Aya.__init GOTPCREL[RIP]
		call	  _d_arraycatT PLT32
		mov	RDI,RAX
		mov	RSI,RDX
		call	   safe void 
std.stdio.writeln!(immutable(char)[]).writeln(immutable(char)[]) PLT32
		xor	EAX,EAX
		leave
		ret
Oct 31 2018
prev sibling next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:

 If your answer is "10" then you're wrong.
https://dlang.org/spec/expression.html#order-of-evaluation
 2. Implementation Defined:
    The order of evaluation of the operands of AssignExpression.
 ...
So indeed, both answers are "right".
Oct 31 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/31/18 10:18 AM, Stanislav Blinov wrote:
 On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 
 If your answer is "10" then you're wrong.
https://dlang.org/spec/expression.html#order-of-evaluation
 2. Implementation Defined:
    The order of evaluation of the operands of AssignExpression.
 ...
So indeed, both answers are "right".
No, the correct answer is "01", according to left to right evaluation. a should be evaluated first, which returns 0, and increments c by 1. Then b is evaluated, returning 1, and decrementing c by 1. But the assembly shows that this binary expression is evaluated right to left, which is not what the spec says. BUT, the lowering in the compiler could change arr1 ~ arr2 to a function call (arrayCat(arr1, arr2)), which then allows the compiler to evaluate in right to left order. So the inconsistency in the spec ("binary expressions are left to right", "function arguments are implementation defined order") leaves a gaping hole. What happens when a binary expression lowers to a function call? -Steve
Oct 31 2018
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 31 October 2018 at 15:00:26 UTC, Steven 
Schveighoffer wrote:
 On 10/31/18 10:18 AM, Stanislav Blinov wrote:
 On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 
 If your answer is "10" then you're wrong.
https://dlang.org/spec/expression.html#order-of-evaluation
 2. Implementation Defined:
    The order of evaluation of the operands of 
 AssignExpression.
 ...
So indeed, both answers are "right".
No, the correct answer is "01", according to left to right evaluation. a should be evaluated first, which returns 0, and increments c by 1.
Not in this case, no.
 Then b is evaluated, returning 1, and decrementing c by 1.

 But the assembly shows that this binary expression is evaluated 
 right to left, which is not what the spec says.

 BUT, the lowering in the compiler could change arr1 ~ arr2 to a 
 function call (arrayCat(arr1, arr2)), which then allows the 
 compiler to evaluate in right to left order.
Could, or could not. It's implementation-defined.
 So the inconsistency in the spec ("binary expressions are left 
 to right", "function arguments are implementation defined 
 order") leaves a gaping hole. What happens when a binary 
 expression lowers to a function call?
You're misreading the spec. The relevant part is the one I quoted. There's no left-to-right evaluation rule here. It's not a binary expression, it's an argument list; in this case - one argument, which is a CatExpression, which is an AssignExpression, the order of evaluation of it's operands is implementation-defined.
Oct 31 2018
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 31.10.18 18:28, Stanislav Blinov wrote:
 
 So the inconsistency in the spec ("binary expressions are left to 
 right", "function arguments are implementation defined order") leaves 
 a gaping hole. What happens when a binary expression lowers to a 
 function call?
You're misreading the spec. The relevant part is the one I quoted. There's no left-to-right evaluation rule here. It's not a binary expression, it's an argument list; in this case - one argument, which is a CatExpression, which is an AssignExpression, the order of evaluation of it's operands is implementation-defined.
I think you are the one who is misreading the spec here.
Oct 31 2018
parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 31 October 2018 at 22:54:50 UTC, Timon Gehr wrote:

 I think you are the one who is misreading the spec here.
Indeed. I'm sorry, I'm gonna go stand in that shaming corner for a while...
Nov 03 2018
prev sibling parent Rubn <where is.this> writes:
On Wednesday, 31 October 2018 at 17:28:43 UTC, Stanislav Blinov 
wrote:
 So the inconsistency in the spec ("binary expressions are left 
 to right", "function arguments are implementation defined 
 order") leaves a gaping hole. What happens when a binary 
 expression lowers to a function call?
You're misreading the spec. The relevant part is the one I quoted. There's no left-to-right evaluation rule here. It's not a binary expression, it's an argument list; in this case - one argument, which is a CatExpression, which is an AssignExpression, the order of evaluation of it's operands is implementation-defined.
What you quoted only relates to "AssignExpression", that means in the following case: a() += b(); Either a() or b() can be evaluated first, it is not defined. Ultimately what is being evaluated is: opBinary!"~"(to!string(a()), to!string(b())); Which the spec clearly specifies is calculated in left to right order:
 Binary expressions are evaluated in strictly left-to-right 
 order. Function arguments are evaluated in strictly 
 left-to-right order for functions with extern (D) linkage.
 
 assert(text(++i, ++i) == "1415"); // left to right evaluation 
 of arguments
Oct 31 2018
prev sibling next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 What's the output of this program? (Try to figure it out 
 without running it.)

 ```
 int c = 0;

 int a()
 {
     return c++;
 }

 int b()
 {
     return c--;
 }

 void main()
 {
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
 }
 ```

 If your answer is "10" then you're wrong.

 You can get the result and answer here: 
 https://run.dlang.io/is/idZurR
01 is the other possible result. It all depends in what order a and b are called. ~ is not a sequence point (does D even have that typical C notion of sequence points?) so the order of evaluation is at the discretion of the compiler. therefore -10 or 01 are both right.
Oct 31 2018
next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Wednesday, 31 October 2018 at 14:54:48 UTC, Patrick Schluter 
wrote:
 On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 What's the output of this program? (Try to figure it out 
 without running it.)

 ```
 int c = 0;

 int a()
 {
     return c++;
 }

 int b()
 {
     return c--;
 }

 void main()
 {
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
 }
 ```

 If your answer is "10" then you're wrong.

 You can get the result and answer here: 
 https://run.dlang.io/is/idZurR
01 is the other possible result. It all depends in what order a and b are called. ~ is not a sequence point (does D even have that typical C notion of sequence points?) so the order of evaluation is at the discretion of the compiler. therefore -10 or 01 are both right.
And I just checked, ldc gives 01 as response.
Oct 31 2018
parent Eugene Wissner <belka caraus.de> writes:
On Wednesday, 31 October 2018 at 15:00:52 UTC, Patrick Schluter 
wrote:
 And I just checked, ldc gives 01 as response.
and gdc "-10"
Oct 31 2018
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 31.10.18 15:54, Patrick Schluter wrote:
 On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 What's the output of this program? (Try to figure it out without 
 running it.)

 ```
 int c = 0;

 int a()
 {
     return c++;
 }

 int b()
 {
     return c--;
 }

 void main()
 {
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
 }
 ```

 If your answer is "10" then you're wrong.

 You can get the result and answer here: https://run.dlang.io/is/idZurR
01 is the other possible result. It all depends in what order a and b are called. ~ is not a sequence point (does D even have that typical C notion of sequence points?) so the order of evaluation is at the discretion of the compiler. therefore -10 or 01 are both right.
No, the spec says that 01 is the only allowed result. https://dlang.org/spec/expression.html#order-of-evaluation Concatenation is a binary expression.
Oct 31 2018
prev sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 31 October 2018 at 14:54:48 UTC, Patrick Schluter 
wrote:
 01 is the other possible result. It all depends in what order a 
 and b are called. ~ is not a sequence point (does D even have 
 that typical C notion of sequence points?) so the order of 
 evaluation is at the discretion of the compiler.
In C function call is a sequence point, undefined order applies only to expressions without sequence points. BTW to!string allocates memory from GC which involves a mutex lock, which can't possibly have undefined order.
Nov 02 2018
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Friday, 2 November 2018 at 08:15:28 UTC, Kagamin wrote:
 On Wednesday, 31 October 2018 at 14:54:48 UTC, Patrick Schluter 
 wrote:
 01 is the other possible result. It all depends in what order 
 a and b are called. ~ is not a sequence point (does D even 
 have that typical C notion of sequence points?) so the order 
 of evaluation is at the discretion of the compiler.
In C function call is a sequence point, undefined order applies only to expressions without sequence points.
Yes, but irrelevant here. f(g(x), h(y)) doesn't mandate in which order g(x) and h(y) are evaluated even though x will be evaluated before g is called and y before h is called. That's all the sequence point of function call can do (btw, I wouldn't rely, in C, on that sequence point as inlining often breaks it).
BTW to!string
 allocates memory from GC which involves a mutex lock, which 
 can't possibly have undefined order.
Same remark, doesn't change anything. f(g(x), h(y)) only garantees these 2 orders push x sequence point call g push g(x) push y sequence point call h push h(y) sequence point call f or push y sequence point call h push h(y) push x sequence point call g push g(x) sequence point call f theoretically also parallel evaluation of g(x) and h(y) but in practice I've never seen it. But all this is moot as D is not C and the specs of D mandate the order of evaluation for ~. dmd and gdc clearly violate the specs and either the specs are loosened or the compiler is corrected.
Nov 02 2018
parent Johan Engelen <j j.nl> writes:
On Friday, 2 November 2018 at 09:27:50 UTC, Patrick Schluter 
wrote:
 But all this is moot as D is not C and the specs of D mandate 
 the order of evaluation for ~. dmd and gdc clearly violate the 
 specs and either the specs are loosened or the compiler is 
 corrected.
Indeed. We found this a very serious bug and made LDC respect this part of the language specification since version 1.8.0. If you still find cases where LDC does not respect strict left-to-right evaluation order, please file a bug with LDC, thanks! -Johan
Nov 03 2018
prev sibling next sibling parent reply bachmeier <no spam.net> writes:
On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 What's the output of this program? (Try to figure it out 
 without running it.)

 ```
 int c = 0;

 int a()
 {
     return c++;
 }

 int b()
 {
     return c--;
 }

 void main()
 {
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
 }
 ```

 If your answer is "10" then you're wrong.

 You can get the result and answer here: 
 https://run.dlang.io/is/idZurR
Hopefully the pain of working with such code teaches one to not write such code. Yes, you can write FORTRAN in D, but that doesn't mean it's a good idea to do so.
Oct 31 2018
parent Jonathan Marler <johnnymarler gmail.com> writes:
On Wednesday, 31 October 2018 at 16:05:47 UTC, bachmeier wrote:
 On Wednesday, 31 October 2018 at 14:00:14 UTC, bauss wrote:
 What's the output of this program? (Try to figure it out 
 without running it.)

 ```
 int c = 0;

 int a()
 {
     return c++;
 }

 int b()
 {
     return c--;
 }

 void main()
 {
     import std.conv : to;
     import std.stdio : writeln;

     writeln(to!string(a) ~ to!string(b));
 }
 ```

 If your answer is "10" then you're wrong.

 You can get the result and answer here: 
 https://run.dlang.io/is/idZurR
Hopefully the pain of working with such code teaches one to not write such code. Yes, you can write FORTRAN in D, but that doesn't mean it's a good idea to do so.
https://www.youtube.com/watch?v=lbp6vwdnE0k&start=1296
Oct 31 2018
prev sibling next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 10/31/18 10:00 AM, bauss wrote:
 What's the output of this program? (Try to figure it out without running 
 it.)
 
 ```
 int c = 0;
 
 int a()
 {
      return c++;
 }
 
 int b()
 {
      return c--;
 }
I'm not sure I understand the point here. I mean, who writes code like that? Pre/Post-incement operators? Combined with globals? To me that just smells of trouble. So if the issue is that the above code should result in an error but currently doesn't, then...honestly, I'm totally onboard with that, because it definitely strikes me as bad code. But I'm unclear about the details. What *exact* part of it should be the error? To me, that's the part that's not obvious. Intuitively, it strikes me as as bad code, but to me, it's not obvious what *specifically* the compiler should be objecting to. So, at least for me, spelling this out in more detail would really help.
Oct 31 2018
next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Thu, 01 Nov 2018 00:39:17 -0400, Nick Sabalausky (Abscissa) wrote:

 On 10/31/18 10:00 AM, bauss wrote:
 What's the output of this program? (Try to figure it out without
 running it.)
 
 ```
 int c = 0;
 
 int a()
 {
      return c++;
 }
 
 int b()
 {
      return c--;
 }
I'm not sure I understand the point here. I mean, who writes code like that? Pre/Post-incement operators? Combined with globals? To me that just smells of trouble.
class Connection { Socket socket; int readInt() { /* read an int from the socket somehow */ } float readFloat() { /* read a float from the socket */ } void doSomethingFunky() { writeln(to!string(readInt()) ~ to!string(readFloat())); } } It's not great, but it doesn't deal with pre/post increment operators or global variables. The point is that both sides of the expression have side effects altering the same data. What happens should be well-defined. A global variable and increment operators are condensed ways of showing what's going on.
 So if the issue is that the above code should result in an error but
 currently doesn't, then...honestly, I'm totally onboard with that,
 because it definitely strikes me as bad code. But I'm unclear about the
 details. What *exact* part of it should be the error? To me, that's the
 part that's not obvious. Intuitively, it strikes me as as bad code, but
 to me, it's not obvious what *specifically* the compiler should be
 objecting to. So, at least for me, spelling this out in more detail
 would really help.
If you don't introduce whole-program analysis, your error is that the operands are not pure / immutable. If you do introduce whole-program analysis, your error is that both sides of the expression modify the same mutable state, and the return value is dependent on that state. Except that's only applicable if the spec doesn't define the appropriate order of evaluation.
Oct 31 2018
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, October 31, 2018 10:58:55 PM MDT Neia Neutuladh via 
Digitalmars-d wrote:
 Except that's only applicable if the spec doesn't define the appropriate
 order of evaluation.
The spec defines left-to-right evaluation, but since _most_ programming languages don't define the order of evaluation so that the compiler can efficiently reorder operations, I'd honestly argue that it's just plain bad practice in general to rely on the order of evaluation of expressions like this. It's the sort of thing that's just begging for trouble. Sure, if D defines it, plays by its own rules properly, you understand its rules properly, and you write your code accordingly, you can theoretically avoid problems, but if you get in the habit of writing such code, you're just begging to shoot yourself in the foot as soon as you have to write code in any other language. And even within D, if the expression is complicated, the exact order of operations can get hard to understand. So, writing anything that's even vaguely like foo() + bar() where foo() or bar() depend on one another at all is just asking for it. It's a code smell, and it should be avoided. - Jonathan M Davis
Oct 31 2018
prev sibling parent Brad Roberts <braddr puremagic.com> writes:
On 10/31/2018 10:57 PM, Jonathan M Davis via Digitalmars-d wrote:
 On Wednesday, October 31, 2018 10:58:55 PM MDT Neia Neutuladh via
 Digitalmars-d wrote:
 Except that's only applicable if the spec doesn't define the appropriate
 order of evaluation.
The spec defines left-to-right evaluation, but since _most_ programming languages don't define the order of evaluation so that the compiler can efficiently reorder operations, I'd honestly argue that it's just plain bad practice in general to rely on the order of evaluation of expressions like this. It's the sort of thing that's just begging for trouble. Sure, if D defines it, plays by its own rules properly, you understand its rules properly, and you write your code accordingly, you can theoretically avoid problems, but if you get in the habit of writing such code, you're just begging to shoot yourself in the foot as soon as you have to write code in any other language. And even within D, if the expression is complicated, the exact order of operations can get hard to understand. So, writing anything that's even vaguely like foo() + bar() where foo() or bar() depend on one another at all is just asking for it. It's a code smell, and it should be avoided. - Jonathan M Davis
That's a not unreasonable argument for not specifying the order of evaluation.  But that's irrelevant since D has defined the order so not following it is a bug that must be fixed.
Nov 01 2018
prev sibling parent bauss <jj_1337 live.dk> writes:
On Thursday, 1 November 2018 at 04:39:17 UTC, Nick Sabalausky 
(Abscissa) wrote:
 On 10/31/18 10:00 AM, bauss wrote:
 What's the output of this program? (Try to figure it out 
 without running it.)
 
 ```
 int c = 0;
 
 int a()
 {
      return c++;
 }
 
 int b()
 {
      return c--;
 }
I'm not sure I understand the point here. I mean, who writes code like that? Pre/Post-incement operators? Combined with globals? To me that just smells of trouble.
The point is not what the functions do, but the order of evaluation. It was just a simple example to reproduce it.
Nov 01 2018
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, November 1, 2018 1:42:27 AM MDT Brad Roberts via Digitalmars-d 
wrote:
 On 10/31/2018 10:57 PM, Jonathan M Davis via Digitalmars-d wrote:
 On Wednesday, October 31, 2018 10:58:55 PM MDT Neia Neutuladh via

 Digitalmars-d wrote:
 Except that's only applicable if the spec doesn't define the
 appropriate
 order of evaluation.
The spec defines left-to-right evaluation, but since _most_ programming languages don't define the order of evaluation so that the compiler can efficiently reorder operations, I'd honestly argue that it's just plain bad practice in general to rely on the order of evaluation of expressions like this. It's the sort of thing that's just begging for trouble. Sure, if D defines it, plays by its own rules properly, you understand its rules properly, and you write your code accordingly, you can theoretically avoid problems, but if you get in the habit of writing such code, you're just begging to shoot yourself in the foot as soon as you have to write code in any other language. And even within D, if the expression is complicated, the exact order of operations can get hard to understand. So, writing anything that's even vaguely like foo() + bar() where foo() or bar() depend on one another at all is just asking for it. It's a code smell, and it should be avoided. - Jonathan M Davis
That's a not unreasonable argument for not specifying the order of evaluation. But that's irrelevant since D has defined the order so not following it is a bug that must be fixed.
Oh, since the spec says that the order is defined, if the implementation doesn't follow it, it should definitely be fixed. I definitely won't argue that. But just the same, any code that relies on it is just begging for trouble and is bad code IMHO. Though honestly, I expect that it's a bit of a nightmare on the implementation end of things to keep the order of evaluation correct when you start lowering one construct to another. That's no excuse. The spec needs to either be followed or changed, but it doesn't surprise me if such bugs exist. - Jonathan M Davis
Nov 01 2018