1 /** 2 Information about types and symbols at compile-time, 3 similar to std.traits. 4 */ 5 module mirror.meta.traits; 6 7 8 import mirror.trait_enums: Protection; 9 static import std.traits; 10 11 12 /// Usable as a predicate to std.meta.Filter 13 enum isEnum(T) = is(T == enum); 14 15 /// Usable as a predicate to std.meta.Filter 16 enum isStruct(T) = is(T == struct); 17 18 /// Usable as a predicate to std.meta.Filter 19 enum isInterface(T) = is(T == interface); 20 21 /// Usable as a predicate to std.meta.Filter 22 enum isClass(T) = is(T == class); 23 24 /// Usable as a predicate to std.meta.Filter 25 enum isUnion(T) = is(T == union); 26 27 /** 28 If a type is a class or an interface. 29 Usable as a predicate to std.meta.Filter 30 */ 31 enum isOOP(alias T) = is(T == class) || is(T == interface); 32 int add() { return 0; } 33 34 template moduleOf(alias T) { 35 import std.traits: moduleName; 36 mixin(`import `, moduleName!T, `;`); 37 mixin(`alias moduleOf = `, moduleName!T, `;`); 38 } 39 40 41 template isPrivate(alias symbol) { 42 // If a module contains an alias to a basic type, e.g. `alias L = long;`, 43 // then __traits(getProtection, member) fails to compile 44 static if(__traits(compiles, __traits(getProtection, symbol))) 45 enum isPrivate = __traits(getProtection, symbol) == "private"; 46 else 47 enum isPrivate = true; // if it doesn't compile, treat it as private 48 } 49 50 51 /** 52 Retrieves the "fundamental type" of a type T. For most types, this 53 will be exactly the same as T itself. For arrays or pointers, it 54 removes as many "layers" of array or pointer indirections to get to 55 the most basic atomic type possible. Examples of inputs and 56 outputs: 57 58 * T -> T 59 * T[] -> T 60 * T[][] -> T 61 * T* -> T 62 63 */ 64 template FundamentalType(T) { 65 66 import std.traits: isArray, isPointer, PointerTarget; 67 import std.range: ElementEncodingType; 68 69 static if(isArray!T) 70 alias removeOneIndirection = ElementEncodingType!T; 71 else static if(isPointer!T) 72 alias removeOneIndirection = PointerTarget!T; 73 74 private enum isArrayOrPointer(U) = isArray!U || isPointer!U; 75 76 static if(isArrayOrPointer!T) { 77 static if(isArrayOrPointer!removeOneIndirection) 78 alias FundamentalType = FundamentalType!removeOneIndirection; 79 else 80 alias FundamentalType = removeOneIndirection; 81 } else 82 alias FundamentalType = T; 83 } 84 85 86 /** 87 Returns an AliasSeq of all field types of `T`, depth-first 88 recursively. 89 */ 90 alias RecursiveFieldTypes(T) = RecursiveFieldTypesImpl!T; 91 92 93 private template RecursiveFieldTypesImpl(T, alreadySeen...) { 94 95 import mirror.meta.traits: isStruct, isClass; 96 import std.meta: staticMap, AliasSeq, NoDuplicates, Filter, 97 templateNot, staticIndexOf; 98 99 enum isStructOrClass(U) = isStruct!(FundamentalType!U) || isClass!(FundamentalType!U); 100 101 static if(isStructOrClass!T) { 102 103 // This check is to deal with forward references such as std.variant.This. 104 // For some reason, checking for __traits(compiles, T.tupleof) always returns 105 // true, but checking the length actually does what we want. 106 // See modules.issues.Issue9. 107 static if(T.tupleof.length) 108 private alias fields = AliasSeq!(T.tupleof); 109 else 110 private alias fields = AliasSeq!(); 111 112 private alias publicFields = Filter!(templateNot!isPrivate, fields); 113 private alias type(alias symbol) = typeof(symbol); 114 private alias types = staticMap!(type, fields); 115 116 private template recurse(U) { 117 118 static if(isStructOrClass!U) { 119 120 // only recurse if the type hasn't been seen yet to 121 // prevent infinite recursion 122 enum shouldRecurse = staticIndexOf!(U, alreadySeen) == -1; 123 124 static if(shouldRecurse) 125 alias recurse = AliasSeq!(U, RecursiveFieldTypesImpl!(FundamentalType!U, NoDuplicates!(T, types, alreadySeen))); 126 else 127 alias recurse = AliasSeq!(); 128 } else 129 alias recurse = U; 130 } 131 132 alias RecursiveFieldTypesImpl = NoDuplicates!(staticMap!(recurse, types)); 133 } else 134 alias RecursiveFieldTypesImpl = T; 135 } 136 137 138 /** 139 An std.meta.AliasSeq of `T` and all its recursive 140 subtypes. 141 */ 142 template RecursiveTypeTree(T...) { 143 import std.meta: staticMap, NoDuplicates; 144 alias RecursiveTypeTree = NoDuplicates!(T, staticMap!(RecursiveFieldTypes, T)); 145 } 146 147 148 /** 149 Whether or not `F` is a property function 150 */ 151 template isProperty(alias F) { 152 import std.traits: functionAttributes, FunctionAttribute; 153 enum isProperty = functionAttributes!F & FunctionAttribute.property; 154 } 155 156 157 /** 158 All member function symbols in T with overloads represented 159 separately. 160 */ 161 template MemberFunctionsByOverload(T) if(isStruct!T || isClass!T || isInterface!T || isUnion!T) 162 { 163 import mirror.meta.reflection: functionsByOverload; 164 import mirror.trait_enums: Protection; 165 import std.meta: Filter, staticMap; 166 167 private enum isPublic(alias F) = F.protection != Protection.private_; 168 private alias symbolOf(alias S) = S.symbol; 169 170 alias members = PublicMembers!T; 171 alias overloads = functionsByOverload!(T, members); 172 173 alias MemberFunctionsByOverload = 174 Filter!(isMemberFunction, 175 staticMap!(symbolOf, 176 Filter!(isPublic, 177 functionsByOverload!(T, PublicMembers!T)))); 178 } 179 180 181 // must be a global template 182 private template isMemberFunction(alias F) { 183 import std.algorithm: startsWith; 184 185 static if(__traits(compiles, __traits(identifier, F))) { 186 enum name = __traits(identifier, F); 187 alias parent = __traits(parent, F); 188 189 static if(isOOP!parent) { 190 private static bool isWantedFunction(string name) { 191 import std.algorithm: among; 192 return 193 !name.among("toString", "toHash", "opCmp", "opEquals", "factory") 194 && !name.startsWith("__") 195 ; 196 } 197 } else { 198 bool isWantedFunction(string name) { return true; } 199 } 200 private bool isOperator(string name) { 201 return name.startsWith("op") && name.length > 2 && name[2] >= 'A'; 202 } 203 204 enum isOp = isOperator(name); 205 enum isMemberFunction = isWantedFunction(name) && !isOperator(name); 206 207 } else 208 enum isMemberFunction = false; 209 } 210 211 212 template PublicMembers(alias A) { 213 import mirror.meta.traits: isPrivate; 214 import std.meta: Filter, staticMap, Alias, AliasSeq; 215 216 private alias member(string name) = MemberFromName!(A, name); 217 private alias members = staticMap!(member, __traits(allMembers, A)); 218 219 // In the `member` template above, if it's not possible to get a member from `A`, 220 // then the symbol is an empty AliasSeq. An example of such a situation can be 221 // found in `modules.problems` from the tests directory, where this causes things 222 // to not compile: `version = OopsVersion;`. 223 // So we filter out such members. 224 private enum hasSymbol(alias member) = !is(member == void); 225 private alias goodMembers = Filter!(hasSymbol, members); 226 227 private enum notPrivate(alias member) = !isPrivate!(member.symbol); 228 229 alias PublicMembers = Filter!(notPrivate, goodMembers); 230 } 231 232 233 template MemberFromName(alias parent, string name) { 234 import std.meta: Alias; 235 236 enum identifier = name; 237 238 static if(__traits(compiles, Alias!(__traits(getMember, parent, name)))) { 239 240 alias symbol = Alias!(__traits(getMember, parent, name)); 241 242 static if(is(symbol)) 243 alias Type = symbol; 244 else static if(is(typeof(symbol))) 245 alias Type = typeof(symbol); 246 else 247 alias Type = void; 248 249 } else 250 alias symbol = void; 251 } 252 253 254 package template memberIsSomeFunction(alias member) { 255 import std.traits: isSomeFunction; 256 enum memberIsSomeFunction = isSomeFunction!(member.symbol); 257 } 258 259 260 package template memberIsRegularFunction(alias member) { 261 static if(memberIsSomeFunction!member) { 262 import std.algorithm: startsWith; 263 enum memberIsRegularFunction = 264 !member.identifier.startsWith("_sharedStaticCtor") 265 && !member.identifier.startsWith("_staticCtor") 266 ; 267 } else 268 enum memberIsRegularFunction = false; 269 } 270 271 272 /** 273 If a function is static member function 274 */ 275 template isStaticMemberFunction(alias F) { 276 import std.traits: hasStaticMember; 277 278 static if(__traits(compiles, hasStaticMember!(__traits(parent, F), __traits(identifier, F)))) 279 enum isStaticMemberFunction = hasStaticMember!(__traits(parent, F), __traits(identifier, F)); 280 else 281 enum isStaticMemberFunction = false; 282 } 283 284 285 /** 286 An AliasSeq of BinaryOperator structs for type T, one for each binary operator. 287 */ 288 template BinaryOperators(T) { 289 import std.meta: staticMap, Filter, AliasSeq; 290 import std.traits: hasMember; 291 292 // See https://dlang.org/spec/operatoroverloading.html#binary 293 private alias overloadable = AliasSeq!( 294 "+", "-", "*", "/", "%", "^^", "&", 295 "|", "^", "<<", ">>", ">>>", "~", "in", 296 ); 297 298 static if(hasMember!(T, "opBinary") || hasMember!(T, "opBinaryRight")) { 299 300 private enum hasOperatorDir(BinOpDir dir, string op) = is(typeof(probeOperator!(T, functionName(dir), op))); 301 private enum hasOperator(string op) = 302 hasOperatorDir!(BinOpDir.left, op) 303 || hasOperatorDir!(BinOpDir.right, op); 304 305 alias ops = Filter!(hasOperator, overloadable); 306 307 template toBinOp(string op) { 308 enum hasLeft = hasOperatorDir!(BinOpDir.left, op); 309 enum hasRight = hasOperatorDir!(BinOpDir.right, op); 310 311 static if(hasLeft && hasRight) 312 enum toBinOp = BinaryOperator(op, BinOpDir.left | BinOpDir.right); 313 else static if(hasLeft) 314 enum toBinOp = BinaryOperator(op, BinOpDir.left); 315 else static if(hasRight) 316 enum toBinOp = BinaryOperator(op, BinOpDir.right); 317 else 318 static assert(false); 319 } 320 321 alias BinaryOperators = staticMap!(toBinOp, ops); 322 } else 323 alias BinaryOperators = AliasSeq!(); 324 } 325 326 327 /** 328 Tests if T has a template function named `funcName` 329 with a string template parameter `op`. 330 */ 331 private auto probeOperator(T, string funcName, string op)() { 332 import std.traits: Parameters; 333 334 mixin(`alias func = T.` ~ funcName ~ `;`); 335 alias P = Parameters!(func!op); 336 337 mixin(`return T.init.` ~ funcName ~ `!op(P.init);`); 338 } 339 340 341 struct BinaryOperator { 342 string op; 343 BinOpDir dirs; /// left, right, or both 344 } 345 346 347 enum BinOpDir { 348 left = 1, 349 right = 2, 350 } 351 352 353 string functionName(BinOpDir dir) { 354 final switch(dir) with(BinOpDir) { 355 case left: return "opBinary"; 356 case right: return "opBinaryRight"; 357 } 358 assert(0); 359 } 360 361 362 template UnaryOperators(T) { 363 import std.meta: AliasSeq, Filter; 364 365 alias overloadable = AliasSeq!("-", "+", "~", "*", "++", "--"); 366 enum hasOperator(string op) = is(typeof(probeOperator!(T, "opUnary", op))); 367 alias UnaryOperators = Filter!(hasOperator, overloadable); 368 } 369 370 371 template AssignOperators(T) { 372 import std.meta: AliasSeq, Filter; 373 374 // See https://dlang.org/spec/operatoroverloading.html#op-assign 375 private alias overloadable = AliasSeq!( 376 "+", "-", "*", "/", "%", "^^", "&", 377 "|", "^", "<<", ">>", ">>>", "~", 378 ); 379 380 private enum hasOperator(string op) = is(typeof(probeOperator!(T, "opOpAssign", op))); 381 alias AssignOperators = Filter!(hasOperator, overloadable); 382 } 383 384 385 template NumDefaultParameters(A...) if(A.length == 1) { 386 import std.traits: isCallable, ParameterDefaults; 387 import std.meta: Filter; 388 389 alias F = A[0]; 390 static assert(isCallable!F); 391 392 private template notVoid(T...) if(T.length == 1) { 393 enum notVoid = !is(T[0] == void); 394 } 395 396 enum NumDefaultParameters = Filter!(notVoid, ParameterDefaults!F).length; 397 } 398 399 400 template NumRequiredParameters(A...) if(A.length == 1) { 401 import std.traits: isCallable, Parameters; 402 alias F = A[0]; 403 static assert(isCallable!F); 404 enum NumRequiredParameters = Parameters!F.length - NumDefaultParameters!F; 405 } 406 407 408 /** 409 AliasSeq of `Parameter` templates with all information on function `F`'s 410 parameters. 411 */ 412 template Parameters(alias F) { 413 import mirror.meta.traits: Parameter; 414 import std.traits: StdParameters = Parameters, 415 ParameterIdentifierTuple, ParameterDefaults, ParameterStorageClassTuple; 416 import std.meta: staticMap, aliasSeqOf; 417 import std.range: iota; 418 419 alias parameter(size_t i) = Parameter!( 420 StdParameters!F[i], 421 ParameterDefaults!F[i], 422 ParameterIdentifierTuple!F[i], 423 ParameterStorageClassTuple!F[i], 424 ); 425 426 // When a default value is a function pointer, things get... weird 427 alias parameterFallback(size_t i) = 428 Parameter!(StdParameters!F[i], void, ParameterIdentifierTuple!F[i]); 429 430 static if(__traits(compiles, staticMap!(parameter, aliasSeqOf!(StdParameters!F.length.iota)))) 431 alias Parameters = staticMap!(parameter, aliasSeqOf!(StdParameters!F.length.iota)); 432 else { 433 import std.traits: fullyQualifiedName; 434 pragma(msg, "WARNING: Cannot get parameter defaults for `", fullyQualifiedName!F, "`"); 435 alias Parameters = staticMap!(parameterFallback, aliasSeqOf!(StdParameters!F.length.iota)); 436 } 437 } 438 439 /** 440 Information on a function's parameter 441 */ 442 template Parameter( 443 T, 444 alias D, 445 string I, 446 std.traits.ParameterStorageClass sc = std.traits.ParameterStorageClass.none) 447 { 448 alias Type = T; 449 alias Default = D; 450 enum identifier = I; 451 enum storageClass = sc; 452 } 453 454 455 /** 456 If the passed in template `T` is `Parameter` 457 */ 458 template isParameter(alias T) { 459 import std.traits: TemplateOf; 460 enum isParameter = __traits(isSame, TemplateOf!T, Parameter); 461 } 462 463 464 template PublicFieldNames(T) { 465 import std.meta: Filter, AliasSeq; 466 import std.traits: FieldNameTuple; 467 468 enum isPublic(string fieldName) = __traits(getProtection, __traits(getMember, T, fieldName)) == "public"; 469 alias PublicFieldNames = Filter!(isPublic, FieldNameTuple!T); 470 } 471 472 473 template isMutableSymbol(alias symbol) { 474 import std.traits: isMutable; 475 476 static if(isMutable!(typeof(symbol))) { 477 enum isMutableSymbol = __traits(compiles, symbol = symbol.init); 478 } else 479 enum isMutableSymbol = false; 480 } 481 482 483 template isVariable(alias member) { 484 485 enum isVariable = 486 is(typeof(member.symbol)) 487 && !is(typeof(member.symbol) == function) 488 && !is(typeof(member.symbol) == void) // can happen with templates 489 && is(typeof(member.symbol.init)) 490 ; 491 } 492 493 494 /** 495 The fields of a struct, union, or class 496 */ 497 template Fields(T) { 498 import mirror.trait_enums: toProtection; 499 import std.meta: staticMap, aliasSeqOf, Filter; 500 import std.traits: FieldTypeTuple, FieldNameTuple; 501 import std.range: iota; 502 503 private static struct NoType{} 504 505 private alias member(string name) = __traits(getMember, T, name); 506 507 template TypeOf(alias A) { 508 static if(is(typeof(A))) 509 alias TypeOf = typeof(A); 510 else 511 alias TypeOf = NoType; 512 } 513 514 enum isFunction(string name) = is(TypeOf!(member!name) == function); 515 enum hasType(string name) = !is(TypeOf!(member!name) == NoType); 516 enum isField(string name) = !isFunction!name && hasType!name; 517 alias fieldNames = Filter!(isField, __traits(allMembers, T)); 518 alias toField(string name) = Field!( 519 TypeOf!(member!name), 520 name, 521 __traits(getProtection, member!name).toProtection 522 ); 523 524 alias Fields = staticMap!(toField, fieldNames); 525 } 526 527 528 /** 529 A field of a struct, union, or class 530 */ 531 template Field(F, string id, Protection prot = Protection.public_) { 532 alias Type = F; 533 enum identifier = id; 534 enum protection = prot; 535 }