xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/importc.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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