1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 29, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module clang.Type;
8 
9 import std.bitmanip;
10 
11 import clang.c.Index;
12 import clang.Cursor;
13 import clang.Util;
14 
15 struct Type
16 {
17     static assert(Type.init.kind == CXTypeKind.invalid);
18 
19     mixin CX;
20 
21     private Type* pointee_;
22     private Type* canonical_;
23 
24     mixin(bitfields!(
25         bool, "isConst", 1,
26         bool, "isVolatile", 1,
27         bool, "isClang", 1,
28         uint, "", 5));
29 
30     string spelling = "";
31 
32     this (CXType cx)
33     {
34         this.cx = cx;
35         spelling = Cursor(clang_getTypeDeclaration(cx)).spelling;
36         isConst = clang_isConstQualifiedType(cx) == 1;
37         isClang = true;
38     }
39 
40     this (CXTypeKind kind, string spelling)
41     {
42         cx.kind = kind;
43         this.spelling = spelling;
44     }
45 
46     static Type makePointer(Type pointee)
47     {
48         Type result = Type(CXTypeKind.pointer, "");
49         result.pointee_ = new Type();
50         *result.pointee_ = pointee;
51         return result;
52     }
53 
54     static Type makeTypedef(string spelling, Type canonical)
55     {
56         Type result = Type(CXTypeKind.typedef_, spelling);
57         result.canonical_ = new Type();
58         *result.canonical_ = canonical;
59         return result;
60     }
61 
62     @property bool isAnonymous ()
63     {
64         return spelling == "";
65     }
66 
67     @property Type underlying ()
68     {
69         return declaration.underlyingType;
70     }
71 
72     @property bool isArray ()
73     {
74         return
75             kind == CXTypeKind.constantArray ||
76             kind == CXTypeKind.incompleteArray ||
77             kind == CXTypeKind.variableArray ||
78             kind == CXTypeKind.dependentSizedArray;
79     }
80 
81     /**
82      * Removes array and pointer modifiers from the type.
83      */
84     @property Type undecorated()
85     {
86         if (isArray)
87             return array.elementType.undecorated;
88         else if (kind == CXTypeKind.pointer && !pointee.isFunctionType)
89             return pointee.undecorated;
90         else
91             return this;
92     }
93 
94     @property bool isDecorated()
95     {
96         return isArray || (kind == CXTypeKind.pointer && !pointee.isFunctionType);
97     }
98 
99     @property bool isEnum ()
100     {
101         return kind == CXTypeKind.enum_;
102     }
103 
104     @property bool isExposed ()
105     {
106         return kind != CXTypeKind.unexposed;
107     }
108 
109     @property bool isFunctionType ()
110     {
111         return canonical.kind == CXTypeKind.functionProto;
112     }
113 
114     @property bool isFunctionPointerType ()
115     {
116         return kind == CXTypeKind.pointer && pointee.isFunctionType;
117     }
118 
119     @property bool isObjCIdType ()
120     {
121         return isTypedef &&
122             canonical.kind == CXTypeKind.objCObjectPointer &&
123             spelling == "id";
124     }
125 
126     @property bool isObjCClassType ()
127     {
128         return isTypedef &&
129             canonical.kind == CXTypeKind.objCObjectPointer &&
130             spelling == "Class";
131     }
132 
133     @property bool isObjCSelType ()
134     {
135         with(CXTypeKind)
136             if (isTypedef)
137             {
138                 auto c = canonical;
139                 return c.kind == pointer &&
140                     c.pointee.kind == objCSel;
141             }
142 
143             else
144                 return false;
145     }
146 
147     @property bool isObjCBuiltinType ()
148     {
149         return isObjCIdType || isObjCClassType || isObjCSelType;
150     }
151 
152     @property bool isPointer ()
153     {
154         return kind == CXTypeKind.pointer;
155     }
156 
157     @property bool isTypedef ()
158     {
159         return kind == CXTypeKind.typedef_;
160     }
161 
162     @property bool isValid ()
163     {
164         return kind != CXTypeKind.invalid;
165     }
166 
167     @property bool isWideCharType ()
168     {
169         return kind == CXTypeKind.wChar;
170     }
171 
172     @property Type canonical()
173     {
174         if (canonical_)
175         {
176             return *canonical_;
177         }
178         else
179         {
180             if (isClang)
181                 return Type(clang_getCanonicalType(cx));
182             else
183                 return Type.init;
184         }
185     }
186 
187     @property Type pointee()
188     {
189         if (pointee_)
190         {
191             return *pointee_;
192         }
193         else
194         {
195             if (isClang)
196                 return Type(clang_getPointeeType(cx));
197             else
198                 return Type.init;
199         }
200     }
201 
202     @property Type element()
203     {
204         return Type(clang_getElementType(cx));
205     }
206 
207     @property Type named()
208     {
209         if (isClang)
210             return Type(clang_Type_getNamedType(cx));
211         else
212             return Type.init;
213     }
214 
215     @property Cursor declaration ()
216     {
217         if (isClang)
218             return Cursor(clang_getTypeDeclaration(cx));
219         else
220             return Cursor.empty;
221     }
222 
223     @property FuncType func ()
224     {
225         return FuncType(this);
226     }
227 
228     @property ArrayType array ()
229     {
230         return ArrayType(this);
231     }
232 
233     @property size_t sizeOf()
234     {
235         if (isClang)
236         {
237             auto result = clang_Type_getSizeOf(cx);
238 
239             if (result < 0)
240                 throwTypeLayoutError(cast(CXTypeLayoutError) result, spelling);
241 
242             return cast(size_t) result;
243         }
244         else
245         {
246             throw new TypeLayoutErrorUnknown(spelling);
247         }
248     }
249 
250     @property string toString() const
251     {
252         import std.format: format;
253         return format("Type(kind = %s, spelling = %s, isConst = %s)", kind, spelling, isConst);
254     }
255 
256     @property string toString()
257     {
258         import std.format : format;
259         return format("Type(kind = %s, spelling = %s)", kind, spelling);
260     }
261 }
262 
263 struct FuncType
264 {
265     Type type;
266     alias type this;
267 
268     @property Type resultType ()
269     {
270         auto r = clang_getResultType(type.cx);
271         return Type(r);
272     }
273 
274     @property Arguments arguments ()
275     {
276         return Arguments(this);
277     }
278 
279     @property bool isVariadic ()
280     {
281         return clang_isFunctionTypeVariadic(type.cx) == 1;
282     }
283 }
284 
285 struct ArrayType
286 {
287     Type type;
288     alias type this;
289 
290     this (Type type)
291     {
292         assert(type.isArray);
293         this.type = type;
294     }
295 
296     @property Type elementType ()
297     {
298         auto r = clang_getArrayElementType(cx);
299         return Type(r);
300     }
301 
302     @property long size ()
303     {
304         return clang_getArraySize(cx);
305     }
306 
307     @property size_t numDimensions ()
308     {
309         size_t result = 1;
310         auto subtype = elementType();
311 
312         while (subtype.isArray)
313         {
314             ++result;
315             subtype = subtype.array.elementType();
316         }
317 
318         return result;
319     }
320 }
321 
322 struct Arguments
323 {
324     FuncType type;
325 
326     @property uint length ()
327     {
328         return clang_getNumArgTypes(type.type.cx);
329     }
330 
331     Type opIndex (uint i)
332     {
333         auto r = clang_getArgType(type.type.cx, i);
334         return Type(r);
335     }
336 
337     int opApply (int delegate (ref Type) dg)
338     {
339         foreach (i ; 0 .. length)
340         {
341             auto type = this[i];
342 
343             if (auto result = dg(type))
344                 return result;
345         }
346 
347         return 0;
348     }
349 }
350 
351 @property bool isIntegral (CXTypeKind kind)
352 {
353     with (CXTypeKind)
354         switch (kind)
355         {
356             case bool_:
357             case charU:
358             case uChar:
359             case char16:
360             case char32:
361             case uShort:
362             case uInt:
363             case uLong:
364             case uLongLong:
365             case uInt128:
366             case charS:
367             case sChar:
368             case wChar:
369             case short_:
370             case int_:
371             case long_:
372             case longLong:
373             case int128:
374                 return true;
375 
376             default:
377                 return false;
378         }
379 }
380 
381 @property bool isUnsigned (CXTypeKind kind)
382 {
383     with (CXTypeKind)
384         switch (kind)
385         {
386             case charU: return true;
387             case uChar: return true;
388             case uShort: return true;
389             case uInt: return true;
390             case uLong: return true;
391             case uLongLong: return true;
392             case uInt128: return true;
393 
394             default: return false;
395         }
396 }
397 
398 class TypeLayoutError : object.Exception
399 {
400     this (string message, string file = __FILE__, size_t line = __LINE__)
401     {
402         super(message, file, line);
403     }
404 }
405 
406 class TypeLayoutErrorUnknown : TypeLayoutError
407 {
408     this (string spelling, string file = __FILE__, size_t line = __LINE__)
409     {
410         super("The layout of the type is unknown: '" ~ spelling ~ "'.");
411     }
412 }
413 
414 class TypeLayoutErrorInvalid : TypeLayoutError
415 {
416     this (string spelling, string file = __FILE__, size_t line = __LINE__)
417     {
418         super("The type is of invalid kind.");
419     }
420 }
421 
422 class TypeLayoutErrorIncomplete : TypeLayoutError
423 {
424     this (string spelling, string file = __FILE__, size_t line = __LINE__)
425     {
426         super("The type '" ~ spelling ~ "' is an incomplete type.");
427     }
428 }
429 
430 class TypeLayoutErrorDependent : TypeLayoutError
431 {
432     this (string spelling, string file = __FILE__, size_t line = __LINE__)
433     {
434         super("The type `" ~ spelling ~ "` is a dependent type.");
435     }
436 }
437 
438 class TypeLayoutErrorNotConstantSize : TypeLayoutError
439 {
440     this (string spelling, string file = __FILE__, size_t line = __LINE__)
441     {
442         super("The type '" ~ spelling ~ "'is not a constant size type.");
443     }
444 }
445 
446 class TypeLayoutErrorInvalidFieldName : TypeLayoutError
447 {
448     this (string spelling, string file = __FILE__, size_t line = __LINE__)
449     {
450         super("The field name '" ~ spelling ~ "' is not valid for this record.");
451     }
452 }
453 
454 void throwTypeLayoutError(
455     CXTypeLayoutError layout,
456     string spelling,
457     string file = __FILE__,
458     size_t line = __LINE__)
459 {
460     final switch (layout)
461     {
462         case CXTypeLayoutError.invalid:
463             throw new TypeLayoutErrorInvalid(spelling, file, line);
464         case CXTypeLayoutError.incomplete:
465             throw new TypeLayoutErrorIncomplete(spelling, file, line);
466         case CXTypeLayoutError.dependent:
467             throw new TypeLayoutErrorDependent(spelling, file, line);
468         case CXTypeLayoutError.notConstantSize:
469             throw new TypeLayoutErrorNotConstantSize(spelling, file, line);
470         case CXTypeLayoutError.invalidFieldName:
471             throw new TypeLayoutErrorInvalidFieldName(spelling, file, line);
472     }
473 }