/** * Contains semantic routines specific to ImportC * * Specification: C11 * * Copyright: Copyright (C) 2021-2022 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/importc.d, _importc.d) * Documentation: https://dlang.org/phobos/dmd_importc.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/importc.d */ module dmd.importc; import core.stdc.stdio; import dmd.astenums; import dmd.dcast; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; import dmd.expression; import dmd.expressionsem; import dmd.identifier; import dmd.init; import dmd.mtype; import dmd.tokens; import dmd.typesem; /************************************** * C11 does not allow array or function parameters. * Hence, adjust those types per C11 6.7.6.3 rules. * Params: * t = parameter type to adjust * sc = context * Returns: * adjusted type */ Type cAdjustParamType(Type t, Scope* sc) { if (!(sc.flags & SCOPE.Cfile)) return t; Type tb = t.toBasetype(); /* C11 6.7.6.3-7 array of T is converted to pointer to T */ if (auto ta = tb.isTypeDArray()) { t = ta.next.pointerTo(); } else if (auto ts = tb.isTypeSArray()) { t = ts.next.pointerTo(); } /* C11 6.7.6.3-8 function is converted to pointer to function */ else if (tb.isTypeFunction()) { t = tb.pointerTo(); } return t; } /*********************************************** * C11 6.3.2.1-3 Convert expression that is an array of type to a pointer to type. * C11 6.3.2.1-4 Convert expression that is a function to a pointer to a function. * Params: * e = ImportC expression to possibly convert * sc = context * Returns: * converted expression */ Expression arrayFuncConv(Expression e, Scope* sc) { //printf("arrayFuncConv() %s\n", e.toChars()); if (!(sc.flags & SCOPE.Cfile)) return e; auto t = e.type.toBasetype(); if (auto ta = t.isTypeDArray()) { e = e.castTo(sc, ta.next.pointerTo()); } else if (auto ts = t.isTypeSArray()) { e = e.castTo(sc, ts.next.pointerTo()); } else if (t.isTypeFunction()) { e = new AddrExp(e.loc, e); } else return e; return e.expressionSemantic(sc); } /**************************************** * Run semantic on `e`. * Expression `e` evaluates to an instance of a struct. * Look up `ident` as a field of that struct. * Params: * e = evaluates to an instance of a struct * sc = context * id = identifier of a field in that struct * Returns: * if successful `e.ident` * if not then `ErrorExp` and message is printed */ Expression fieldLookup(Expression e, Scope* sc, Identifier id) { e = e.expressionSemantic(sc); if (e.isErrorExp()) return e; Dsymbol s; auto t = e.type; if (t.isTypePointer()) { t = t.isTypePointer().next; e = new PtrExp(e.loc, e); } if (auto ts = t.isTypeStruct()) s = ts.sym.search(e.loc, id, 0); if (!s) { e.error("`%s` is not a member of `%s`", id.toChars(), t.toChars()); return ErrorExp.get(); } Expression ef = new DotVarExp(e.loc, e, s.isDeclaration()); return ef.expressionSemantic(sc); } /**************************************** * C11 6.5.2.1-2 * Apply C semantics to `E[I]` expression. * E1[E2] is lowered to *(E1 + E2) * Params: * ae = ArrayExp to run semantics on * sc = context * Returns: * Expression if this was a C expression with completed semantic, null if not */ Expression carraySemantic(ArrayExp ae, Scope* sc) { if (!(sc.flags & SCOPE.Cfile)) return null; auto e1 = ae.e1.expressionSemantic(sc); assert(ae.arguments.length == 1); Expression e2 = (*ae.arguments)[0]; /* CTFE cannot do pointer arithmetic, but it can index arrays. * So, rewrite as an IndexExp if we can. */ auto t1 = e1.type.toBasetype(); if (t1.isTypeDArray() || t1.isTypeSArray()) { e2 = e2.expressionSemantic(sc).arrayFuncConv(sc); // C doesn't do array bounds checking, so `true` turns it off return new IndexExp(ae.loc, e1, e2, true).expressionSemantic(sc); } e1 = e1.arrayFuncConv(sc); // e1 might still be a function call e2 = e2.expressionSemantic(sc); auto t2 = e2.type.toBasetype(); if (t2.isTypeDArray() || t2.isTypeSArray()) { return new IndexExp(ae.loc, e2, e1, true).expressionSemantic(sc); // swap operands } e2 = e2.arrayFuncConv(sc); auto ep = new PtrExp(ae.loc, new AddExp(ae.loc, e1, e2)); return ep.expressionSemantic(sc); } /****************************************** * Determine default initializer for const global symbol. */ void addDefaultCInitializer(VarDeclaration dsym) { //printf("addDefaultCInitializer() %s\n", dsym.toChars()); if (!(dsym.storage_class & (STC.static_ | STC.gshared))) return; if (dsym.storage_class & (STC.extern_ | STC.field | STC.in_ | STC.foreach_ | STC.parameter | STC.result)) return; Type t = dsym.type; if (t.isTypeSArray() && t.isTypeSArray().isIncomplete()) { dsym._init = new VoidInitializer(dsym.loc); return; // incomplete arrays will be diagnosed later } if (t.isMutable()) return; auto e = dsym.type.defaultInit(dsym.loc, true); dsym._init = new ExpInitializer(dsym.loc, e); } /******************************************** * Resolve cast/call grammar ambiguity. * Params: * e = expression that might be a cast, might be a call * sc = context * Returns: * null means leave as is, !=null means rewritten AST */ Expression castCallAmbiguity(Expression e, Scope* sc) { Expression* pe = &e; while (1) { // Walk down the postfix expressions till we find a CallExp or something else switch ((*pe).op) { case EXP.dotIdentifier: pe = &(*pe).isDotIdExp().e1; continue; case EXP.plusPlus: case EXP.minusMinus: pe = &(*pe).isPostExp().e1; continue; case EXP.array: pe = &(*pe).isArrayExp().e1; continue; case EXP.call: auto ce = (*pe).isCallExp(); if (ce.e1.parens) { ce.e1 = expressionSemantic(ce.e1, sc); if (ce.e1.op == EXP.type) { const numArgs = ce.arguments ? ce.arguments.length : 0; if (numArgs >= 1) { ce.e1.parens = false; Expression arg; foreach (a; (*ce.arguments)[]) { arg = arg ? new CommaExp(a.loc, arg, a) : a; } auto t = ce.e1.isTypeExp().type; *pe = arg; return new CastExp(ce.loc, e, t); } } } return null; default: return null; } } } /******************************************** * Implement the C11 notion of function equivalence, * which allows prototyped functions to match K+R functions, * even though they are different. * Params: * tf1 = type of first function * tf2 = type of second function * Returns: * true if C11 considers them equivalent */ bool cFuncEquivalence(TypeFunction tf1, TypeFunction tf2) { //printf("cFuncEquivalence()\n %s\n %s\n", tf1.toChars(), tf2.toChars()); if (tf1.equals(tf2)) return true; if (tf1.linkage != tf2.linkage) return false; // Allow func(void) to match func() if (tf1.parameterList.length == 0 && tf2.parameterList.length == 0) return true; if (!cTypeEquivalence(tf1.next, tf2.next)) return false; // function return types don't match if (tf1.parameterList.length != tf2.parameterList.length) return false; if (!tf1.parameterList.hasIdentifierList && !tf2.parameterList.hasIdentifierList) // if both are prototyped { if (tf1.parameterList.varargs != tf2.parameterList.varargs) return false; } foreach (i, fparam ; tf1.parameterList) { Type t1 = fparam.type; Type t2 = tf2.parameterList[i].type; /* Strip off head const. * Not sure if this is C11, but other compilers treat * `void fn(int)` and `fn(const int x)` * as equivalent. */ t1 = t1.mutableOf(); t2 = t2.mutableOf(); if (!t1.equals(t2)) return false; } //printf("t1: %s\n", tf1.toChars()); //printf("t2: %s\n", tf2.toChars()); return true; } /******************************* * Types haven't been merged yet, because we haven't done * semantic() yet. * But we still need to see if t1 and t2 are the same type. * Params: * t1 = first type * t2 = second type * Returns: * true if they are equivalent types */ bool cTypeEquivalence(Type t1, Type t2) { if (t1.equals(t2)) return true; // that was easy if (t1.ty != t2.ty || t1.mod != t2.mod) return false; if (auto tp = t1.isTypePointer()) return cTypeEquivalence(tp.next, t2.nextOf()); if (auto ta = t1.isTypeSArray()) // Bug: should check array dimension return cTypeEquivalence(ta.next, t2.nextOf()); if (auto ts = t1.isTypeStruct()) return ts.sym is t2.isTypeStruct().sym; if (auto te = t1.isTypeEnum()) return te.sym is t2.isTypeEnum().sym; if (auto tf = t1.isTypeFunction()) return cFuncEquivalence(tf, tf.isTypeFunction()); return false; }