1 /** 2 Type translations 3 */ 4 module dpp.translation.type; 5 6 7 import dpp.from: from; 8 9 10 alias Translator = string function( 11 in from!"clang".Type type, 12 ref from!"dpp.runtime.context".Context context, 13 in from!"std.typecons".Flag!"translatingFunction" translatingFunction 14 ) @safe pure; 15 16 alias Translators = Translator[from!"clang".Type.Kind]; 17 18 19 string translate(in from!"clang".Type type, 20 ref from!"dpp.runtime.context".Context context, 21 in from!"std.typecons".Flag!"translatingFunction" translatingFunction = from!"std.typecons".No.translatingFunction) 22 @safe pure 23 { 24 import std.conv: text; 25 if(type.kind !in translators) 26 throw new Exception(text("Type kind ", type.kind, " not supported: ", type)); 27 28 return translators[type.kind](type, context, translatingFunction); 29 } 30 31 32 Translators translators() @safe pure { 33 import clang: Type; 34 35 with(Type.Kind) { 36 return [ 37 Long: &simple!"c_long", 38 ULong: &simple!"c_ulong", 39 Void: &simple!"void", 40 NullPtr: &simple!"void*", 41 Bool: &simple!"bool", 42 WChar: &simple!"wchar", 43 SChar: &simple!"byte", 44 Char16: &simple!"wchar", 45 Char32: &simple!"dchar", 46 UChar: &simple!"ubyte", 47 UShort: &simple!"ushort", 48 Short: &simple!"short", 49 Int: &simple!"int", 50 UInt: &simple!"uint", 51 LongLong: &simple!"long", 52 ULongLong: &simple!"ulong", 53 Float: &simple!"float", 54 Double: &simple!"double", 55 Char_U: &simple!"ubyte", 56 Char_S: &simple!"char", 57 Int128: &simple!"Int128", 58 UInt128: &simple!"UInt128", 59 Float128: &simple!"real", 60 Half: &simple!"float", 61 LongDouble: &simple!"real", 62 Enum: &translateAggregate, 63 Pointer: &translatePointer, 64 FunctionProto: &translateFunctionProto, 65 Record: &translateRecord, 66 FunctionNoProto: &translateFunctionProto, 67 Elaborated: &translateAggregate, 68 ConstantArray: &translateConstantArray, 69 IncompleteArray: &translateIncompleteArray, 70 Typedef: &translateTypedef, 71 LValueReference: &translateLvalueRef, 72 RValueReference: &translateRvalueRef, 73 Complex: &translateComplex, 74 Unexposed: &translateUnexposed, 75 DependentSizedArray: &translateDependentSizedArray, 76 Vector: &translateSimdVector, 77 MemberPointer: &translatePointer, // FIXME #83 78 Invalid: &ignore, // FIXME C++ stdlib <type_traits> 79 ]; 80 } 81 } 82 83 private string ignore(in from!"clang".Type type, 84 ref from!"dpp.runtime.context".Context context, 85 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 86 @safe pure 87 { 88 return ""; 89 } 90 91 92 private string simple(string translation) 93 (in from!"clang".Type type, 94 ref from!"dpp.runtime.context".Context context, 95 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 96 @safe pure 97 { 98 return addModifiers(type, translation); 99 } 100 101 102 private string translateRecord(in from!"clang".Type type, 103 ref from!"dpp.runtime.context".Context context, 104 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 105 @safe pure 106 { 107 // see it.compile.projects.va_list 108 return type.spelling == "struct __va_list_tag" 109 ? "va_list" 110 : translateAggregate(type, context, translatingFunction); 111 } 112 113 private string translateAggregate(in from!"clang".Type type, 114 ref from!"dpp.runtime.context".Context context, 115 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 116 @safe pure 117 { 118 import std.array: replace; 119 import std.algorithm: canFind; 120 121 // if it's anonymous, find the nickname, otherwise return the spelling 122 string spelling() { 123 // clang names anonymous types with a long name indicating where the type 124 // was declared, so we check here with `hasAnonymousSpelling` 125 if(hasAnonymousSpelling(type)) return context.spellingOrNickname(type.declaration); 126 127 // A struct in a namespace will have a type of kind Record with the fully 128 // qualified name (e.g. std::random_access_iterator_tag), but the cursor 129 // itself has only the name (e.g. random_access_iterator_tag), so we get 130 // the spelling from the type's declaration instead of from the type itself. 131 // See it.cpp.templates.__copy_move and contract.namespace.struct. 132 return type.spelling.canFind(":") ? type.declaration.spelling : type.spelling; 133 } 134 135 return addModifiers(type, spelling) 136 // "struct Foo" -> Foo, "union Foo" -> Foo, "enum Foo" -> Foo 137 .replace("struct ", "") 138 .replace("union ", "") 139 .replace("enum ", "") 140 .replace("<", "!(") 141 .replace(">", ")") 142 ; 143 } 144 145 146 private string translateConstantArray(in from!"clang".Type type, 147 ref from!"dpp.runtime.context".Context context, 148 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 149 @safe pure 150 { 151 import std.conv: text; 152 153 context.indent.log("Constant array of # ", type.numElements); 154 155 return translatingFunction 156 ? translate(type.elementType, context) ~ `*` 157 : translate(type.elementType, context) ~ `[` ~ type.numElements.text ~ `]`; 158 } 159 160 161 private string translateDependentSizedArray( 162 in from!"clang".Type type, 163 ref from!"dpp.runtime.context".Context context, 164 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 165 @safe pure 166 { 167 import std.conv: text; 168 import std.algorithm: find, countUntil; 169 170 // FIXME: hacky, only works for the only test in it.cpp.class_.template (array) 171 auto start = type.spelling.find("["); start = start[1 .. $]; 172 auto endIndex = start.countUntil("]"); 173 174 return translate(type.elementType, context) ~ `[` ~ start[0 .. endIndex] ~ `]`; 175 } 176 177 178 private string translateIncompleteArray(in from!"clang".Type type, 179 ref from!"dpp.runtime.context".Context context, 180 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 181 @safe pure 182 { 183 const dType = translate(type.elementType, context); 184 // if translating a function, we want C's T[] to translate 185 // to T*, otherwise we want a flexible array 186 return translatingFunction ? dType ~ `*` : dType ~ "[0]"; 187 188 } 189 190 private string translateTypedef(in from!"clang".Type type, 191 ref from!"dpp.runtime.context".Context context, 192 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 193 @safe pure 194 { 195 import std..string: replace; 196 // Here we may get a Typedef with a canonical type of Enum. It might be worth 197 // translating to int for function parameters 198 return addModifiers(type, type.spelling.replace("::", ".")); 199 } 200 201 private string translatePointer(in from!"clang".Type type, 202 ref from!"dpp.runtime.context".Context context, 203 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 204 @safe pure 205 { 206 import clang: Type; 207 import std.conv: text; 208 209 assert(type.kind == Type.Kind.Pointer || type.kind == Type.Kind.MemberPointer, "type kind not Pointer"); 210 assert(!type.pointee.isInvalid, "pointee is invalid"); 211 212 const isFunction = 213 type.pointee.kind == Type.Kind.Unexposed && 214 (type.pointee.canonical.kind == Type.Kind.FunctionProto || 215 type.pointee.canonical.kind == Type.Kind.FunctionNoProto); 216 217 // usually "*" but sometimes not needed if already a reference type 218 const maybeStar = isFunction ? "" : "*"; 219 context.log("Pointee: ", type.pointee); 220 context.log("Pointee canonical: ", type.pointee.canonical); 221 222 const translateCanonical = type.pointee.kind == Type.Kind.Unexposed; 223 context.log("Translate canonical? ", translateCanonical); 224 225 const indentation = context.indentation; 226 const rawType = translateCanonical 227 ? translate(type.pointee.canonical, context.indent) 228 : translate(type.pointee, context.indent); 229 context.setIndentation(indentation); 230 231 context.log("Raw type: ", rawType); 232 233 // Only add top-level const if it's const all the way down 234 bool addConst() @trusted { 235 auto ptr = Type(type); 236 while(ptr.kind == Type.Kind.Pointer) { 237 if(!ptr.isConstQualified || !ptr.pointee.isConstQualified) 238 return false; 239 ptr = ptr.pointee; 240 } 241 242 return true; 243 } 244 245 const ptrType = addConst 246 ? `const(` ~ rawType ~ maybeStar ~ `)` 247 : rawType ~ maybeStar; 248 249 return ptrType; 250 } 251 252 // currently only getting here from function pointer variables 253 // with have kind unexposed but canonical kind FunctionProto 254 private string translateFunctionProto(in from!"clang".Type type, 255 ref from!"dpp.runtime.context".Context context, 256 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 257 @safe pure 258 { 259 import std.conv: text; 260 import std.algorithm: map; 261 import std.array: join, array; 262 263 const params = type.paramTypes.map!(a => translate(a, context)).array; 264 const isVariadic = params.length > 0 && type.isVariadicFunction; 265 const variadicParams = isVariadic ? ["..."] : []; 266 const allParams = params ~ variadicParams; 267 return text(translate(type.returnType, context), " function(", allParams.join(", "), ")"); 268 } 269 270 private string translateLvalueRef(in from!"clang".Type type, 271 ref from!"dpp.runtime.context".Context context, 272 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 273 @safe pure 274 { 275 const pointeeTranslation = translate(type.canonical.pointee, context, translatingFunction); 276 return translatingFunction 277 ? "ref " ~ pointeeTranslation 278 : pointeeTranslation ~ "*"; 279 } 280 281 // we cheat and pretend it's a value 282 private string translateRvalueRef(in from!"clang".Type type, 283 ref from!"dpp.runtime.context".Context context, 284 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 285 @safe pure 286 { 287 const dtype = translate(type.canonical.pointee, context, translatingFunction); 288 return `dpp.Move!(` ~ dtype ~ `)`; 289 } 290 291 292 private string translateComplex(in from!"clang".Type type, 293 ref from!"dpp.runtime.context".Context context, 294 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 295 @safe pure 296 { 297 return "c" ~ translate(type.elementType, context, translatingFunction); 298 } 299 300 private string translateUnexposed(in from!"clang".Type type, 301 ref from!"dpp.runtime.context".Context context, 302 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 303 @safe pure 304 { 305 import std..string: replace; 306 // we might get template arguments here 307 return type.spelling 308 .translateString 309 .replace("-", "_") 310 ; 311 } 312 313 string translateString(in string spelling) @safe pure nothrow { 314 import std..string: replace; 315 return spelling 316 .replace("<", "!(") 317 .replace(">", ")") 318 .replace("decltype", "typeof") 319 .replace("typename ", "") 320 .replace("::", ".") 321 .replace("volatile ", "") 322 .replace("long long", "long") 323 .replace("unsigned ", "u") 324 ; 325 } 326 327 328 private string translateSimdVector(in from!"clang".Type type, 329 ref from!"dpp.runtime.context".Context context, 330 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 331 @safe pure 332 { 333 import std.conv: text; 334 import std.algorithm: canFind; 335 336 const numBytes = type.numElements; 337 const dtype = 338 translate(type.elementType, context, translatingFunction) ~ 339 text(type.getSizeof / numBytes); 340 341 const isUnsupportedType = 342 [ 343 "long8", "short2", "char1", "double8", "ubyte1", "ushort2", 344 "ulong8", "byte1", 345 ].canFind(dtype); 346 347 return isUnsupportedType ? "int /* FIXME: unsupported SIMD type */" : "core.simd." ~ dtype; 348 } 349 350 351 private string addModifiers(in from!"clang".Type type, in string translation) @safe pure { 352 import std.array: replace; 353 const realTranslation = translation.replace("const ", "").replace("volatile ", ""); 354 return type.isConstQualified 355 ? `const(` ~ realTranslation ~ `)` 356 : realTranslation; 357 } 358 359 bool hasAnonymousSpelling(in from!"clang".Type type) @safe pure nothrow { 360 import std.algorithm: canFind; 361 return type.spelling.canFind("(anonymous"); 362 }