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 }