Search
```On Monday, 28 January 2013 at 16:55:40 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 16:50:37 UTC, Maxim Fomin wrote:
>> Than value of this expression (note, that evaluated expression is not b, it is b = c) is assigned to a.
>
> Quoting you (that was exactly part of standard I was referring too):
> "assignment expression has the value of the left operand"
>
> Left operand for (b = c) is b. Thus (b = c) has value of b. Value of b is b.getter(). Thus compiler is re-writing it wrong if we copy C rules.

You are mixing value of expression and expression itself. If assignment expression has value of right operand, it does not mean that in complex assignment like a = b = c , right part of second assignment disappears and it becomes a = b. It is still a = b = c , and a is assigned to value of the whole expression b = c, not just value of b. This means it is a.setter((b = c)) = > a.setter(b.setter(c.getter)). Note, that original program does exactly what is required by ISO C.

>> b.setter cannot be called prior to b.getter as in your example #1 due to sequence rules. The example #2 would be for expression a = c, b = c which is not a = b = c in the presence of properties.
>
> What sequence rules are you speaking about?

About that side effects are sequenced after evaluation, but it is actually irrelevant, because b.getter is not called.
```
```On Monday, 28 January 2013 at 16:55:40 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 16:50:37 UTC, Maxim Fomin wrote:
>> Than value of this expression (note, that evaluated expression is not b, it is b = c) is assigned to a.
>
> Quoting you (that was exactly part of standard I was referring too):
> "assignment expression has the value of the left operand"
>
> Left operand for (b = c) is b. Thus (b = c) has value of b. Value of b is b.getter(). Thus compiler is re-writing it wrong if we copy C rules.
>

Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).
```
```On Monday, 28 January 2013 at 17:20:13 UTC, Maxim Fomin wrote:
> ...

Those are C rules, do not forget it. Thus value of (b = c) IS equivalent to value of b when lvalues are considered. And you are perfectly allowed to forget about c and do a = b after b = c was evaluated. It is plain old data, after all, if b = c is not interchangeable with b, something is wrong.

At least I see nothing in standard that proves your statement.

>
>
> Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).

You are applying it wrong. Thing of it as of recursion.
1) evaluate "a = b = c"
2) evaluate a.set( (b = c).get() ), result of 1 will be a.get() ( if needed )
3) evaluate "b = c"
4) evaluate b.set( c.get() ), result of 3 will be b.get() ( if needed )
5) combine: a.set( b.set( c.get() ), b.get() )

Note that evaluation order for comma expression is defined, so "c.get() ), b.get()" is valid and correct code.

```
```On Monday, 28 January 2013 at 03:24:09 UTC, Steven Schveighoffer wrote:
> There are three intentions when creating a function w/ regards to properties:
>
> 1. You intend the function to be called without parentheses to clarify it is a property.
> 2. You intend the function to be only called with parentheses to clarify it is a function.
> 3. You don't care whether it's called with parentheses or not, because the name of the function is clearly a property or a function.

This is a good point. Possibly since I'm the author of the following proposal, but also because I hope it will garner greater consideration than it has so far, this is how you would do these with my suggested language features single-instance structs and opGet:

The new syntax changes:

struct __foo {}
__foo foo;

to:

foo struct {}

This makes single-instance structs as easy to write as lambda functions. Combining them with two new operator definitions opGet and... let me see... opDo should cover it... it would look like this:

struct Goo
{
int _n;
foo struct
{
int opGet() { return _n; } // Exactly like opCall, but may not be used with parentheses
}
}

Goo g;
g.foo(); // Error

A struct may have exactly one of opGet, opCall, and opDo.

foo struct
{
int opGet() { ... }
int opDo() { ... } // Error: a struct may not have both opGet and opDo
}

opDo does exactly the opposite of opGet: it mandates the uses of parentheses instead of prohibits them. opCall, on the other hand, permits either one freely.

foo struct { int opDo() { return 4; } }
foo; // Error: foo must be called with parentheses

Note how the single-instance struct does not store data of its own, but is used for its value as a namespace and its ability to overload variables. That doesn't mean you *can't* store data in it. It's just like any other struct except that is has what I was thinking of calling a Highlander type: There can be only one!
```
```On Monday, January 28, 2013 13:31:34 Jacob Carlborg wrote:
> > Having the compiler lower the following:
> >
> > @property int a;
> >
> > to
> >
> > private int __a;
> >
> > @property int a() {
> > return __a;
> > }
> > @property int a(int new_a) {
> >
> > __a=new_a;
> > return __a;
> >
> > }
>
> I would love that. But the setter should return void and the compiler should to property rewrites.

Both should work. It's more efficient to chain assignments if the setter returns a value, but chaining should still work if it returns void. It would just be lowered differently in that case.

- Jonathan M Davis
```
```On Monday, 28 January 2013 at 17:41:07 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 17:20:13 UTC, Maxim Fomin wrote:
>> ...
>
> Those are C rules, do not forget it. Thus value of (b = c) IS equivalent to value of b when lvalues are considered. And you are perfectly allowed to forget about c and do a = b after b = c was evaluated. It is plain old data, after all, if b = c is not interchangeable with b, something is wrong.
>
> At least I see nothing in standard that proves your statement.
>

You are still breaking expression a = b = c and mixing terms of value of expression and expression itself.

>>
>>
>> Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).
>
> You are applying it wrong. Thing of it as of recursion.

I applied exactly as you did.

> 1) evaluate "a = b = c"
> 2) evaluate a.set( (b = c).get() ), result of 1 will be a.get() ( if needed )

.get() is wrong here. Expression is a = b = c, not a = ( (b = c).get ))

> 3) evaluate "b = c"
> 4) evaluate b.set( c.get() ), result of 3 will be b.get() ( if needed )
> 5) combine: a.set( b.set( c.get() ), b.get() )

This breaks requirement from quoted paragraph that side effects are sequenced after value computations on both operands: computing value of b (b.get) happened after updating value (b.set).

In Bugzilla there were close to this case like following (do not remember #):

struct S { int a; int b; int c; }
...
S s1, s2;

s1 = { 1, s1.a 2 }

Compiler was wrongly applied side effects before evaluating s1.a (it should be zero, not 1).

> Note that evaluation order for comma expression is defined, so "c.get() ), b.get()" is valid and correct code.

Well, discussion went repetitive - I still insists that a = b = c should be evaluated as it is currently a.set(b=c) = > a.set(b.set(c)) => a.set(b.set(c.get)). By the way, this C# program also behaves as I understand rules:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class S
{
int _i;
public int i
{
get { Console.WriteLine("getter"); return _i; }
set { Console.WriteLine("setter"); _i = value; }
}
}

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
S s1 = new S() , s2 = new S(), s3 = new S();
s1.i = s2.i = s3.i;
}
}
}

getter
setter
setter
```
```On 2013-01-28 15:00, Maxim Fomin wrote:

> Returning void instead of int in the example break assignment chaining a
> = b = c. Besides, how such implicitly defined functions may call user
> defined code (check input validity, call events, etc.)?

No, the compiler should do a rewrite, as follows:

class Foo
{
int bar_;

@property int bar () { return bar_; }
@property void bar (int value) { bar_ = value; }
}

auto foo = new Foo;
int a = foo.bar = 3;

The above line should be rewritten as:

foo.bar = 3;
int a = foo.bar;

The compiler also need to rewrite the following:

struct Bar
{
int a;
}

class Foo
{
Bar bar_;

@property Bar bar () { return bar_; }
@property void bar (Bar value) { bar_ = value; }
}

auto foo = new Foo;
foo.bar.a = 3;

The above line should be rewritten to:

auto __tmp = foo.bar;
__tmp.a = 3;
foo.bar = __tmp;

If not, the value of "foo.bar.a" hasn't really changed since you returned a copy by value. If you instead return by reference you can bypass the setter using the getter.

--
/Jacob Carlborg
```
```On 2013-01-28 17:21, Steven Schveighoffer wrote:

> I think Jacob's point is that a = b = c would lower to:
>
> b = c;
> a = b;

This is how the semantics should be. This also shows a clear complete example:

> But I think it would be wasteful in the given case.  __a is already in
> the register, I think actually the return __a is a noop.
>
> In other cases, where the property value may be a large struct or
> whatnot, not returning the new value from a setter would make sense.
>
> It would be nice if the compiler made the right choice depending on
> whether you returned a value from the property or not.

Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

--
/Jacob Carlborg
```
```On Mon, 28 Jan 2013 15:00:41 -0500, Jacob Carlborg <doob@me.com> wrote:

> On 2013-01-28 17:21, Steven Schveighoffer wrote:

>> But I think it would be wasteful in the given case.  __a is already in
>> the register, I think actually the return __a is a noop.
>>
>> In other cases, where the property value may be a large struct or
>> whatnot, not returning the new value from a setter would make sense.
>>
>> It would be nice if the compiler made the right choice depending on
>> whether you returned a value from the property or not.
>
> Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

Like I said, the synthesizing of properties can be done via a library.
The compiler simply needs to call the getter when the setter returns void,
or return the setter's return value when it returns a value.

-Steve
```
```On Mon, 28 Jan 2013 15:00:41 -0500, Jacob Carlborg <doob@me.com> wrote:

> On 2013-01-28 17:21, Steven Schveighoffer wrote:

>> But I think it would be wasteful in the given case.  __a is already in
>> the register, I think actually the return __a is a noop.
>>
>> In other cases, where the property value may be a large struct or
>> whatnot, not returning the new value from a setter would make sense.
>>
>> It would be nice if the compiler made the right choice depending on
>> whether you returned a value from the property or not.
>
> Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

Like I said, the synthesizing of properties can be done via a library.
The compiler simply needs to call the getter when the setter returns void,
or return the setter's return value when it returns a value.

-Steve
```