Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImportC: Add __module declarations #20659

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
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
45 changes: 45 additions & 0 deletions changelog/dmd.import-c-modules.dd
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 14 additions & 0 deletions compiler/src/dmd/cparse.d
Original file line number Diff line number Diff line change
Expand Up @@ -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 ;
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 8 additions & 7 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dmd/tokens.d
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ enum TOK : ubyte
// C only extended keywords
_assert,
_import,
_module,
__cdecl,
__declspec,
__stdcall,
Expand Down Expand Up @@ -584,6 +585,7 @@ private immutable TOK[] keywords =
// C only extended keywords
TOK._assert,
TOK._import,
TOK._module,
TOK.__cdecl,
TOK.__declspec,
TOK.__stdcall,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ enum class TOK : unsigned char
// C only extended keywords
_assert,
_import,
_module,
cdecl_,
declspec,
stdcall,
Expand Down
9 changes: 9 additions & 0 deletions compiler/test/compilable/cmodules.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
EXTRA_SOURCES: imports/cpkg/cmodule.c
*/

import imports.cpkg.cmodule;

static assert(sqr(3) == 9);

void main() {}
13 changes: 13 additions & 0 deletions compiler/test/compilable/imports/cpkg/cmodule.c
Original file line number Diff line number Diff line change
@@ -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; }
31 changes: 31 additions & 0 deletions compiler/test/fail_compilation/cmodule_malformed.c
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions compiler/test/unit/lexer/location_offset.d
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ enum ignoreTokens

_assert,
_import,
_module,
__cdecl,
__declspec,
__stdcall,
Expand Down
Loading