1 /**
2 * Contains semantic routines specific to ImportC
3 *
4 * Specification: C11
5 *
6 * Copyright: Copyright (C) 2021-2022 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/importc.d, _importc.d)
10 * Documentation: https://dlang.org/phobos/dmd_importc.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/importc.d
12 */
13
14 module dmd.importc;
15
16 import core.stdc.stdio;
17
18 import dmd.astenums;
19 import dmd.dcast;
20 import dmd.declaration;
21 import dmd.dscope;
22 import dmd.dsymbol;
23 import dmd.expression;
24 import dmd.expressionsem;
25 import dmd.identifier;
26 import dmd.init;
27 import dmd.mtype;
28 import dmd.tokens;
29 import dmd.typesem;
30
31 /**************************************
32 * C11 does not allow array or function parameters.
33 * Hence, adjust those types per C11 6.7.6.3 rules.
34 * Params:
35 * t = parameter type to adjust
36 * sc = context
37 * Returns:
38 * adjusted type
39 */
cAdjustParamType(Type t,Scope * sc)40 Type cAdjustParamType(Type t, Scope* sc)
41 {
42 if (!(sc.flags & SCOPE.Cfile))
43 return t;
44
45 Type tb = t.toBasetype();
46
47 /* C11 6.7.6.3-7 array of T is converted to pointer to T
48 */
49 if (auto ta = tb.isTypeDArray())
50 {
51 t = ta.next.pointerTo();
52 }
53 else if (auto ts = tb.isTypeSArray())
54 {
55 t = ts.next.pointerTo();
56 }
57 /* C11 6.7.6.3-8 function is converted to pointer to function
58 */
59 else if (tb.isTypeFunction())
60 {
61 t = tb.pointerTo();
62 }
63 return t;
64 }
65
66 /***********************************************
67 * C11 6.3.2.1-3 Convert expression that is an array of type to a pointer to type.
68 * C11 6.3.2.1-4 Convert expression that is a function to a pointer to a function.
69 * Params:
70 * e = ImportC expression to possibly convert
71 * sc = context
72 * Returns:
73 * converted expression
74 */
arrayFuncConv(Expression e,Scope * sc)75 Expression arrayFuncConv(Expression e, Scope* sc)
76 {
77 //printf("arrayFuncConv() %s\n", e.toChars());
78 if (!(sc.flags & SCOPE.Cfile))
79 return e;
80
81 auto t = e.type.toBasetype();
82 if (auto ta = t.isTypeDArray())
83 {
84 e = e.castTo(sc, ta.next.pointerTo());
85 }
86 else if (auto ts = t.isTypeSArray())
87 {
88 e = e.castTo(sc, ts.next.pointerTo());
89 }
90 else if (t.isTypeFunction())
91 {
92 e = new AddrExp(e.loc, e);
93 }
94 else
95 return e;
96 return e.expressionSemantic(sc);
97 }
98
99 /****************************************
100 * Run semantic on `e`.
101 * Expression `e` evaluates to an instance of a struct.
102 * Look up `ident` as a field of that struct.
103 * Params:
104 * e = evaluates to an instance of a struct
105 * sc = context
106 * id = identifier of a field in that struct
107 * Returns:
108 * if successful `e.ident`
109 * if not then `ErrorExp` and message is printed
110 */
fieldLookup(Expression e,Scope * sc,Identifier id)111 Expression fieldLookup(Expression e, Scope* sc, Identifier id)
112 {
113 e = e.expressionSemantic(sc);
114 if (e.isErrorExp())
115 return e;
116
117 Dsymbol s;
118 auto t = e.type;
119 if (t.isTypePointer())
120 {
121 t = t.isTypePointer().next;
122 e = new PtrExp(e.loc, e);
123 }
124 if (auto ts = t.isTypeStruct())
125 s = ts.sym.search(e.loc, id, 0);
126 if (!s)
127 {
128 e.error("`%s` is not a member of `%s`", id.toChars(), t.toChars());
129 return ErrorExp.get();
130 }
131 Expression ef = new DotVarExp(e.loc, e, s.isDeclaration());
132 return ef.expressionSemantic(sc);
133 }
134
135 /****************************************
136 * C11 6.5.2.1-2
137 * Apply C semantics to `E[I]` expression.
138 * E1[E2] is lowered to *(E1 + E2)
139 * Params:
140 * ae = ArrayExp to run semantics on
141 * sc = context
142 * Returns:
143 * Expression if this was a C expression with completed semantic, null if not
144 */
carraySemantic(ArrayExp ae,Scope * sc)145 Expression carraySemantic(ArrayExp ae, Scope* sc)
146 {
147 if (!(sc.flags & SCOPE.Cfile))
148 return null;
149
150 auto e1 = ae.e1.expressionSemantic(sc);
151
152 assert(ae.arguments.length == 1);
153 Expression e2 = (*ae.arguments)[0];
154
155 /* CTFE cannot do pointer arithmetic, but it can index arrays.
156 * So, rewrite as an IndexExp if we can.
157 */
158 auto t1 = e1.type.toBasetype();
159 if (t1.isTypeDArray() || t1.isTypeSArray())
160 {
161 e2 = e2.expressionSemantic(sc).arrayFuncConv(sc);
162 // C doesn't do array bounds checking, so `true` turns it off
163 return new IndexExp(ae.loc, e1, e2, true).expressionSemantic(sc);
164 }
165
166 e1 = e1.arrayFuncConv(sc); // e1 might still be a function call
167 e2 = e2.expressionSemantic(sc);
168 auto t2 = e2.type.toBasetype();
169 if (t2.isTypeDArray() || t2.isTypeSArray())
170 {
171 return new IndexExp(ae.loc, e2, e1, true).expressionSemantic(sc); // swap operands
172 }
173
174 e2 = e2.arrayFuncConv(sc);
175 auto ep = new PtrExp(ae.loc, new AddExp(ae.loc, e1, e2));
176 return ep.expressionSemantic(sc);
177 }
178
179 /******************************************
180 * Determine default initializer for const global symbol.
181 */
addDefaultCInitializer(VarDeclaration dsym)182 void addDefaultCInitializer(VarDeclaration dsym)
183 {
184 //printf("addDefaultCInitializer() %s\n", dsym.toChars());
185 if (!(dsym.storage_class & (STC.static_ | STC.gshared)))
186 return;
187 if (dsym.storage_class & (STC.extern_ | STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result))
188 return;
189
190 Type t = dsym.type;
191 if (t.isTypeSArray() && t.isTypeSArray().isIncomplete())
192 {
193 dsym._init = new VoidInitializer(dsym.loc);
194 return; // incomplete arrays will be diagnosed later
195 }
196
197 if (t.isMutable())
198 return;
199
200 auto e = dsym.type.defaultInit(dsym.loc, true);
201 dsym._init = new ExpInitializer(dsym.loc, e);
202 }
203
204 /********************************************
205 * Resolve cast/call grammar ambiguity.
206 * Params:
207 * e = expression that might be a cast, might be a call
208 * sc = context
209 * Returns:
210 * null means leave as is, !=null means rewritten AST
211 */
castCallAmbiguity(Expression e,Scope * sc)212 Expression castCallAmbiguity(Expression e, Scope* sc)
213 {
214 Expression* pe = &e;
215
216 while (1)
217 {
218 // Walk down the postfix expressions till we find a CallExp or something else
219 switch ((*pe).op)
220 {
221 case EXP.dotIdentifier:
222 pe = &(*pe).isDotIdExp().e1;
223 continue;
224
225 case EXP.plusPlus:
226 case EXP.minusMinus:
227 pe = &(*pe).isPostExp().e1;
228 continue;
229
230 case EXP.array:
231 pe = &(*pe).isArrayExp().e1;
232 continue;
233
234 case EXP.call:
235 auto ce = (*pe).isCallExp();
236 if (ce.e1.parens)
237 {
238 ce.e1 = expressionSemantic(ce.e1, sc);
239 if (ce.e1.op == EXP.type)
240 {
241 const numArgs = ce.arguments ? ce.arguments.length : 0;
242 if (numArgs >= 1)
243 {
244 ce.e1.parens = false;
245 Expression arg;
246 foreach (a; (*ce.arguments)[])
247 {
248 arg = arg ? new CommaExp(a.loc, arg, a) : a;
249 }
250 auto t = ce.e1.isTypeExp().type;
251 *pe = arg;
252 return new CastExp(ce.loc, e, t);
253 }
254 }
255 }
256 return null;
257
258 default:
259 return null;
260 }
261 }
262 }
263
264 /********************************************
265 * Implement the C11 notion of function equivalence,
266 * which allows prototyped functions to match K+R functions,
267 * even though they are different.
268 * Params:
269 * tf1 = type of first function
270 * tf2 = type of second function
271 * Returns:
272 * true if C11 considers them equivalent
273 */
274
cFuncEquivalence(TypeFunction tf1,TypeFunction tf2)275 bool cFuncEquivalence(TypeFunction tf1, TypeFunction tf2)
276 {
277 //printf("cFuncEquivalence()\n %s\n %s\n", tf1.toChars(), tf2.toChars());
278 if (tf1.equals(tf2))
279 return true;
280
281 if (tf1.linkage != tf2.linkage)
282 return false;
283
284 // Allow func(void) to match func()
285 if (tf1.parameterList.length == 0 && tf2.parameterList.length == 0)
286 return true;
287
288 if (!cTypeEquivalence(tf1.next, tf2.next))
289 return false; // function return types don't match
290
291 if (tf1.parameterList.length != tf2.parameterList.length)
292 return false;
293
294 if (!tf1.parameterList.hasIdentifierList && !tf2.parameterList.hasIdentifierList) // if both are prototyped
295 {
296 if (tf1.parameterList.varargs != tf2.parameterList.varargs)
297 return false;
298 }
299
300 foreach (i, fparam ; tf1.parameterList)
301 {
302 Type t1 = fparam.type;
303 Type t2 = tf2.parameterList[i].type;
304
305 /* Strip off head const.
306 * Not sure if this is C11, but other compilers treat
307 * `void fn(int)` and `fn(const int x)`
308 * as equivalent.
309 */
310 t1 = t1.mutableOf();
311 t2 = t2.mutableOf();
312
313 if (!t1.equals(t2))
314 return false;
315 }
316
317 //printf("t1: %s\n", tf1.toChars());
318 //printf("t2: %s\n", tf2.toChars());
319 return true;
320 }
321
322 /*******************************
323 * Types haven't been merged yet, because we haven't done
324 * semantic() yet.
325 * But we still need to see if t1 and t2 are the same type.
326 * Params:
327 * t1 = first type
328 * t2 = second type
329 * Returns:
330 * true if they are equivalent types
331 */
cTypeEquivalence(Type t1,Type t2)332 bool cTypeEquivalence(Type t1, Type t2)
333 {
334 if (t1.equals(t2))
335 return true; // that was easy
336
337 if (t1.ty != t2.ty || t1.mod != t2.mod)
338 return false;
339
340 if (auto tp = t1.isTypePointer())
341 return cTypeEquivalence(tp.next, t2.nextOf());
342
343 if (auto ta = t1.isTypeSArray())
344 // Bug: should check array dimension
345 return cTypeEquivalence(ta.next, t2.nextOf());
346
347 if (auto ts = t1.isTypeStruct())
348 return ts.sym is t2.isTypeStruct().sym;
349
350 if (auto te = t1.isTypeEnum())
351 return te.sym is t2.isTypeEnum().sym;
352
353 if (auto tf = t1.isTypeFunction())
354 return cFuncEquivalence(tf, tf.isTypeFunction());
355
356 return false;
357 }
358