From 9813e87c1ed296b7d0d1a876a1ae66215a33adaa Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 16 Feb 2024 19:12:10 -0800 Subject: [PATCH] fix bugzilla Issue 24397 ImportC: support function-like macros --- compiler/src/dmd/cparse.d | 141 +++++++++++++++++---- compiler/src/dmd/link.d | 12 +- compiler/src/dmd/toir.d | 2 +- compiler/test/compilable/imports/defines.c | 12 ++ compiler/test/compilable/testdefines.d | 7 + 5 files changed, 147 insertions(+), 27 deletions(-) diff --git a/compiler/src/dmd/cparse.d b/compiler/src/dmd/cparse.d index 536a212536da..44426c45aad6 100644 --- a/compiler/src/dmd/cparse.d +++ b/compiler/src/dmd/cparse.d @@ -5520,7 +5520,7 @@ final class CParser(AST) : Parser!AST defines.writeByte('#'); defines.writestring(n.ident.toString()); skipToNextLine(defines); - defines.writeByte('\n'); + defines.writeByte(0); // each #define line is 0 terminated return true; } else if (n.ident == Id.__pragma) @@ -5834,13 +5834,16 @@ final class CParser(AST) : Parser!AST { if (!defines || defines.length < 10) // minimum length of a #define line return; + if (!symbols) + symbols = new AST.Dsymbols(); OutBuffer* buf = defines; defines = null; // prevent skipToNextLine() and parseSpecialTokenSequence() // from appending to slice[] const length = buf.length; buf.writeByte(0); auto slice = buf.peekChars()[0 .. length]; - resetDefineLines(slice); // reset lexer + auto scanlocSave = scanloc; + resetDefineLines(slice); // reset lexer auto save = eSink; auto eLatch = new ErrorSinkLatch(); eSink = eLatch; @@ -5865,6 +5868,7 @@ final class CParser(AST) : Parser!AST (*symbols)[*pd] = s; return; } + assert(symbols, "symbols is null"); defineTab[cast(void*)s.ident] = symbols.length; symbols.push(s); } @@ -5884,10 +5888,11 @@ final class CParser(AST) : Parser!AST AST.Type t; + Lswitch: switch (token.value) { - case TOK.endOfLine: // #define identifier - nextDefineLine(); + case TOK.endOfFile: // #define identifier + ++p; continue; case TOK.int32Literal: @@ -5901,7 +5906,7 @@ final class CParser(AST) : Parser!AST Linteger: const intvalue = token.intvalue; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = intvalue; @@ -5909,7 +5914,7 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; @@ -5924,7 +5929,7 @@ final class CParser(AST) : Parser!AST Lfloat: const floatvalue = token.floatvalue; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = floatvalue; @@ -5932,7 +5937,7 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.RealExp(scanloc, floatvalue, t); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; @@ -5942,7 +5947,7 @@ final class CParser(AST) : Parser!AST const len = token.len; const postfix = token.postfix; nextToken(); - if (token.value == TOK.endOfLine) + if (token.value == TOK.endOfFile) { /* Declare manifest constant: * enum id = "string"; @@ -5950,19 +5955,20 @@ final class CParser(AST) : Parser!AST AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix); auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest); addVar(v); - nextDefineLine(); + ++p; continue; } break; case TOK.leftParenthesis: + { /* Look for: * #define ID ( expression ) * and rewrite it to a template function: * auto ID()() { return expression; } */ if (params) - break; // no parameters + goto caseFunctionLike; // version with parameters nextToken(); eLatch.sawErrors = false; auto exp = cparseExpression(); @@ -5971,7 +5977,7 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.rightParenthesis) break; nextToken(); - if (token.value != TOK.endOfLine) + if (token.value != TOK.endOfFile) break; auto ret = new AST.ReturnStatement(exp.loc, exp); auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0); @@ -5985,26 +5991,115 @@ final class CParser(AST) : Parser!AST AST.Expression constraint = null; auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false); addVar(tempdecl); - nextDefineLine(); + ++p; continue; + } + + caseFunctionLike: + { + /* Parse `( a, b ) expression` + * Create template function: + * auto id(__MP1, __MP2)(__MP1 a, __MP1 b) { return expression; } + */ + //printf("functionlike %s\n", id.toChars()); + + // Capture the parameter list + VarArg varargs = VarArg.none; + auto parameters = new AST.Parameters(); + nextToken(); // skip past `(` + Lwhile: + while (1) + { + if (token.value == TOK.rightParenthesis) + break; + if (token.value == TOK.dotDotDot) + { + static if (0) // variadic macros not supported yet + { + varargs = AST.VarArg.variadic; // C-style variadics + nextToken(); + if (token.value == TOK.rightParenthesis) + break Lwhile; + } + break Lswitch; + } + + if (token.value != TOK.identifier) + break Lswitch; + auto param = new AST.Parameter(token.loc, 0, null, token.ident, null, null); + parameters.push(param); + nextToken(); + if (token.value == TOK.comma) + { + nextToken(); + continue; + } + break; + } + if (token.value != TOK.rightParenthesis) + break; + + //auto pstart = p; + nextToken(); + auto parameterList = AST.ParameterList(parameters, varargs, 0); + /* Create a type for each parameter. Add it to the template parameter list, + * and the parameter list. + */ + auto tpl = new AST.TemplateParameters(); + foreach (param; (*parameters)[]) + { + auto idtype = Identifier.generateId("__MP"); + auto loc = param.loc; + auto tp = new AST.TemplateTypeParameter(loc, idtype, null, null); + tpl.push(tp); + + auto at = new AST.TypeIdentifier(loc, idtype); + param.type = at; + } + + eLatch.sawErrors = false; + auto exp = cparseExpression(); + + //printf("exp: %s tok: %s\n", exp.toChars(), Token.toChars(token.value)); + //printf("parsed: '%.*s'\n", cast(int)(p - pstart), pstart); + assert(symbols); + + if (eLatch.sawErrors) // parsing errors + break; // abandon this #define + + if (token.value != TOK.endOfFile) // did not consume the entire line + break; + + // Generate function + auto ret = new AST.ReturnStatement(exp.loc, exp); + StorageClass stc = STC.auto_; + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); + fd.fbody = ret; + + // Wrap it in an eponymous template + AST.Dsymbols* decldefs = new AST.Dsymbols(); + decldefs.push(fd); + auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, null, decldefs, false); + addVar(tempdecl); + + ++p; + continue; + } default: break; } } - skipToNextLine(); - } - else - { - scan(&token); - if (token.value != TOK.endOfLine) - { - skipToNextLine(); - } } - nextDefineLine(); + // scan to end of line + while (*p) + ++p; + ++p; // advance to start of next line + scanloc.linnum = scanloc.linnum + 1; } + scanloc = scanlocSave; eSink = save; defines = buf; } diff --git a/compiler/src/dmd/link.d b/compiler/src/dmd/link.d index 64df21b19ffb..890673f407e6 100644 --- a/compiler/src/dmd/link.d +++ b/compiler/src/dmd/link.d @@ -1255,9 +1255,16 @@ public DArray!ubyte runPreprocessor(ref const Loc loc, const(char)[] cpp, const( break; case S.hash: - defines.writeByte(c); - if (c == '\n') + if (c == '\r') + { + } + else if (c == '\n') + { + defines.writeByte(0); // 0-terminate lines in defines[] state = S.start; + } + else + defines.writeByte(c); break; case S.other: @@ -1267,7 +1274,6 @@ public DArray!ubyte runPreprocessor(ref const Loc loc, const(char)[] cpp, const( break; } } - //printf("%.*s", cast(int)data.length, data.ptr); } // Convert command to wchar diff --git a/compiler/src/dmd/toir.d b/compiler/src/dmd/toir.d index 35c5ce01b903..82657d2fea3c 100644 --- a/compiler/src/dmd/toir.d +++ b/compiler/src/dmd/toir.d @@ -172,7 +172,7 @@ extern (D) elem *incUsageElem(ref IRState irs, const ref Loc loc) uint linnum = loc.linnum; Module m = cast(Module)irs.blx._module; - //printf("m.cov %p linnum %d filename %s srcfile %s\n", m.cov, linnum, loc.filename, m.srcfile.toChars()); + //printf("m.cov %p linnum %d filename %s srcfile %s numlines %d\n", m.cov, linnum, loc.filename, m.srcfile.toChars(), m.numlines); if (!m.cov || !linnum || strcmp(loc.filename, m.srcfile.toChars())) return null; diff --git a/compiler/test/compilable/imports/defines.c b/compiler/test/compilable/imports/defines.c index 8a5601a2d089..f45004a6b34c 100644 --- a/compiler/test/compilable/imports/defines.c +++ b/compiler/test/compilable/imports/defines.c @@ -30,3 +30,15 @@ _Static_assert(SSS[0] == 'h', "10"); #define ABC 12 #define GHI (size) abbadabba #define DEF (ABC + 5) + +#define ADD(a, b) a + b +#define SUB() 3 - 2 + +#define NO_BODY() +#define NO_BODY_PARAMS(a, b) +#define DO_WHILE() do { } while(0) + +#define pr16199_trigger(cond,func,args) _Generic (cond, default: func args) +#define pr16199_skipped1(a) (1) +#define pr16199_skipped2(b) (2) +#define pr16199_ice 0x3 diff --git a/compiler/test/compilable/testdefines.d b/compiler/test/compilable/testdefines.d index 9dd8cf2af8d5..d73f3b310a21 100644 --- a/compiler/test/compilable/testdefines.d +++ b/compiler/test/compilable/testdefines.d @@ -15,3 +15,10 @@ static assert(SSS == "hello"); static assert(ABC == 12); static assert(DEF == 17); + +static assert(ADD(3, 4) == 7); +static assert(SUB() == 1); + +static assert(pr16199_skipped1(5) == 1); +static assert(pr16199_skipped2(6) == 2); +static assert(pr16199_ice == 3);