January 31, 2019
On Thursday, 31 January 2019 at 21:44:53 UTC, jmh530 wrote:
> It doesn't compile with dip1000 without first giving the getter functions a return attribute for this.

But it still compiles with -dip1000 once you give x() and y() return attributes, even though what's happening is clearly different from what the user wants (and the compiler has enough info to know that).
January 31, 2019
On 1/31/19 4:46 PM, Olivier FAURE wrote:
> On Thursday, 31 January 2019 at 18:31:22 UTC, Steven Schveighoffer wrote:
>> BTW, the DIP discusses how to annotate these rare situations:
>>
>> int doubleMyValue(ref int x) { ... }
>> @disable int doubleMyValue(int x);
>>
> 
> I don't think that's a solution. The problem is in the getter method, not in doubleMyValue. If nothing else, since the DIP is designed to work on existing functions, it could happen on doubleMyValue functions which would be both designed by and used by people completely unaware of DIP-1016.

How is the problem not in doubleMyValue? It's sole purpose is to update an lvalue. It is the perfect candidate to mark with @disable for rvalues.

-Steve
January 31, 2019
On 1/31/19 4:42 PM, Olivier FAURE wrote:
> On Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:
>> Yeah, that's already a thing that ref in D doesn't protect against:
> 
> It took me a while to understand what the compiler was doing.
> 
> This really feels like something that shouldn't compile.

The problem is that `this` is passed by reference EVEN for rvalues. Knowing this, you can construct difficult situations, but normally these don't appear in the wild.

If you've ever tried to make a simple math wrapper type, you would see how this is weird. And it has been like this since the beginning of D2.

You get things like:

a + Foo(1); // error
Foo(1) + a; // OK!

That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.

-Steve
January 31, 2019
On 1/31/2019 1:46 PM, Andrei Alexandrescu wrote:
> The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.

That's why it's problematic to have a rule that rvalues can be implicitly converted, but not lvalues. There's not a hard line between lvalues and rvalues. For example,

  foreach(i; 0..2)
  {
    int[] a = [1, 2];
    assert(a[0] == 1]);
    a[0] = 3;  // will this cause the assert to fail?
  }
January 31, 2019
On 1/31/19 4:46 PM, Andrei Alexandrescu wrote:
> On 1/31/19 4:42 PM, Olivier FAURE wrote:
>> On Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:
>>> Yeah, that's already a thing that ref in D doesn't protect against:
>>
>> It took me a while to understand what the compiler was doing.
>>
>> This really feels like something that shouldn't compile.
> 
> The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.

No, because those calls might actually do something with side effects! rvalues can contain other references.

The only way to fix this would be to overload member functions on the lvalue-ness of `this`. I don't recommend this at all, as I see this as a weird but rare problem that doesn't affect most D code.

-Steve
January 31, 2019
On Thursday, 31 January 2019 at 21:50:19 UTC, Olivier FAURE wrote:
> On Thursday, 31 January 2019 at 21:44:53 UTC, jmh530 wrote:
>> It doesn't compile with dip1000 without first giving the getter functions a return attribute for this.
>
> But it still compiles with -dip1000 once you give x() and y() return attributes, even though what's happening is clearly different from what the user wants (and the compiler has enough info to know that).

Agreed. I had checked that it didn't work and as I figured out how to get it work I got distracted reading the documentation and return and scope.
January 31, 2019
On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:
> [snip]
>
> That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.
>

The way you put it makes it sound like a bug...

I don't know if it helps, but below compiles without error.

struct Foo
{
   private int _x;
   int* x() { return &_x; }
}

struct Bar
{
   private Foo _y;
   Foo* y() { return &_y; }
   void y(Foo foo) { _y = foo; }
}

void main() {
    Foo a = Foo(1);
    assert(*a.x == 1);
    *a.x *= 2;
    assert(*a.x == 2);

    Bar b;
    b.y = Foo(1);
    assert(*b.y.x == 1);
    *b.y.x *= 2;
    assert(*b.y.x == 2);
}
January 31, 2019
On Thu, Jan 31, 2019 at 10:26:39PM +0000, jmh530 via Digitalmars-d-announce wrote:
> On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:
[...]
> > That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.
> > 
> 
> The way you put it makes it sound like a bug...
> 
> I don't know if it helps, but below compiles without error.
> 
> struct Foo
> {
>    private int _x;
>    int* x() { return &_x; }
> }
> 
> struct Bar
> {
>    private Foo _y;
>    Foo* y() { return &_y; }
>    void y(Foo foo) { _y = foo; }
> }
> 
> void main() {
>     Foo a = Foo(1);
>     assert(*a.x == 1);
>     *a.x *= 2;
>     assert(*a.x == 2);
> 
>     Bar b;
>     b.y = Foo(1);
>     assert(*b.y.x == 1);
>     *b.y.x *= 2;
>     assert(*b.y.x == 2);
> }

Why is it a problem that this code compiles without error?


T

-- 
Perhaps the most widespread illusion is that if we were in power we would behave very differently from those who now hold it---when, in truth, in order to get power we would have to become very much like them. -- Unknown
January 31, 2019
On Thursday, 31 January 2019 at 22:00:10 UTC, Walter Bright wrote:
> On 1/31/2019 1:46 PM, Andrei Alexandrescu wrote:
>> The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.
>
> That's why it's problematic to have a rule that rvalues can be implicitly converted, but not lvalues. There's not a hard line between lvalues and rvalues. For example,
>
>   foreach(i; 0..2)
>   {
>     int[] a = [1, 2];
>     assert(a[0] == 1]);
>     a[0] = 3;  // will this cause the assert to fail?
>   }

Why would it cause the assert to fail? A new array is constructed each loop.
February 01, 2019
On Thursday, 31 January 2019 at 22:35:26 UTC, H. S. Teoh wrote:
> On Thu, Jan 31, 2019 at 10:26:39PM +0000, jmh530 via Digitalmars-d-announce wrote:
>> On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:
> [...]
>> > That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.
>> > 
>> 
>> The way you put it makes it sound like a bug...
>> 
>> I don't know if it helps, but below compiles without error.
>> 
>> struct Foo
>> {
>>    private int _x;
>>    int* x() { return &_x; }
>> }
>> 
>> struct Bar
>> {
>>    private Foo _y;
>>    Foo* y() { return &_y; }
>>    void y(Foo foo) { _y = foo; }
>> }
>> 
>> void main() {
>>     Foo a = Foo(1);
>>     assert(*a.x == 1);
>>     *a.x *= 2;
>>     assert(*a.x == 2);
>> 
>>     Bar b;
>>     b.y = Foo(1);
>>     assert(*b.y.x == 1);
>>     *b.y.x *= 2;
>>     assert(*b.y.x == 2);
>> }
>
> Why is it a problem that this code compiles without error?
>
>
> T

Sorry if I didn't really complete my thought.

The code below corresponds to the ref version mentioned above and gets the same error originally reported. The only difference is that I use Foo instead of Foo* for the getter in Bar. So if you instead make that member function a ref function, then it also compiles without error (regardless of if you use the doubleMyValue function or not).

So they were right that the issue is with the getter. However, you're not really protected in any way if you have a ref getter at one point in a chain and a non-ref getter somewhere else. Making all the getters auto ref also avoids the issue.

struct Foo
{
   private int _x;
   ref int x() { return _x; }
}

struct Bar
{
   private Foo _y;
   Foo y() { return _y; }
   void y(Foo foo) { _y = foo; }
}

void main() {
    Foo a = Foo(1);
    assert(a.x == 1);
    a.x *= 2;
    assert(a.x == 2);

    Bar b;
    b.y = Foo(1);
    assert(b.y.x == 1);
    b.y.x *= 2;
    assert(b.y.x == 2);
}