Skip to content

Commit

Permalink
fix bugzilla Issue 24397 ImportC: support function-like macros (#16199)
Browse files Browse the repository at this point in the history
  • Loading branch information
WalterBright authored Feb 19, 2024
1 parent 3505e14 commit f834870
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 29 deletions.
150 changes: 125 additions & 25 deletions compiler/src/dmd/cparse.d
Original file line number Diff line number Diff line change
Expand Up @@ -1682,9 +1682,12 @@ final class CParser(AST) : Parser!AST
AST.ParameterList parameterList;
StorageClass stc = 0;
const loc = token.loc;
auto symbolsSave = symbols;
symbols = new AST.Dsymbols();
typedefTab.push(null);
auto fbody = cparseStatement(ParseStatementFlags.scope_);
typedefTab.pop(); // end of function scope
symbols = symbolsSave;

// Rewrite last ExpStatement (if there is one) as a ReturnStatement
auto ss = fbody.isScopeStatement();
Expand All @@ -1693,8 +1696,11 @@ final class CParser(AST) : Parser!AST
if (const len = (*cs.statements).length)
{
auto s = (*cs.statements)[len - 1];
if (auto es = s.isExpStatement())
(*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
if (s) // error recovery should be with ErrorStatement, not null
{
if (auto es = s.isExpStatement())
(*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp);
}
}

auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc);
Expand Down Expand Up @@ -5520,7 +5526,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)
Expand Down Expand Up @@ -5840,7 +5846,8 @@ final class CParser(AST) : Parser!AST
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;
Expand All @@ -5865,12 +5872,14 @@ 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);
}

while (p < endp)
{
//printf("|%s|\n", p);
if (p[0 .. 7] == "#define")
{
p += 7;
Expand All @@ -5884,10 +5893,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:
Expand All @@ -5901,15 +5911,15 @@ 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;
*/
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;
Expand All @@ -5924,15 +5934,15 @@ 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;
*/
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;
Expand All @@ -5942,27 +5952,28 @@ 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";
*/
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();
Expand All @@ -5971,7 +5982,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);
Expand All @@ -5985,26 +5996,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;
}
Expand Down
12 changes: 9 additions & 3 deletions compiler/src/dmd/link.d
Original file line number Diff line number Diff line change
Expand Up @@ -1265,9 +1265,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:
Expand All @@ -1277,7 +1284,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
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/toir.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
25 changes: 25 additions & 0 deletions compiler/test/compilable/imports/defines.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,28 @@ _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

#define M16199Ea(TYPE) (TYPE __x;)
#define M16199E(X,S,M) ({ M16199Ea(S *); })

#define M16199Da(TYPE,VAR) ((TYPE)(VAR))
#define M16199D(X,S,M) ({ int *__x = (X); M16199Da(S *, __x); })
int pr16199d() { return 7; }

#define M16199C(X,S,M) ({ int __x; })
int pr16199c()
{
return 8;
}
10 changes: 10 additions & 0 deletions compiler/test/compilable/testdefines.d
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ 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);

static assert(pr16199d() == 7);
static assert(pr16199c() == 8);

0 comments on commit f834870

Please sign in to comment.