Property Discussion Proposal 1
- @property functions can't be called with parentheses:
x._property()
is illegal! (Normal functions are explicitly not affected by this rule!)- 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.
- The only exception to this rule: If the returned value can be called (delegate, function pointer, opCall), then
- Getting a reference is illegal:
&x._property
does not compile.- 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...
- 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. - The getter/setter function can be obtained via __trais:
__traits(propertyGetter, x._property)
__traits(propertySetter, x._property)
- The function types can be obtained like this:
typeof(__traits(propertyGetter, x._property)) == int delegate()
- The function types can be obtained like this:
- The type of the returned value is obtained with typeof:
typeof(x._property) == int
- The compiler uses semantic rewriting to make @property access as similar as field access as possible.
- Property functions cannot return ref values (as this could bypass the setter)
- The setter may take it's parameter by ref.
- The
-property
compiler switch is removed - @property on free functions
- Two categories: UFCS properties and global properties (see example)
- A setters parameter type and the corresponding getters return type must match
Proposal 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:
- 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.)
- 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".
- 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
}