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