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 }