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 }