ImportC: Add a pragma for ignoring function declarations and definitions with specific identifiers. #16464
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Spec PR: dlang/dlang.org#3828
This PR adds an ImportC-specific
#pragma importc_ignore
.This pragma is used to ignore kinds of declarations and definitions with specific identifiers.
This is for situations where it is desirable for ImportC to look past a C declaration/definition of a symbol, and instead use a declaration/definition from an
__import
ed D module.A real world use-case for this would be handling MSVC compiler intrinsics when MSVC's
#pragma intrinsic
is used. (More on that later.)I can also think of situations where it could be very convenient to be able to ignore a function-definition: such as to replace a C function's implementations with an
__import
ed D function, if the C function happens to use compiler-extensions not supported by ImportC; or, even just to fix bugs, or add logging, or whatever, without having to bother with a whole fork.#pragma importc_ignore
has one form:#pragma importc_ignore(+/-<category>... : <identifier>,...)
That is, a sequence of plus-or-minus-prefixed categories, followed by a colon, and then a comma-separated list of identifiers.
The categories recognized by
#pragma importc_ignore
are as follows:function_decl
: which ignores function declarations, e.g.void foo(int);
.function_def
: which ignores function definitions, e.g.void foo(int x) {}
.When a category is prefixed with a plus (
+
), declarations/definitions of that category will begin to be ignored if their identifier is included in the list of identifiers supplied to#pragma importc_ignore
.When a category is prefixed with a minus (
-
), declarations/definitions of that category will no longer be ignored if their identifier is included in the list of identifiers supplied to#pragma importc_ignore
.For example:
The rationale behind allowing declarations and definitions to be ignored separately is to make it easier to account for situations where a C library may conditionally declare or define a function based on preprocessor
#defines
.E.g. the same function is declared when targeting one platform but defined when targeting a different platform.
Back to a real world use-case for this—MSVC has
#pragma intrinsic
, which causes the compiler to treat calls to identifiers supplied to#pragma intrinsic
as calls to intrinsics, and not calls to functions.For example, in the standard Windows headers
_ReadWriteBarrier
, and hundreds of other intrinsics, are defined as so:The presence of
#pragma intrinsic(_ReadWriteBarrier)
means that no actual calls to_ReadWriteBarrier
are emitted, despite the body-less declaration of_ReadWriteBarrier
.Whereas, in ImportC, because
#pragma intrinsic
has no effect, the body-less declaration of_ReadWriteBarrier
will cause actual calls to_ReadWriteBarrier
to be emitted—and worse, the body-less declaration of_ReadWriteBarrier
will prevent the compiler from using an__import
ed definition of_ReadWriteBarrier
.This worsens runtime performance, as D implementations of
#pragma intrinsic
intrinsics cannot be inlined, and increases compile-times as codegen for such D implementations must be performed unconditionally.However, if we were to implement and use
#pragma importc_ignore
, then D implementations of#pragma intrinsic
intrinsics could be inlined, and can be templates: improving runtime performance, and reducing compile-times as codegen need be performed only for the intrinsics that are actually used.This would be achieved with a simple:
As a side-note, this is my third iteration of this kind of pragma.
The first was to reimplement MSVC's
#pragma intrinsic
, and#pragma function
, by means of forwarding#pragma intrinsic
functions to a D module specified by#pragma intrinsicsModule
.The second was to refine that to just one
#pragma forwardTo
, which would rewrite function calls to a target, like so:#pragma forwardTo(set c_intrinsics : foo)
, which would rewritefoo()
toc_intrinsics.foo()
.But, the implementation for that ends up being more complex than the implementation for
importc_ignore
, andimportc_ignore
achieves more-or-less the same effect.Outside of parsing the pragma and keeping track of what's to be ignored, the implementation for this is only two if-statements.
I did consider also allowing declarations/definitions of structs, unions, and enums, and typedefs to be ignored, but that then raises the question of whether they're ignored in all scopes or only the global scope. Whereas, functions have only global scope in C; so to keep things simple: functions only.