From eb5cc59026f9461e1b83d1d57a83e21f9de61626 Mon Sep 17 00:00:00 2001 From: look-at-me <42397654+look-at-me@users.noreply.github.com> Date: Sun, 3 Feb 2019 21:33:28 -0500 Subject: [PATCH 1/3] Add C++ Const Mangling DIP --- DIPs/DIPxxxx.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 DIPs/DIPxxxx.md diff --git a/DIPs/DIPxxxx.md b/DIPs/DIPxxxx.md new file mode 100644 index 000000000..808650660 --- /dev/null +++ b/DIPs/DIPxxxx.md @@ -0,0 +1,112 @@ +# C++ Const Mangling + +| Field | Value | +|-----------------|-----------------------------------------------------------------| +| DIP: | (number/id -- assigned by DIP Manager) | +| Review Count: | 0 (edited by DIP Manager) | +| Author: | look-at-me | +| Implementation: | (links to implementation PR if any) | +| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | + +## Abstract + +Implementing a new mangle feature to more easily link with C++ which contain `const` pointers to mutable data. + +### Reference + +[core.stdcpp.allocator Implementation Workaround](https://github.com/dlang/druntime/blob/bc940316b4cd7cf6a76e34b7396de2003867fbef/src/core/stdcpp/allocator.d#L50) + +## Contents +* [Rationale](#rationale) +* [Description](#description) +* [Workarounds](#workarounds) +* [Breaking Changes and Deprecations](#breaking-changes-and-deprecations) +* [Copyright & License](#copyright--license) +* [Reviews](#reviews) + +## Rationale + +As it stands currently D does not allow `const` pointers to mutable data but this is allowed in C++. When trying to link to a C++ symbol that contains this type it can be difficult to do so as D has no way to express this type for use in mangling. This will ease linking with any C++ library that makes use of `const` pointers to mutable data, including the current efforts in DRuntime for `core.stdcpp`. + +## Description + +This DIP proposes to allow for `const` to be applied to only the pointer and not the type the pointer points to when a symbol is `extern(C++)`. This is in regards to C++ mangling only. This DIP does *not* propose to implement the actual functionality of a `const` pointer to a mutable type. + +```D +extern(C++) void foo( char const* ptr ); +``` + +This syntax would keep in line with D's implementation of `const` whereby `const` is applied to the current type and forward. In the following example if there is a pointer to a pointer and `const` is applied to the first pointer. Both pointers will be const and will point to a mutable type. + +```D +extern(C++) void foo( char const** ptr ); // mangle to equivalent C++ char *const *const +``` + +The same will be true if the const is included after the first pointer. Only the second pointer will be const and will point to a mutable pointer type. + +```D +extern(C++) void foo( char* const* ptr ); // mangle to equivalent C++ char **const +``` + +Should only the first pointer be `const` then brackets can be used to identify which pointer should be const. + +```D +extern(C++) void foo( char const(*)* ptr ); // mangle to equivalent C++ char *const * +extern(C++) void foo( char const(**)* ptr ); // mangle to equivalent C++ char *const *const * +``` + +The underlying type will be assigned the most closely equvalent D type. Effectively removing any `const` past the first mutable pointer. + +```D +extern(C++) const(char*)*const* p1; // typeof(p1) == const(char*)** +extern(C++) char**const* p2; // typeof(p2) == char*** +``` + +// TODO + +```D +extern(C++) void foo(T)( T const ); +foo!(char*)(); + +// vs + +extern(C++) void bar(T)( const T ); +bar!(char*)(); +``` + +## Workarounds + +A possible workaround that is current being utilized in DRuntime `core.stdcpp.allocator` is the use of `pragma` to include a linker flag with an alternative name for the function. This requires working knowledge of the underlying implementation of C++ mangling to modify the mangle to include the `const`. This also has the usual problems using using pragma for mangling, that it cannot be used with templates. + +```D +version (NeedsMangleHack) +{ + // HACK: workaround to make `deallocate` link as a `T * const` + private extern (D) enum string constHack(string name) = (){ + version (Win64) + enum sub = "AAXPE"; + else + enum sub = "AEXPA"; + foreach (i; 0 .. name.length - sub.length) + if (name[i .. i + sub.length] == sub[]) + return name[0 .. i + 3] ~ 'Q' ~ name[i + 4 .. $]; + assert(false, "substitution string not found!"); + }(); + pragma(linkerDirective, "/alternatename:" ~ deallocate.mangleof ~ "=" ~ constHack!(deallocate.mangleof)); +} +``` + +## Breaking Changes and Deprecations + +No breaking changes are to be expected. + +## Copyright & License + +Copyright (c) 2019 by the D Language Foundation + +Licensed under [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) + +## Reviews + +The DIP Manager will supplement this section with a summary of each review stage +of the DIP process beyond the Draft Review. From cec7af8cbd79434abc92f9972246feb251b7a215 Mon Sep 17 00:00:00 2001 From: look-at-me <42397654+look-at-me@users.noreply.github.com> Date: Sun, 10 Feb 2019 09:27:36 -0500 Subject: [PATCH 2/3] Add template tail const implementation. --- DIPs/DIPxxxx.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/DIPs/DIPxxxx.md b/DIPs/DIPxxxx.md index 808650660..74d38bdea 100644 --- a/DIPs/DIPxxxx.md +++ b/DIPs/DIPxxxx.md @@ -36,47 +36,48 @@ This DIP proposes to allow for `const` to be applied to only the pointer and not extern(C++) void foo( char const* ptr ); ``` -This syntax would keep in line with D's implementation of `const` whereby `const` is applied to the current type and forward. In the following example if there is a pointer to a pointer and `const` is applied to the first pointer. Both pointers will be const and will point to a mutable type. +This syntax would keep in line with D's implementation of `const` with the limitation of requiring the use of brackets `const(*)` which will apply `const` to the specified pointers. In the following example if there is a pointer to a pointer and `const` is applied to the first pointer. Both pointers will be const and will point to a mutable type. ```D -extern(C++) void foo( char const** ptr ); // mangle to equivalent C++ char *const *const +extern(C++) void foo1( char const(**) ptr ); // mangle to equivalent C++ char *const *const +extern(C++) void foo2( char const(*)* ptr ); // mangle to equivalent C++ char *const * +extern(C++) void foo3( char const(**)* ptr ); // mangle to equivalent C++ char *const *const * ``` -The same will be true if the const is included after the first pointer. Only the second pointer will be const and will point to a mutable pointer type. +The underlying type will be assigned the most closely equvalent D type. Effectively removing any `const` past the first mutable pointer or type. ```D -extern(C++) void foo( char* const* ptr ); // mangle to equivalent C++ char **const +extern(C++) const(char*)* const(*) p1; // typeof(p1) == const(char*)** +extern(C++) char** const(*) p2; // typeof(p2) == char*** +extern(C++) char const(***) p3; // typeof(p2) == char*** ``` +The tail const will also be allowed to exist to be able to link with C++ templates correctly when the same type is passed through as an argument to the template. For C++ when `const` is applied to a templated type, it is applied like so `T const` where `T` is the templated type. This means the `const` is applied in the exact same manor when `T` is substituted for the actual type. -Should only the first pointer be `const` then brackets can be used to identify which pointer should be const. - -```D -extern(C++) void foo( char const(*)* ptr ); // mangle to equivalent C++ char *const * -extern(C++) void foo( char const(**)* ptr ); // mangle to equivalent C++ char *const *const * +```C++ +template void foo( const T ); // equivalent to `T const` +foo( nullptr ); // foo( char* const ) ``` -The underlying type will be assigned the most closely equvalent D type. Effectively removing any `const` past the first mutable pointer. +In the above example when the type `char*` is passed to the template. The function's parameter is assigned the type of `char* const`. But for equivalent D code when using a `const` in such a manner the entire type will be assigned `const`. ```D -extern(C++) const(char*)*const* p1; // typeof(p1) == const(char*)** -extern(C++) char**const* p2; // typeof(p2) == char*** +extern(C++) void foo(T)( const T ); +foo!(char*)( null ); // foo( const(char*) ) ``` -// TODO +To maintain backwards compatiblity but to also be able to mangle correctly with C++ in these instances when using `const` with a template. Using a tail `const` will be applied to only to the tail end of the type. ```D extern(C++) void foo(T)( T const ); -foo!(char*)(); - -// vs - -extern(C++) void bar(T)( const T ); -bar!(char*)(); +foo!(char*)( null ); // foo( char const(*) ) +foo!(char**)( null ); // foo( char* const(*) ) +foo!(const(char)*)( null ); // foo( const(char*) ) +foo!(char)( '0' ); // foo( const(char) ) ``` ## Workarounds -A possible workaround that is current being utilized in DRuntime `core.stdcpp.allocator` is the use of `pragma` to include a linker flag with an alternative name for the function. This requires working knowledge of the underlying implementation of C++ mangling to modify the mangle to include the `const`. This also has the usual problems using using pragma for mangling, that it cannot be used with templates. +A possible workaround that is current being utilized in DRuntime `core.stdcpp.allocator` is the use of `pragma` to include a linker flag with an alternative name for the function. This requires working knowledge of the underlying implementation of C++ mangling to modify the mangle to include the `const`. This also has the usual problems when using pragma for mangling, that it cannot be used with templates, and requires working knowledge of each C++ mangling used. ```D version (NeedsMangleHack) From a31b4f7233b961916235198ff542f0734c1c5a08 Mon Sep 17 00:00:00 2001 From: look-at-me <42397654+look-at-me@users.noreply.github.com> Date: Mon, 11 Feb 2019 17:40:12 -0500 Subject: [PATCH 3/3] Draft --- DIPs/DIPxxxx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DIPs/DIPxxxx.md b/DIPs/DIPxxxx.md index 74d38bdea..f5f33a513 100644 --- a/DIPs/DIPxxxx.md +++ b/DIPs/DIPxxxx.md @@ -6,7 +6,7 @@ | Review Count: | 0 (edited by DIP Manager) | | Author: | look-at-me | | Implementation: | (links to implementation PR if any) | -| Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | +| Status: | Draft | ## Abstract