diff --git a/changelog/dmd.importc-pragma-stc.dd b/changelog/dmd.importc-pragma-stc.dd new file mode 100644 index 000000000000..c27ec513263c --- /dev/null +++ b/changelog/dmd.importc-pragma-stc.dd @@ -0,0 +1,25 @@ +A pragma for ImportC allows to set `nothrow`, `@nogc` or `pure` + +The following new pragma for ImportC allows to set default storage +classes for function declarations: +``` +#pragma D push([storage classes...]) +``` +The storage classes `nothrow`, `nogc` and `pure` are supported. +Enabling a default storage class affects all function declarations +after the pragma until it is disabled with another pragma. +Declarations in includes are also affected. The following example +enables `@nogc` and `nothrow` for a library: + +``` +#pragma D push(nogc, nothrow) +#include +``` + +The changed storage classes are pushed on a stack. The last change can +be undone with the following pragma: +``` +#pragma D pop +``` +This can also disable multiple default storage classes at the same time, +if they were enabled with a single `#pragma D push` directive. diff --git a/compiler/src/dmd/cparse.d b/compiler/src/dmd/cparse.d index e02ff43097ba..37d47029c8a0 100644 --- a/compiler/src/dmd/cparse.d +++ b/compiler/src/dmd/cparse.d @@ -45,6 +45,9 @@ final class CParser(AST) : Parser!AST // #pragma pack stack Array!Identifier* records; // identifers (or null) Array!structalign_t* packs; // parallel alignment values + + STC defaultStorageClasses; + Array!STC* defaultStorageClassesStack; } /* C cannot be parsed without determining if an identifier is a type or a variable. @@ -3019,6 +3022,7 @@ final class CParser(AST) : Parser!AST StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; if (specifier._pure) stc |= STC.pure_; + stc |= defaultStorageClasses; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); //tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -5670,6 +5674,8 @@ final class CParser(AST) : Parser!AST scan(&n); if (n.value == TOK.identifier && n.ident == Id.pack) return pragmaPack(loc, true); + if (n.value == TOK.identifier && n.ident == Id.D) + return pragmaD(loc); if (n.value != TOK.endOfLine) skipToNextLine(); } @@ -5877,6 +5883,102 @@ final class CParser(AST) : Parser!AST skipToNextLine(); } + /********* + * # pragma D ... + * Sets default storage classes + * Params: + * startloc = location to use for error messages + */ + private void pragmaD(const ref Loc startloc) + { + const loc = startloc; + + if (!defaultStorageClassesStack) + { + defaultStorageClassesStack = new Array!STC; + } + + Token n; + Lexer.scan(&n); + if (n.value == TOK.endOfLine) + return; + + /* # pragma D push (...) + */ + if (n.value == TOK.identifier && n.ident == Id.push) + { + Lexer.scan(&n); + if (n.value != TOK.leftParenthesis) + { + error(loc, "left parenthesis expected to follow `#pragma D push`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + while (1) + { + Lexer.scan(&n); + if (n.value == TOK.endOfLine) + { + error(loc, "right parenthesis expected to close `#pragma D push(`"); + break; + } + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value == TOK.identifier) + { + if (n.ident == Id._nothrow) + defaultStorageClasses |= STC.nothrow_; + else if (n.ident == Id.nogc) + defaultStorageClasses |= STC.nogc; + else if (n.ident == Id._pure) + defaultStorageClasses |= STC.pure_; + // Ignore unknown identifiers + } + else + { + error(loc, "unrecognized `#pragma D push(%s)`", n.toChars()); + break; + } + + Lexer.scan(&n); + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value != TOK.comma) + { + error(loc, "unrecognized `#pragma D push(%s)`", n.toChars()); + break; + } + } + + this.defaultStorageClassesStack.push(defaultStorageClasses); + } + /* # pragma D pop + */ + else if (n.value == TOK.identifier && n.ident == Id.pop) + { + scan(&n); + size_t len = this.defaultStorageClassesStack.length; + + if (len) + { + this.defaultStorageClassesStack.setDim(len - 1); + if (len == 1) // stack is now empty + defaultStorageClasses = STC.init; + else + defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2]; + } + } + + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + //} /******************************************************************************/ diff --git a/compiler/test/compilable/imports/imp23812.c b/compiler/test/compilable/imports/imp23812.c new file mode 100644 index 000000000000..1af180af87b4 --- /dev/null +++ b/compiler/test/compilable/imports/imp23812.c @@ -0,0 +1,35 @@ + +void funcDefault(void); + +#pragma D push(nothrow) +void funcNothrow(void); +#pragma D pop + +#pragma D push(nogc) +void funcNogc(void); +#pragma D pop + +#pragma D push(pure) +void funcPure(void); +#pragma D pop + +#pragma D push(nothrow, nogc) +void funcNothrowNogc(void); +#pragma D pop + +void funcDefault2(void); + +#pragma D push(nothrow) +#pragma D push(nogc) +void funcNothrowNogc2(void); +#pragma D pop +void funcNothrow2(void); +#pragma D pop + +#pragma D push(nothrow) +void funcWithCallback(void (*f)(void)); +struct Callbacks +{ + void (*f)(void); +}; +#pragma D pop diff --git a/compiler/test/compilable/test23812.d b/compiler/test/compilable/test23812.d new file mode 100644 index 000000000000..502a9d0a2a94 --- /dev/null +++ b/compiler/test/compilable/test23812.d @@ -0,0 +1,75 @@ +// EXTRA_FILES: imports/imp23812.c + +import imports.imp23812; + +void callDefault() +{ + funcDefault(); + funcDefault2(); +} + +static assert(!__traits(compiles, () nothrow { funcDefault(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault(); } )); +static assert(!__traits(compiles, () pure { funcDefault(); } )); + +static assert(!__traits(compiles, () nothrow { funcDefault2(); } )); +static assert(!__traits(compiles, () @nogc { funcDefault2(); } )); +static assert(!__traits(compiles, () pure { funcDefault2(); } )); + +void callNothrow() nothrow +{ + funcNothrow(); + funcNothrow2(); +} + +static assert(!__traits(compiles, () @nogc { funcNothrow(); } )); +static assert(!__traits(compiles, () pure { funcNothrow(); } )); + +static assert(!__traits(compiles, () @nogc { funcNothrow2(); } )); +static assert(!__traits(compiles, () pure { funcNothrow2(); } )); + +void callNogc() @nogc +{ + funcNogc(); +} + +static assert(!__traits(compiles, () nothrow { funcNogc(); } )); +static assert(!__traits(compiles, () pure { funcNogc(); } )); + +void callPure() pure +{ + funcPure(); +} + +static assert(!__traits(compiles, () nothrow { funcPure(); } )); +static assert(!__traits(compiles, () @nogc { funcPure(); } )); + +void callNothrowNogc() nothrow @nogc +{ + funcNothrowNogc(); + funcNothrowNogc2(); +} + +static assert(!__traits(compiles, () pure { funcNothrowNogc(); } )); + +static assert(!__traits(compiles, () pure { funcNothrowNogc2(); } )); + +extern(C) void callbackDefault() +{ +} + +extern(C) void callbackNothrow() nothrow +{ +} + +void callFuncWithCallback() nothrow +{ + funcWithCallback(&callbackNothrow); + + Callbacks callbacks; + callbacks.f = &callbackNothrow; +} + +static assert(!__traits(compiles, () { funcWithCallback(&callbackDefault); } )); + +static assert(!__traits(compiles, () { Callbacks callbacks; callbacks.f = &callbackDefault; } ));