695 changes: 593 additions & 102 deletions src/dmd/cppmangle.d

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions test/compilable/cppmangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ extern (C++, std)
uint foof();
}

struct vector (T);

struct test18957 {}
}

Expand Down Expand Up @@ -771,3 +773,165 @@ version(Windows)
"??$test19043b@U?$test19043@$$CBD@@@@YAXU?$test19043@$$CBD@@@Z");
}

// https://issues.dlang.org/show_bug.cgi?id=16479
// Missing substitution while mangling C++ template parameter for functions
version (Posix) extern (C++)
{
// Make sure aliases are still resolved
alias Alias16479 = int;
Alias16479 func16479_0 (FuncT1) (FuncT1, Alias16479);
static assert(func16479_0!(int).mangleof == `_Z11func16479_0IiEiT_i`);
Geod24 marked this conversation as resolved.
Show resolved Hide resolved

// Simple substitution on return type
FuncT1* func16479_1 (FuncT1) ();
static assert(func16479_1!(int).mangleof == `_Z11func16479_1IiEPT_v`);

// Simple substitution on parameter
void func16479_2 (FuncT1) (FuncT1);
static assert(func16479_2!(int).mangleof == `_Z11func16479_2IiEvT_`);

// Make sure component substition is prefered over template parameter
FuncT1* func16479_3 (FuncT1) (FuncT1);
static assert(func16479_3!(int).mangleof == `_Z11func16479_3IiEPT_S0_`);

struct Array16479 (Arg) { Arg* data; }
struct Array16479_2 (Arg, int Size) { Arg[Size] data; }
struct Value16479 (int Value1, int Value2) { int data; }

// Make sure template parameter substitution happens on templated return
Array16479!(FuncT2) func16479_4 (FuncT1, FuncT2) (FuncT1);
static assert(func16479_4!(int, float).mangleof
== `_Z11func16479_4IifE10Array16479IT0_ET_`);

// Make sure template parameter substitution happens with values
Value16479!(Value2, Value1)* func16479_5 (int Value1, int Value2) ();
static assert(func16479_5!(1, 1).mangleof
== `_Z11func16479_5ILi1ELi1EEP10Value16479IXT0_EXT_EEv`);

// But make sure it's not substituting *too many* values
Value16479!(1, 1)* func16479_6 (int Value1, int Value2) ();
static assert(func16479_6!(1, 1).mangleof
== `_Z11func16479_6ILi1ELi1EEP10Value16479ILi1ELi1EEv`);

// Or too many types
Array16479!(int) func16479_7 (FuncT1, FuncT2) (FuncT1);
static assert(func16479_7!(int, int).mangleof
== `_Z11func16479_7IiiE10Array16479IiET_`);

// Also must check the parameters for template param substitution
void func16479_8 (FuncT1) (Array16479!(FuncT1));
static assert(func16479_8!(int).mangleof
== `_Z11func16479_8IiEv10Array16479IT_E`);

// And non-substitution
void func16479_9 (FuncT1) (Array16479!(int));
static assert(func16479_9!(int).mangleof
== `_Z11func16479_9IiEv10Array16479IiE`);

// Now let's have a bit of fun with alias parameters,
// starting with C functions
// TODO: Why is this mangled by g++:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this working?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-looking at this, it seems correct.
I'll do what Iain requested in a few hours and will update the comment.

/*
extern "C"
{
void externC16479 (int);
}
template<void (*Print)(int)>
void func16479_10 ();
void foo () { func16479_10<externC16479>(); }
*/
extern (C) void externC16479 (int);
void func16479_10 (alias Print) ();
static assert(func16479_10!(externC16479).mangleof
== `_Z12func16479_10IXadL_Z12externC16479EEEvv`);

/**
* Let's not exclude C++ functions
* Note:
* Passing a function as template parameter has an implicit
* `&` operator prepended to it, so the following code:
* ---
* void CPPPrinter16479(const char*);
* template<void (*Print)(const char*)> void func16479_11 ();
* void foo () { func16479_11<CPPPrinter16479>(); }
* ---
* Gets mangled as `func16479_11<&CPPPrinter16479>()` would,
* which means the expression part of the template argument is
* mangled as `XadL_Z[...]E` not `XL_Z[...]E`
* (expressions always begin with a code anyway).
*/
extern(C++) void CPPPrinter16479(const(char)*);
extern(C++, Namespace16479) void CPPPrinterNS16479(const(char)*);
void func16479_11 (alias Print) ();
static assert(func16479_11!(CPPPrinter16479).mangleof
== `_Z12func16479_11IXadL_Z15CPPPrinter16479PKcEEEvv`);
static assert(func16479_11!(CPPPrinterNS16479).mangleof
== `_Z12func16479_11IXadL_ZN14Namespace1647917CPPPrinterNS16479EPKcEEEvv`);

// Functions are fine, but templates are finer
// ---
// template<template<typename, int> class Container, typename T, int Val>
// Container<T, Val> func16479_12 ();
// ---
Container!(T, Val) func16479_12 (alias Container, T, int Val) ();
static assert(func16479_12!(Array16479_2, int, 42).mangleof
== `_Z12func16479_12I12Array16479_2iLi42EET_IT0_XT1_EEv`);

// Substitution needs to happen on the most specialized type
// Iow, `ref T identity (T) (ref T v);` should be mangled as
// `_Z8identityIiET_*S1_*`, not as `_Z8identityIiET_*RS0_*`
ref FuncT1 func16479_13_1 (FuncT1) (ref FuncT1);
FuncT1* func16479_13_2 (FuncT1) (FuncT1*);
void func16479_13_3 (FuncT1) (FuncT1*, FuncT1*);
FuncT1** func16479_13_4 (FuncT1) (FuncT1*, FuncT1);
FuncT1 func16479_13_5 (FuncT1) (FuncT1*, FuncT1**);
static assert(func16479_13_1!(int).mangleof == `_Z14func16479_13_1IiERT_S1_`);
static assert(func16479_13_2!(float).mangleof == `_Z14func16479_13_2IfEPT_S1_`);
static assert(func16479_13_3!(int).mangleof == `_Z14func16479_13_3IiEvPT_S1_`);
static assert(func16479_13_4!(int).mangleof == `_Z14func16479_13_4IiEPPT_S1_S0_`);
static assert(func16479_13_5!(int).mangleof == `_Z14func16479_13_5IiET_PS0_PS1_`);

// Opaque types result in a slightly different AST
vector!T* func16479_14 (T) (T v);
static assert(func16479_14!(int).mangleof == `_Z12func16479_14IiEPSt6vectorIT_ES1_`);

struct Foo16479_15 (T);
struct Baguette16479_15 (T);
struct Bar16479_15 (T);
struct FooBar16479_15 (A, B);
void inst16479_15_2 (A, B) ();
void inst16479_15_3 (A, B, C) ();

static assert(inst16479_15_2!(Bar16479_15!int, int).mangleof
== `_Z14inst16479_15_2I11Bar16479_15IiEiEvv`);
static assert(inst16479_15_2!(int, Bar16479_15!int).mangleof
== `_Z14inst16479_15_2Ii11Bar16479_15IiEEvv`);
static assert(inst16479_15_2!(Bar16479_15!int, FooBar16479_15!(Bar16479_15!int, Foo16479_15!(Bar16479_15!(Foo16479_15!int)))).mangleof
== `_Z14inst16479_15_2I11Bar16479_15IiE14FooBar16479_15IS1_11Foo16479_15IS0_IS3_IiEEEEEvv`);
static assert(inst16479_15_3!(int, Bar16479_15!int, FooBar16479_15!(Bar16479_15!int, Foo16479_15!(Bar16479_15!(Foo16479_15!int)))).mangleof
== `_Z14inst16479_15_3Ii11Bar16479_15IiE14FooBar16479_15IS1_11Foo16479_15IS0_IS3_IiEEEEEvv`);

static import cppmangle2;
cppmangle2.Struct18922* func16479_16_1 (T) (T*);
static assert(func16479_16_1!int.mangleof == `_Z14func16479_16_1IiEPN14Namespace1892211Struct18922EPT_`);
T* func16479_16_2 (T) (T*);
static assert(func16479_16_2!int.mangleof == `_Z14func16479_16_2IiEPT_S1_`);
static assert(func16479_16_2!(cppmangle2.vector!int).mangleof == `_Z14func16479_16_2ISt6vectorIiEEPT_S3_`);
static assert(func16479_16_2!(cppmangle2.vector!int).mangleof
== func16479_16_2!(cppmangle2.vector!int).mangleof);
cppmangle2.vector!T* func16479_16_3 (T) (T*);
static assert(func16479_16_3!int.mangleof == `_Z14func16479_16_3IiEPSt6vectorIiEPT_`);

extern(C++, `fakestd`) {
extern (C++, `__1`) {
struct allocator16479 (T);
struct vector16479(T, alloc = allocator16479!T);
}
}
vector16479!(T, allocator16479!T)* func16479_17_1(T)();
vector16479!(T)* func16479_17_2(T)();
static assert(func16479_17_1!int.mangleof == `_Z14func16479_17_1IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
static assert(func16479_17_2!int.mangleof == `_Z14func16479_17_2IiEPN7fakestd3__111vector16479IT_NS1_14allocator16479IS3_EEEEv`);
}
5 changes: 5 additions & 0 deletions test/compilable/cppmangle2.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ extern(C++, Namespace18922)
{
struct Struct18922 { int i; }
}

extern(C++, std)
{
struct vector (T);
}
59 changes: 59 additions & 0 deletions test/runnable/cpp_stdlib.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// DISABLED: win32 win64 osx32
// EXTRA_CPP_SOURCES: cpp_stdlib.cpp
// CXXFLAGS: -std=c++11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this means that we now have mixed C++98 and C++11 support in the mangler, and no way to choose (on the D side) whether we explicitly want one or the other?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I originally went full C++11 but throwle failed to link.
Do you have any document about the mangling changes between 98 and 11 ?
I didn't find anything relevant and figured this was too big of a matter to address in this PR.

import core.stdc.stdio;

// Disabled on windows because it needs bindings
// Disabled on osx32 because size_t is not properly mangled

version (CppRuntime_Clang)
{
extern(C++, `std`, `__1`)
{
struct allocator(T);
struct vector (T, A = allocator!T);
struct array (T, size_t N);
}
}
else
{
extern(C++, `std`)
{
struct allocator(T);
struct vector (T, A = allocator!T);
struct array (T, size_t N);
}
}

extern(C++):

ref T identity (T) (ref T v);
T** identityPP (T) (T** v);
vector!T* getVector (T) (size_t length, const T* ptr);
array!(T, N)* getArray(T, size_t N) (const T* ptr);

void main ()
{
int i = 42;
float f = 21.0f;

int* pi = &i;
float* pf = &f;

assert(42 == identity(i));
assert(21.0f == identity(f));
assert(&pi == identityPP(&pi));
assert(&pf == identityPP(&pf));

auto vi = getVector(1, &i);
auto vf = getVector(3, [f, f, f].ptr);
assert(vi !is null);
assert(vf !is null);

auto ai = getArray!(int, 4)([2012, 10, 11, 42].ptr);
auto af = getArray!(float, 4)([42.0f, 21.0f, 14.0f, 1957.0f].ptr);
assert(ai !is null);
assert(af !is null);

printf("Success\n");
}
53 changes: 53 additions & 0 deletions test/runnable/extra-files/cpp_stdlib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <array>
#include <string>
#include <vector>

template<typename T>
T& identity (T& v) { return v; }
template<typename T>
T** identityPP (T** v) { return v; }

template<typename T>
std::vector<T>* getVector(size_t len, const T* ptr)
{
std::vector<T>* ret = new std::vector<T>(len);
for (size_t i = 0; i < len; ++i)
(*ret)[i] = ptr[i];
return ret;
}

std::string* getString(int len, const char* ptr)
{
return new std::string(ptr, len);
}

template<typename T, size_t N>
std::array<T, N>* getArray(const T* ptr)
{
std::array<T, N>* ret = new std::array<T, N>();
for (size_t x = 0; x < N; ++x)
(*ret)[x] = ptr[x];
return ret;
}

// This function should never be called
void instantiate ()
{
int i;
float f;
int* pi;
float* pf;

identityPP(&pi);
identityPP(&pf);
identity(i);
identity(f);

getVector<int>(0, 0);
getVector<float>(0, 0);
getVector<std::vector<float> >(0, 0);

getArray<int, 4>(0);
getArray<float, 4>(0);
//getArray<Foo<int, 42>, 4>(0);
}
22 changes: 14 additions & 8 deletions test/tools/d_do_test.d
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct TestArgs
bool link;
string executeArgs;
string dflags;
string cxxflags;
string[] sources;
string[] compiledImports;
string[] cppSources;
Expand Down Expand Up @@ -269,6 +270,7 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_
testArgs.compiledImports ~= input_dir ~ "/" ~ s;
}

findTestParameter(envData, file, "CXXFLAGS", testArgs.cxxflags);
string extraCppSourcesStr;
findTestParameter(envData, file, "EXTRA_CPP_SOURCES", extraCppSourcesStr);
testArgs.cppSources = [];
Expand Down Expand Up @@ -434,7 +436,9 @@ unittest
== `fail_compilation\diag.d(2): Error: fail_compilation\imports\fail.d must be imported`);
}

bool collectExtraSources (in string input_dir, in string output_dir, in string[] extraSources, ref string[] sources, in EnvData envData, in string compiler)
bool collectExtraSources (in string input_dir, in string output_dir, in string[] extraSources,
ref string[] sources, in EnvData envData, in string compiler,
const(char)[] cxxflags)
{
foreach (cur; extraSources)
{
Expand All @@ -460,6 +464,8 @@ bool collectExtraSources (in string input_dir, in string output_dir, in string[]
{
command ~= " -m"~envData.model~" -c "~curSrc~" -o "~curObj;
}
if (compiler == "c++" && cxxflags)
command ~= " " ~ cxxflags;

auto rc = system(command);
if(rc)
Expand Down Expand Up @@ -618,8 +624,11 @@ int tryMain(string[] args)
}
}

if (testArgs.disabledPlatforms.any!(a => envData.os.chain(envData.model).canFind(a)))
testArgs.disabled = true;

//prepare cpp extra sources
if (testArgs.cppSources.length)
if (!testArgs.disabled && testArgs.cppSources.length)
{
switch (envData.compiler)
{
Expand All @@ -637,11 +646,11 @@ int tryMain(string[] args)
writeln("unknown compiler: "~envData.compiler);
return 1;
}
if (!collectExtraSources(input_dir, output_dir, testArgs.cppSources, testArgs.sources, envData, envData.ccompiler))
if (!collectExtraSources(input_dir, output_dir, testArgs.cppSources, testArgs.sources, envData, envData.ccompiler, testArgs.cxxflags))
return 1;
}
//prepare objc extra sources
if (!collectExtraSources(input_dir, output_dir, testArgs.objcSources, testArgs.sources, envData, "clang"))
if (!testArgs.disabled && !collectExtraSources(input_dir, output_dir, testArgs.objcSources, testArgs.sources, envData, "clang", null))
return 1;

writef(" ... %-30s %s%s(%s)",
Expand All @@ -661,11 +670,8 @@ int tryMain(string[] args)
}

// allows partial matching, e.g. win for both win32 and win64
if (testArgs.disabledPlatforms.any!(a => envData.os.chain(envData.model).canFind(a)))
{
testArgs.disabled = true;
if (testArgs.disabled)
writefln("!!! [DISABLED on %s]", envData.os);
}
else
write("\n");

Expand Down