1 /**
2  * Contains semantic routines specific to ImportC
3  *
4  * Specification: C11
5  *
6  * Copyright:   Copyright (C) 2021-2023 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  */
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  */
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         if (!checkAddressable(e, sc))
85             return ErrorExp.get();
86         e = e.castTo(sc, ta.next.pointerTo());
87     }
88     else if (auto ts = t.isTypeSArray())
89     {
90         if (!checkAddressable(e, sc))
91             return ErrorExp.get();
92         e = e.castTo(sc, ts.next.pointerTo());
93     }
94     else if (t.isTypeFunction())
95     {
96         e = new AddrExp(e.loc, e);
97     }
98     else
99         return e;
100     return e.expressionSemantic(sc);
101 }
102 
103 /****************************************
104  * Run semantic on `e`.
105  * Expression `e` evaluates to an instance of a struct.
106  * Look up `ident` as a field of that struct.
107  * Params:
108  *   e = evaluates to an instance of a struct
109  *   sc = context
110  *   id = identifier of a field in that struct
111  * Returns:
112  *   if successful `e.ident`
113  *   if not then `ErrorExp` and message is printed
114  */
115 Expression fieldLookup(Expression e, Scope* sc, Identifier id)
116 {
117     e = e.expressionSemantic(sc);
118     if (e.isErrorExp())
119         return e;
120 
121     Dsymbol s;
122     auto t = e.type;
123     if (t.isTypePointer())
124     {
125         t = t.isTypePointer().next;
126         e = new PtrExp(e.loc, e);
127     }
128     if (auto ts = t.isTypeStruct())
129         s = ts.sym.search(e.loc, id, 0);
130     if (!s)
131     {
132         e.error("`%s` is not a member of `%s`", id.toChars(), t.toChars());
133         return ErrorExp.get();
134     }
135     Expression ef = new DotVarExp(e.loc, e, s.isDeclaration());
136     return ef.expressionSemantic(sc);
137 }
138 
139 /****************************************
140  * C11 6.5.2.1-2
141  * Apply C semantics to `E[I]` expression.
142  * E1[E2] is lowered to *(E1 + E2)
143  * Params:
144  *      ae = ArrayExp to run semantics on
145  *      sc = context
146  * Returns:
147  *      Expression if this was a C expression with completed semantic, null if not
148  */
149 Expression carraySemantic(ArrayExp ae, Scope* sc)
150 {
151     if (!(sc.flags & SCOPE.Cfile))
152         return null;
153 
154     auto e1 = ae.e1.expressionSemantic(sc);
155 
156     assert(ae.arguments.length == 1);
157     Expression e2 = (*ae.arguments)[0];
158 
159     /* CTFE cannot do pointer arithmetic, but it can index arrays.
160      * So, rewrite as an IndexExp if we can.
161      */
162     auto t1 = e1.type.toBasetype();
163     if (t1.isTypeDArray() || t1.isTypeSArray())
164     {
165         e2 = e2.expressionSemantic(sc).arrayFuncConv(sc);
166         // C doesn't do array bounds checking, so `true` turns it off
167         return new IndexExp(ae.loc, e1, e2, true).expressionSemantic(sc);
168     }
169 
170     e1 = e1.arrayFuncConv(sc);   // e1 might still be a function call
171     e2 = e2.expressionSemantic(sc);
172     auto t2 = e2.type.toBasetype();
173     if (t2.isTypeDArray() || t2.isTypeSArray())
174     {
175         return new IndexExp(ae.loc, e2, e1, true).expressionSemantic(sc); // swap operands
176     }
177 
178     e2 = e2.arrayFuncConv(sc);
179     auto ep = new PtrExp(ae.loc, new AddExp(ae.loc, e1, e2));
180     return ep.expressionSemantic(sc);
181 }
182 
183 /******************************************
184  * Determine default initializer for const global symbol.
185  */
186 void addDefaultCInitializer(VarDeclaration dsym)
187 {
188     //printf("addDefaultCInitializer() %s\n", dsym.toChars());
189     if (!(dsym.storage_class & (STC.static_ | STC.gshared)))
190         return;
191     if (dsym.storage_class & (STC.extern_ | STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result))
192         return;
193 
194     Type t = dsym.type;
195     if (t.isTypeSArray() && t.isTypeSArray().isIncomplete())
196     {
197         dsym._init = new VoidInitializer(dsym.loc);
198         return; // incomplete arrays will be diagnosed later
199     }
200 
201     if (t.isMutable())
202         return;
203 
204     auto e = dsym.type.defaultInit(dsym.loc, true);
205     dsym._init = new ExpInitializer(dsym.loc, e);
206 }
207 
208 /********************************************
209  * Resolve cast/call grammar ambiguity.
210  * Params:
211  *      e = expression that might be a cast, might be a call
212  *      sc = context
213  * Returns:
214  *      null means leave as is, !=null means rewritten AST
215  */
216 Expression castCallAmbiguity(Expression e, Scope* sc)
217 {
218     Expression* pe = &e;
219 
220     while (1)
221     {
222         // Walk down the postfix expressions till we find a CallExp or something else
223         switch ((*pe).op)
224         {
225             case EXP.dotIdentifier:
226                 pe = &(*pe).isDotIdExp().e1;
227                 continue;
228 
229             case EXP.plusPlus:
230             case EXP.minusMinus:
231                 pe = &(*pe).isPostExp().e1;
232                 continue;
233 
234             case EXP.array:
235                 pe = &(*pe).isArrayExp().e1;
236                 continue;
237 
238             case EXP.call:
239                 auto ce = (*pe).isCallExp();
240                 if (ce.e1.parens)
241                 {
242                     ce.e1 = expressionSemantic(ce.e1, sc);
243                     if (ce.e1.op == EXP.type)
244                     {
245                         const numArgs = ce.arguments ? ce.arguments.length : 0;
246                         if (numArgs >= 1)
247                         {
248                             ce.e1.parens = false;
249                             Expression arg;
250                             foreach (a; (*ce.arguments)[])
251                             {
252                                 arg = arg ? new CommaExp(a.loc, arg, a) : a;
253                             }
254                             auto t = ce.e1.isTypeExp().type;
255                             *pe = arg;
256                             return new CastExp(ce.loc, e, t);
257                         }
258                     }
259                 }
260                 return null;
261 
262             default:
263                 return null;
264         }
265     }
266 }
267 
268 /********************************************
269  * Implement the C11 notion of function equivalence,
270  * which allows prototyped functions to match K+R functions,
271  * even though they are different.
272  * Params:
273  *      tf1 = type of first function
274  *      tf2 = type of second function
275  * Returns:
276  *      true if C11 considers them equivalent
277  */
278 
279 bool cFuncEquivalence(TypeFunction tf1, TypeFunction tf2)
280 {
281     //printf("cFuncEquivalence()\n  %s\n  %s\n", tf1.toChars(), tf2.toChars());
282     if (tf1.equals(tf2))
283         return true;
284 
285     if (tf1.linkage != tf2.linkage)
286         return false;
287 
288     // Allow func(void) to match func()
289     if (tf1.parameterList.length == 0 && tf2.parameterList.length == 0)
290         return true;
291 
292     if (!cTypeEquivalence(tf1.next, tf2.next))
293         return false;   // function return types don't match
294 
295     if (tf1.parameterList.length != tf2.parameterList.length)
296         return false;
297 
298     if (!tf1.parameterList.hasIdentifierList && !tf2.parameterList.hasIdentifierList) // if both are prototyped
299     {
300         if (tf1.parameterList.varargs != tf2.parameterList.varargs)
301             return false;
302     }
303 
304     foreach (i, fparam ; tf1.parameterList)
305     {
306         Type t1 = fparam.type;
307         Type t2 = tf2.parameterList[i].type;
308 
309         /* Strip off head const.
310          * Not sure if this is C11, but other compilers treat
311          * `void fn(int)` and `fn(const int x)`
312          * as equivalent.
313          */
314         t1 = t1.mutableOf();
315         t2 = t2.mutableOf();
316 
317         if (!t1.equals(t2))
318             return false;
319     }
320 
321     //printf("t1: %s\n", tf1.toChars());
322     //printf("t2: %s\n", tf2.toChars());
323     return true;
324 }
325 
326 /*******************************
327  * Types haven't been merged yet, because we haven't done
328  * semantic() yet.
329  * But we still need to see if t1 and t2 are the same type.
330  * Params:
331  *      t1 = first type
332  *      t2 = second type
333  * Returns:
334  *      true if they are equivalent types
335  */
336 bool cTypeEquivalence(Type t1, Type t2)
337 {
338     if (t1.equals(t2))
339         return true;    // that was easy
340 
341     if (t1.ty != t2.ty || t1.mod != t2.mod)
342         return false;
343 
344     if (auto tp = t1.isTypePointer())
345         return cTypeEquivalence(tp.next, t2.nextOf());
346 
347     if (auto ta = t1.isTypeSArray())
348         // Bug: should check array dimension
349         return cTypeEquivalence(ta.next, t2.nextOf());
350 
351     if (auto ts = t1.isTypeStruct())
352         return ts.sym is t2.isTypeStruct().sym;
353 
354     if (auto te = t1.isTypeEnum())
355         return te.sym is t2.isTypeEnum().sym;
356 
357     if (auto tf = t1.isTypeFunction())
358         return cFuncEquivalence(tf, tf.isTypeFunction());
359 
360     return false;
361 }