Property Discussion Proposal 1

From D Wiki
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
    1. Two categories: UFCS properties and global properties (see example)
  11. A setters parameter type and the corresponding getters return type must match

Proposal 1.1

  1. Like proposal 1, but instead of rule 7, returning ref values is allowed as long as there is not setter function.

Proposal 1.2

This is the same as proposal 1 except that marking a function or method as @property turns it into a variable-like entity, such that to the outside world, it behaves exactly like a variable, and not like a function:

  1. Writing "&prop" is legal only if (1) func returns by ref, and (2) there is no setter method prop(T). It simply returns the address of the returned value. It does not return a function pointer to prop. (To the outside world, prop is a variable, not a function.)
  2. It is illegal to call a @property function using regular function-call syntax: if you have @property void ufcsProp(U, T) {...}, then it is illegal to write "ufcsProp(u,t)". Instead, you must write "u.ufcsProp = t".
  3. Assignment syntax f=x; is only valid if f is @property. It is illegal for non-@property f. Same goes for assignment-related operations like f++, f+=2, etc..
Clarifications with code examples
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
}

// OK: returns void, has 1 parameter --> global setter
@property void external1(int) { }
// OK: returns !void, has 0 parameters --> global getter
@property int external1() { return 42; }

// OK: returns void, has 2 parameters --> global UFCS setter
@property void external2(Test, int) { }
// OK; returns !void, has 1 parameter --> global UFCS getter 
@property int external2(Test) { return 42; }

//external2 can only be called on a Type instance.
//calling it manually "external2(Type(), 42)" is invalid
 
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, a getter can't return void (void + 1 args is always a global variable property)

    
    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
}