Skip to content

Commit

Permalink
Fix Issue 7540 - Catch multiple exception types with single catch block
Browse files Browse the repository at this point in the history
Allow catching multiple exceptions with 'catch(auto e : E1, E2, E3)' syntax.
Expands TypeTuples in the type list.
  • Loading branch information
yebblies committed Jul 16, 2012
1 parent b9afb03 commit 9f9c0b7
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 57 deletions.
19 changes: 11 additions & 8 deletions src/interpret.c
Expand Up @@ -1558,16 +1558,19 @@ Expression *TryCatchStatement::interpret(InterState *istate)
#else
Catch *ca = catches->tdata()[i];
#endif
Type *catype = ca->type;
for (size_t j = 0; j < ca->types->dim; ++j)
{
Type *catype = (*ca->types)[j];

if (catype->equals(extype) || catype->isBaseOf(extype, NULL))
{ // Execute the handler
if (ca->var)
{
ctfeStack.push(ca->var);
ca->var->setValue(ex->thrown);
if (catype->equals(extype) || catype->isBaseOf(extype, NULL))
{ // Execute the handler
if (ca->var)
{
ctfeStack.push(ca->var);
ca->var->setValue(ex->thrown);
}
return ca->handler ? ca->handler->interpret(istate) : NULL;
}
return ca->handler ? ca->handler->interpret(istate) : NULL;
}
}
return e;
Expand Down
29 changes: 25 additions & 4 deletions src/parse.c
Expand Up @@ -4254,21 +4254,42 @@ Statement *Parser::parseStatement(int flags)
{
Statement *handler;
Catch *c;
Type *t;
Types *t = NULL;
Identifier *id;
Loc loc = this->loc;

nextToken();
if (token.value == TOKlcurly || token.value != TOKlparen)
{
t = NULL;
id = NULL;
}
else
{
t = new Types();
check(TOKlparen);
id = NULL;
t = parseType(&id);
if (token.value == TOKauto)
{
nextToken();
if (token.value == TOKidentifier)
{
id = token.ident;
nextToken();
check(TOKcolon);
t->push(parseBasicType());
while (token.value == TOKcomma)
{
nextToken();
t->push(parseBasicType());
}
}
else
check(TOKidentifier);
}
else
{
id = NULL;
t->push(parseType(&id));
}
check(TOKrparen);
}
handler = parseStatement(0);
Expand Down
24 changes: 14 additions & 10 deletions src/s2ir.c
Expand Up @@ -1520,18 +1520,22 @@ void TryCatchStatement::toIR(IRState *irs)
Catch *cs = (*catches)[i];
if (cs->var)
cs->var->csym = tryblock->jcatchvar;
block *bcatch = blx->curblock;
if (cs->type)
bcatch->Bcatchtype = cs->type->toBasetype()->toSymbol();
list_append(&tryblock->Bsucc,bcatch);
block_goto(blx,BCjcatch,NULL);
if (cs->handler != NULL)

for (size_t j = 0; j < cs->types->dim; j++)
{
IRState catchState(irs, this);
cs->handler->toIR(&catchState);
block *bcatch = blx->curblock;
bcatch->Bcatchtype = (*cs->types)[j]->toBasetype()->toSymbol();
list_append(&tryblock->Bsucc,bcatch);
block_goto(blx,BCjcatch,NULL);
if (cs->handler != NULL &&
j == cs->types->dim - 1)
{
IRState catchState(irs, this);
cs->handler->toIR(&catchState);
}
list_append(&blx->curblock->Bsucc, breakblock);
block_next(blx, BCgoto, NULL);
}
list_append(&blx->curblock->Bsucc, breakblock);
block_next(blx, BCgoto, NULL);
}

block_next(blx,(enum BC)blx->curblock->BC, breakblock);
Expand Down
154 changes: 121 additions & 33 deletions src/statement.c
Expand Up @@ -4579,8 +4579,14 @@ Statement *TryCatchStatement::semantic(Scope *sc)
char *si = c->loc.toChars();
char *sj = cj->loc.toChars();

if (c->type->toBasetype()->implicitConvTo(cj->type->toBasetype()))
error("catch at %s hides catch at %s", sj, si);
for (size_t k = 0; k < c->types->dim; k++)
{
for (size_t l = 0; l < cj->types->dim; l++)
{
if ((*c->types)[k]->toBasetype()->implicitConvTo((*cj->types)[l]->toBasetype()))
error("catch at %s hides catch at %s", sj, si);
}
}
}
}

Expand Down Expand Up @@ -4610,17 +4616,29 @@ int TryCatchStatement::blockExit(bool mustNotThrow)
for (size_t i = 0; i < catches->dim; i++)
{
Catch *c = (*catches)[i];
if (c->type == Type::terror)
int hasError = 0;
for (size_t j = 0; j < c->types->dim; j++)
{
if ((*c->types)[j] == Type::terror)
{
hasError = 1;
break;
}
}
if (hasError)
continue;

catchresult |= c->blockExit(mustNotThrow);

/* If we're catching Object, then there is no throwing
*/
Identifier *id = c->type->toBasetype()->isClassHandle()->ident;
if (id == Id::Object || id == Id::Throwable || id == Id::Exception)
for (size_t j = 0; j < c->types->dim; j++)
{
result &= ~BEthrow;
/* If we're catching Object, then there is no throwing
*/
Identifier *id = (*c->types)[j]->toBasetype()->isClassHandle()->ident;
if (id == Id::Object || id == Id::Throwable || id == Id::Exception)
{
result &= ~BEthrow;
}
}
}
if (mustNotThrow && (result & BEthrow))
Expand All @@ -4646,11 +4664,11 @@ void TryCatchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)

/******************************** Catch ***************************/

Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler)
Catch::Catch(Loc loc, Types *t, Identifier *id, Statement *handler)
{
//printf("Catch(%s, loc = %s)\n", id->toChars(), loc.toChars());
this->loc = loc;
this->type = t;
this->types = t;
this->ident = id;
this->handler = handler;
var = NULL;
Expand All @@ -4659,8 +4677,15 @@ Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler)

Catch *Catch::syntaxCopy()
{
Types *ts = NULL;
if (types)
{
ts = types->copy();
for (size_t i = 0; i < ts->dim; i++)
(*ts)[i] = (*ts)[i]->syntaxCopy();
}
Catch *c = new Catch(loc,
(type ? type->syntaxCopy() : NULL),
ts,
ident,
(handler ? handler->syntaxCopy() : NULL));
c->internalCatch = internalCatch;
Expand All @@ -4669,7 +4694,7 @@ Catch *Catch::syntaxCopy()

void Catch::semantic(Scope *sc)
{
if (type && type->deco)
if (types && (*types)[0] && (*types)[0]->deco)
return;

//printf("Catch::semantic(%s)\n", ident->toChars());
Expand All @@ -4691,28 +4716,78 @@ void Catch::semantic(Scope *sc)
sym->parent = sc->scopesym;
sc = sc->push(sym);

if (!type)
type = new TypeIdentifier(0, Id::Throwable);
type = type->semantic(loc, sc);
ClassDeclaration *cd = type->toBasetype()->isClassHandle();
if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL)))
if (!types)
types = new Types();
Lcatchany:
if (!types->dim)
types->push(new TypeIdentifier(0, Id::Throwable));
Type *type = NULL;
for (size_t i = 0; i < types->dim; i++)
{
if (type != Type::terror)
{ error(loc, "can only catch class objects derived from Throwable, not '%s'", type->toChars());
type = Type::terror;
Type *tn = (*types)[i]->semantic(loc, sc);
if (!type)
type = tn;
ClassDeclaration *cd = tn->toBasetype()->isClassHandle();
if (tn->ty == Ttuple)
{
TypeTuple *tt = (TypeTuple *)tn;
types->remove(i);
if (type == tn)
type = NULL;
if (tt->arguments)
{
for (size_t j = 0; j < tt->arguments->dim; j++)
{
Parameter *p = (*tt->arguments)[j];
types->insert(i + j, p->type);
}
}
if (types->dim == 0)
goto Lcatchany;
i--;
continue;
}
else if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL)))
{
if (tn != Type::terror)
{ error(loc, "can only catch class objects derived from Throwable, not '%s'", tn->toChars());
tn = Type::terror;
}
}
else if (sc->func &&
!sc->intypeof &&
!internalCatch &&
cd != ClassDeclaration::exception &&
!ClassDeclaration::exception->isBaseOf(cd, NULL) &&
sc->func->setUnsafe())
{
error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", tn->toChars());
tn = Type::terror;
}
(*types)[i] = tn;

// Look for redundant types
for (size_t j = 0; j < i; j++)
{
Type *t = (*types)[j];
if (tn->equals(t))
error(loc, "duplicate catch type %s", tn->toChars());
else if (tn->implicitConvTo(t))
error(loc, "catch of %s hides catch of %s", t->toChars(), tn->toChars());
else if (t->implicitConvTo(tn))
error(loc, "catch of %s hides catch of %s", tn->toChars(), t->toChars());
}

// Find common base class
if (i != 0)
{
IntegerExp integerexp(0);
CondExp condexp(0, &integerexp, type->defaultInit(0), tn->defaultInit(0));
condexp.semantic(sc);
type = condexp.type;
}
}
else if (sc->func &&
!sc->intypeof &&
!internalCatch &&
cd != ClassDeclaration::exception &&
!ClassDeclaration::exception->isBaseOf(cd, NULL) &&
sc->func->setUnsafe())
{
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 && type->ty != Terror)
{
var = new VarDeclaration(loc, type, ident, NULL);
var->parent = sc->parent;
Expand All @@ -4731,9 +4806,22 @@ int Catch::blockExit(bool mustNotThrow)
void Catch::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("catch");
if (type)
if (types)
{ buf->writebyte('(');
type->toCBuffer(buf, ident, hgs);
if (types->dim == 1)
(*types)[0]->toCBuffer(buf, ident, hgs);
else
{
buf->writestring("auto ");
buf->writestring(ident->toChars());
buf->writestring(" : ");
for (size_t i = 0; i < types->dim; i++)
{
(*types)[i]->toCBuffer(buf, NULL, hgs);
if (types->dim > i+1)
buf->writestring(", ");
}
}
buf->writebyte(')');
}
buf->writenl();
Expand Down
4 changes: 2 additions & 2 deletions src/statement.h
Expand Up @@ -714,13 +714,13 @@ struct TryCatchStatement : Statement
struct Catch : Object
{
Loc loc;
Type *type;
Types *types;
Identifier *ident;
VarDeclaration *var;
Statement *handler;
bool internalCatch;

Catch(Loc loc, Type *t, Identifier *id, Statement *handler);
Catch(Loc loc, Types *t, Identifier *id, Statement *handler);
Catch *syntaxCopy();
void semantic(Scope *sc);
int blockExit(bool mustNotThrow);
Expand Down

0 comments on commit 9f9c0b7

Please sign in to comment.