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