October 31, 2018
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.
October 31, 2018
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.
November 01, 2018
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

November 01, 2018
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.
November 01, 2018
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.
October 31, 2018
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



November 01, 2018
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.
November 01, 2018
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.


November 01, 2018
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



November 02, 2018
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.