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