Skip to content

Commit cf67313

Browse files
dkorpelclaude
andcommitted
Fix #23143 - importC: translate alias macros to aliases
A `#define ID identifier` whose body is a single declared identifier (e.g. `#define mpz_init __gmpz_init` in gmp.h) was abandoned, leaving the macro name undefined in D. Translate such object-like macros to `alias ID = identifier;` so they resolve to the function/type/variable. The target must be declared in the same translation unit (or an earlier #define): aliasing an undefined identifier would error eagerly, which system headers trigger by #define-ing keywords to compiler builtins (e.g. `#define __func__ __FUNCTION__`, `#define __restrict restrict`). Self-referential macros (glibc's `#define stdin stdin`) are skipped, as they would otherwise shadow the declaration they name. On Windows the C library's `complex` macro resolves to a struct, which ImportC now aliases; druntime's `core.stdc.complex.complex` is an alias of `creal`, so importc_compare flags the expected size difference - add it to that test's known problems. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 781c835 commit cf67313

4 files changed

Lines changed: 54 additions & 0 deletions

File tree

compiler/src/dmd/cparse.d

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5657,6 +5657,18 @@ final class CParser(AST) : Parser!AST
56575657
addSym(tempdecl);
56585658
}
56595659

5660+
// Set of all identifiers declared at module scope. (Not just translated macros like defineTab)
5661+
// Used to decide whether `#define ID identifier` can be turned into an alias.
5662+
// Aliasing an undefined identifier would error eagerly, which system headers trigger by
5663+
// #define-ing keywords to compiler builtins (e.g. `#define __func__ __FUNCTION__`).
5664+
bool[const(void)*] declaredIdents;
5665+
if (symbols)
5666+
{
5667+
foreach (sym; (*symbols)[])
5668+
if (sym && sym.ident)
5669+
declaredIdents[cast(const(void)*) sym.ident] = true;
5670+
}
5671+
56605672
while (p < endp)
56615673
{
56625674
//printf("|%s|\n", p);
@@ -5788,6 +5800,13 @@ final class CParser(AST) : Parser!AST
57885800
* (no additional operators that could cause precedence issues).
57895801
* Rewrite to a template function:
57905802
* auto ID()() { return identifier(args); }
5803+
* Or:
5804+
* #define ID identifier
5805+
* where the macro body is a single identifier declared in this
5806+
* translation unit (e.g. a function alias macro).
5807+
* https://github.com/dlang/dmd/issues/23143
5808+
* Rewrite to an alias:
5809+
* alias ID = identifier;
57915810
*/
57925811
assert(!params); // would be TOK.leftParenthesis
57935812
eLatch.sawErrors = false;
@@ -5796,6 +5815,22 @@ final class CParser(AST) : Parser!AST
57965815
break; // abandon this #define
57975816
if (token.value != TOK.endOfFile) // did not consume the entire line
57985817
break;
5818+
if (auto ie = exp.isIdentifierExp())
5819+
{
5820+
// Skip self-referential macros (e.g. glibc's `#define stdin stdin`)
5821+
if (id == ie.ident)
5822+
break;
5823+
if ((cast(const(void)*) ie.ident in declaredIdents) ||
5824+
(cast(void*) ie.ident in defineTab))
5825+
{
5826+
auto t2 = new AST.TypeIdentifier(ie.loc, ie.ident);
5827+
auto ad = new AST.AliasDeclaration(scanloc, id, t2);
5828+
addSym(ad);
5829+
++p;
5830+
continue;
5831+
}
5832+
break;
5833+
}
57995834
// Only allow bare function calls to avoid precedence issues.
58005835
// E.g., `#define X FUNC(5)` is safe, but `#define X FUNC(5) + 1`
58015836
// would have different semantics in D vs C when used as `X * 2`.

compiler/test/compilable/imports/defines.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,12 @@ int pr16199c()
6969
#define BARE_CALL DOUBLE(5) // bare function-like macro call (no outer parens)
7070
#define PAREN_CALL (DOUBLE(5)) // parenthesized call (already works)
7171
#define WRAPPER(x) DOUBLE(x) // function-like macro calling another macro
72+
73+
// https://github.com/dlang/dmd/issues/23143 - alias macros for declared identifiers
74+
int __real_func(int x) { return x + 1; }
75+
#define ALIAS_FUNC __real_func // alias macro to a function
76+
typedef int real_int;
77+
#define ALIAS_TYPE real_int // alias macro to a type
78+
#define ALIAS_ENUM ABC // alias macro to a manifest constant
79+
int self_ref;
80+
#define self_ref self_ref // self-referential macro (cf. glibc stdin), must not alias

compiler/test/compilable/testdefines.d

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,9 @@ static assert(DOUBLE(5) == 10);
4545
static assert(BARE_CALL == 10); // bare call now works
4646
static assert(PAREN_CALL == 10); // parenthesized already worked
4747
static assert(WRAPPER(3) == 6); // function-like macro calling another macro
48+
49+
// https://github.com/dlang/dmd/issues/23143 - alias macros for declared identifiers
50+
static assert(ALIAS_FUNC(4) == 5); // alias to a function
51+
static assert(is(ALIAS_TYPE == int)); // alias to a type
52+
static assert(ALIAS_ENUM == 12); // alias to a manifest constant
53+
static assert(is(typeof(self_ref) == int)); // self-referential macro stays the variable

druntime/test/importc_compare/src/importc_compare.d

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ immutable string[] growingTypes = [
2828
// List of problems, which are known and should only be treated as
2929
// warnings for now.
3030
immutable ErrorFilter[] knownProblems = [
31+
// ImportC now translates the C library's `complex` alias macro, which on Windows resolves
32+
// to a `{ double; double; }` struct, while druntime's `complex` is an alias of `creal`.
33+
// The representations genuinely differ, so this is an expected difference.
34+
ErrorFilter("core.stdc.complex.complex", "", "Windows", 0, "https://github.com/dlang/dmd/issues/23143"),
3135
ErrorFilter("core.stdc.config.c_long_double", "", "Windows", 32, ""),
3236
ErrorFilter("core.stdc.config.c_complex_real", "", "Windows", 32, ""), // complex real is two long doubles so same for x86 Windows
3337
ErrorFilter("core.stdc.config.__c_complex_real", "", "Windows", 32, ""),

0 commit comments

Comments
 (0)