diff --git a/changelog/dslices-cpp-mangling.dd b/changelog/dslices-cpp-mangling.dd new file mode 100644 index 000000000000..e4193524b25b --- /dev/null +++ b/changelog/dslices-cpp-mangling.dd @@ -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`. diff --git a/src/dmd/cppmangle.d b/src/dmd/cppmangle.d index 1d2b7d94ac6d..d8b5f98a4818 100644 --- a/src/dmd/cppmangle.d +++ b/src/dmd/cppmangle.d @@ -1910,7 +1910,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) diff --git a/src/dmd/cppmanglewin.d b/src/dmd/cppmanglewin.d index 6bd26228742b..a351601f8067 100644 --- a/src/dmd/cppmanglewin.d +++ b/src/dmd/cppmanglewin.d @@ -126,6 +126,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; @@ -1036,7 +1041,7 @@ private: // ::= $0 //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(); } @@ -1047,7 +1052,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(); } diff --git a/src/dmd/dmangle.d b/src/dmd/dmangle.d index 7660afcac5d4..20c8d9c43491 100644 --- a/src/dmd/dmangle.d +++ b/src/dmd/dmangle.d @@ -1177,6 +1177,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 diff --git a/src/dmd/id.d b/src/dmd/id.d index 2adeb1081040..d0d9c838246a 100644 --- a/src/dmd/id.d +++ b/src/dmd/id.d @@ -463,6 +463,7 @@ immutable Msgtable[] msgtable = { "basic_ostream" }, { "basic_iostream" }, { "char_traits" }, + { "__dslice" }, // Compiler recognized UDA's { "udaSelector", "selector" }, diff --git a/src/dmd/mtype.d b/src/dmd/mtype.d index dc7cac7f9495..3b96cb04d13e 100644 --- a/src/dmd/mtype.d +++ b/src/dmd/mtype.d @@ -543,6 +543,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. @@ -3720,6 +3738,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); @@ -3744,6 +3765,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"); diff --git a/src/dmd/mtype.h b/src/dmd/mtype.h index 20a1ec4f1478..af4190870e8d 100644 --- a/src/dmd/mtype.h +++ b/src/dmd/mtype.h @@ -221,6 +221,7 @@ class Type : public ASTNode virtual Type *syntaxCopy(); bool equals(const RootObject *o) const; bool equivalent(Type *t); + virtual Type* typeForMangling(LINK linkage); // kludge for template.isType() DYNCAST dyncast() const { return DYNCAST_TYPE; } int covariant(Type *t, StorageClass *pstc = NULL, bool fix17349 = true); @@ -453,9 +454,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(); diff --git a/src/dmd/root/array.d b/src/dmd/root/array.d index dca7c3b78bf2..c9e4359e5b14 100644 --- a/src/dmd/root/array.d +++ b/src/dmd/root/array.d @@ -44,6 +44,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); diff --git a/src/dmd/root/array.h b/src/dmd/root/array.h index 2a1c71d2622d..18c788dead3d 100644 --- a/src/dmd/root/array.h +++ b/src/dmd/root/array.h @@ -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 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 diff --git a/test/compilable/cppmangle.d b/test/compilable/cppmangle.d index 1a84059a56e8..eaf778142a37 100644 --- a/test/compilable/cppmangle.d +++ b/test/compilable/cppmangle.d @@ -1105,6 +1105,40 @@ else version(Posix) static assert(test19278_var.mangleof == "_ZN5hello5world13test19278_varE"); } +/*****************************************/ +// 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"); +} /**************************************/ // https://issues.dlang.org/show_bug.cgi?id=18958 // Issue 18958 - extern(C++) wchar, dchar mangling not correct