Thread overview
The relationship of invariants and alias this
Nov 11, 2014
Mark Isaacson
Nov 11, 2014
Mark Isaacson
Nov 11, 2014
H. S. Teoh
Nov 11, 2014
Mark Isaacson
Nov 11, 2014
angel
Nov 11, 2014
Mark Isaacson
Nov 11, 2014
ZombineDev
November 11, 2014
I've encountered something which is probably intentional behavior, but that I think is interesting and may merit discussion. When you do "alias this" and have an invariant, any methods that are forwarded to the aliased member do not invoke your invariant methods.

This prevents me from writing a really sleek 10-liner to the tune of:

struct ValueRestrictedInteger(int lowerBound, int upperBound) {
  int value;
  alias value this;

  this (int rhs) { value = rhs; }

  invariant() {
    assert (value >= lowerBound && value <= upperBound);
  }

  void forDemonstrationOnly() {}
}

unittest {
  ValueRestrictedInteger!(0, 100) x = 0;
  x += 10;
  x -= 100; //This works, but I don't think it should
  x.forDemonstrationOnly(); //This causes the assertion to fire

  ValueRestrictedInteger!(0, 100) y = -100; //This also would hit the assertion
}

I realize that this is not the most efficient way to implement this concept and that the invariant gets compiled out in a -release build... I still think this is a pretty nifty piece of code whose semantics are not unreasonable. Why shouldn't an aliased method be subject to our invariants?
November 11, 2014
Should be noted that this behavior is the same regardless of whether or not I do an "alias this" on a primitive type as shown above or on a user defined type.
November 11, 2014
On Tue, Nov 11, 2014 at 05:03:00AM +0000, Mark Isaacson via Digitalmars-d wrote:
> Should be noted that this behavior is the same regardless of whether or not I do an "alias this" on a primitive type as shown above or on a user defined type.

Sounds like a bug. Please file an issue at http://issues.dlang.org/


T

-- 
It's amazing how careful choice of punctuation can leave you hanging:
November 11, 2014
On Tuesday, 11 November 2014 at 06:21:18 UTC, H. S. Teoh via Digitalmars-d wrote:
> On Tue, Nov 11, 2014 at 05:03:00AM +0000, Mark Isaacson via Digitalmars-d wrote:
>> Should be noted that this behavior is the same regardless of whether
>> or not I do an "alias this" on a primitive type as shown above or on a
>> user defined type.
>
> Sounds like a bug. Please file an issue at http://issues.dlang.org/
>
>
> T

Exciting/encouraging! Done. https://issues.dlang.org/show_bug.cgi?id=13710
November 11, 2014
Wait !
"x.value -= 100;" would call the invariant ?
Alias this only rewrites your expression:
"x -= 100;" becomes "x.value -= 100;"
No method is called. Then there is no reason (is there ?) to call the invariant.
If you would create getter/setter properties ...
November 11, 2014
On Tuesday, 11 November 2014 at 08:18:36 UTC, angel wrote:
> Wait !
> "x.value -= 100;" would call the invariant ?
> Alias this only rewrites your expression:
> "x -= 100;" becomes "x.value -= 100;"
> No method is called. Then there is no reason (is there ?) to call the invariant.
> If you would create getter/setter properties ...

Intriguing - thanks :). Looks like you found the reason for the current implementation; what you've written is the "lowering" of alias this. From a semantic perspective though, we are claiming that "value *is* this"; there is an IsA relationship and I'd argue that the invariant should still apply because we are modifying "this", in a way.

I totally see where you're coming from though; that's why I wanted to open the floor for discussion as to what the more desirable behavior is.
November 11, 2014
AFAIU, even if you had a getter in the alias this:

---
import std.stdio;

struct ValueRestrictedInteger(int lowerBound, int upperBound) {
  int value;
  auto ref get() { return value; }	
  alias get this;

  this (int rhs) { value = rhs; }

  invariant() {
    assert (value >= lowerBound && value <= upperBound);
  }

  void forDemonstrationOnly() {}
}
---

It would still not work:

---
unittest {
  ValueRestrictedInteger!(0, 100) x = 0;

  x -= 100; //is probably lowered to something like this:
  // 1) invariant();
  // 2) int* __temp = &x.value; // this is what get() does
  // 3) invariant();
  // 4) *__temp = *temp - 100;

  //...
}
---

Obviously, 4) will not trigger the invariant because it doesn't call any public functions.
November 11, 2014
On 11/11/14 6:48 AM, ZombineDev wrote:
> AFAIU, even if you had a getter in the alias this:
>
> ---
> import std.stdio;
>
> struct ValueRestrictedInteger(int lowerBound, int upperBound) {
>    int value;
>    auto ref get() { return value; }
>    alias get this;
>
>    this (int rhs) { value = rhs; }
>
>    invariant() {
>      assert (value >= lowerBound && value <= upperBound);
>    }
>
>    void forDemonstrationOnly() {}
> }
> ---
>
> It would still not work:
>
> ---
> unittest {
>    ValueRestrictedInteger!(0, 100) x = 0;
>
>    x -= 100; //is probably lowered to something like this:
>    // 1) invariant();
>    // 2) int* __temp = &x.value; // this is what get() does
>    // 3) invariant();
>    // 4) *__temp = *temp - 100;
>
>    //...
> }
> ---
>
> Obviously, 4) will not trigger the invariant because it doesn't call any
> public functions.

Correct, the only real solution is to wrap with the actual operators and opDispatch.

-Steve