Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.
Open
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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ endif

ifeq ($(OS),win32)
DISABLED_FAIL_TESTS += fail13939
else
DISABLED_TESTS += ldc_cpp_eh
endif

####
Expand Down
2 changes: 1 addition & 1 deletion d_do_test.d
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ bool collectExtraSources (in string input_dir, in string output_dir, in string[]
{
if (msc)
{
command ~= ` /c /nologo `~curSrc~` /Fo`~curObj;
command ~= ` /c /nologo /EHsc `~curSrc~` /Fo`~curObj;
}
else if (envData.os == "win32")
{
Expand Down
63 changes: 63 additions & 0 deletions runnable/extra-files/dexcept.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// D exceptions to be caught by C++

#pragma once

namespace D {

struct string
{
size_t length;
const char* ptr;
};

class Object;

extern "C" {
string _d_toString(Object* o);
}

class Object
{
void* __monitor;

virtual void _classinfo_data() = 0;

protected:
// don't call directly, ABI mismatch
Copy link
Member

Choose a reason for hiding this comment

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

Could you elaborate a bit on the ABI mismatch? I assumed ABI compliance between D and C++ would already be checked by dmd-testsuite...

Copy link
Member

Choose a reason for hiding this comment

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

Isn't this just duplicating the normal – extern(D)! – class definition in C++ and hoping that it somehow works?

Copy link
Member

Choose a reason for hiding this comment

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

If I understand rainers/druntime@9dce356 correctly, it allows you to catch a D exception in C++ by declaring a C++ type with the same class name as the native D exception in a D namespace (convention © Rainer ;)). That's why each thrown exception's type incl. base types are 'mangled' twice. Note that MSVC EH is based on strings specifying an exception's types (incl. base types).
The definition of the C++ type doesn't really matter, as long as the exception object isn't accessed; Rainer's test only uses toString(), for which he wrote a specific C++ implementation anyway (calling back into druntime, apparently due to ABI mismatches). By duplicating the D class definitions of Object, Throwable etc. I guess he wanted to pave the way for a catch-D-exceptions lib for C++.

As soon as clang supports extern "D" name mangling, we don't need this additional mangling for MSVC++ anymore. :D

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, that's what I gathered too – but what's way worse than the different mangling are the differences in object layout. I suppose you could manually "decode" and call the vtable entries on the C++ side, though, for a proper solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I meant this test as a show case how easy it is now to mix exception handling between D and C++. Catching C++ exception in D needs a few changes to the front end (allowing C++ classes as catch type) that Walter added only very recently, so I guess it'll need some time until this is in LDC. Mirroring the D definitions with a C++ definition is just the same as declaring a C++ class in D.

Unfortunately, Walter chose a different ABI than the C++ compiler for x86 (http://dlang.org/spec/abi.html), especially this is passed in EAX, not ECX as for __thiscall. That's why mapping virtual functions doesn't work without making the class extern(C++) in D. I don't think this should be done to the D exceptions.

The actual virtual functions are just for documentation here, just exposing the useful wrappers should be ok, too.

Writing the binding function _d_toString on the D side has the big advantage that D knows both sides of the ABI, so you don't have to dig into assembly. It could be generalized to call any virtual function by specifying the offset in the vtbl, though.

Copy link
Member

Choose a reason for hiding this comment

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

extern(D)!

Yeah, stupid me, completely forgot about that for a moment. The fever I had the last few days must have damaged my brain...

Do you think this alternative C++ mangling + _d_toString should be merged into druntime as-is? Or is it meant as prototype for a standardized upstream approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you think this alternative C++ mangling + _d_toString should be merged into druntime as-is? Or is it meant as prototype for a standardized upstream approach?

I'm not sure. This possibility is currently very specific to the WinEH implementation, I doubt it is feasible in dmd. I don't know if something similar would be possible for other platforms.

virtual string _toString();
virtual size_t _toHash();
virtual int _opCmp(Object* o);
virtual bool _opEquals(Object* o);

public:
string toString() { return _d_toString(this); }
};

class Throwable : public Object
{
public:
string msg; /// A message describing the error.

/**
* The _file name and line number of the D source code corresponding with
* where the error was thrown from.
*/
string file;
size_t line; /// ditto

void* info;
Throwable* next;

virtual string _toString();
};

class Exception : public Throwable
{
};

class Error : public Throwable
{
Throwable* bypassedException;
};

} // namespace D
24 changes: 24 additions & 0 deletions runnable/extra-files/ldc_cpp_eh1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "dexcept.h"
#include <string.h>
#include <assert.h>

void throwException();

bool test_eh()
{
try
{
throwException();
}
catch (D::Exception* excpt)
{
D::string s = excpt->toString();
assert(memcmp(s.ptr + s.length - 9, "Hello C++", 9) == 0);
return true;
}
catch(...)
{
assert(false);
}
return false;
}
15 changes: 15 additions & 0 deletions runnable/ldc_cpp_eh.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// EXTRA_CPP_SOURCES: ldc_cpp_eh1.cpp

extern(C++) bool test_eh();

extern(C++)
void throwException()
{
throw new Exception("Hello C++");
}

void main()
{
bool rc = test_eh();
assert(rc);
}