1 /** 2 Date: 2015-2016, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 Extracted information of types. 7 8 TODO replace the fmt with a specialized formatting struct for the purpose 9 needed by TypeKind. fmt is namely a typeless strict that do not encode the 10 assumed number or arguments when it is used. In other words it do not contain 11 information regarding the number of '%s'. 12 */ 13 module cpptooling.data.kind; 14 15 import std.conv : to; 16 import std..string : format; 17 import std.traits; 18 import std.typecons : Nullable, Flag; 19 import logger = std.experimental.logger; 20 21 import cpptooling.data.symbol.types : USRType; 22 23 version (unittest) { 24 import unit_threaded : Name, shouldEqual; 25 } else { 26 private struct Name { 27 string name_; 28 } 29 } 30 31 alias ArrayInfoIndex = Nullable!long; 32 33 struct FuncInfoParam { 34 USRType usr; 35 TypeAttr attr; 36 string id; 37 Flag!"isVariadic" isVariadic; 38 } 39 40 /// Convert an array of indexes to a string representation 41 string toRepr(const(ArrayInfoIndex[]) indexes) @safe pure { 42 import std.algorithm : map, joiner; 43 import std.conv : text; 44 45 // dfmt off 46 return indexes 47 // a null is a dynamic index 48 .map!(a => a.isNull ? "[]" : "[" ~ text(a.get) ~ "]") 49 .joiner 50 .text; 51 // dfmt on 52 } 53 54 bool isIncompleteArray(const(ArrayInfoIndex)[] indexes) @safe pure nothrow @nogc { 55 foreach (index; indexes) { 56 if (index.isNull) 57 return true; 58 } 59 60 return false; 61 } 62 63 /** Type representation and information. 64 */ 65 struct TypeKind { 66 import std.traits : isSomeString; 67 import taggedalgebraic : TaggedAlgebraic; 68 import cpptooling.data.kind_type_format; 69 70 this(T)(T info, USRType usr) @safe if (!is(T == TypeKind)) { 71 this.info = info; 72 this.usr = usr; 73 } 74 75 this(T)(T info) @safe if (!is(T == TypeKind)) { 76 this(info, USRType("")); 77 } 78 79 invariant { 80 final switch (this.info.kind) with (TypeKind.Info) { 81 case Kind.ctor: 82 // Assuming that a ctor or dtor must always have a id, otherwise 83 // unusable 84 goto case; 85 case Kind.dtor: 86 assert(info.id.length > 0); 87 break; 88 case Kind.array: 89 case Kind.func: 90 case Kind.funcSignature: 91 case Kind.primitive: 92 case Kind.record: 93 case Kind.simple: 94 case Kind.typeRef: 95 break; 96 case Kind.funcPtr: 97 assert(info.attrs.length > 0); 98 break; 99 case Kind.pointer: 100 assert(info.attrs.length > 0); 101 break; 102 case Kind.null_: 103 break; 104 } 105 } 106 107 /// Formatting information needed to reproduce the type and identifier. 108 static @safe pure nothrow @nogc union InternalInfo { 109 typeof(null) null_; 110 PrimitiveInfo primitive; 111 SimpleInfo simple; 112 ArrayInfo array; 113 114 FuncInfo func; 115 FuncPtrInfo funcPtr; 116 FuncSignatureInfo funcSignature; 117 118 RecordInfo record; 119 CtorInfo ctor; 120 DtorInfo dtor; 121 PointerInfo pointer; 122 TypeRefInfo typeRef; 123 } 124 125 alias Info = TaggedAlgebraic!InternalInfo; 126 127 Info info; 128 USRType usr; 129 130 pure @safe nothrow @nogc: 131 132 /** The type 'const int x[2][3]' 133 */ 134 static struct ArrayInfo { 135 /// int %s%s 136 ArrayFmt fmt; 137 /// [2, 3] 138 ArrayInfoIndex[] indexes; 139 /// usr for 'int' 140 USRType element; 141 } 142 143 /** The type 'extern int (*e_g)(int pa)'. 144 * 145 * attrs is only for the pointers, never the final pointee. 146 * In the example shown about it would have length 1. 147 * 148 * TODO improve formatting with more separation, f.e return, ptr and args. 149 * TODO add a USRType for the FuncPrototype. 150 */ 151 static struct FuncPtrInfo { 152 /// int (%s %s)(int pa) 153 FuncPtrFmt fmt; 154 /// USRs up the pointee 155 USRType pointee; 156 /// attributes of the pointer hierarchy. attr[0] is the right most ptr. 157 TypeAttr[] attrs; 158 } 159 160 /** The type of a function signature, 'void foo(int)'. 161 */ 162 static struct FuncSignatureInfo { 163 /// void %s(int) 164 FuncSignatureFmt fmt; 165 USRType return_; 166 TypeAttr returnAttr; 167 FuncInfoParam[] params; 168 } 169 170 /** The type of a function prototype, 'void foo(int)'. 171 * 172 * TODO consider changing the chain to be a FuncInfo referencing a FuncSignatureInfo. 173 * 174 * This coupled with FuncSignatureInfo having the USR of the signature 175 * would mean that it would be possible to merge/detect/find all those 176 * FuncInfo with the same symbol mangling/signature. 177 * 178 * Which is needed when doing cross-translation unit analyse to find 179 * connections between "points of interest. 180 * 181 * It would also lower the amount of data in a FuncInfo. 182 * 183 */ 184 static struct FuncInfo { 185 /// void %s(int) 186 FuncFmt fmt; 187 USRType return_; 188 TypeAttr returnAttr; 189 FuncInfoParam[] params; 190 } 191 192 /** The type of a ctor prototype, 'Class::Class(int)' 193 */ 194 static struct CtorInfo { 195 /// %s(int) 196 CtorFmt fmt; 197 /// Class 198 string id; 199 FuncInfoParam[] params; 200 } 201 202 /** The type of a dtor prototype, 'Class::~Class()' 203 */ 204 static struct DtorInfo { 205 /// ~%s() 206 DtorFmt fmt; 207 /// identifier, in the example it would be 'Class' 208 string id; 209 } 210 211 /** The type of a pointer (may be recursive), 'const int ** const x' 212 * 213 * attrs is only for the pointers, never the final pointee. 214 * In the example shown about it would have length 2. 215 */ 216 static struct PointerInfo { 217 /// int%s %s 218 PtrFmt fmt; 219 /// USRs up the pointee 220 USRType pointee; 221 /// attributes of the pointer hierarchy. attr[0] is the left most ptr. 222 TypeAttr[] attrs; 223 } 224 225 /** Representation of a typedef, 'typedef int tx' 226 * 227 * canonicalType is the final resolved in a chain of typedef's. 228 */ 229 static struct TypeRefInfo { 230 /// tx %s 231 SimpleFmt fmt; 232 /// usr of the child type 233 USRType typeRef; 234 /// usr of the canonical type 235 USRType canonicalRef; 236 } 237 238 /** Represent a primitive type. 239 * 240 * Similar to a $(D SimpleInfo) but used to distinguish primitive types 241 * from "other" simple representations. 242 * 243 * The type 'int x' would be: 244 */ 245 static struct PrimitiveInfo { 246 /// int %s 247 SimpleFmt fmt; 248 } 249 250 /** Textual representation of simple types. 251 * 252 * A simple type is one that do not need the features or distinction of the 253 * other infos. 254 * 255 * The type 'const int x' would be: 256 */ 257 static struct SimpleInfo { 258 /// int %s 259 SimpleFmt fmt; 260 } 261 262 /** The type 'const A' 263 */ 264 static struct RecordInfo { 265 /// A %s 266 SimpleFmt fmt; 267 } 268 } 269 270 /// Attributes for a type. 271 @safe @nogc struct TypeAttr { 272 import std.typecons : Flag; 273 import std.format : FormatSpec; 274 275 Flag!"isConst" isConst; 276 Flag!"isRef" isRef; 277 Flag!"isPtr" isPtr; 278 Flag!"isFuncPtr" isFuncPtr; 279 Flag!"isArray" isArray; 280 Flag!"isDefinition" isDefinition; 281 282 /// Returns: a string range of the attributes 283 auto stringRange() const { 284 import std.range : chain, only; 285 import std.algorithm : filter; 286 287 // dfmt off 288 return chain(only(isConst ? "const" : null), 289 only(isRef ? "ref" : null), 290 only(isPtr ? "ptr" : null), 291 only(isFuncPtr ? "funcPtr" : null), 292 only(isArray ? "array" : null)) 293 .filter!(a => a !is null); 294 // dfmt on 295 } 296 297 /// 298 void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt = "%s") const { 299 import std.algorithm : joiner, copy; 300 301 // dfmt off 302 this.stringRange 303 .joiner(";") 304 .copy(w); 305 // dfmt on 306 } 307 308 string toString() @safe pure const { 309 import std.exception : assumeUnique; 310 import std.format : FormatSpec; 311 312 char[] buf; 313 buf.reserve(100); 314 auto fmt = FormatSpec!char("%s"); 315 toString((const(char)[] s) { buf ~= s; }, fmt); 316 auto trustedUnique(T)(T t) @trusted { 317 return assumeUnique(t); 318 } 319 320 return trustedUnique(buf); 321 } 322 } 323 324 /** Resolve the canonical type. 325 * 326 * TODO merge with resolvePointeeType. (It wasn't done as of this writing 327 * because I'm not sure they will stay similare enough to allow a merge). 328 * 329 * TODO I think that the resuilt from array/funcPtr/pointee should be checked 330 * if they are a typedef. May be a bug that complicates the result at other 331 * places. 332 * 333 * Params: 334 * LookupT = a type supporting the method "kind" taking a USR and returning a 335 * TypeKind. 336 * type = the type to resolve 337 * attr_ = attributes for the type, depending on the result from the lookup 338 * either this attributes are used or those from the lookup 339 * lookup = see $(D LookupT) 340 * 341 * Returns: TypeKindAttr of the canonical type. 342 */ 343 auto resolveCanonicalType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup) 344 if (__traits(hasMember, LookupT, "kind")) { 345 import std.algorithm : among; 346 import std.range : only, dropOne; 347 import cpptooling.data : TypeKindAttr; 348 349 auto rval = only(TypeKindAttr.init).dropOne; 350 auto found = typeof(lookup.kind(USRType.init)).init; 351 352 final switch (type.info.kind) with (TypeKind.Info) { 353 case Kind.array: 354 found = lookup.kind(type.info.element); 355 break; 356 case Kind.funcPtr: 357 found = lookup.kind(type.info.pointee); 358 break; 359 case Kind.pointer: 360 found = lookup.kind(type.info.pointee); 361 break; 362 case Kind.typeRef: 363 found = lookup.kind(type.info.canonicalRef); 364 break; 365 case Kind.ctor: 366 case Kind.dtor: 367 case Kind.func: 368 case Kind.funcSignature: 369 case Kind.primitive: 370 case Kind.record: 371 case Kind.simple: 372 rval = only(TypeKindAttr(type, attr)); 373 break; 374 case Kind.null_: 375 break; 376 } 377 378 foreach (item; found) { 379 rval = only(TypeKindAttr(item.get, attr)); 380 } 381 382 return rval; 383 } 384 385 /** Resolve the typeref type. 386 * 387 * TODO merge with resolvePointeeType. (It wasn't done as of this writing 388 * because I'm not sure they will stay similare enough to allow a merge). 389 * 390 * Params: 391 * LookupT = a type supporting taking a USR and returning a TypeKind. 392 * type = the type to resolve 393 * lookup = see $(D LookupT) 394 * 395 * Returns: TypeKind of the canonical type. 396 */ 397 TypeKind resolveTypeRef(LookupT)(const TypeKind type, LookupT lookup) { 398 import std.algorithm : among; 399 import std.range : only, dropOne; 400 import cpptooling.data : TypeKindAttr; 401 402 if (type.info.kind == TypeKind.Info.Kind.typeRef) { 403 foreach (a; lookup(type.info.canonicalRef)) { 404 TypeKind t = a; 405 return t; 406 } 407 } 408 409 return type; 410 } 411 412 /** Resolve the pointe type. 413 * 414 * Params: 415 * LookupT = a type supporting the method "kind" taking a USR as parameter, 416 * returning the result as a TypeKind wrapped in a range. 417 * type = the type to resolve 418 * attr_ = attributes for the type, depending on the result from the lookup 419 * either this attributes are used or those from the lookup 420 * lookup = see $(D LookupT) 421 * 422 * Returns: TypeKindAttr of the pointee type. 423 */ 424 auto resolvePointeeType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup) 425 if (__traits(hasMember, LookupT, "kind")) { 426 import std.algorithm : among; 427 import std.range : only, dropOne; 428 import cpptooling.data : TypeKindAttr; 429 430 auto rval = only(TypeKindAttr.init).dropOne; 431 auto found = typeof(lookup.kind(USRType.init)).init; 432 433 final switch (type.info.kind) with (TypeKind.Info) { 434 case Kind.array: 435 found = lookup.kind(type.info.element); 436 break; 437 case Kind.funcPtr: 438 found = lookup.kind(type.info.pointee); 439 break; 440 case Kind.pointer: 441 found = lookup.kind(type.info.pointee); 442 break; 443 case Kind.typeRef: 444 case Kind.ctor: 445 case Kind.dtor: 446 case Kind.func: 447 case Kind.funcSignature: 448 case Kind.primitive: 449 case Kind.record: 450 case Kind.simple: 451 rval = only(TypeKindAttr(type, attr)); 452 break; 453 case Kind.null_: 454 break; 455 } 456 457 foreach (item; found) { 458 rval = only(TypeKindAttr(item.get, attr)); 459 } 460 461 return rval; 462 } 463 464 // Test instantiation 465 @safe unittest { 466 TypeKind tk; 467 }