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

Reply via email to