Skip to content

Commit 3572ad0

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 8d0f6af commit 3572ad0

11 files changed

Lines changed: 247 additions & 1 deletion

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
@@ -1120,7 +1120,12 @@ extern(C++):
11201120

11211121
override void visit(Type t)
11221122
{
1123-
error(t);
1123+
auto typeForMangling = t.typeForMangling(LINK.cpp);
1124+
1125+
if (typeForMangling is t)
1126+
error(t);
1127+
else
1128+
typeForMangling.accept(this);
11241129
}
11251130

11261131
override void visit(TypeNull t)

src/dmd/cppmanglewin.d

Lines changed: 5 additions & 0 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

src/dmd/dmangle.d

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

11201120
extern (C++) void mangleToBuffer(Type t, OutBuffer* buf)
11211121
{
1122+
auto typeForMangling = t.typeForMangling(LINK.d);
1123+
1124+
if (t !is typeForMangling)
1125+
return mangleToBuffer(typeForMangling, buf);
1126+
11221127
if (t.deco)
11231128
buf.writestring(t.deco);
11241129
else

src/dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ immutable Msgtable[] msgtable =
411411
{ "basic_ostream" },
412412
{ "basic_iostream" },
413413
{ "char_traits" },
414+
{ "__dslice" },
414415

415416
// Compiler recognized UDA's
416417
{ "udaSelector", "selector" },

src/dmd/mtype.d

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,24 @@ extern (C++) abstract class Type : RootObject
531531
return DYNCAST.type;
532532
}
533533

534+
/**
535+
* Returns the type the receiver should be treated as during mangling.
536+
*
537+
* This allows for a type to be treated as a different type during mangling.
538+
* This is useful, for example, when interfacing with C++, for D types that
539+
* don't have a corresponding C++ type. This can allow `int[]` to be
540+
* mangled as `__dslice!int` during C++ mangling.
541+
*
542+
* Params:
543+
* linkage = the type of mangling that is requested
544+
*
545+
* Returns: the type the receiver should be treated as during mangling
546+
*/
547+
Type typeForMangling(LINK linkage) pure nothrow
548+
{
549+
return this;
550+
}
551+
534552
/*******************************
535553
* Covariant means that 'this' can substitute for 't',
536554
* i.e. a pure function is a match for an impure type.
@@ -3754,6 +3772,9 @@ extern (C++) final class TypeSArray : TypeArray
37543772
*/
37553773
extern (C++) final class TypeDArray : TypeArray
37563774
{
3775+
/// Mangle D array as this type when mangling for C++.
3776+
package Type typeForCppMangling;
3777+
37573778
extern (D) this(Type t)
37583779
{
37593780
super(Tarray, t);
@@ -3778,6 +3799,11 @@ extern (C++) final class TypeDArray : TypeArray
37783799
return t;
37793800
}
37803801

3802+
override Type typeForMangling (LINK linkage) pure nothrow
3803+
{
3804+
return linkage == LINK.cpp ? typeForCppMangling : this;
3805+
}
3806+
37813807
override d_uns64 size(const ref Loc loc) const
37823808
{
37833809
//printf("TypeDArray::size()\n");

src/dmd/mtype.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ class Type : public RootObject
236236
bool equivalent(Type *t);
237237
// kludge for template.isType()
238238
int dyncast() const { return DYNCAST_TYPE; }
239+
virtual Type* typeForMangling(LINK linkage) /*pure nothrow*/;
239240
int covariant(Type *t, StorageClass *pstc = NULL, bool fix17349 = true);
240241
const char *toChars();
241242
char *toPrettyChars(bool QualifyTypes = false);
@@ -454,9 +455,14 @@ class TypeSArray : public TypeArray
454455
// Dynamic array, no dimension
455456
class TypeDArray : public TypeArray
456457
{
458+
private:
459+
Type* typeForCppMangling;
460+
457461
public:
462+
458463
const char *kind();
459464
Type *syntaxCopy();
465+
Type* typeForMangling(LINK linkage) /*pure nothrow*/;
460466
d_uns64 size(const Loc &loc) /*const*/;
461467
unsigned alignsize() /*const*/;
462468
bool isString();

src/dmd/root/array.d

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ private:
2929
public:
3030
@disable this(this);
3131

32+
/**
33+
* Convenience constructor to add the given elements to this array.
34+
*
35+
* Params:
36+
* elements = elements to add to this array
37+
*/
38+
extern(D) this(T[] elements ...) nothrow
39+
{
40+
if (elements.length > 0)
41+
reserve(elements.length);
42+
43+
foreach (e ; elements)
44+
push(e);
45+
}
46+
3247
~this() nothrow
3348
{
3449
if (data != &smallarray[0])

src/dmd/root/array.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,17 @@ struct BitArray
240240
BitArray(const BitArray&);
241241
};
242242

243+
// This is the type that the D compiler will mangle D slices as when mangling
244+
// for C++. This type is ABI compatible with native D slices.
245+
template <typename T> struct __dslice
246+
{
247+
d_size_t length;
248+
T* ptr;
249+
250+
__dslice() : length(0), ptr(NULL) {}
251+
__dslice(size_t length, T* ptr) : length(length), ptr(ptr) {}
252+
};
253+
254+
#define DSlice __dslice
255+
243256
#endif

src/dmd/typesem.d

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,83 @@ private extern (C++) final class TypeSemanticVisitor : Visitor
852852

853853
override void visit(TypeDArray mtype)
854854
{
855+
/**
856+
* Returns a template declaration corresponding to the following code:
857+
*
858+
* ---
859+
* template __dslice(T) {}
860+
* ---
861+
*
862+
* Returns: a template declaration corresponding to the above code
863+
*
864+
* See_Also: `typeForCppMangling`
865+
*/
866+
TemplateDeclaration dsliceTemplateDeclaration()
867+
{
868+
__gshared TemplateDeclaration td;
869+
870+
if (td)
871+
return td;
872+
873+
auto ttp = new TemplateTypeParameter(loc, Id.p, null, null);
874+
auto parameters = new TemplateParameters(ttp);
875+
876+
return td = new TemplateDeclaration(loc, Id.__dslice, parameters,
877+
null, null);
878+
}
879+
880+
/**
881+
* Returns a template instantiation of the template declaration returned
882+
* by `dsliceTemplateDeclaration`.
883+
*
884+
* The template is instantiated with the element type of this array. For
885+
* an array of ints it would correspond to the following D code:
886+
* `__dslice!int`.
887+
*
888+
* Returns: a template instance
889+
*
890+
* See_Also: `dsliceTemplateDeclaration`
891+
* See_Also: `typeForCppMangling`
892+
*/
893+
TemplateInstance dsliceTemplateInstance()
894+
{
895+
auto tiargs = new Objects(mtype.next);
896+
auto ti = new TemplateInstance(loc, Id.__dslice, tiargs);
897+
ti.tempdecl = dsliceTemplateDeclaration();
898+
899+
return ti;
900+
}
901+
902+
/**
903+
* Returns the type that this array type should be treated as when it's
904+
* mangled as a C++ type.
905+
*
906+
* D arrays don't have any corresponding type in C++. Instead we mangle
907+
* it as a templated struct with the name `__dslice`, i.e.
908+
* `struct __dslice(T)`, where `T` is the element type of the array. For
909+
* an array of ints it would be mangled as the following type
910+
* `__dslice!int`.
911+
*
912+
* Returns: the type that this should be treated as when mangling as a
913+
* C++ type
914+
*
915+
* See_Also: `dsliceTemplateInstance`
916+
*/
917+
Type typeForCppMangling()
918+
{
919+
__gshared Type[char*] cachedTypes;
920+
auto elementType = mtype.next;
921+
922+
if (auto type = elementType.deco in cachedTypes)
923+
return *type;
924+
925+
auto sd = new StructDeclaration(loc, Id.__dslice, false);
926+
sd.parent = dsliceTemplateInstance();
927+
928+
auto type = new TypeStruct(sd).typeSemantic(loc, sc);
929+
return cachedTypes[elementType.deco] = type;
930+
}
931+
855932
Type tn = mtype.next.typeSemantic(loc, sc);
856933
Type tbn = tn.toBasetype();
857934
switch (tbn.ty)
@@ -878,6 +955,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor
878955
}
879956
mtype.next = tn;
880957
mtype.transitive();
958+
mtype.typeForCppMangling = typeForCppMangling();
959+
881960
result = merge(mtype);
882961
}
883962

0 commit comments

Comments
 (0)