From 9f9c0b7142572f8fe67890e4742d4037792fd874 Mon Sep 17 00:00:00 2001 From: Daniel Murphy Date: Mon, 16 Jul 2012 15:42:45 +1000 Subject: [PATCH] Fix Issue 7540 - Catch multiple exception types with single catch block Allow catching multiple exceptions with 'catch(auto e : E1, E2, E3)' syntax. Expands TypeTuples in the type list. --- src/interpret.c | 19 ++--- src/parse.c | 29 ++++++-- src/s2ir.c | 24 ++++--- src/statement.c | 154 +++++++++++++++++++++++++++++++--------- src/statement.h | 4 +- test/runnable/xtest46.d | 70 ++++++++++++++++++ 6 files changed, 243 insertions(+), 57 deletions(-) diff --git a/src/interpret.c b/src/interpret.c index 70ae178d4fcc..1e09c4e4bec3 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -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; diff --git a/src/parse.c b/src/parse.c index 921087d6b8a6..15f2d4ead6a7 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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); diff --git a/src/s2ir.c b/src/s2ir.c index dd139ca80940..76210f716575 100644 --- a/src/s2ir.c +++ b/src/s2ir.c @@ -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); diff --git a/src/statement.c b/src/statement.c index ccd7a42309a3..0ae8eeaadba6 100644 --- a/src/statement.c +++ b/src/statement.c @@ -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); + } + } } } @@ -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)) @@ -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; @@ -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; @@ -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()); @@ -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; @@ -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(); diff --git a/src/statement.h b/src/statement.h index b24babefe119..f7be9f33131e 100644 --- a/src/statement.h +++ b/src/statement.h @@ -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); diff --git a/test/runnable/xtest46.d b/test/runnable/xtest46.d index 7a257e21497c..073bd39b2910 100644 --- a/test/runnable/xtest46.d +++ b/test/runnable/xtest46.d @@ -2605,6 +2605,75 @@ struct S132 alias cond this; } +/***************************************************/ + +template TT7540(T...) { alias T TT7540; } +void test7540() +{ + class ExceptionA : Exception { this() { super(null); } } + class ExceptionB : Exception { this() { super(null); } } + class ExceptionC : Exception { this() { super(null); } } + + nothrow int test7540a() + { + try + { throw new ExceptionA(); } + catch(auto e : ExceptionA) + { static assert(is(typeof(e) == ExceptionA)); } + catch + { assert(0); } + + try + { throw new ExceptionB(); } + catch(auto e : ExceptionA) + { static assert(is(typeof(e) == ExceptionA)); assert(0); } + catch + { } + + try + { throw new ExceptionB(); } + catch(auto e : ExceptionA, ExceptionB) + { static assert(is(typeof(e) == Exception)); } + catch + { assert(0); } + + try + { throw new ExceptionB(); } + catch(auto e : ExceptionA, ExceptionB) + { static assert(is(typeof(e) == Exception)); } + catch(auto e : ExceptionC) + { assert(0); } + catch + { assert(0); } + + try + { throw new ExceptionC(); } + catch(auto e : ExceptionA, ExceptionB) + { assert(0); } + catch(auto e : ExceptionC) + { } + catch + { assert(0); } + + alias TT7540!(ExceptionA, ExceptionB) EAB; + alias TT7540!(ExceptionC) EC; + alias TT7540!() EX; + + try + { throw new ExceptionC(); } + catch(auto e : EAB) + { assert(0); } + catch(auto e : EC) + { } + catch(auto e : EX) + { assert(0); } + + return 1; + } + auto x = test7540a(); + enum y = test7540a(); +} + /***************************************************/ // 5343 @@ -5167,6 +5236,7 @@ int main() test34(); test35(); test36(); + test7540(); test37(); test38(); test39();