Skip to content

Commit

Permalink
Partially working C++ exception catching.
Browse files Browse the repository at this point in the history
This adds catch (C++) (..) {..} statements to enable catching C++ exceptions of any type. The std::type_info generated by Clang for that type is added alongside LDC's ClassInfo catch clauses in the EH landing pad, and when _d_eh_personality encounters C++ exceptions it will let a C++ handler query the catches and determine the right selector.

The std::type_info pointers are wrapped inside a D class since there would be no way to discriminate between raw type_info* pointers and ClassInfo, and LLVM allows little LSDA customization so the catch clause can only be one global variable pointer, no additional info allowed.

The remaining issue is that _cxa_begin_catch returns null instead of the exception object, so the example segfaults at e.what().
  • Loading branch information
Syniurge committed Nov 14, 2015
1 parent 8388ecd commit 8b55ec1
Show file tree
Hide file tree
Showing 19 changed files with 346 additions and 22 deletions.
2 changes: 1 addition & 1 deletion deps/clang
11 changes: 11 additions & 0 deletions dmd2/cpp/calypso.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ class LangPlugin : public ::LangPlugin, public ::ForeignCodeGen
Loc loc, Identifiers *packages, Identifier *id,
Identifier *aliasId, int isstatic) override;

bool doesHandleCatch(LINK lang) override;
::Catch *createCatch(Loc loc, Type *t,
Identifier *id, Statement *handler) override;

const char *mangle(Dsymbol *s) override;
Visitor *getForeignMangler(OutBuffer *buf, bool forEquiv, Visitor *base) override;

Expand Down Expand Up @@ -186,6 +190,10 @@ class LangPlugin : public ::LangPlugin, public ::ForeignCodeGen
void toPreInitClass(TypeClass* tc, LLValue* dst) override;
void toPostNewClass(Loc& loc, TypeClass* tc, DValue* val) override;

void toBeginCatch(IRState *irs, ::Catch *cj) override;
void toEndCatch(IRState *irs, ::Catch *cj) override;
llvm::Constant *toCatchScopeType(IRState *irs, Type *t) override;

void EmitInternalDeclsForFields(const clang::RecordDecl *RD);

// ==== ==== ====
Expand All @@ -195,6 +203,9 @@ class LangPlugin : public ::LangPlugin, public ::ForeignCodeGen
BuiltinTypes &builtinTypes;
DeclReferencer &declReferencer;

::ClassDeclaration *type_info_ptr; // wrapper around std::type_info for EH
std::map<llvm::Constant*, llvm::GlobalVariable*> type_infoWrappers; // FIXME put into module state with the CodeGenModule

std::string executablePath; // from argv[0] to locate Clang builtin headers

struct GenModSet : public llvm::StringSet<> // already compiled modules
Expand Down
34 changes: 34 additions & 0 deletions dmd2/cpp/cppstatement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Contributed by Elie Morisse, same license DMD uses

#include "cpp/cppstatement.h"

namespace cpp
{

Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler)
: ::Catch(loc, t, id, handler)
{
}

::Catch *Catch::syntaxCopy()
{
Catch *c = new Catch(loc,
type ? type->syntaxCopy() : NULL,
ident,
(handler ? handler->syntaxCopy() : NULL));
c->internalCatch = internalCatch;
return c;
}

bool LangPlugin::doesHandleCatch(LINK lang)
{
return lang == LINKcpp;
}

::Catch *LangPlugin::createCatch(Loc loc, Type *t,
Identifier *id, Statement *handler)
{
return new Catch(loc, t, id, handler);
}

}
33 changes: 33 additions & 0 deletions dmd2/cpp/cppstatement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Contributed by Elie Morisse, same license DMD uses

#ifndef DMD_CPP_CPPSTATEMENT_H
#define DMD_CPP_CPPSTATEMENT_H

#ifdef __DMC__
#pragma once
#endif /* __DMC__ */

#include "root.h"
#include "statement.h"
#include "cpp/calypso.h"

namespace cpp
{
struct IrCatch;

class Catch : public ::Catch
{
public:
CALYPSO_LANGPLUGIN

Catch(Loc loc, Type *t, Identifier *id, Statement *handler);
::Catch *syntaxCopy() override;

bool onlyCatchThrowable() override { return false; }

IrCatch *ir = nullptr;
};

}

#endif
6 changes: 6 additions & 0 deletions dmd2/import.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Module;
class Package;
class AliasDeclaration;
class StringExp;
class Catch;

class Import : public Dsymbol
{
Expand Down Expand Up @@ -98,6 +99,11 @@ class LangPlugin
Loc loc, Identifiers *packages, Identifier *id,
Identifier *aliasId, int isstatic) = 0;

// foreign exceptions
virtual bool doesHandleCatch(LINK lang) = 0;
virtual Catch *createCatch(Loc loc, Type *t,
Identifier *id, Statement *handler) = 0;

// ===== - - - - - ===== //

virtual const char *mangle(Dsymbol *s) = 0; // TODO replace by getForeignMangler
Expand Down
33 changes: 29 additions & 4 deletions dmd2/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,8 @@ LINK Parser::parseLinkage(Identifiers **pidents)
link = LINKd; // default
}
check(TOKrparen);
*pidents = idents;
if (pidents) // CALYPSO
*pidents = idents;
return link;
}

Expand Down Expand Up @@ -5440,21 +5441,45 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc
Identifier *id;
Loc catchloc = token.loc;

nextToken();
if (token.value == TOKlcurly || token.value != TOKlparen)
LangPlugin *langPlugin = NULL; // CALYPSO

Token *tk = peek(&token);
if (tk->value == TOKlcurly || tk->value != TOKlparen)
{
nextToken();
t = NULL;
id = NULL;
}
else
{
if (tk->value == TOKlparen && peekPastParen(tk)->value == TOKlparen) // CALYPSO
{
LINK lang = parseLinkage(); // TODO: remove the parenthesedSpecialToken hack and rename LINK

for (int i = 0; i < global.langPlugins.dim; i++)
{
if (global.langPlugins[i]->doesHandleCatch(lang))
{
langPlugin = global.langPlugins[i];
break;
}
}
if (!langPlugin)
error("no language plugin was found to support language %s", token.toChars());
}
else
nextToken();

check(TOKlparen);
id = NULL;
t = parseType(&id);
check(TOKrparen);
}
handler = parseStatement(0);
c = new Catch(catchloc, t, id, handler);
if (langPlugin)
c = langPlugin->createCatch(catchloc, t, id, handler); // CALYPSO
else
c = new Catch(catchloc, t, id, handler);
if (!catches)
catches = new Catches();
catches->push(c);
Expand Down
2 changes: 1 addition & 1 deletion dmd2/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class Parser : public Lexer
StaticAssert *parseStaticAssert();
TypeQualified *parseTypeof();
Type *parseVector();
LINK parseLinkage(Identifiers **);
LINK parseLinkage(Identifiers **pidents = NULL);
Identifiers *parseQualifiedIdentifier(const char *entity);
Condition *parseDebugCondition();
Condition *parseVersionCondition();
Expand Down
12 changes: 10 additions & 2 deletions dmd2/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,10 @@ int Statement::blockExit(FuncDeclaration *func, bool mustNotThrow)

/* If we're catching Object, then there is no throwing
*/
Identifier *id = c->type->toBasetype()->isClassHandle()->ident;
Identifier *id = NULL;
ClassDeclaration *cd = c->type->toBasetype()->isClassHandle(); // CALYPSO
if (cd && !cd->langPlugin())
id = cd->ident;
if (c->internalCatch && (cresult & BEfallthru))
{
// Bugzilla 11542: leave blockExit flags of the body
Expand Down Expand Up @@ -4619,6 +4622,9 @@ void Catch::semantic(Scope *sc)
type = tid;
}
type = type->semantic(loc, sc);

if (onlyCatchThrowable()) // CALYPSO
{
ClassDeclaration *cd = type->toBasetype()->isClassHandle();
if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL)))
{
Expand All @@ -4638,7 +4644,9 @@ void Catch::semantic(Scope *sc)
error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", type->toChars());
type = Type::terror;
}
else if (ident)
}

if (ident)
{
var = new VarDeclaration(loc, type, ident, NULL);
var->semantic(sc);
Expand Down
4 changes: 3 additions & 1 deletion dmd2/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -657,8 +657,10 @@ class Catch : public RootObject
bool internalCatch;

Catch(Loc loc, Type *t, Identifier *id, Statement *handler);
Catch *syntaxCopy();
virtual Catch *syntaxCopy(); // CALYPSO
void semantic(Scope *sc);
virtual bool onlyCatchThrowable() { return true; } // CALYPSO
virtual LangPlugin *langPlugin() { return NULL; }
};

class TryFinallyStatement : public Statement
Expand Down
6 changes: 6 additions & 0 deletions gen/cgforeign.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Type;
class ClassDeclaration;
class StructDeclaration;
class VarDeclaration;
class Catch;

class ForeignCodeGen
{
Expand Down Expand Up @@ -54,4 +55,9 @@ class ForeignCodeGen
// Called for any aggregate (TODO: less ambiguous names?)
virtual void toPreInitClass(TypeClass* tc, LLValue* dst) = 0;
virtual void toPostNewClass(Loc& loc, TypeClass* tc, DValue* val) = 0;

// Exception handling
virtual void toBeginCatch(IRState *irs, Catch *cj) = 0;
virtual void toEndCatch(IRState *irs, Catch *cj) = 0;
virtual llvm::Constant *toCatchScopeType(IRState *irs, Type *t) = 0;
};
127 changes: 127 additions & 0 deletions gen/cpp/cppeh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Contributed by Elie Morisse, same license DMD uses
#include "cpp/astunit.h"
#include "cpp/calypso.h"
#include "cpp/cppstatement.h"
#include "cpp/cpptypes.h"

#include "ir/irfunction.h"
#include "ir/irtypeclass.h"
#include "gen/irstate.h"
#include "gen/llvmhelpers.h"
#include "gen/rttibuilder.h"
#include "gen/tollvm.h"

#include "clang/lib/CodeGen/CGCXXABI.h"
#include "clang/lib/CodeGen/CGException.h"
#include "clang/lib/CodeGen/CodeGenFunction.h"

namespace cpp
{

using llvm::cast;
using llvm::dyn_cast;
using llvm::isa;

namespace clangCG = clang::CodeGen;

struct IrCatch
{
clangCG::CodeGenFunction::RunCleanupsScope *CatchScope = nullptr; // may be overkill
};

IrCatch *getIrCatch(cpp::Catch *cj)
{
if (!cj->ir)
cj->ir = new IrCatch;
return cj->ir;
}

void LangPlugin::toBeginCatch(IRState *irs, ::Catch *cj)
{
auto c_cj = static_cast<cpp::Catch*>(cj);
auto irc = getIrCatch(c_cj);
llvm::Value* ehPtr = irs->func()->getOrCreateEhPtrSlot();
ehPtr = irs->ir->CreateLoad(ehPtr);

updateCGFInsertPoint();

// Enter a cleanup scope, including the catch variable and the
// end-catch.
irc->CatchScope = new clangCG::CodeGenFunction::RunCleanupsScope(*CGF());

// For catches handling a specific type, create storage for it.
// We will set it in the code that branches from the landing pads
// (there might be more than one) to catchBB.
if (cj->var) {
// // Use the same storage for all exceptions that are not accessed in
// // nested functions
// if (!cj->var->nestedrefs.dim) {
// assert(!isIrLocalCreated(cj->var));
// IrLocal* irLocal = getIrLocal(cj->var, true);
// irLocal->value = DtoBitCast(ehPtr, getPtrToType(llCatchVarType));
// } else {
// This will alloca if we haven't already and take care of nested refs
DtoDeclarationExp(cj->var);
IrLocal* irLocal = getIrLocal(cj->var);

auto CatchParamTy = TypeMapper().toType(cj->loc, cj->var->type,
irs->func()->decl->scope);
auto CatchParamAlign = clang::CharUnits();

clangCG::InitCatchParam(*CGF(), ehPtr, CatchParamTy, CatchParamAlign,
nullptr, irLocal->value, clang::SourceLocation());
// }
}
else
clangCG::CallBeginCatch(*CGF(), ehPtr, false);
}

void LangPlugin::toEndCatch(IRState *irs, ::Catch *cj)
{
auto c_cj = static_cast<cpp::Catch*>(cj);

// Call __cxa_end_catch, fall out through the catch cleanups.
c_cj->ir->CatchScope->ForceCleanup();
delete c_cj->ir->CatchScope;
}

llvm::Constant *LangPlugin::toCatchScopeType(IRState *irs, Type *t)
{
auto loc = irs->func()->decl->loc;
auto ThrowType = TypeMapper().toType(loc, t, irs->func()->decl->scope);

auto TypeInfo = CGM->GetAddrOfRTTIDescriptor(ThrowType, /*ForEH=*/true);
auto& wrapper = type_infoWrappers[TypeInfo];

if (!wrapper)
{
if (!type_info_ptr)
type_info_ptr = ::ClassDeclaration::exception->parent->search(
loc, Identifier::idPool("__cpp_type_info_ptr"))->isClassDeclaration();

assert(type_info_ptr);

RTTIBuilder b(type_info_ptr);
b.push(TypeInfo);

auto initType = cast<llvm::StructType>(static_cast<IrTypeClass*>(type_info_ptr->type->ctype)->getMemoryLLType());
auto finalinit = b.get_constant(initType);

llvm::SmallString<256> InitName("_D");
llvm::raw_svector_ostream Out(InitName);
CGM->getCXXABI().getMangleContext().mangleCXXRTTI(ThrowType, Out);
Out.flush();
InitName.append("7__tiwrap");

wrapper = getOrCreateGlobal(irs->func()->decl->loc,
gIR->module, initType, true, llvm::GlobalValue::ExternalLinkage, NULL, InitName);
wrapper->setAlignment(DtoAlignment(type_info_ptr->type));
wrapper->setInitializer(finalinit);
wrapper->setLinkage(DtoLinkage(type_info_ptr).first);
}

return wrapper;
}

}

1 change: 1 addition & 0 deletions gen/cpp/cpptoir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ void LangPlugin::enterModule(::Module *, llvm::Module *lm)

CGM.reset(new clangCG::CodeGenModule(Context,
*Opts, *lm, *gDataLayout, *pch.Diags));
type_infoWrappers.clear();
EmittedStaticVars.clear();
}

Expand Down
Loading

0 comments on commit 8b55ec1

Please sign in to comment.