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