diff --git a/changelog/dmd.import-c-modules.dd b/changelog/dmd.import-c-modules.dd new file mode 100644 index 000000000000..c4ec80395f8e --- /dev/null +++ b/changelog/dmd.import-c-modules.dd @@ -0,0 +1,45 @@ +C files can now include a module statement + +Similar to the `__import` extension, the `__module` keyword brings D's module declaration to C. + +It's particularly useful when you want to import multiple C files with the same name +(e.g. hello/utils.c and world/utils.c), since both have to be imported with `import utils` when +they are listed on the command line, resulting in conflicts. + +Now you can do: + +hello/utils.c: + +```C + +#if __IMPORTC__ +__module hello.utils; +#endif + +int sqr(int x) { return x * x; } +``` + +world/utils.c: +```C + +#if __IMPORTC__ +__module world.utils; +#endif + +int max(int a, int b) { return a > b ? a : b; } +``` + +app.d: +```D +import hello.utils; +import world.utils; + +static assert(sqr(3) == 9); +static assert(max(3, 5) == 5); +``` + +A __module declaration can appear anywhere in the top level scope. +When there are multiple, the first one will be used. +Therefore, every `#include` containing a __module declaration should come after the file's own module declaration, +or it will be overwritten. +When you always put the __module declaration at the very top like in D, there won't be such problems. diff --git a/compiler/src/dmd/cparse.d b/compiler/src/dmd/cparse.d index 354d83bd4ba4..863247530902 100644 --- a/compiler/src/dmd/cparse.d +++ b/compiler/src/dmd/cparse.d @@ -143,6 +143,20 @@ final class CParser(AST) : Parser!AST return wrap; } + if (token.value == TOK._module) + { + token.value = TOK.module_; + auto oldMd = this.md; + parseModuleDeclaration(); + if (oldMd) + { + // We only use the first module declaration, + // subsequent __module statements should only come from #included files + this.md = oldMd; + } + continue; + } + /* GNU Extensions * external-declaration: * simple-asm-expr ; diff --git a/compiler/src/dmd/dmodule.d b/compiler/src/dmd/dmodule.d index f26c5812ddf9..d2fd4675776b 100644 --- a/compiler/src/dmd/dmodule.d +++ b/compiler/src/dmd/dmodule.d @@ -806,7 +806,13 @@ extern (C++) final class Module : Package p.nextToken(); checkCompiledImport(); members = p.parseModule(); - assert(!p.md); // C doesn't have module declarations + md = p.md; + if (md) + { + this.ident = md.id; + dst = Package.resolve(md.packages, &this.parent, &ppack); + } + numlines = p.scanloc.linnum; } else diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 9722a57d3553..a790371e10c3 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3011,13 +3011,14 @@ enum class TOK : uint8_t _Thread_local_ = 216u, _assert_ = 217u, _import_ = 218u, - __cdecl_ = 219u, - __declspec_ = 220u, - __stdcall_ = 221u, - __thread_ = 222u, - __pragma_ = 223u, - __int128_ = 224u, - __attribute___ = 225u, + _module = 219u, + __cdecl_ = 220u, + __declspec_ = 221u, + __stdcall_ = 222u, + __thread_ = 223u, + __pragma_ = 224u, + __int128_ = 225u, + __attribute___ = 226u, }; class FuncExp final : public Expression diff --git a/compiler/src/dmd/tokens.d b/compiler/src/dmd/tokens.d index e82a582bee32..aed737901509 100644 --- a/compiler/src/dmd/tokens.d +++ b/compiler/src/dmd/tokens.d @@ -276,6 +276,7 @@ enum TOK : ubyte // C only extended keywords _assert, _import, + _module, __cdecl, __declspec, __stdcall, @@ -584,6 +585,7 @@ private immutable TOK[] keywords = // C only extended keywords TOK._assert, TOK._import, + TOK._module, TOK.__cdecl, TOK.__declspec, TOK.__stdcall, @@ -619,7 +621,7 @@ static immutable TOK[TOK.max + 1] Ckeywords = union_, unsigned, void_, volatile, while_, asm_, typeof_, _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local, - _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__, + _import, _module, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__, _assert ]; foreach (kw; Ckwds) @@ -898,6 +900,7 @@ extern (C++) struct Token // C only extended keywords TOK._assert : "__check", TOK._import : "__import", + TOK._module : "__module", TOK.__cdecl : "__cdecl", TOK.__declspec : "__declspec", TOK.__stdcall : "__stdcall", diff --git a/compiler/src/dmd/tokens.h b/compiler/src/dmd/tokens.h index c12a00b7bb6e..f37e3f36c010 100644 --- a/compiler/src/dmd/tokens.h +++ b/compiler/src/dmd/tokens.h @@ -282,6 +282,7 @@ enum class TOK : unsigned char // C only extended keywords _assert, _import, + _module, cdecl_, declspec, stdcall, diff --git a/compiler/test/compilable/cmodules.d b/compiler/test/compilable/cmodules.d new file mode 100644 index 000000000000..6cec6c814ed1 --- /dev/null +++ b/compiler/test/compilable/cmodules.d @@ -0,0 +1,9 @@ +/** +EXTRA_SOURCES: imports/cpkg/cmodule.c +*/ + +import imports.cpkg.cmodule; + +static assert(sqr(3) == 9); + +void main() {} diff --git a/compiler/test/compilable/imports/cpkg/cmodule.c b/compiler/test/compilable/imports/cpkg/cmodule.c new file mode 100644 index 000000000000..b926ef160018 --- /dev/null +++ b/compiler/test/compilable/imports/cpkg/cmodule.c @@ -0,0 +1,13 @@ +// D module declaration + +#if __IMPORTC__ + +__module imports.cpkg.cmodule; + +// Only the first module statement is used, +// subsequent __module declarations are assumed to come from #included other files +__module some.header; + +#endif + +int sqr(int i) { return i * i; } diff --git a/compiler/test/fail_compilation/cmodule_malformed.c b/compiler/test/fail_compilation/cmodule_malformed.c new file mode 100644 index 000000000000..6dd888b4fe36 --- /dev/null +++ b/compiler/test/fail_compilation/cmodule_malformed.c @@ -0,0 +1,31 @@ +/** +TEST_OUTPUT: +--- +fail_compilation/cmodule_malformed.c(15): Error: identifier expected following `module` +fail_compilation/cmodule_malformed.c(15): Error: no type for declarator before `"a"` +fail_compilation/cmodule_malformed.c(21): Error: no type-specifier for struct member +fail_compilation/cmodule_malformed.c(21): Error: identifier or `(` expected +fail_compilation/cmodule_malformed.c(21): Error: expected identifier for declarator +fail_compilation/cmodule_malformed.c(26): Error: found `__module` instead of statement +--- +*/ + +#if __IMPORTC__ + +__module "a"; + +typedef struct S +{ + int x; + + __module b; +} S; + +void main(void) +{ + __module c.d; +} + +__module e; + +#endif diff --git a/compiler/test/unit/lexer/location_offset.d b/compiler/test/unit/lexer/location_offset.d index fb7edfd5eb21..d599946b387a 100644 --- a/compiler/test/unit/lexer/location_offset.d +++ b/compiler/test/unit/lexer/location_offset.d @@ -542,6 +542,7 @@ enum ignoreTokens _assert, _import, + _module, __cdecl, __declspec, __stdcall,