Skip to content

Commit 0e8e670

Browse files
authored
Fix bugzilla 23812 - ImportC: allow adding function attributes to imported C functions (#16820)
This adds a new pragma for ImportC, which allows to set default storage classes. Only `nothrow`, `@nogc` and `pure` are supported for now. They can be disabled later using `#pragma attribute(pop)`. Unknown storage classes are ignored.
1 parent 422b27e commit 0e8e670

File tree

4 files changed

+261
-0
lines changed

4 files changed

+261
-0
lines changed

changelog/dmd.importc-pragma-stc.dd

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
A pragma for ImportC allows to set `nothrow`, `@nogc` or `pure`
2+
3+
The following new pragma for ImportC allows to set default storage
4+
classes for function declarations:
5+
```
6+
#pragma attribute(push, [storage classes...])
7+
```
8+
The storage classes `nothrow`, `nogc` and `pure` are supported.
9+
Unrecognized attributes are ignored.
10+
Enabling a default storage class affects all function declarations
11+
after the pragma until it is disabled with another pragma.
12+
Declarations in includes are also affected. The following example
13+
enables `@nogc` and `nothrow` for a library:
14+
15+
```
16+
#pragma attribute(push, nogc, nothrow)
17+
#include <somelibrary.h>
18+
```
19+
20+
The changed storage classes are pushed on a stack. The last change can
21+
be undone with the following pragma:
22+
```
23+
#pragma attribute(pop)
24+
```
25+
This can also disable multiple default storage classes at the same time,
26+
if they were enabled with a single `#pragma attribute(push, ...)` directive.

compiler/src/dmd/cparse.d

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ final class CParser(AST) : Parser!AST
4545
// #pragma pack stack
4646
Array!Identifier* records; // identifers (or null)
4747
Array!structalign_t* packs; // parallel alignment values
48+
49+
STC defaultStorageClasses;
50+
Array!STC* defaultStorageClassesStack;
4851
}
4952

5053
/* 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
30193022
StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0;
30203023
if (specifier._pure)
30213024
stc |= STC.pure_;
3025+
stc |= defaultStorageClasses;
30223026
AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc);
30233027
//tf = tf.addSTC(storageClass); // TODO
30243028
insertTx(ts, tf, t); // ts -> ... -> tf -> t
@@ -5670,6 +5674,8 @@ final class CParser(AST) : Parser!AST
56705674
scan(&n);
56715675
if (n.value == TOK.identifier && n.ident == Id.pack)
56725676
return pragmaPack(loc, true);
5677+
if (n.value == TOK.identifier && n.ident == Id.attribute)
5678+
return pragmaAttribute(loc);
56735679
if (n.value != TOK.endOfLine)
56745680
skipToNextLine();
56755681
}
@@ -5877,6 +5883,125 @@ final class CParser(AST) : Parser!AST
58775883
skipToNextLine();
58785884
}
58795885

5886+
/*********
5887+
* # pragma attribute(...)
5888+
* Sets default storage classes
5889+
* Params:
5890+
* startloc = location to use for error messages
5891+
*/
5892+
private void pragmaAttribute(const ref Loc startloc)
5893+
{
5894+
const loc = startloc;
5895+
5896+
if (!defaultStorageClassesStack)
5897+
{
5898+
defaultStorageClassesStack = new Array!STC;
5899+
}
5900+
5901+
Token n;
5902+
Lexer.scan(&n);
5903+
if (n.value != TOK.leftParenthesis)
5904+
{
5905+
error(loc, "left parenthesis expected to follow `#pragma attribute`");
5906+
if (n.value != TOK.endOfLine)
5907+
skipToNextLine();
5908+
return;
5909+
}
5910+
5911+
void closingParen()
5912+
{
5913+
if (n.value != TOK.rightParenthesis)
5914+
{
5915+
error(loc, "right parenthesis expected to close `#pragma attribute(`");
5916+
}
5917+
if (n.value != TOK.endOfLine)
5918+
skipToNextLine();
5919+
}
5920+
5921+
Lexer.scan(&n);
5922+
5923+
/* # pragma attribute (push, ...)
5924+
*/
5925+
if (n.value == TOK.identifier && n.ident == Id.push)
5926+
{
5927+
Lexer.scan(&n);
5928+
if (n.value != TOK.comma)
5929+
{
5930+
error(loc, "comma expected to follow `#pragma attribute(push`");
5931+
if (n.value != TOK.endOfLine)
5932+
skipToNextLine();
5933+
return;
5934+
}
5935+
5936+
while (1)
5937+
{
5938+
Lexer.scan(&n);
5939+
if (n.value == TOK.endOfLine)
5940+
{
5941+
error(loc, "right parenthesis expected to close `#pragma attribute(push, `");
5942+
break;
5943+
}
5944+
5945+
if (n.value == TOK.rightParenthesis)
5946+
break;
5947+
5948+
if (n.value == TOK.identifier)
5949+
{
5950+
if (n.ident == Id._nothrow)
5951+
defaultStorageClasses |= STC.nothrow_;
5952+
else if (n.ident == Id.nogc)
5953+
defaultStorageClasses |= STC.nogc;
5954+
else if (n.ident == Id._pure)
5955+
defaultStorageClasses |= STC.pure_;
5956+
// Ignore unknown identifiers
5957+
}
5958+
else
5959+
{
5960+
error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars());
5961+
break;
5962+
}
5963+
5964+
Lexer.scan(&n);
5965+
5966+
if (n.value == TOK.rightParenthesis)
5967+
break;
5968+
5969+
if (n.value != TOK.comma)
5970+
{
5971+
error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars());
5972+
break;
5973+
}
5974+
}
5975+
5976+
this.defaultStorageClassesStack.push(defaultStorageClasses);
5977+
5978+
return closingParen();
5979+
}
5980+
5981+
/* # pragma attribute(pop)
5982+
*/
5983+
if (n.value == TOK.identifier && n.ident == Id.pop)
5984+
{
5985+
scan(&n);
5986+
size_t len = this.defaultStorageClassesStack.length;
5987+
5988+
if (len)
5989+
{
5990+
this.defaultStorageClassesStack.setDim(len - 1);
5991+
if (len == 1) // stack is now empty
5992+
defaultStorageClasses = STC.init;
5993+
else
5994+
defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2];
5995+
}
5996+
5997+
return closingParen();
5998+
}
5999+
6000+
error(loc, "unrecognized `#pragma attribute(%s)`", n.toChars());
6001+
if (n.value != TOK.endOfLine)
6002+
skipToNextLine();
6003+
}
6004+
58806005
//}
58816006

58826007
/******************************************************************************/
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
void funcDefault(void);
3+
4+
#pragma attribute(push, nothrow)
5+
void funcNothrow(void);
6+
#pragma attribute(pop)
7+
8+
#pragma attribute(push, nogc)
9+
void funcNogc(void);
10+
#pragma attribute(pop)
11+
12+
#pragma attribute(push, pure)
13+
void funcPure(void);
14+
#pragma attribute(pop)
15+
16+
#pragma attribute(push, nothrow, nogc)
17+
void funcNothrowNogc(void);
18+
#pragma attribute(pop)
19+
20+
void funcDefault2(void);
21+
22+
#pragma attribute(push, nothrow)
23+
#pragma attribute(push, nogc)
24+
void funcNothrowNogc2(void);
25+
#pragma attribute(pop)
26+
void funcNothrow2(void);
27+
#pragma attribute(pop)
28+
29+
#pragma attribute(push, nothrow)
30+
void funcWithCallback(void (*f)(void));
31+
struct Callbacks
32+
{
33+
void (*f)(void);
34+
};
35+
#pragma attribute(pop)

compiler/test/compilable/test23812.d

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// EXTRA_FILES: imports/imp23812.c
2+
3+
import imports.imp23812;
4+
5+
void callDefault()
6+
{
7+
funcDefault();
8+
funcDefault2();
9+
}
10+
11+
static assert(!__traits(compiles, () nothrow { funcDefault(); } ));
12+
static assert(!__traits(compiles, () @nogc { funcDefault(); } ));
13+
static assert(!__traits(compiles, () pure { funcDefault(); } ));
14+
15+
static assert(!__traits(compiles, () nothrow { funcDefault2(); } ));
16+
static assert(!__traits(compiles, () @nogc { funcDefault2(); } ));
17+
static assert(!__traits(compiles, () pure { funcDefault2(); } ));
18+
19+
void callNothrow() nothrow
20+
{
21+
funcNothrow();
22+
funcNothrow2();
23+
}
24+
25+
static assert(!__traits(compiles, () @nogc { funcNothrow(); } ));
26+
static assert(!__traits(compiles, () pure { funcNothrow(); } ));
27+
28+
static assert(!__traits(compiles, () @nogc { funcNothrow2(); } ));
29+
static assert(!__traits(compiles, () pure { funcNothrow2(); } ));
30+
31+
void callNogc() @nogc
32+
{
33+
funcNogc();
34+
}
35+
36+
static assert(!__traits(compiles, () nothrow { funcNogc(); } ));
37+
static assert(!__traits(compiles, () pure { funcNogc(); } ));
38+
39+
void callPure() pure
40+
{
41+
funcPure();
42+
}
43+
44+
static assert(!__traits(compiles, () nothrow { funcPure(); } ));
45+
static assert(!__traits(compiles, () @nogc { funcPure(); } ));
46+
47+
void callNothrowNogc() nothrow @nogc
48+
{
49+
funcNothrowNogc();
50+
funcNothrowNogc2();
51+
}
52+
53+
static assert(!__traits(compiles, () pure { funcNothrowNogc(); } ));
54+
55+
static assert(!__traits(compiles, () pure { funcNothrowNogc2(); } ));
56+
57+
extern(C) void callbackDefault()
58+
{
59+
}
60+
61+
extern(C) void callbackNothrow() nothrow
62+
{
63+
}
64+
65+
void callFuncWithCallback() nothrow
66+
{
67+
funcWithCallback(&callbackNothrow);
68+
69+
Callbacks callbacks;
70+
callbacks.f = &callbackNothrow;
71+
}
72+
73+
static assert(!__traits(compiles, () { funcWithCallback(&callbackDefault); } ));
74+
75+
static assert(!__traits(compiles, () { Callbacks callbacks; callbacks.f = &callbackDefault; } ));

0 commit comments

Comments
 (0)