Skip to content

Commit c1d94e1

Browse files
authored
Add ReturnType to phobos.sys.traits. (#10907)
This is essentially the same as the one in std.traits except that it only works on types. It wouldn't surprise me if some folks end up complaining that it requires a type, but it avoids the whole typeof expression vs typeof symbol issue that way. And really, in most situations, if the function can be used as a getter property, then using PropertyType is a better solution anyway (since then it works with variables as well), and if the function cannot be used as a getter property, then getting the type of the actual function call is often a better solution (since that automatically selects the correct overload). But there are still some situations where ReturnType is desirable, and if nothing else, it provides a good place to document what the better alternatives are and why they're better. As a bonus, the fact that ReturnType only works on types simplifies its implementation. functionAttributes is mentioned in the documentation for ReturnType, since it's relevant for functions which return by ref, but it's not part of these changes.
1 parent 999b129 commit c1d94e1

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed

phobos/sys/traits.d

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
$(LREF isFunction)
7878
$(LREF isFunctionPointer)
7979
$(LREF isReturn)
80+
$(LREF ReturnType)
8081
$(LREF ToFunctionType)
8182
))
8283
$(TR $(TD Aggregate Type Traits) $(TD
@@ -3275,6 +3276,212 @@ enum isReturn(T) = is(T == return);
32753276
static assert( is(typeof(&S.init.foo) == return));
32763277
}
32773278

3279+
/++
3280+
Evaluates to the return type of the given function type, function pointer
3281+
type, or delegate type.
3282+
3283+
Note that $(K_REF) is an attribute / storage class, not part of the type.
3284+
So, when the return type is marked with $(K_REF), $(K_REF) is an attribute
3285+
of the function and not part of the return type. $(LREF functionAttributes)
3286+
can be used to determine whether the return value is returned by $(K_REF).
3287+
3288+
Also note that in most cases, ReturnType is probably not the best solution.
3289+
3290+
In situations where a function is used as a getter property, then using
3291+
$(LREF PropertyType) would usually make more sense than using ReturnType,
3292+
particularly since in such a situation, the property could potentially be a
3293+
variable, which would not compile with ReturnType.
3294+
3295+
In situations where a function may be overloaded, it can often make more
3296+
sense to get the type of the expression where the function is called
3297+
instead of getting the return type of the function itself - e.g.
3298+
$(D typeof(foo(42))) instead of $(D ReturnType!(SymbolType!foo)), since
3299+
then that will automatically get the correct overload, whereas with
3300+
ReturnType, getting the return type of the correct overload would require
3301+
using $(D __traits(getOverloads, foo)) and then selecting the correct
3302+
overload. Getting the type of the actual function call can also can be less
3303+
verbose and require fewer template instantiations, since a function call is
3304+
clearly an expression and thus avoids the need for $(LREF SymbolType).
3305+
3306+
So, with functions which can be used as getter properties, it's often
3307+
better to use $(LREF PropertyType) than to use ReturnType, and with
3308+
functions which cannot be used as getter properties, it's often better to
3309+
simply get the type of the actual function call. So, ReturnType is
3310+
usually not the best choice, but there are of course situations where it's
3311+
exactly what's needed (e.g. if code already has the symbol or type for a
3312+
specific function overload and needs to get its return type).
3313+
3314+
See_Also:
3315+
$(LREF functionAttributes)
3316+
$(LREF PropertyType)
3317+
$(LREF SymbolType)
3318+
+/
3319+
template ReturnType(T)
3320+
if (is(T == return))
3321+
{
3322+
static if (is(T R == return))
3323+
alias ReturnType = R;
3324+
else
3325+
static assert(false, "Somehow, ReturnType was instantiated with a type which has no return type");
3326+
}
3327+
3328+
///
3329+
@safe unittest
3330+
{
3331+
void foo();
3332+
static assert(is(ReturnType!(SymbolType!foo) == void));
3333+
3334+
int bar();
3335+
static assert(is(ReturnType!(SymbolType!bar) == int));
3336+
3337+
// ReturnType requires a type.
3338+
static assert(!__traits(compiles, ReturnType!bar));
3339+
3340+
// ReturnType requires a function type, function pointer type, or delegate
3341+
// type, so the result of PropertyType only works with it if the function
3342+
// returns such a type.
3343+
static assert(!__traits(compiles, ReturnType!(PropertyType!bar)));
3344+
3345+
string function(int) funcPtr;
3346+
static assert(is(ReturnType!(SymbolType!funcPtr) == string));
3347+
3348+
int delegate(string) del;
3349+
static assert(is(ReturnType!(SymbolType!del) == int));
3350+
3351+
int delegate(string) retDel();
3352+
static assert(is(ReturnType!(SymbolType!retDel) == int delegate(string)));
3353+
static assert(is(ReturnType!(PropertyType!retDel) == int));
3354+
static assert(is(ReturnType!(typeof(retDel)) == int delegate(string)));
3355+
3356+
@property int delegate(string) prop();
3357+
static assert(is(ReturnType!(SymbolType!prop) == int delegate(string)));
3358+
static assert(is(ReturnType!(PropertyType!prop) == int));
3359+
static assert(is(ReturnType!(typeof(prop)) == int));
3360+
3361+
ref int returnByRef();
3362+
static assert(is(ReturnType!(SymbolType!returnByRef) == int));
3363+
}
3364+
3365+
///
3366+
@safe unittest
3367+
{
3368+
static struct S
3369+
{
3370+
void foo(string);
3371+
bool foo(string, int);
3372+
string foo();
3373+
3374+
@property void bar(int);
3375+
@property int bar();
3376+
}
3377+
3378+
// SymbolType gives the type of the first overload, whereas PropertyType
3379+
// gives the type of the overload which can be used as a getter property
3380+
// (or fails to compile if there is no such overload). Of course, the
3381+
// result of PropertyType won't compile with ReturnType unless the property
3382+
// gives a function pointer or delegate.
3383+
// __traits(getOverloads, ...) can be used to get specific overloads (or to
3384+
// iterate through all of them).
3385+
{
3386+
static assert( is(ReturnType!(SymbolType!(S.foo)) == void));
3387+
static assert( is(PropertyType!(S.foo) == string));
3388+
3389+
static assert( is(typeof(S.init.foo("")) == void));
3390+
static assert( is(typeof(S.init.foo("", 42)) == bool));
3391+
static assert( is(typeof(S.init.foo()) == string));
3392+
3393+
alias overloads = __traits(getOverloads, S, "foo");
3394+
3395+
// string foo();
3396+
static assert( is(ReturnType!(SymbolType!(overloads[0])) == void));
3397+
3398+
// void foo(string);
3399+
static assert( is(ReturnType!(SymbolType!(overloads[1])) == bool));
3400+
3401+
// void foo(string, int);
3402+
static assert( is(ReturnType!(SymbolType!(overloads[2])) == string));
3403+
}
3404+
{
3405+
static assert( is(ReturnType!(SymbolType!(S.bar)) == void));
3406+
static assert( is(PropertyType!(S.bar) == int));
3407+
3408+
// Normal function call syntax can be used with @property functions
3409+
// (which is obviously not the intended way to use them, but it does
3410+
// provide a way to distinguish between overloads).
3411+
static assert( is(typeof(S.init.bar(42)) == void));
3412+
static assert( is(typeof(S.init.bar()) == int));
3413+
3414+
static assert( is(typeof(S.init.bar = 42) == void));
3415+
static assert( is(PropertyType!(S.init.bar) == int));
3416+
3417+
alias overloads = __traits(getOverloads, S, "bar");
3418+
3419+
// @property void bar(int);
3420+
static assert( is(ReturnType!(SymbolType!(overloads[0])) == void));
3421+
3422+
// @property int bar();
3423+
static assert( is(ReturnType!(SymbolType!(overloads[1])) == int));
3424+
}
3425+
}
3426+
3427+
@safe unittest
3428+
{
3429+
int func1(string);
3430+
static assert(is(ReturnType!(SymbolType!func1) == int));
3431+
static assert(!__traits(compiles, ReturnType!func1));
3432+
3433+
const(int) func2(string);
3434+
static assert(is(ReturnType!(SymbolType!func2) == const int));
3435+
3436+
immutable(int) func3(string);
3437+
static assert(is(ReturnType!(SymbolType!func3) == immutable int));
3438+
3439+
shared(int) func4(string);
3440+
static assert(is(ReturnType!(SymbolType!func4) == shared int));
3441+
3442+
const(shared(int)) func5(string);
3443+
static assert(is(ReturnType!(SymbolType!func5) == const shared int));
3444+
3445+
ref int func6(string);
3446+
static assert(is(ReturnType!(SymbolType!func6) == int));
3447+
3448+
ref const(int) func7(string);
3449+
static assert(is(ReturnType!(SymbolType!func7) == const int));
3450+
3451+
static struct S
3452+
{
3453+
real foo();
3454+
real bar() const;
3455+
inout(real) baz() inout;
3456+
ref real func() shared;
3457+
}
3458+
static assert(is(ReturnType!(SymbolType!(S.foo)) == real));
3459+
static assert(is(ReturnType!(SymbolType!(S.bar)) == real));
3460+
static assert(is(ReturnType!(SymbolType!(S.baz)) == inout real));
3461+
static assert(is(ReturnType!(SymbolType!(S.func)) == real));
3462+
3463+
static class C
3464+
{
3465+
byte foo() { assert(0); }
3466+
byte bar() const { assert(0); }
3467+
inout(byte) baz() inout { assert(0); }
3468+
ref byte func() shared { assert(0); }
3469+
}
3470+
static assert(is(ReturnType!(SymbolType!(C.foo)) == byte));
3471+
static assert(is(ReturnType!(SymbolType!(C.bar)) == byte));
3472+
static assert(is(ReturnType!(SymbolType!(C.baz)) == inout byte));
3473+
static assert(is(ReturnType!(SymbolType!(C.func)) == byte));
3474+
3475+
static struct NoCopy
3476+
{
3477+
@disable this(this);
3478+
}
3479+
static assert(!__traits(isCopyable, NoCopy));
3480+
3481+
NoCopy retNC();
3482+
static assert(is(ReturnType!(SymbolType!retNC) == NoCopy));
3483+
}
3484+
32783485
/++
32793486
Converts a function type, function pointer type, or delegate type to the
32803487
corresponding function type.

0 commit comments

Comments
 (0)