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 }