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
- A setters parameter type and the corresponding getters return type must match
- 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
}