Skip to content

Commit fd88f12

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 44d849e commit fd88f12

10 files changed

Lines changed: 233 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
@@ -735,7 +735,12 @@ public:
735735

736736
override void visit(Type t)
737737
{
738-
error(t);
738+
auto typeForMangling = t.typeForMangling(LINK.cpp);
739+
740+
if (typeForMangling is t)
741+
error(t);
742+
else
743+
typeForMangling.accept(this);
739744
}
740745

741746
/******

src/dmd/dmangle.d

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

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

src/dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ immutable Msgtable[] msgtable =
407407
{ "basic_ostream" },
408408
{ "basic_iostream" },
409409
{ "char_traits" },
410+
{ "__dslice" },
410411

411412
// Compiler recognized UDA's
412413
{ "udaSelector", "selector" },

src/dmd/mtype.d

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

568+
/**
569+
* Returns the type the receiver should be treated as during mangling.
570+
*
571+
* This allows for a type to be treated as a different type during mangling.
572+
* This is useful, for example, when interfacing with C++, for D types that
573+
* don't have a corresponding C++ type. This can allow `int[]` to be
574+
* mangled as `__dslice!int` during C++ mangling.
575+
*
576+
* Params:
577+
* linkage = the type of mangling that is requested
578+
*
579+
* Returns: the type the receiver should be treated as during mangling
580+
*/
581+
Type typeForMangling(LINK linkage) pure nothrow
582+
{
583+
return this;
584+
}
585+
568586
/*******************************
569587
* Covariant means that 'this' can substitute for 't',
570588
* i.e. a pure function is a match for an impure type.
@@ -3846,6 +3864,9 @@ extern (C++) final class TypeSArray : TypeArray
38463864
*/
38473865
extern (C++) final class TypeDArray : TypeArray
38483866
{
3867+
/// Mangle D array as this type when mangling for C++.
3868+
package Type typeForCppMangling;
3869+
38493870
extern (D) this(Type t)
38503871
{
38513872
super(Tarray, t);
@@ -3870,6 +3891,11 @@ extern (C++) final class TypeDArray : TypeArray
38703891
return t;
38713892
}
38723893

3894+
override Type typeForMangling (LINK linkage) pure nothrow
3895+
{
3896+
return linkage == LINK.cpp ? typeForCppMangling : this;
3897+
}
3898+
38733899
override d_uns64 size(const ref Loc loc) const
38743900
{
38753901
//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);
@@ -459,9 +460,14 @@ class TypeSArray : public TypeArray
459460
// Dynamic array, no dimension
460461
class TypeDArray : public TypeArray
461462
{
463+
private:
464+
Type* typeForCppMangling;
465+
462466
public:
467+
463468
const char *kind();
464469
Type *syntaxCopy();
470+
Type* typeForMangling(LINK linkage) /*pure nothrow*/;
465471
d_uns64 size(const Loc &loc) /*const*/;
466472
unsigned alignsize() /*const*/;
467473
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+
* Connivence 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
@@ -497,6 +497,83 @@ private extern (C++) final class TypeSemanticVisitor : Visitor
497497

498498
override void visit(TypeDArray mtype)
499499
{
500+
/**
501+
* Returns a template declaration corresponding to the following code:
502+
*
503+
* ---
504+
* template __dslice(T) {}
505+
* ---
506+
*
507+
* Returns: a template declaration corresponding to the above code
508+
*
509+
* See_Also: `typeForCppMangling`
510+
*/
511+
TemplateDeclaration dsliceTemplateDeclaration()
512+
{
513+
__gshared TemplateDeclaration td;
514+
515+
if (td)
516+
return td;
517+
518+
auto ttp = new TemplateTypeParameter(loc, Id.p, null, null);
519+
auto parameters = new TemplateParameters(ttp);
520+
521+
return td = new TemplateDeclaration(loc, Id.__dslice, parameters,
522+
null, null);
523+
}
524+
525+
/**
526+
* Returns a template instantiation of the template declaration returned
527+
* by `dsliceTemplateDeclaration`.
528+
*
529+
* The template is instantiated with the element type of this array. For
530+
* an array of ints it would correspond to the following D code:
531+
* `__dslice!int`.
532+
*
533+
* Returns: a template instance
534+
*
535+
* See_Also: `dsliceTemplateDeclaration`
536+
* See_Also: `typeForCppMangling`
537+
*/
538+
TemplateInstance dsliceTemplateInstance()
539+
{
540+
auto tiargs = new Objects(mtype.next);
541+
auto ti = new TemplateInstance(loc, Id.__dslice, tiargs);
542+
ti.tempdecl = dsliceTemplateDeclaration();
543+
544+
return ti;
545+
}
546+
547+
/**
548+
* Returns the type that this array type should be treated as when it's
549+
* mangled as a C++ type.
550+
*
551+
* D arrays don't have any corresponding type in C++. Instead we mangle
552+
* it as a templated struct with the name `__dslice`, i.e.
553+
* `struct __dslice(T)`, where `T` is the element type of the array. For
554+
* an array of ints it would be mangled as the following type
555+
* `__dslice!int`.
556+
*
557+
* Returns: the type that this should be treated as when mangling as a
558+
* C++ type
559+
*
560+
* See_Also: `dsliceTemplateInstance`
561+
*/
562+
Type typeForCppMangling()
563+
{
564+
__gshared Type[char*] cachedTypes;
565+
auto elementType = mtype.next;
566+
567+
if (auto type = elementType.deco in cachedTypes)
568+
return *type;
569+
570+
auto sd = new StructDeclaration(loc, Id.__dslice, false);
571+
sd.parent = dsliceTemplateInstance();
572+
573+
auto type = new TypeStruct(sd).typeSemantic(loc, sc);
574+
return cachedTypes[elementType.deco] = type;
575+
}
576+
500577
Type tn = mtype.next.typeSemantic(loc, sc);
501578
Type tbn = tn.toBasetype();
502579
switch (tbn.ty)
@@ -523,6 +600,8 @@ private extern (C++) final class TypeSemanticVisitor : Visitor
523600
}
524601
mtype.next = tn;
525602
mtype.transitive();
603+
mtype.typeForCppMangling = typeForCppMangling();
604+
526605
result = merge(mtype);
527606
}
528607

test/compilable/cppmangle.d

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,28 @@ version (Posix) // all non-Windows machines
450450
static assert(test37!int.mangleof == "_ZN5SPACE6test37IiEEiv");
451451
}
452452

453+
/*****************************************/
454+
// https://issues.dlang.org/show_bug.cgi?id=18716
455+
456+
struct Test18716 {}
457+
struct __dslice(T) {}
458+
459+
extern(C++) void test18716a(char[]);
460+
extern(C++) void test18716b(__dslice!char);
461+
extern(C++) void test18716c(const(char)[]);
462+
extern(C++) void test18716d(Test18716[]);
463+
464+
void test18716()
465+
{
466+
test18716a("foo".dup);
467+
test18716c("foo");
468+
test18716d([Test18716()]);
469+
}
470+
471+
version (Posix)
472+
{
473+
static assert(test18716a.mangleof == "_Z10test18716a8__dsliceIcE");
474+
static assert(test18716b.mangleof == "_Z10test18716b8__dsliceIcE");
475+
static assert(test18716c.mangleof == "_Z10test18716c8__dsliceIKcE");
476+
static assert(test18716d.mangleof == "_Z10test18716d8__dsliceI9Test18716E");
477+
}

0 commit comments

Comments
 (0)