Fix issue 18716: type const(char)[] can not be mapped to C++#8120
Fix issue 18716: type const(char)[] can not be mapped to C++#8120jacob-carlborg wants to merge 2 commits into
const(char)[] can not be mapped to C++#8120Conversation
|
Thanks for your pull request and interest in making D better, @jacob-carlborg! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + dmd#8120" |
7a3dcd6 to
fd88f12
Compare
|
Thanks for doing the work on this, it's a nice job. I've thought about doing this now and then, having run into the issue myself, but I have reservations:
The conclusion I always come to is it is not worth the extra complexity in the D core language. |
|
@WalterBright This primarily for use within DMD. Based on your conclusion, you don’t want this feature? |
As @jacob-carlborg mentioned this is primarily intended for DMD, s.t. D strings can be used everywhere and passed to LDC/GDC. See also: #7893 (another attempt at making the interoperability possible)
FWIW: Last time I checked, this hit multiple dead-ends (see: dlang/druntime#1316) :/
Hmm what's your plan on passing D strings to LDC/GDC then? |
I think this is a much more sensible approach. Instead of introducing another symbol, could we turn |
I disagree. It would be more complicated to rewrite the AST or the IR. I think it’s too late to do it in the backend. My approach is ABI compatible so no rewriting is required. |
|
@wilzbach auto-tester is reporting that all tests passed here, but looking at the details, one test failed. |
d854bde to
deb51eb
Compare
Then I definitely feel this is not worth it. It would be simple enough to manually rewrite: as:
Right, not worth it. |
|
Hmm. When I think about moving the dmd implementation from
|
How can we know which methods are used in GDC/LDC? |
We will tell you. 😉 ... Let's start with a rule of thumb. The role of the interface is to initialize the frontend, call entrypoints to read, parse, semantic, and optionally generate header/ddoc/json files for all modules. Whatever doesn't fall into any of those categories should be good for the taking. i.e: AST types that only live during semantic analysis, anything that sets or mutates state, and resolver methods and functions. Off the top of my head, the only functions that need to continue returning a |
For example (this might already have been done), the only function that needs to be exposed to C++ for header generation is |
|
@WalterBright with the (questionable) power of C++ implicit conversions this is a #include <vector>
template<typename T> struct __dslice {
size_t length; T* ptr;
__dslice(const std::vector<T>& x)
{
length = x.size();
ptr = (T*)&x[0]; // seems to default to const not sure why hence the cast
}
};
void foo(__dslice<int> a) {};
int main(int a, char** b)
{
std::vector<int> x;
foo(x);
}Proof this compiles: https://godbolt.org/g/VFA1xV If somebody is using the code then they are already |
@ibuclaw Unfortunately it's not possible to overload a function taking void symbol_name(const(char)* name) {}
void symbol_name(const(char)[] name) {}
void main()
{
symbol_name("foo");
} |
deb51eb to
e72d80f
Compare
|
@jacob-carlborg - then make the C++ overload include a length parameter? Identifier::idPool is a good example of this, the D: Lines 175 to 193 in f1effe0 C++: Lines 42 to 47 in f1effe0 |
@ibuclaw there’s already one overload with and one without the length parameter. I’m trying to avoid breaking all existing usages. https://github.com/dlang/dmd/blob/master/src/dmd/backend/global.d#L335 |
e72d80f to
3572ad0
Compare
|
@thewilsonator rebased. |
|
Hmm, it seems to be segfaulting on windows? No idea why though. See e.g. https://auto-tester.puremagic.com/show-run.ghtml?projectid=1&runid=3231832&isPull=true |
|
Also with the |
I can do that. |
@thewilsonator to do that I need access to a |
| }; | ||
|
|
||
| // This is the type that the D compiler will mangle D slices as when mangling | ||
| // for C++. This type is ABI compatible with native D slices. |
There was a problem hiding this comment.
Is ABI compatibility actually tested somewhere? I seriously doubt it is on Win64 for example. LDC on Win64 returns and passes slices (and delegates IIRC) as pairs (in 2 registers if available), whereas a 128-bit struct is passed/returned indirectly by value (hidden ref), and there is no way for a struct to emulate that behaviour.
[Btw, the __dslice ctors below make MSVC++ treat this as a non-POD and return it via hidden pointer, but that's irrelevant here, as it's > 64 bits anyway.]
There was a problem hiding this comment.
The C++ T[] ABI need not be the same as the D T[] ABI, but I take your point. I don't expect this to be used straight away in DMD because that will require an upgrade of the minimum DMDFE required to build, and there has to be at least one release that can build it w/o using it.
There was a problem hiding this comment.
Apparently the same for DMD. DMD 2.081.0, -m64 on Windows, just checking the return value:
const(char)[] getNativeSlice() { return "lala"[0..3]; }
struct Slice(T)
{
size_t length;
T* ptr;
}
extern(C++) Slice!(const(char)) getSlice() { return Slice!(const(char))(3, "lala".ptr); }=>
; native slice returned in RAX and RDX
_D5slice14getNativeSliceFZAxa:
0000000000000000: 55 push rbp
0000000000000001: 48 8B EC mov rbp,rsp
0000000000000004: 48 8D 15 00 00 00 lea rdx,[__a3_6c616c]
00
000000000000000B: B8 03 00 00 00 mov eax,3
0000000000000010: 5D pop rbp
0000000000000011: C3 ret
; pointer to pre-allocated return value passed in RCX
?getSlice@@YA?AU?$Slice@D@@XZ (struct Slice<char> __cdecl getSlice(void)):
0000000000000000: 55 push rbp
0000000000000001: 48 8B EC mov rbp,rsp
0000000000000004: 48 C7 01 03 00 00 mov qword ptr [rcx],3
00
000000000000000B: 48 8D 05 00 00 00 lea rax,[_TMP0]
00
0000000000000012: 48 89 41 08 mov qword ptr [rcx+8],rax
0000000000000016: 48 89 C8 mov rax,rcx
0000000000000019: 5D pop rbp
000000000000001A: C3 retThere was a problem hiding this comment.
Is ABI compatibility actually tested somewhere?
No.
LDC on Win64 returns and passes slices (and delegates IIRC) as pairs (in 2 registers if available), whereas a 128-bit struct is passed/returned indirectly by value (hidden ref), and there is no way for a struct to emulate that behavior.
That's unfortunate.
I don't follow, who is "they"? C++ types? C++ people? The former is a contradiction in terms, it cannot be both flexible and not flexible. |
|
@thewilsonator Every time I suggest to someone who is having problems interfacing C++ to D that they adjust the C++ side, they have a strong negative reaction. Passing arrays as a special __dslice they have to add a special code for on the C++ side is not going to go over well, even if you supply that code. Just pass the [ ] as .ptr, .length and then there's no issue. |
|
FWIW in the state of D survey this has been requested a few times too. With this change, people can choice whether they want to adapt the C++ side or the D side, before there was no choice. If someone prefers to add pointer+length overloads or wrapping it in custom types, he can already do this and won't be affected by this improvement. However, those who prefer the D style will get an additional reason to like D, no? |
|
@WalterBright ahh I get you.
The point of its constructor is that given you can pass a Likewise template<typename T struct __dslice
{
size_t length; T* ptr;
...
operator gsl::span<T>(){ return gsl::span<T>(ptr,length);}
};
__dslice<int> bar();
...
gsl::span<int> c = bar();works. This means that you don't ever have to use We can synthesise D |
D arrays don't have any corresponding type in C++. Instead we mangle it as a templated struct with the name `__dslice`, i.e. `struct __dslice(T)`, where `T` is the element type of the array. For an array of ints it would be mangled as the following type: `__dslice!int`.
54ac4b5 to
740a866
Compare
|
@thewilsonator rebased. |
|
Thanks. @WalterBright can we please get the green light for this? |
|
I don't see how you can simply discard the ABI mismatch on Win64. If the purpose is direct slices interop between D and C++, but it doesn't work on all platforms, it's useless to me. Edit: I think that at least for LDC, there are potential ABI mismatches for non-Windows x86_64 targets as well. If there's only one available GP register left, the slice's size is put in there, and the ptr is pushed on the stack, IIRC. This isn't done for a regular struct, which is either passed completely in registers or on the stack. |
| // D slices as parameters in extern(C++) functions | ||
|
|
||
| struct Test18716 {} | ||
| struct __dslice(T) {} |
There was a problem hiding this comment.
The tests needs to be in runnable
There was a problem hiding this comment.
The ABI doesn't match, so there's no point, at least not yet.
There was a problem hiding this comment.
The point is exactly that the ABI does not match.
There was a problem hiding this comment.
I'm not following. This whole PR is moot as long as the ABI problem exists.
There was a problem hiding this comment.
Yep, I was just saying that it's not obvious, since there are tests, and the PR is green.
But IMO, this PR should ultimately be accepted, but it looks like you fear it to be vetoed.
There was a problem hiding this comment.
Yes. It should, but Walter hasn't responded to the reasoning why his concerns are unfounded.
There was a problem hiding this comment.
I’m not going to spend time on moving the tests if/until I fix the ABI issue.
There was a problem hiding this comment.
Walter did already vetoed it.
There was a problem hiding this comment.
#11073 contains a runnable_cxx test (if someone wants to use it)
|
Sorry for the confusion, I meant green light as in he's be happy with the feature, so that it would then not be a waste of time fixing ABI issues for a PR that would be rejected anyway. |
|
WIP due to missing ABI compatibility. |
|
I'm not familiar with the DMD ABI details, but at least for LDC, a half-feasible option would be mangling a slice parameter as 2 separate C++ parameters - |
|
Any update on this? |
|
@WalterBright how should we proceed here? |
|
Ping @WalterBright . This is a good step to make the compiler |
How? Many things are still being passed to the front-end from C++ land. |
I'm sure there is a lot of benefits on make this step, especially making the compiler use more |
D arrays don't have any corresponding type in C++. Instead we mangle it as a templated struct with the name
__dslice, i.e.struct __dslice(T), whereTis the element type of the array. For an array of ints it would be mangled as the following type:__dslice!int.Reboot of #7893.
@WalterBright this should interesting for you since you want to kill C string in DMD.