Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions changelog/dslices-cpp-mangling.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Added support for D arrays in C++ functions

D arrays don't have any corresponding type in C++. This change adds support for
mangling a D array as a templated struct with the special name: `__dslice`.
Under the hood, a D array is represented as a struct, containing the length of
the array and a pointer to the data:

---
struct DArray(T)
{
size_t length;
T* ptr;
}
---

Any D array in the form of, `T[]`, when used in a C++ function, will be mangled
as the following struct:

---
struct __dslice(T);
---

For example, an array of ints, `int[]`, will be mangled as `__dslice!int` when
used in a C++ function. This allows to declare C++ functions that accepts D
arrays and can be accessed to C++ if the right struct declaration is present
on the C++ side.

On the D side:

---
extern(C++) void foo(const(char)[] str);

void main()
{
foo("bar");
}
---

On the C++ side:

$(CPPCODE
#include <stdio.h>

template<typename T> struct __dslice
{
size_t length;
T* ptr;
};

void foo(__dslice<const char> array)
{
printf("%.*s\n", (int) array.length, array.ptr);
}
)

Since the `__dslice` struct is ABI compatible with D arrays, it works perfectly
fine to pass a D array to a C++ function taking a `__dslice`.
7 changes: 6 additions & 1 deletion src/dmd/cppmangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -1970,7 +1970,12 @@ extern(C++):

override void visit(Type t)
{
error(t);
auto typeForMangling = t.typeForMangling(LINK.cpp);

if (typeForMangling is t)
error(t);
else
typeForMangling.accept(this);
}

void visit(Tuple t)
Expand Down
9 changes: 7 additions & 2 deletions src/dmd/cppmanglewin.d
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ public:

override void visit(Type type)
{
auto typeForMangling = type.typeForMangling(LINK.cpp);

if (typeForMangling !is type)
return typeForMangling.accept(this);

if (checkImmutableShared(type))
return;

Expand Down Expand Up @@ -1037,7 +1042,7 @@ private:
// ::= $0<encoded integral number>
//printf("mangleIdent('%s')\n", sym.toChars());
Dsymbol p = sym;
if (p.toParent() && p.toParent().isTemplateInstance())
if (p && p.toParent() && p.toParent().isTemplateInstance())
{
p = p.toParent();
}
Expand All @@ -1048,7 +1053,7 @@ private:
for (auto ns = p.cppnamespace; ns !is null; ns = ns.cppnamespace)
mangleName(ns, dont_use_back_reference);
p = p.toParent();
if (p.toParent() && p.toParent().isTemplateInstance())
if (p && p.toParent() && p.toParent().isTemplateInstance())
{
p = p.toParent();
}
Expand Down
5 changes: 5 additions & 0 deletions src/dmd/dmangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,11 @@ extern (C++) const(char)* mangleExact(FuncDeclaration fd)

extern (C++) void mangleToBuffer(Type t, OutBuffer* buf)
{
auto typeForMangling = t.typeForMangling(LINK.d);

if (t !is typeForMangling)
return mangleToBuffer(typeForMangling, buf);

if (t.deco)
buf.writestring(t.deco);
else
Expand Down
1 change: 1 addition & 0 deletions src/dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ immutable Msgtable[] msgtable =
{ "basic_ostream" },
{ "basic_iostream" },
{ "char_traits" },
{ "__dslice" },

// Compiler recognized UDA's
{ "udaGNUAbiTag", "gnuAbiTag" },
Expand Down
103 changes: 103 additions & 0 deletions src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,24 @@ extern (C++) abstract class Type : ASTNode
return DYNCAST.type;
}

/**
* Returns the type the receiver should be treated as during mangling.
*
* This allows for a type to be treated as a different type during mangling.
* This is useful, for example, when interfacing with C++, for D types that
* don't have a corresponding C++ type. This can allow `int[]` to be
* mangled as `__dslice!int` during C++ mangling.
*
* Params:
* linkage = the type of mangling that is requested
*
* Returns: the type the receiver should be treated as during mangling
*/
Type typeForMangling(LINK linkage)
{
return this;
}

/*******************************
* Covariant means that 'this' can substitute for 't',
* i.e. a pure function is a match for an impure type.
Expand Down Expand Up @@ -3728,6 +3746,9 @@ extern (C++) final class TypeSArray : TypeArray
*/
extern (C++) final class TypeDArray : TypeArray
{
/// Mangle D array as this type when mangling for C++.
private Type typeForCppMangling;

extern (D) this(Type t)
{
super(Tarray, t);
Expand All @@ -3752,6 +3773,88 @@ extern (C++) final class TypeDArray : TypeArray
return t;
}

override Type typeForMangling(LINK linkage)
{
/**
* Returns a template declaration corresponding to the following code:
*
* ---
* template __dslice(T) {}
* ---
*
* Returns: a template declaration corresponding to the above code
*
* See_Also: `typeForCppMangling`
*/
static TemplateDeclaration dsliceTemplateDeclaration()
{
__gshared TemplateDeclaration td;

if (td)
return td;

auto ttp = new TemplateTypeParameter(Loc.initial, Id.p, null, null);
auto parameters = new TemplateParameters(ttp);

return td = new TemplateDeclaration(Loc.initial, Id.__dslice, parameters,
null, null);
}

/**
* Returns a template instantiation of the template declaration returned
* by `dsliceTemplateDeclaration`.
*
* The template is instantiated with the element type of this array. For
* an array of ints it would correspond to the following D code:
* `__dslice!int`.
*
* Returns: a template instance
*
* See_Also: `dsliceTemplateDeclaration`
* See_Also: `typeForCppMangling`
*/
TemplateInstance dsliceTemplateInstance()
{
auto tiargs = new Objects(next);
auto ti = new TemplateInstance(Loc.initial, Id.__dslice, tiargs);
ti.tempdecl = dsliceTemplateDeclaration();

return ti;
}

/**
* Returns the type that this array type should be treated as when it's
* mangled as a C++ type.
*
* 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`.
*
* Returns: the type that this should be treated as when mangling as a
* C++ type
*
* See_Also: `dsliceTemplateInstance`
*/
Type typeForCppMangling()
{
__gshared Type[char*] cachedTypes;
auto elementType = next;

if (auto type = elementType.deco in cachedTypes)
return *type;

auto sd = new StructDeclaration(Loc.initial, Id.__dslice, false);
sd.parent = dsliceTemplateInstance();

auto type = new TypeStruct(sd).typeSemantic(Loc.initial, null);
return cachedTypes[elementType.deco] = type;
}

return linkage == LINK.cpp ? typeForCppMangling() : this;
}

override d_uns64 size(const ref Loc loc) const
{
//printf("TypeDArray::size()\n");
Expand Down
6 changes: 6 additions & 0 deletions src/dmd/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ class Type : public ASTNode
bool equivalent(Type *t);
// kludge for template.isType()
DYNCAST dyncast() const { return DYNCAST_TYPE; }
virtual Type* typeForMangling(LINK linkage);
int covariant(Type *t, StorageClass *pstc = NULL, bool fix17349 = true);
const char *toChars() const;
char *toPrettyChars(bool QualifyTypes = false);
Expand Down Expand Up @@ -456,9 +457,14 @@ class TypeSArray : public TypeArray
// Dynamic array, no dimension
class TypeDArray : public TypeArray
{
private:
Type* typeForCppMangling;

public:

const char *kind();
Type *syntaxCopy();
Type* typeForMangling(LINK linkage);
d_uns64 size(const Loc &loc) /*const*/;
unsigned alignsize() /*const*/;
bool isString();
Expand Down
17 changes: 16 additions & 1 deletion src/dmd/root/array.d
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ public:

@disable this(this);

/**
* Convenience constructor to add the given elements to this array.
*
* Params:
* elements = elements to add to this array
*/
extern(D) this(T[] elements ...) nothrow
{
if (elements.length > 0)
reserve(elements.length);

foreach (e ; elements)
push(e);
}

~this() pure nothrow
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
Expand Down Expand Up @@ -383,7 +398,7 @@ unittest

unittest
{
auto arrayA = Array!int(0);
auto arrayA = Array!int(size_t(0));
int[3] buf = [10, 15, 20];
arrayA.pushSlice(buf);
assert(arrayA[] == buf[]);
Expand Down
12 changes: 12 additions & 0 deletions src/dmd/root/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,15 @@ struct Array
}
};

// 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.
template <typename T> struct __dslice
{
d_size_t length;
T* ptr;

__dslice() : length(0), ptr(NULL) {}
__dslice(size_t length, T* ptr) : length(length), ptr(ptr) {}
};

#define DSlice __dslice
35 changes: 35 additions & 0 deletions test/compilable/cppmangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -1279,3 +1279,38 @@ version (Win64) extern(C++)
extern(C++, struct) class DefaultClass20700_2 {}
static assert(test20700_4.mangleof == `?test20700_4@@YAXPEAU?$TStruct20700_2@PEAUDefaultClass20700_2@@VDefaultStruct20700_2@@@@@Z`);
}

/*****************************************/
// https://issues.dlang.org/show_bug.cgi?id=18716
// D slices as parameters in extern(C++) functions

struct Test18716 {}
struct __dslice(T) {}

extern(C++) void test18716a(char[]);
extern(C++) void test18716b(__dslice!char);
extern(C++) void test18716c(const(char)[]);
extern(C++) void test18716d(Test18716[]);

void test18716()
{
test18716a("foo".dup);
test18716c("foo");
test18716d([Test18716()]);
}

version (Posix)
{
static assert(test18716a.mangleof == "_Z10test18716a8__dsliceIcE");
static assert(test18716b.mangleof == "_Z10test18716b8__dsliceIcE");
static assert(test18716c.mangleof == "_Z10test18716c8__dsliceIKcE");
static assert(test18716d.mangleof == "_Z10test18716d8__dsliceI9Test18716E");
}

else version (Windows)
{
static assert(test18716a.mangleof == "?test18716a@@YAXU?$__dslice@D@@@Z");
static assert(test18716b.mangleof == "?test18716b@@YAXU?$__dslice@D@@@Z");
static assert(test18716c.mangleof == "?test18716c@@YAXU?$__dslice@$$CBD@@@Z");
static assert(test18716d.mangleof == "?test18716d@@YAXU?$__dslice@UTest18716@@@@@Z");
}
Loading