Property Discussion Proposal 1

From D Wiki
Revision as of 15:24, 27 January 2013 by Dicebot (talk | contribs) (Created page with " # @property functions can't be called with parentheses: <code>x._property()</code> is illegal! (Normal functions are explicitly not affected by this rule!) ## The only except...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
  1. @property functions can't be called with parentheses: x._property() is illegal! (Normal functions are explicitly not affected by this rule!)
    1. The only exception to this rule: If the returned value can be called (delegate, function pointer, opCall), then x._property() calls the returned value! It essentially behaves exactly as a field would.
  2. Getting a reference is illegal: &x._property does not compile.
    1. Rationale: Returning a reference to a temporary is not the same as taking the address of a field and is therefore confusing and dangerous in generic code. A real pointer could be returned if the property getter returned a value by ref. But then it would only work for some properties, could potentially bypass setters, would break if a setter was added later...
  3. To detect if something is a property or a field, use __traits(isProperty, x._property)! Do not use any tricks in user code to detect if something is a property or field.
  4. The getter/setter function can be obtained via __trais: __traits(propertyGetter, x._property) __traits(propertySetter, x._property)
    1. The function types can be obtained like this: typeof(__traits(propertyGetter, x._property)) == int delegate()
  5. The type of the returned value is obtained with typeof: typeof(x._property) == int
  6. The compiler uses semantic rewriting to make @property access as similar as field access as possible.
  7. Property functions cannot return ref values (as this could bypass the setter)
  8. The setter may take it's parameter by ref.
  9. The -property compiler switch is removed
  10. @property on free functions
  11. A setters parameter type and the corresponding getters return type must match
    1. Two categories: UFCS properties and global properties (see example)
struct Type
{
    @property void native(int);
}

//Global properties:
//returns void, has 1 parameter --> global setter
@property void external1(int);
//returns !void, has 0 parameters --> global getter
@property int external1();

//Used like a global variable:
void a()
{
    external1 = 41;
    // 41.external1; is
    // invalid, a getter can't return void (void + 1 args is always a global variable property)
    external1++;
    assert(external1 == 42);
}

//UFCS properties:
//returns void, has 2 parameters --> global setter
@property void external2(Type, int);
//returns !void, has 1 parameters --> global getter
@property int external2(Type);

//external2 can only be called on a Type instance.
//calling it manually "external2(Type(), 42)" is invalid

//Used like a property/field declared on Type:
void a()
{
    Type t;
    t.external2 = 41;
    t.external2++;
    assert(t.external2 == 42);
    //all "t.external1" combinations are invalid! 
}


Unified test suite
alias Deleg = int delegate();

struct Test
{
    int var;
    
    @property void native2(int param = 1337) { } //invalid. default param creates invalid case with no args which returns void
    @property void native(int) { } //OK
    @property int native() { return 42; } //OK
    
    @property void strange() { } //not allowed
    
    @property Deleg delegRet() { return () => 42; } //OK
}
 
@property void external1(int) { } //OK (Global setter)
@property int external1() { return 42; } //OK (Global getter)

@property void external2(Test, int) { } //OK (UFCS setter)
@property int external2(Test) { return 42; } //OK (UFCS getter)
 
void main()
{
    Test t;
    t.native = 42; //OK
    t.native(42); //invalid
    int a = t.native; //OK
    t.strange; //invalid (definition of strange already invalid)
    
    external1 = 42; //OK
    external1(42); //invalid
    int b = external1; //OK    
    42.external1; //invalid
    
    t.external2 = 42; //OK
    auto c = t.external2; //OK
    external2(t).external1; //invalid
    
    t.delegRet; //basically valid, could complain about statement has no effect if delegRet getter is pure
    t.delegRet(); //valid, gets delegate then calls it(but as the result
                  //of the delegate call isn't used the compiler could complain if the delegate is pure)
    t.delegRet()(); //invalid
    
    pragma(msg, is(typeof(t.var) == typeof(t.native))); //OK
    
    pragma(msg, typeof(external1)); //int
    import std.traits;
    // pragma(msg, typeof(ReturnType!(typeof(external1)))); // error (typeof(external1) returns int, not a function type)
    
    pragma(msg, typeof(&(t.delegRet))); //illegal, can't get address of temporary? (Not sure here)
    pragma(msg, typeof(t.delegRet));    //int delegate()
    pragma(msg, typeof(t.delegRet()));  //OK (is this valid D code in general?)
    pragma(msg, typeof(t.delegRet()())); //invalid
    
    pragma(msg, ReturnType!(typeof(t.delegRet))); //int
}