Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions compiler/src/dmd/cparse.d
Original file line number Diff line number Diff line change
Expand Up @@ -5657,6 +5657,18 @@ final class CParser(AST) : Parser!AST
addSym(tempdecl);
}

// Set of all identifiers declared at module scope. (Not just translated macros like defineTab)
// Used to decide whether `#define ID identifier` can be turned into an alias.
// Aliasing an undefined identifier would error eagerly, which system headers trigger by
// #define-ing keywords to compiler builtins (e.g. `#define __func__ __FUNCTION__`).
bool[const(void)*] declaredIdents;
if (symbols)
{
foreach (sym; (*symbols)[])
if (sym && sym.ident)
declaredIdents[cast(const(void)*) sym.ident] = true;
}

while (p < endp)
{
//printf("|%s|\n", p);
Expand Down Expand Up @@ -5788,6 +5800,13 @@ final class CParser(AST) : Parser!AST
* (no additional operators that could cause precedence issues).
* Rewrite to a template function:
* auto ID()() { return identifier(args); }
* Or:
* #define ID identifier
* where the macro body is a single identifier declared in this
* translation unit (e.g. a function alias macro).
* https://github.com/dlang/dmd/issues/23143
* Rewrite to an alias:
* alias ID = identifier;
*/
assert(!params); // would be TOK.leftParenthesis
eLatch.sawErrors = false;
Expand All @@ -5796,6 +5815,22 @@ final class CParser(AST) : Parser!AST
break; // abandon this #define
if (token.value != TOK.endOfFile) // did not consume the entire line
break;
if (auto ie = exp.isIdentifierExp())
{
// Skip self-referential macros (e.g. glibc's `#define stdin stdin`)
if (id == ie.ident)
break;
if ((cast(const(void)*) ie.ident in declaredIdents) ||
(cast(void*) ie.ident in defineTab))
{
auto t2 = new AST.TypeIdentifier(ie.loc, ie.ident);
auto ad = new AST.AliasDeclaration(scanloc, id, t2);
addSym(ad);
++p;
continue;
}
break;
}
// Only allow bare function calls to avoid precedence issues.
// E.g., `#define X FUNC(5)` is safe, but `#define X FUNC(5) + 1`
// would have different semantics in D vs C when used as `X * 2`.
Expand Down
9 changes: 9 additions & 0 deletions compiler/test/compilable/imports/defines.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,12 @@ int pr16199c()
#define BARE_CALL DOUBLE(5) // bare function-like macro call (no outer parens)
#define PAREN_CALL (DOUBLE(5)) // parenthesized call (already works)
#define WRAPPER(x) DOUBLE(x) // function-like macro calling another macro

// https://github.com/dlang/dmd/issues/23143 - alias macros for declared identifiers
int __real_func(int x) { return x + 1; }
#define ALIAS_FUNC __real_func // alias macro to a function
typedef int real_int;
#define ALIAS_TYPE real_int // alias macro to a type
#define ALIAS_ENUM ABC // alias macro to a manifest constant
int self_ref;
#define self_ref self_ref // self-referential macro (cf. glibc stdin), must not alias
6 changes: 6 additions & 0 deletions compiler/test/compilable/testdefines.d
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,9 @@ static assert(DOUBLE(5) == 10);
static assert(BARE_CALL == 10); // bare call now works
static assert(PAREN_CALL == 10); // parenthesized already worked
static assert(WRAPPER(3) == 6); // function-like macro calling another macro

// https://github.com/dlang/dmd/issues/23143 - alias macros for declared identifiers
static assert(ALIAS_FUNC(4) == 5); // alias to a function
static assert(is(ALIAS_TYPE == int)); // alias to a type
static assert(ALIAS_ENUM == 12); // alias to a manifest constant
static assert(is(typeof(self_ref) == int)); // self-referential macro stays the variable
4 changes: 4 additions & 0 deletions druntime/test/importc_compare/src/importc_compare.d
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ immutable string[] growingTypes = [
// List of problems, which are known and should only be treated as
// warnings for now.
immutable ErrorFilter[] knownProblems = [
// ImportC now translates the C library's `complex` alias macro, which on Windows resolves
// to a `{ double; double; }` struct, while druntime's `complex` is an alias of `creal`.
// The representations genuinely differ, so this is an expected difference.
ErrorFilter("core.stdc.complex.complex", "", "Windows", 0, "https://github.com/dlang/dmd/issues/23143"),
ErrorFilter("core.stdc.config.c_long_double", "", "Windows", 32, ""),
ErrorFilter("core.stdc.config.c_complex_real", "", "Windows", 32, ""), // complex real is two long doubles so same for x86 Windows
ErrorFilter("core.stdc.config.__c_complex_real", "", "Windows", 32, ""),
Expand Down
Loading