1 /** 2 Type translations 3 */ 4 module dpp.type; 5 6 import dpp.from: from; 7 8 alias Translator = string function( 9 in from!"clang".Type type, 10 ref from!"dpp.runtime.context".Context context, 11 in from!"std.typecons".Flag!"translatingFunction" translatingFunction 12 ) @safe pure; 13 14 alias Translators = Translator[from!"clang".Type.Kind]; 15 16 string translate(in from!"clang".Type type, 17 ref from!"dpp.runtime.context".Context context, 18 in from!"std.typecons".Flag!"translatingFunction" translatingFunction = from!"std.typecons".No.translatingFunction) 19 @safe pure 20 { 21 import std.conv: text; 22 if(type.kind !in translators) 23 throw new Exception(text("Type kind ", type.kind, " not supported: ", type)); 24 25 return translators[type.kind](type, context, translatingFunction); 26 } 27 28 Translators translators() @safe pure { 29 import clang: Type; 30 31 with(Type.Kind) { 32 return [ 33 Long: &simple!"c_long", 34 ULong: &simple!"c_ulong", 35 Void: &simple!"void", 36 NullPtr: &simple!"void*", 37 Bool: &simple!"bool", 38 WChar: &simple!"wchar", 39 SChar: &simple!"byte", 40 Char16: &simple!"wchar", 41 Char32: &simple!"dchar", 42 UChar: &simple!"ubyte", 43 UShort: &simple!"ushort", 44 Short: &simple!"short", 45 Int: &simple!"int", 46 UInt: &simple!"uint", 47 LongLong: &simple!"long", 48 ULongLong: &simple!"ulong", 49 Float: &simple!"float", 50 Double: &simple!"double", 51 Char_U: &simple!"ubyte", 52 Char_S: &simple!"char", 53 Int128: &simple!"cent", 54 UInt128: &simple!"ucent", 55 Float128: &simple!"real", 56 Half: &simple!"float", 57 LongDouble: &simple!"real", 58 Enum: &translateAggregate, 59 Pointer: &translatePointer, 60 FunctionProto: &translateFunctionProto, 61 Record: &translateRecord, 62 FunctionNoProto: &translateFunctionProto, 63 Elaborated: &translateAggregate, 64 ConstantArray: &translateConstantArray, 65 IncompleteArray: &translateIncompleteArray, 66 Typedef: &translateTypedef, 67 LValueReference: &translateLvalueRef, 68 RValueReference: &translateRvalueRef, 69 Complex: &translateComplex, 70 Unexposed: &translateUnexposed, 71 DependentSizedArray: &translateDependentSizedArray, 72 ]; 73 } 74 } 75 76 77 private string simple(string translation) 78 (in from!"clang".Type type, 79 ref from!"dpp.runtime.context".Context context, 80 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 81 @safe pure 82 { 83 return addModifiers(type, translation); 84 } 85 86 87 private string translateRecord(in from!"clang".Type type, 88 ref from!"dpp.runtime.context".Context context, 89 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 90 @safe pure 91 { 92 // see it.compile.projects.va_list 93 return type.spelling == "struct __va_list_tag" 94 ? "va_list" 95 : translateAggregate(type, context, translatingFunction); 96 } 97 98 private string translateAggregate(in from!"clang".Type type, 99 ref from!"dpp.runtime.context".Context context, 100 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 101 @safe pure 102 { 103 import std.array: replace; 104 105 // if it's anonymous, find the nickname, otherwise return the spelling 106 string spelling() { 107 import std.algorithm: canFind; 108 // clang names anonymous types with a long name indicating where the type 109 // was declared 110 return type.spelling.canFind("(anonymous") ? context.popLastNickName : type.spelling; 111 } 112 113 return addModifiers(type, spelling) 114 // "struct Foo" -> Foo, "union Foo" -> Foo, "enum Foo" -> Foo 115 .replace("struct ", "").replace("union ", "").replace("enum ", "") 116 ; 117 } 118 119 120 private string translateConstantArray(in from!"clang".Type type, 121 ref from!"dpp.runtime.context".Context context, 122 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 123 @safe pure 124 { 125 import std.conv: text; 126 127 context.indent.log("Constant array of # ", type.numElements); 128 129 return translatingFunction 130 ? translate(type.elementType, context) ~ `*` 131 : translate(type.elementType, context) ~ `[` ~ type.numElements.text ~ `]`; 132 } 133 134 135 private string translateDependentSizedArray( 136 in from!"clang".Type type, 137 ref from!"dpp.runtime.context".Context context, 138 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 139 @safe pure 140 { 141 import std.conv: text; 142 import std.algorithm: find, countUntil; 143 144 // FIXME: hacky, only works for the only test in it.cpp.class_.template (array) 145 auto start = type.spelling.find("["); start = start[1 .. $]; 146 auto endIndex = start.countUntil("]"); 147 148 return translate(type.elementType, context) ~ `[` ~ start[0 .. endIndex] ~ `]`; 149 } 150 151 152 private string translateIncompleteArray(in from!"clang".Type type, 153 ref from!"dpp.runtime.context".Context context, 154 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 155 @safe pure 156 { 157 const dType = translate(type.elementType, context); 158 // if translating a function, we want C's T[] to translate 159 // to T*, otherwise we want a flexible array 160 return translatingFunction ? dType ~ `*` : dType ~ "[0]"; 161 162 } 163 164 private string translateTypedef(in from!"clang".Type type, 165 ref from!"dpp.runtime.context".Context context, 166 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 167 @safe pure 168 { 169 // Here we may get a Typedef with a canonical type of Enum. It might be worth 170 // translating to int for function parameters 171 return addModifiers(type, type.spelling); 172 } 173 174 private string translatePointer(in from!"clang".Type type, 175 ref from!"dpp.runtime.context".Context context, 176 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 177 @safe pure 178 { 179 import clang: Type; 180 import std.conv: text; 181 182 assert(type.kind == Type.Kind.Pointer, "type kind not Pointer"); 183 if(type.pointee is null) throw new Exception("null pointee for " ~ type.toString); 184 assert(type.pointee !is null, "Pointee is null for " ~ type.toString); 185 186 const isFunction = 187 type.pointee.kind == Type.Kind.Unexposed && 188 (type.pointee.canonical.kind == Type.Kind.FunctionProto || 189 type.pointee.canonical.kind == Type.Kind.FunctionNoProto); 190 191 // usually "*" but sometimes not needed if already a reference type 192 const maybeStar = isFunction ? "" : "*"; 193 context.log("Pointee: ", *type.pointee); 194 context.log("Pointee canonical: ", type.pointee.canonical); 195 196 const translateCanonical = type.pointee.kind == Type.Kind.Unexposed; 197 context.log("Translate canonical? ", translateCanonical); 198 199 const indentation = context.indentation; 200 const rawType = translateCanonical 201 ? translate(type.pointee.canonical, context.indent) 202 : translate(*type.pointee, context.indent); 203 context.setIndentation(indentation); 204 205 context.log("Raw type: ", rawType); 206 207 // Only add top-level const if it's const all the way down 208 bool addConst() @trusted { 209 auto ptr = &type; 210 while(ptr.kind == Type.Kind.Pointer) { 211 if(!ptr.isConstQualified || !ptr.pointee.isConstQualified) 212 return false; 213 ptr = ptr.pointee; 214 } 215 216 return true; 217 } 218 219 const ptrType = addConst 220 ? `const(` ~ rawType ~ maybeStar ~ `)` 221 : rawType ~ maybeStar; 222 223 return ptrType; 224 } 225 226 // currently only getting here from function pointer variables 227 // with have kind unexposed but canonical kind FunctionProto 228 private string translateFunctionProto(in from!"clang".Type type, 229 ref from!"dpp.runtime.context".Context context, 230 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 231 @safe pure 232 { 233 import std.conv: text; 234 import std.algorithm: map; 235 import std.array: join, array; 236 237 const params = type.paramTypes.map!(a => translate(a, context)).array; 238 const isVariadic = params.length > 0 && type.isVariadicFunction; 239 const variadicParams = isVariadic ? ["..."] : []; 240 const allParams = params ~ variadicParams; 241 return text(translate(type.returnType, context), " function(", allParams.join(", "), ")"); 242 } 243 244 private string translateLvalueRef(in from!"clang".Type type, 245 ref from!"dpp.runtime.context".Context context, 246 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 247 @safe pure 248 { 249 return "ref " ~ translate(*type.canonical.pointee, context, translatingFunction); 250 } 251 252 // we cheat and pretend it's a value 253 private string translateRvalueRef(in from!"clang".Type type, 254 ref from!"dpp.runtime.context".Context context, 255 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 256 @safe pure 257 { 258 return translate(*type.canonical.pointee, context, translatingFunction); 259 } 260 261 262 private string translateComplex(in from!"clang".Type type, 263 ref from!"dpp.runtime.context".Context context, 264 in from!"std.typecons".Flag!"translatingFunction" translatingFunction) 265 @safe pure 266 { 267 return "c" ~ translate(type.elementType, context, translatingFunction); 268 } 269 270 private string translateUnexposed(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 return type.spelling; 276 } 277 278 279 private string addModifiers(in from!"clang".Type type, in string translation) @safe pure { 280 import std.array: replace; 281 const realTranslation = translation.replace("const ", ""); 282 return type.isConstQualified 283 ? `const(` ~ realTranslation ~ `)` 284 : realTranslation; 285 }