| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c |
| */ |
| |
| #include "root/dsystem.h" |
| |
| #include "mars.h" |
| #include "init.h" |
| #include "expression.h" |
| #include "template.h" |
| #include "statement.h" |
| #include "mtype.h" |
| #include "utf.h" |
| #include "declaration.h" |
| #include "aggregate.h" |
| #include "scope.h" |
| #include "attrib.h" |
| #include "tokens.h" |
| |
| bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow); |
| bool walkPostorder(Expression *e, StoppableVisitor *v); |
| |
| /******************************************** |
| * Returns true if the expression may throw exceptions. |
| * If 'mustNotThrow' is true, generate an error if it throws |
| */ |
| |
| bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow) |
| { |
| //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); |
| |
| // stop walking if we determine this expression can throw |
| class CanThrow : public StoppableVisitor |
| { |
| FuncDeclaration *func; |
| bool mustNotThrow; |
| |
| public: |
| CanThrow(FuncDeclaration *func, bool mustNotThrow) |
| : func(func), mustNotThrow(mustNotThrow) |
| { |
| } |
| |
| void visit(Expression *) |
| { |
| } |
| |
| void visit(DeclarationExp *de) |
| { |
| stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow); |
| } |
| |
| void visit(CallExp *ce) |
| { |
| if (global.errors && !ce->e1->type) |
| return; // error recovery |
| |
| /* If calling a function or delegate that is typed as nothrow, |
| * then this expression cannot throw. |
| * Note that pure functions can throw. |
| */ |
| Type *t = ce->e1->type->toBasetype(); |
| if (ce->f && ce->f == func) |
| return; |
| if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) |
| return; |
| if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) |
| return; |
| |
| if (mustNotThrow) |
| { |
| if (ce->f) |
| { |
| ce->error("%s '%s' is not nothrow", |
| ce->f->kind(), ce->f->toPrettyChars()); |
| } |
| else |
| { |
| Expression *e1 = ce->e1; |
| if (e1->op == TOKstar) // print 'fp' if e1 is (*fp) |
| e1 = ((PtrExp *)e1)->e1; |
| ce->error("'%s' is not nothrow", e1->toChars()); |
| } |
| } |
| stop = true; |
| } |
| |
| void visit(NewExp *ne) |
| { |
| if (ne->member) |
| { |
| if (ne->allocator) |
| { |
| // Bugzilla 14407 |
| Type *t = ne->allocator->type->toBasetype(); |
| if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) |
| { |
| if (mustNotThrow) |
| { |
| ne->error("%s '%s' is not nothrow", |
| ne->allocator->kind(), ne->allocator->toPrettyChars()); |
| } |
| stop = true; |
| } |
| } |
| // See if constructor call can throw |
| Type *t = ne->member->type->toBasetype(); |
| if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) |
| { |
| if (mustNotThrow) |
| { |
| ne->error("%s '%s' is not nothrow", |
| ne->member->kind(), ne->member->toPrettyChars()); |
| } |
| stop = true; |
| } |
| } |
| // regard storage allocation failures as not recoverable |
| } |
| |
| void visit(DeleteExp *de) |
| { |
| Type *tb = de->e1->type->toBasetype(); |
| AggregateDeclaration *ad = NULL; |
| switch (tb->ty) |
| { |
| case Tclass: |
| ad = ((TypeClass *)tb)->sym; |
| break; |
| |
| case Tpointer: |
| tb = ((TypePointer *)tb)->next->toBasetype(); |
| if (tb->ty == Tstruct) |
| ad = ((TypeStruct *)tb)->sym; |
| break; |
| |
| case Tarray: |
| { |
| Type *tv = tb->nextOf()->baseElemOf(); |
| if (tv->ty == Tstruct) |
| { |
| ad = ((TypeStruct *)tv)->sym; |
| break; |
| } |
| } |
| |
| default: |
| break; |
| } |
| if (!ad) |
| return; |
| |
| if (ad->dtor) |
| { |
| Type *t = ad->dtor->type->toBasetype(); |
| if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) |
| { |
| if (mustNotThrow) |
| { |
| de->error("%s '%s' is not nothrow", |
| ad->dtor->kind(), ad->dtor->toPrettyChars()); |
| } |
| stop = true; |
| } |
| } |
| if (ad->aggDelete && tb->ty != Tarray) |
| { |
| Type *t = ad->aggDelete->type; |
| if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) |
| { |
| if (mustNotThrow) |
| { |
| de->error("%s '%s' is not nothrow", |
| ad->aggDelete->kind(), ad->aggDelete->toPrettyChars()); |
| } |
| stop = true; |
| } |
| } |
| } |
| |
| void visit(AssignExp *ae) |
| { |
| // blit-init cannot throw |
| if (ae->op == TOKblit) |
| return; |
| |
| /* Element-wise assignment could invoke postblits. |
| */ |
| Type *t; |
| if (ae->type->toBasetype()->ty == Tsarray) |
| { |
| if (!ae->e2->isLvalue()) |
| return; |
| t = ae->type; |
| } |
| else if (ae->e1->op == TOKslice) |
| t = ((SliceExp *)ae->e1)->e1->type; |
| else |
| return; |
| |
| Type *tv = t->baseElemOf(); |
| if (tv->ty != Tstruct) |
| return; |
| StructDeclaration *sd = ((TypeStruct *)tv)->sym; |
| if (!sd->postblit || sd->postblit->type->ty != Tfunction) |
| return; |
| |
| if (((TypeFunction *)sd->postblit->type)->isnothrow) |
| ; |
| else |
| { |
| if (mustNotThrow) |
| { |
| ae->error("%s '%s' is not nothrow", |
| sd->postblit->kind(), sd->postblit->toPrettyChars()); |
| } |
| stop = true; |
| } |
| } |
| |
| void visit(NewAnonClassExp *) |
| { |
| assert(0); // should have been lowered by semantic() |
| } |
| }; |
| |
| CanThrow ct(func, mustNotThrow); |
| return walkPostorder(e, &ct); |
| } |
| |
| /************************************** |
| * Does symbol, when initialized, throw? |
| * Mirrors logic in Dsymbol_toElem(). |
| */ |
| |
| bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow) |
| { |
| AttribDeclaration *ad; |
| VarDeclaration *vd; |
| TemplateMixin *tm; |
| TupleDeclaration *td; |
| |
| //printf("Dsymbol_toElem() %s\n", s->toChars()); |
| ad = s->isAttribDeclaration(); |
| if (ad) |
| { |
| Dsymbols *decl = ad->include(NULL, NULL); |
| if (decl && decl->dim) |
| { |
| for (size_t i = 0; i < decl->dim; i++) |
| { |
| s = (*decl)[i]; |
| if (Dsymbol_canThrow(s, func, mustNotThrow)) |
| return true; |
| } |
| } |
| } |
| else if ((vd = s->isVarDeclaration()) != NULL) |
| { |
| s = s->toAlias(); |
| if (s != vd) |
| return Dsymbol_canThrow(s, func, mustNotThrow); |
| if (vd->storage_class & STCmanifest) |
| ; |
| else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) |
| ; |
| else |
| { |
| if (vd->_init) |
| { |
| ExpInitializer *ie = vd->_init->isExpInitializer(); |
| if (ie && canThrow(ie->exp, func, mustNotThrow)) |
| return true; |
| } |
| if (vd->needsScopeDtor()) |
| return canThrow(vd->edtor, func, mustNotThrow); |
| } |
| } |
| else if ((tm = s->isTemplateMixin()) != NULL) |
| { |
| //printf("%s\n", tm->toChars()); |
| if (tm->members) |
| { |
| for (size_t i = 0; i < tm->members->dim; i++) |
| { |
| Dsymbol *sm = (*tm->members)[i]; |
| if (Dsymbol_canThrow(sm, func, mustNotThrow)) |
| return true; |
| } |
| } |
| } |
| else if ((td = s->isTupleDeclaration()) != NULL) |
| { |
| for (size_t i = 0; i < td->objects->dim; i++) |
| { |
| RootObject *o = (*td->objects)[i]; |
| if (o->dyncast() == DYNCAST_EXPRESSION) |
| { |
| Expression *eo = (Expression *)o; |
| if (eo->op == TOKdsymbol) |
| { |
| DsymbolExp *se = (DsymbolExp *)eo; |
| if (Dsymbol_canThrow(se->s, func, mustNotThrow)) |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |