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