DIP23 draft: Fixing properties redux

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Feb 3 09:34:43 PST 2013


On 2/3/13 7:37 AM, kenji hara wrote:
> 1. When a function is annotated with @property, it cannot be called with
> parenthesis syntax.
> 2. 0-arg functions which not annotated with @property can be called
> without parentheses.
> 3. Ref return getter can make "auxiliary setter", if formal getter is
> missing.
> 4. `typeof(exp)` never returns "function type". In other words, the
> actual type of `exp` and `typeof(exp)` should be same.
> 5. Both `&prop` and `&func` should return function pointer / delegate object
> 6. UFCS CAN NOT call global setter by getter syntax.

Yah. There is still some inconsistency, but I think there's no way to be 
100% consistent. For properties &a.prop is not the same as &(a.prop), 
which is unlike other expressions.

> I think that 4 to 6 are important points of this DIP. Based on the
> rules, I could write an exhaustive test case.

I'll insert comments inline.

> alias Type = int;
>
> unittest
> {
>      struct S
>      {
>          @property Type foo();       // formal getter
>          @property void bar(Type);   // formal setter
>          @property ref Type baz();   // ref return getter == auxiliary
> setter
>      }
>      S s;
>      static assert( __traits(compiles, { s.foo;     }));
>      static assert(!__traits(compiles, { s.foo();   }));
>      static assert(is(typeof(s.foo) == Type));
>      static assert(is(typeof(&s.foo) == Type delegate()));

Yes, great. You may want to also add:

static assert(!__traits(compiles, { auto p = &(s.foo);   }));

because that would apply & to the Type rvalue returned by s.foo.

>      static assert( __traits(compiles, { s.bar = 1; }));
>      static assert(!__traits(compiles, { s.bar(1);  }));
>      static assert(is(typeof(s.bar)) == false);
>      static assert(is(typeof(&s.bar) == void delegate(Type)));

Yes. Also:

static assert(is(typeof(s.bar = 1) == void));

>      static assert( __traits(compiles, { s.baz;     }));
>      static assert(!__traits(compiles, { s.baz();   }));
>      static assert( __traits(compiles, { s.baz = 1; }));
>      static assert(is(typeof(s.baz) == Type));
>      static assert(is(typeof(&s.foo) == ref Type delegate()));

Yes, assuming you meant "baz" on the last line, too.

> }
> unittest
> {
>      struct S
>      {
>          Type foo();         // 0-arg function
>          void bar(Type n);   // 1-arg function
>          ref Type baz();     // 0-arg ref return function
>      }
>      S s;
>      static assert( __traits(compiles, { s.foo;     }));
>      static assert( __traits(compiles, { s.foo();   }));
>      static assert(is(typeof(s.foo) == Type));
>      static assert(is(typeof(&s.foo) == Type delegate()));

Correct. Also add:

static assert(!is(typeof(&(s.foo))));

>      static assert(!__traits(compiles, { s.bar = 1; }));
>      static assert( __traits(compiles, { s.bar(1);  }));
>      static assert(is(typeof(s.bar)) == false);
>      static assert(is(typeof(&s.bar) == void delegate(Type)));

Correct. Also:

static assert(!is(typeof(&(s.bar))));

because the expression s.bar is meaningless. (This is NEW BEHAVIOR.) The 
basic idea here is to disallow expressions that can't be typed.

>      static assert( __traits(compiles, { s.baz;     }));
>      static assert( __traits(compiles, { s.baz = 1; }));
>      static assert( __traits(compiles, { s.baz();   }));
>      static assert(is(typeof(s.baz) == Type));
>      static assert(is(typeof(&s.baz) == ref Type delegate()));
> }

Correct. Also:

static assert(is(typeof(&(s.baz)) == Type*));

> @property Type foo();

I'm not sure we should allow this at module level. (And there is no way 
to write a corresponding setter.)

> @property void bar(Type);
> @property ref Type baz();

I think we should disallow this as well.

> unittest
> {
>      static assert( __traits(compiles, { foo;     }));
>      static assert(!__traits(compiles, { foo();   }));
>      static assert(is(typeof(foo) == Type));
>      static assert(is(typeof(&foo) == Type function()));

If we disallow top-level global @properties neither of these will 
compile. If we do allow them, then the asserts would pass.

>      static assert( __traits(compiles, { bar = 1; }));
>      static assert(!__traits(compiles, { bar(1);  }));
>      static assert(is(typeof(bar)) == false);
>      static assert(is(typeof(&bar) == Type function()));

Nope, all of these are getters for int. The following should compile 
instead:

static assert( __traits(compiles, { 1.bar; }));
static assert(!__traits(compiles, { bar(1);  }));
static assert(is(typeof(bar)) == false);
static assert(is(typeof(&bar) == void function(int)));

>      static assert( __traits(compiles, { baz;       }));
>      static assert(!__traits(compiles, { baz();     }));
>      static assert( __traits(compiles, { baz = 1;   }));
>      static assert(!__traits(compiles, { baz() = 1; }));
>      static assert(is(typeof(baz) == Type));
>      static assert(is(typeof(&baz) == ref Type function()));

If we allow top-level properties with 0 parameters, these should inded 
compile.

> }
>
> @property Type foh(Type);

This is always a getter for Type returning a Type.

> @property void bah(Type n, Type m);

This is always a setter having Type on the left-hand side and Type on 
the right-hand side.

> @property ref Type bas(Type);

This is always a getter for Type.

> Type hoo(Type);
> void var(Type, Type);
> ref Type vaz(Type);
>
> unittest
> {
>      static assert( __traits(compiles, { foh = 1; }) &&
> !__traits(compiles, { hoo = 1; }));

No, replace with:

static assert( __traits(compiles, { 1.foh; }) &&
     !__traits(compiles, { hoo = 1; }));

>      static assert(!__traits(compiles, { foh(1);  }) &&
>   __traits(compiles, { hoo(1);  }));

Correct.

>      static assert(!__traits(compiles, { 1.foh;   }) &&
>   __traits(compiles, { 1.hoo;   }));

Incorrect, foh is a getter for Type so the first should work.

static assert(__traits(compiles, { 1.foh;   }) &&
    __traits(compiles, { 1.hoo;   }));

>      static assert(!__traits(compiles, { 1.foh(); }) &&
>   __traits(compiles, { 1.hoo(); }));

This is identical to the one above.

>      static assert(!__traits(compiles, { bah(1, 2); }) &&
>   __traits(compiles, { var(1, 2); }));

Correct.

>      static assert( __traits(compiles, { 1.bah = 2; }) &&
> !__traits(compiles, { 1.var = 2; }));

Correct.

>      static assert(!__traits(compiles, { 1.bah(2);  }) &&
>   __traits(compiles, { 1.var(2);  }));

First expression fails.

>      static assert( __traits(compiles, { bas = 1;     }) &&
> !__traits(compiles, { vaz = 1;     }));

Both branches fail.

>      static assert(!__traits(compiles, { bas(1);      }) &&
>   __traits(compiles, { vaz(1);      }));

Correct.

>      static assert(!__traits(compiles, { bas(1) = 2;  }) &&
>   __traits(compiles, { vaz(1) = 2;  }));

Correct.

>      static assert(!__traits(compiles, { 1.bas;       }) &&
>   __traits(compiles, { 1.vaz;       }));

The first branch fails because 1.bas is legit.

>      static assert(!__traits(compiles, { 1.bas = 2;   }) &&
>   __traits(compiles, { 1.vaz = 2;   }));

First branch fails because 1.bas is legit and yields a ref int. Second 
branch passes.

>      static assert(!__traits(compiles, { 1.bas();     }) &&
>   __traits(compiles, { 1.vaz();     }));

Correct.

>      static assert(!__traits(compiles, { 1.bas() = 2; }) &&
>   __traits(compiles, { 1.vaz() = 2; }));
> }

Correct.

> Is this correct?

As noted above, I might have missed some. I will update the doc with a 
copy of your unittests. Thanks!


Andrei



More information about the Digitalmars-d mailing list