Skip to content

Commit 740a866

Browse files
Fix issue 18716: type const(char)[] can not be mapped to C++
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`.
1 parent e1a88a5 commit 740a866

10 files changed

Lines changed: 248 additions & 3 deletions

File tree

changelog/dslices-cpp-mangling.dd

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
Added support for D arrays in C++ functions
2+
3+
D arrays don't have any corresponding type in C++. This change adds support for
4+
mangling a D array as a templated struct with the special name: `__dslice`.
5+
Under the hood, a D array is represented as a struct, containing the length of
6+
the array and a pointer to the data:
7+
8+
---
9+
struct DArray(T)
10+
{
11+
size_t length;
12+
T* ptr;
13+
}
14+
---
15+
16+
Any D array in the form of, `T[]`, when used in a C++ function, will be mangled
17+
as the following struct:
18+
19+
---
20+
struct __dslice(T);
21+
---
22+
23+
For example, an array of ints, `int[]`, will be mangled as `__dslice!int` when
24+
used in a C++ function. This allows to declare C++ functions that accepts D
25+
arrays and can be accessed to C++ if the right struct declaration is present
26+
on the C++ side.
27+
28+
On the D side:
29+
30+
---
31+
extern(C++) void foo(const(char)[] str);
32+
33+
void main()
34+
{
35+
foo("bar");
36+
}
37+
---
38+
39+
On the C++ side:
40+
41+
$(CPPCODE
42+
#include <stdio.h>
43+
44+
template<typename T> struct __dslice
45+
{
46+
size_t length;
47+
T* ptr;
48+
};
49+
50+
void foo(__dslice<const char> array)
51+
{
52+
printf("%.*s\n", (int) array.length, array.ptr);
53+
}
54+
)
55+
56+
Since the `__dslice` struct is ABI compatible with D arrays, it works perfectly
57+
fine to pass a D array to a C++ function taking a `__dslice`.

src/dmd/cppmangle.d

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1576,7 +1576,12 @@ extern(C++):
15761576

15771577
override void visit(Type t)
15781578
{
1579-
error(t);
1579+
auto typeForMangling = t.typeForMangling(LINK.cpp);
1580+
1581+
if (typeForMangling is t)
1582+
error(t);
1583+
else
1584+
typeForMangling.accept(this);
15801585
}
15811586

15821587
void visit(Tuple t)

src/dmd/cppmanglewin.d

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public:
125125

126126
override void visit(Type type)
127127
{
128+
auto typeForMangling = type.typeForMangling(LINK.cpp);
129+
130+
if (typeForMangling !is type)
131+
return typeForMangling.accept(this);
132+
128133
if (checkImmutableShared(type))
129134
return;
130135

@@ -1089,15 +1094,15 @@ private:
10891094
// ::= $0<encoded integral number>
10901095
//printf("mangleIdent('%s')\n", sym.toChars());
10911096
Dsymbol p = sym;
1092-
if (p.toParent() && p.toParent().isTemplateInstance())
1097+
if (p && p.toParent() && p.toParent().isTemplateInstance())
10931098
{
10941099
p = p.toParent();
10951100
}
10961101
while (p && !p.isModule())
10971102
{
10981103
mangleName(p, dont_use_back_reference);
10991104
p = p.toParent();
1100-
if (p.toParent() && p.toParent().isTemplateInstance())
1105+
if (p && p.toParent() && p.toParent().isTemplateInstance())
11011106
{
11021107
p = p.toParent();
11031108
}

src/dmd/dmangle.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,11 @@ extern (C++) const(char)* mangleExact(FuncDeclaration fd)
11381138

11391139
extern (C++) void mangleToBuffer(Type t, OutBuffer* buf)
11401140
{
1141+
auto typeForMangling = t.typeForMangling(LINK.d);
1142+
1143+
if (t !is typeForMangling)
1144+
return mangleToBuffer(typeForMangling, buf);
1145+
11411146
if (t.deco)
11421147
buf.writestring(t.deco);
11431148
else

src/dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ immutable Msgtable[] msgtable =
414414
{ "basic_ostream" },
415415
{ "basic_iostream" },
416416
{ "char_traits" },
417+
{ "__dslice" },
417418

418419
// Compiler recognized UDA's
419420
{ "udaSelector", "selector" },

src/dmd/mtype.d

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,24 @@ extern (C++) abstract class Type : RootObject
524524
return DYNCAST.type;
525525
}
526526

527+
/**
528+
* Returns the type the receiver should be treated as during mangling.
529+
*
530+
* This allows for a type to be treated as a different type during mangling.
531+
* This is useful, for example, when interfacing with C++, for D types that
532+
* don't have a corresponding C++ type. This can allow `int[]` to be
533+
* mangled as `__dslice!int` during C++ mangling.
534+
*
535+
* Params:
536+
* linkage = the type of mangling that is requested
537+
*
538+
* Returns: the type the receiver should be treated as during mangling
539+
*/
540+
Type typeForMangling(LINK linkage)
541+
{
542+
return this;
543+
}
544+
527545
/*******************************
528546
* Covariant means that 'this' can substitute for 't',
529547
* i.e. a pure function is a match for an impure type.
@@ -3728,6 +3746,9 @@ extern (C++) final class TypeSArray : TypeArray
37283746
*/
37293747
extern (C++) final class TypeDArray : TypeArray
37303748
{
3749+
/// Mangle D array as this type when mangling for C++.
3750+
private Type typeForCppMangling;
3751+
37313752
extern (D) this(Type t)
37323753
{
37333754
super(Tarray, t);
@@ -3752,6 +3773,88 @@ extern (C++) final class TypeDArray : TypeArray
37523773
return t;
37533774
}
37543775

3776+
override Type typeForMangling(LINK linkage)
3777+
{
3778+
/**
3779+
* Returns a template declaration corresponding to the following code:
3780+
*
3781+
* ---
3782+
* template __dslice(T) {}
3783+
* ---
3784+
*
3785+
* Returns: a template declaration corresponding to the above code
3786+
*
3787+
* See_Also: `typeForCppMangling`
3788+
*/
3789+
static TemplateDeclaration dsliceTemplateDeclaration()
3790+
{
3791+
__gshared TemplateDeclaration td;
3792+
3793+
if (td)
3794+
return td;
3795+
3796+
auto ttp = new TemplateTypeParameter(Loc.initial, Id.p, null, null);
3797+
auto parameters = new TemplateParameters(ttp);
3798+
3799+
return td = new TemplateDeclaration(Loc.initial, Id.__dslice, parameters,
3800+
null, null);
3801+
}
3802+
3803+
/**
3804+
* Returns a template instantiation of the template declaration returned
3805+
* by `dsliceTemplateDeclaration`.
3806+
*
3807+
* The template is instantiated with the element type of this array. For
3808+
* an array of ints it would correspond to the following D code:
3809+
* `__dslice!int`.
3810+
*
3811+
* Returns: a template instance
3812+
*
3813+
* See_Also: `dsliceTemplateDeclaration`
3814+
* See_Also: `typeForCppMangling`
3815+
*/
3816+
TemplateInstance dsliceTemplateInstance()
3817+
{
3818+
auto tiargs = new Objects(next);
3819+
auto ti = new TemplateInstance(Loc.initial, Id.__dslice, tiargs);
3820+
ti.tempdecl = dsliceTemplateDeclaration();
3821+
3822+
return ti;
3823+
}
3824+
3825+
/**
3826+
* Returns the type that this array type should be treated as when it's
3827+
* mangled as a C++ type.
3828+
*
3829+
* D arrays don't have any corresponding type in C++. Instead we mangle
3830+
* it as a templated struct with the name `__dslice`, i.e.
3831+
* `struct __dslice(T)`, where `T` is the element type of the array. For
3832+
* an array of ints it would be mangled as the following type
3833+
* `__dslice!int`.
3834+
*
3835+
* Returns: the type that this should be treated as when mangling as a
3836+
* C++ type
3837+
*
3838+
* See_Also: `dsliceTemplateInstance`
3839+
*/
3840+
Type typeForCppMangling()
3841+
{
3842+
__gshared Type[char*] cachedTypes;
3843+
auto elementType = next;
3844+
3845+
if (auto type = elementType.deco in cachedTypes)
3846+
return *type;
3847+
3848+
auto sd = new StructDeclaration(Loc.initial, Id.__dslice, false);
3849+
sd.parent = dsliceTemplateInstance();
3850+
3851+
auto type = new TypeStruct(sd).typeSemantic(Loc.initial, null);
3852+
return cachedTypes[elementType.deco] = type;
3853+
}
3854+
3855+
return linkage == LINK.cpp ? typeForCppMangling() : this;
3856+
}
3857+
37553858
override d_uns64 size(const ref Loc loc) const
37563859
{
37573860
//printf("TypeDArray::size()\n");

src/dmd/mtype.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ class Type : public RootObject
226226
bool equivalent(Type *t);
227227
// kludge for template.isType()
228228
int dyncast() const { return DYNCAST_TYPE; }
229+
virtual Type* typeForMangling(LINK linkage);
229230
int covariant(Type *t, StorageClass *pstc = NULL, bool fix17349 = true);
230231
const char *toChars();
231232
char *toPrettyChars(bool QualifyTypes = false);
@@ -435,9 +436,14 @@ class TypeSArray : public TypeArray
435436
// Dynamic array, no dimension
436437
class TypeDArray : public TypeArray
437438
{
439+
private:
440+
Type* typeForCppMangling;
441+
438442
public:
443+
439444
const char *kind();
440445
Type *syntaxCopy();
446+
Type* typeForMangling(LINK linkage);
441447
d_uns64 size(const Loc &loc) /*const*/;
442448
unsigned alignsize() /*const*/;
443449
bool isString();

src/dmd/root/array.d

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ public:
3939

4040
@disable this(this);
4141

42+
/**
43+
* Convenience constructor to add the given elements to this array.
44+
*
45+
* Params:
46+
* elements = elements to add to this array
47+
*/
48+
extern(D) this(T[] elements ...) nothrow
49+
{
50+
if (elements.length > 0)
51+
reserve(elements.length);
52+
53+
foreach (e ; elements)
54+
push(e);
55+
}
56+
4257
~this() nothrow
4358
{
4459
if (data != &smallarray[0])

src/dmd/root/array.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,16 @@ struct BitArray
234234
private:
235235
BitArray(const BitArray&);
236236
};
237+
238+
// This is the type that the D compiler will mangle D slices as when mangling
239+
// for C++. This type is ABI compatible with native D slices.
240+
template <typename T> struct __dslice
241+
{
242+
d_size_t length;
243+
T* ptr;
244+
245+
__dslice() : length(0), ptr(NULL) {}
246+
__dslice(size_t length, T* ptr) : length(length), ptr(ptr) {}
247+
};
248+
249+
#define DSlice __dslice

test/compilable/cppmangle.d

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,3 +976,38 @@ else version(Posix)
976976
static assert(test19278_4.mangleof == "_ZN5hello5world3yay11test19278_4Ev");
977977
static assert(test19278_var.mangleof == "_ZN5hello5world13test19278_varE");
978978
}
979+
980+
/*****************************************/
981+
// https://issues.dlang.org/show_bug.cgi?id=18716
982+
// D slices as parameters in extern(C++) functions
983+
984+
struct Test18716 {}
985+
struct __dslice(T) {}
986+
987+
extern(C++) void test18716a(char[]);
988+
extern(C++) void test18716b(__dslice!char);
989+
extern(C++) void test18716c(const(char)[]);
990+
extern(C++) void test18716d(Test18716[]);
991+
992+
void test18716()
993+
{
994+
test18716a("foo".dup);
995+
test18716c("foo");
996+
test18716d([Test18716()]);
997+
}
998+
999+
version (Posix)
1000+
{
1001+
static assert(test18716a.mangleof == "_Z10test18716a8__dsliceIcE");
1002+
static assert(test18716b.mangleof == "_Z10test18716b8__dsliceIcE");
1003+
static assert(test18716c.mangleof == "_Z10test18716c8__dsliceIKcE");
1004+
static assert(test18716d.mangleof == "_Z10test18716d8__dsliceI9Test18716E");
1005+
}
1006+
1007+
else version (Windows)
1008+
{
1009+
static assert(test18716a.mangleof == "?test18716a@@YAXU?$__dslice@D@@@Z");
1010+
static assert(test18716b.mangleof == "?test18716b@@YAXU?$__dslice@D@@@Z");
1011+
static assert(test18716c.mangleof == "?test18716c@@YAXU?$__dslice@$$CBD@@@Z");
1012+
static assert(test18716d.mangleof == "?test18716d@@YAXU?$__dslice@UTest18716@@@@@Z");
1013+
}

0 commit comments

Comments
 (0)