mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
fix Issue 22323: Link error for virtual destructor of C++ class in DLL
The new test test/dshell/dll_cxx.d is based on test/dshell/dll.d, but builds the DLL/SO from C++ instead of D.
This commit is contained in:
parent
454b526340
commit
1e6217535f
7 changed files with 281 additions and 13 deletions
2
.github/workflows/runnable_cxx.yml
vendored
2
.github/workflows/runnable_cxx.yml
vendored
|
@ -293,7 +293,7 @@ jobs:
|
|||
########################################
|
||||
- name: Run C++ test suite
|
||||
run: |
|
||||
./dmd/test/run.d --environment runnable_cxx MODEL=64
|
||||
./dmd/test/run.d --environment runnable_cxx dshell/dll_cxx.d MODEL=64
|
||||
#if [ ${{ matrix.compiler }} == "g++" ]; then
|
||||
# ./dmd/test/run.d clean
|
||||
# ./dmd/test/run.d runnable_cxx MODEL=32
|
||||
|
|
|
@ -1079,17 +1079,16 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara
|
|||
auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, dtor.storage_class);
|
||||
auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor);
|
||||
func.type = ftype;
|
||||
if (dtor.fbody)
|
||||
{
|
||||
const loc = dtor.loc;
|
||||
auto stmts = new Statements;
|
||||
auto call = new CallExp(loc, dtor, null);
|
||||
call.directcall = true;
|
||||
stmts.push(new ExpStatement(loc, call));
|
||||
stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
|
||||
func.fbody = new CompoundStatement(loc, stmts);
|
||||
func.generated = true;
|
||||
}
|
||||
|
||||
// Always generate the function with body, because it is not exported from DLLs.
|
||||
const loc = dtor.loc;
|
||||
auto stmts = new Statements;
|
||||
auto call = new CallExp(loc, dtor, null);
|
||||
call.directcall = true;
|
||||
stmts.push(new ExpStatement(loc, call));
|
||||
stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
|
||||
func.fbody = new CompoundStatement(loc, stmts);
|
||||
func.generated = true;
|
||||
|
||||
auto sc2 = sc.push();
|
||||
sc2.stc &= ~STC.static_; // not a static destructor
|
||||
|
|
|
@ -501,7 +501,7 @@ Symbol *toImport(Symbol *sym)
|
|||
}
|
||||
else
|
||||
{
|
||||
idlen = sprintf(id,(target.os == Target.OS.Windows && target.is64bit) ? "__imp_%s" : "_imp__%s",n);
|
||||
idlen = sprintf(id,(target.os == Target.OS.Windows && target.is64bit) ? "__imp_%s" : (sym.Stype.Tmangle == mTYman_cpp) ? "_imp_%s" : "_imp__%s",n);
|
||||
}
|
||||
auto t = type_alloc(TYnptr | mTYconst);
|
||||
t.Tnext = sym.Stype;
|
||||
|
|
64
test/dshell/dll_cxx.d
Normal file
64
test/dshell/dll_cxx.d
Normal file
|
@ -0,0 +1,64 @@
|
|||
import dshell;
|
||||
|
||||
import std.stdio;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Only run this test, if CC has been set.
|
||||
if (Vars.CC.empty)
|
||||
return DISABLED;
|
||||
|
||||
version (Windows)
|
||||
if (environment.get("C_RUNTIME", "") == "mingw")
|
||||
return DISABLED;
|
||||
|
||||
version (FreeBSD)
|
||||
if (Vars.MODEL == "32")
|
||||
return DISABLED;
|
||||
|
||||
version (OSX)
|
||||
Vars.set(`SOEXT`, `.dylib`);
|
||||
|
||||
Vars.set(`SRC`, `$EXTRA_FILES${SEP}dll_cxx`);
|
||||
Vars.set(`EXE_NAME`, `$OUTPUT_BASE${SEP}testdll$EXE`);
|
||||
Vars.set(`DLL`, `$OUTPUT_BASE${SEP}mydll$SOEXT`);
|
||||
|
||||
string[] dllCmd = [Vars.CC];
|
||||
string mainExtra;
|
||||
version (Windows)
|
||||
{
|
||||
Vars.set(`DLL_LIB`, `$OUTPUT_BASE${SEP}mydll.lib`);
|
||||
if (Vars.MODEL == "32")
|
||||
{
|
||||
// CC should be dmc for win32.
|
||||
dllCmd ~= [`-mn`, `-L/implib:` ~ Vars.DLL_LIB, `-WD`, `-o` ~ Vars.DLL, `kernel32.lib`, `user32.lib`];
|
||||
mainExtra = `$DLL_LIB`;
|
||||
}
|
||||
else
|
||||
{
|
||||
// CC should be cl for win32mscoff.
|
||||
dllCmd ~= [`/LD`, `/nologo`, `/Fe` ~ Vars.DLL];
|
||||
mainExtra = `$DLL_LIB`;
|
||||
}
|
||||
}
|
||||
else version(OSX)
|
||||
{
|
||||
dllCmd ~= [`-dynamiclib`, `-fPIC`, `-o`, Vars.DLL, `-lstdc++`];
|
||||
mainExtra = `-fPIC -L-L$OUTPUT_BASE -L$DLL -L-lstdc++ -L--no-demangle`;
|
||||
}
|
||||
else
|
||||
{
|
||||
dllCmd ~= [`-shared`, `-fPIC`, `-o`, Vars.DLL];
|
||||
mainExtra = `-fPIC -L-L$OUTPUT_BASE -L$DLL -L-lstdc++ -L--no-demangle`;
|
||||
}
|
||||
|
||||
dllCmd ~= Vars.SRC ~ Vars.SEP ~ `mydll.cpp`;
|
||||
// The arguments have to be passed as an array, because run would replace '/' with '\\' otherwise.
|
||||
run(dllCmd);
|
||||
|
||||
run(`$DMD -m$MODEL -I$SRC -g -od=$OUTPUT_BASE -of=$EXE_NAME $SRC/testdll.d $SRC/cppnew.d ` ~ mainExtra);
|
||||
|
||||
run(`$EXE_NAME`, stdout, stderr, [`LD_LIBRARY_PATH`: Vars.OUTPUT_BASE]);
|
||||
|
||||
return 0;
|
||||
}
|
81
test/dshell/extra-files/dll_cxx/cppnew.d
Normal file
81
test/dshell/extra-files/dll_cxx/cppnew.d
Normal file
|
@ -0,0 +1,81 @@
|
|||
module cppnew;
|
||||
|
||||
/* This module contains copies from core.stdcpp.new_.d, but with
|
||||
* modifications for DMC. */
|
||||
|
||||
T* cpp_new(T, Args...)(auto ref Args args) if (!is(T == class))
|
||||
{
|
||||
import core.lifetime : emplace, forward;
|
||||
|
||||
T* mem = cast(T*)__cpp_new(T.sizeof);
|
||||
return mem.emplace(forward!args);
|
||||
}
|
||||
|
||||
T cpp_new(T, Args...)(auto ref Args args) if (is(T == class))
|
||||
{
|
||||
import core.lifetime : emplace, forward;
|
||||
|
||||
T mem = cast(T)__cpp_new(__traits(classInstanceSize, T));
|
||||
return mem.emplace(forward!args);
|
||||
}
|
||||
|
||||
void cpp_delete(T)(T* ptr) if (!is(T == class))
|
||||
{
|
||||
destroy!false(*ptr);
|
||||
__cpp_delete(ptr);
|
||||
}
|
||||
|
||||
void cpp_delete(T)(T instance) if (is(T == class))
|
||||
{
|
||||
destroy!false(instance);
|
||||
__cpp_delete(cast(void*) instance);
|
||||
}
|
||||
|
||||
/// Binding for ::operator new(std::size_t count)
|
||||
pragma(mangle, __new_mangle)
|
||||
extern(C++) void* __cpp_new(size_t count);
|
||||
|
||||
/// Binding for ::operator delete(void* ptr)
|
||||
pragma(mangle, __delete_mangle)
|
||||
extern(C++) void __cpp_delete(void* ptr);
|
||||
|
||||
// we have to hard-code the mangling for the global new/delete operators
|
||||
version (CppRuntime_Microsoft)
|
||||
{
|
||||
version (D_LP64)
|
||||
{
|
||||
enum __new_mangle = "??2@YAPEAX_K@Z";
|
||||
enum __delete_mangle = "??3@YAXPEAX@Z";
|
||||
}
|
||||
else
|
||||
{
|
||||
enum __new_mangle = "??2@YAPAXI@Z";
|
||||
enum __delete_mangle = "??3@YAXPAX@Z";
|
||||
}
|
||||
}
|
||||
else version (CppRuntime_DigitalMars)
|
||||
{
|
||||
version (D_LP64)
|
||||
{
|
||||
enum __new_mangle = "??2@YAPEAX_K@Z";
|
||||
enum __delete_mangle = "??3@YAXPEAX@Z";
|
||||
}
|
||||
else
|
||||
{
|
||||
enum __new_mangle = "??2@YAPAXI@Z";
|
||||
enum __delete_mangle = "??3@YAXPAX@Z";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
version (D_LP64)
|
||||
{
|
||||
enum __new_mangle = "_Znwm";
|
||||
enum __delete_mangle = "_ZdlPv";
|
||||
}
|
||||
else
|
||||
{
|
||||
enum __new_mangle = "_Znwj";
|
||||
enum __delete_mangle = "_ZdlPv";
|
||||
}
|
||||
}
|
51
test/dshell/extra-files/dll_cxx/mydll.cpp
Normal file
51
test/dshell/extra-files/dll_cxx/mydll.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#ifdef _WIN32
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT
|
||||
#endif
|
||||
|
||||
class EXPORT C22323
|
||||
{
|
||||
public:
|
||||
C22323();
|
||||
virtual ~C22323();
|
||||
|
||||
static int ctorCount;
|
||||
static int dtorCount;
|
||||
};
|
||||
|
||||
int C22323::ctorCount;
|
||||
int C22323::dtorCount;
|
||||
|
||||
C22323::C22323()
|
||||
{
|
||||
ctorCount++;
|
||||
}
|
||||
|
||||
C22323::~C22323()
|
||||
{
|
||||
dtorCount++;
|
||||
}
|
||||
|
||||
struct EXPORT S22323
|
||||
{
|
||||
public:
|
||||
S22323(int dummy);
|
||||
~S22323();
|
||||
|
||||
static int ctorCount;
|
||||
static int dtorCount;
|
||||
};
|
||||
|
||||
int S22323::ctorCount;
|
||||
int S22323::dtorCount;
|
||||
|
||||
S22323::S22323(int dummy)
|
||||
{
|
||||
ctorCount++;
|
||||
}
|
||||
|
||||
S22323::~S22323()
|
||||
{
|
||||
dtorCount++;
|
||||
}
|
73
test/dshell/extra-files/dll_cxx/testdll.d
Normal file
73
test/dshell/extra-files/dll_cxx/testdll.d
Normal file
|
@ -0,0 +1,73 @@
|
|||
version(Windows)
|
||||
enum EXPORT = "export ";
|
||||
else
|
||||
enum EXPORT = "";
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=22323
|
||||
extern(C++) class C22323
|
||||
{
|
||||
this();
|
||||
~this();
|
||||
|
||||
mixin(EXPORT ~ q{static extern __gshared int ctorCount;});
|
||||
mixin(EXPORT ~ q{static extern __gshared int dtorCount;});
|
||||
}
|
||||
|
||||
extern(C++) struct S22323
|
||||
{
|
||||
this(int dummy);
|
||||
~this();
|
||||
|
||||
mixin(EXPORT ~ q{static extern __gshared int ctorCount;});
|
||||
mixin(EXPORT ~ q{static extern __gshared int dtorCount;});
|
||||
}
|
||||
|
||||
void test22323()
|
||||
{
|
||||
import cppnew;
|
||||
|
||||
assert(C22323.ctorCount == 0);
|
||||
assert(C22323.dtorCount == 0);
|
||||
C22323 o = cpp_new!C22323;
|
||||
assert(C22323.ctorCount == 1);
|
||||
assert(C22323.dtorCount == 0);
|
||||
cpp_delete(o);
|
||||
assert(C22323.ctorCount == 1);
|
||||
assert(C22323.dtorCount == 1);
|
||||
|
||||
o = new C22323;
|
||||
assert(C22323.ctorCount == 2);
|
||||
assert(C22323.dtorCount == 1);
|
||||
o.destroy;
|
||||
assert(C22323.ctorCount == 2);
|
||||
assert(C22323.dtorCount == 2);
|
||||
|
||||
assert(S22323.ctorCount == 0);
|
||||
assert(S22323.dtorCount == 0);
|
||||
{
|
||||
S22323 s = S22323(0);
|
||||
assert(S22323.ctorCount == 1);
|
||||
assert(S22323.dtorCount == 0);
|
||||
}
|
||||
assert(S22323.ctorCount == 1);
|
||||
assert(S22323.dtorCount == 1);
|
||||
|
||||
S22323 *s = cpp_new!S22323(0);
|
||||
assert(S22323.ctorCount == 2);
|
||||
assert(S22323.dtorCount == 1);
|
||||
cpp_delete(s);
|
||||
assert(S22323.ctorCount == 2);
|
||||
assert(S22323.dtorCount == 2);
|
||||
|
||||
s = new S22323(0);
|
||||
assert(S22323.ctorCount == 3);
|
||||
assert(S22323.dtorCount == 2);
|
||||
(*s).destroy();
|
||||
assert(S22323.ctorCount == 3);
|
||||
assert(S22323.dtorCount == 3);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
test22323();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue