1 2/* Compiler implementation of the D programming language 3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved 4 * written by Walter Bright 5 * http://www.digitalmars.com 6 * Distributed under the Boost Software License, Version 1.0. 7 * http://www.boost.org/LICENSE_1_0.txt 8 * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c 9 */ 10 11#include "root/dsystem.h" 12 13#include "mars.h" 14#include "init.h" 15#include "expression.h" 16#include "template.h" 17#include "statement.h" 18#include "mtype.h" 19#include "utf.h" 20#include "declaration.h" 21#include "aggregate.h" 22#include "scope.h" 23#include "attrib.h" 24#include "tokens.h" 25 26bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow); 27bool walkPostorder(Expression *e, StoppableVisitor *v); 28 29/******************************************** 30 * Returns true if the expression may throw exceptions. 31 * If 'mustNotThrow' is true, generate an error if it throws 32 */ 33 34bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow) 35{ 36 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); 37 38 // stop walking if we determine this expression can throw 39 class CanThrow : public StoppableVisitor 40 { 41 FuncDeclaration *func; 42 bool mustNotThrow; 43 44 public: 45 CanThrow(FuncDeclaration *func, bool mustNotThrow) 46 : func(func), mustNotThrow(mustNotThrow) 47 { 48 } 49 50 void visit(Expression *) 51 { 52 } 53 54 void visit(DeclarationExp *de) 55 { 56 stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow); 57 } 58 59 void visit(CallExp *ce) 60 { 61 if (global.errors && !ce->e1->type) 62 return; // error recovery 63 64 /* If calling a function or delegate that is typed as nothrow, 65 * then this expression cannot throw. 66 * Note that pure functions can throw. 67 */ 68 Type *t = ce->e1->type->toBasetype(); 69 if (ce->f && ce->f == func) 70 return; 71 if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) 72 return; 73 if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) 74 return; 75 76 if (mustNotThrow) 77 { 78 if (ce->f) 79 { 80 ce->error("%s '%s' is not nothrow", 81 ce->f->kind(), ce->f->toPrettyChars()); 82 } 83 else 84 { 85 Expression *e1 = ce->e1; 86 if (e1->op == TOKstar) // print 'fp' if e1 is (*fp) 87 e1 = ((PtrExp *)e1)->e1; 88 ce->error("'%s' is not nothrow", e1->toChars()); 89 } 90 } 91 stop = true; 92 } 93 94 void visit(NewExp *ne) 95 { 96 if (ne->member) 97 { 98 if (ne->allocator) 99 { 100 // Bugzilla 14407 101 Type *t = ne->allocator->type->toBasetype(); 102 if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) 103 { 104 if (mustNotThrow) 105 { 106 ne->error("%s '%s' is not nothrow", 107 ne->allocator->kind(), ne->allocator->toPrettyChars()); 108 } 109 stop = true; 110 } 111 } 112 // See if constructor call can throw 113 Type *t = ne->member->type->toBasetype(); 114 if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) 115 { 116 if (mustNotThrow) 117 { 118 ne->error("%s '%s' is not nothrow", 119 ne->member->kind(), ne->member->toPrettyChars()); 120 } 121 stop = true; 122 } 123 } 124 // regard storage allocation failures as not recoverable 125 } 126 127 void visit(DeleteExp *de) 128 { 129 Type *tb = de->e1->type->toBasetype(); 130 AggregateDeclaration *ad = NULL; 131 switch (tb->ty) 132 { 133 case Tclass: 134 ad = ((TypeClass *)tb)->sym; 135 break; 136 137 case Tpointer: 138 tb = ((TypePointer *)tb)->next->toBasetype(); 139 if (tb->ty == Tstruct) 140 ad = ((TypeStruct *)tb)->sym; 141 break; 142 143 case Tarray: 144 { 145 Type *tv = tb->nextOf()->baseElemOf(); 146 if (tv->ty == Tstruct) 147 { 148 ad = ((TypeStruct *)tv)->sym; 149 break; 150 } 151 } 152 153 default: 154 break; 155 } 156 if (!ad) 157 return; 158 159 if (ad->dtor) 160 { 161 Type *t = ad->dtor->type->toBasetype(); 162 if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) 163 { 164 if (mustNotThrow) 165 { 166 de->error("%s '%s' is not nothrow", 167 ad->dtor->kind(), ad->dtor->toPrettyChars()); 168 } 169 stop = true; 170 } 171 } 172 if (ad->aggDelete && tb->ty != Tarray) 173 { 174 Type *t = ad->aggDelete->type; 175 if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) 176 { 177 if (mustNotThrow) 178 { 179 de->error("%s '%s' is not nothrow", 180 ad->aggDelete->kind(), ad->aggDelete->toPrettyChars()); 181 } 182 stop = true; 183 } 184 } 185 } 186 187 void visit(AssignExp *ae) 188 { 189 // blit-init cannot throw 190 if (ae->op == TOKblit) 191 return; 192 193 /* Element-wise assignment could invoke postblits. 194 */ 195 Type *t; 196 if (ae->type->toBasetype()->ty == Tsarray) 197 { 198 if (!ae->e2->isLvalue()) 199 return; 200 t = ae->type; 201 } 202 else if (ae->e1->op == TOKslice) 203 t = ((SliceExp *)ae->e1)->e1->type; 204 else 205 return; 206 207 Type *tv = t->baseElemOf(); 208 if (tv->ty != Tstruct) 209 return; 210 StructDeclaration *sd = ((TypeStruct *)tv)->sym; 211 if (!sd->postblit || sd->postblit->type->ty != Tfunction) 212 return; 213 214 if (((TypeFunction *)sd->postblit->type)->isnothrow) 215 ; 216 else 217 { 218 if (mustNotThrow) 219 { 220 ae->error("%s '%s' is not nothrow", 221 sd->postblit->kind(), sd->postblit->toPrettyChars()); 222 } 223 stop = true; 224 } 225 } 226 227 void visit(NewAnonClassExp *) 228 { 229 assert(0); // should have been lowered by semantic() 230 } 231 }; 232 233 CanThrow ct(func, mustNotThrow); 234 return walkPostorder(e, &ct); 235} 236 237/************************************** 238 * Does symbol, when initialized, throw? 239 * Mirrors logic in Dsymbol_toElem(). 240 */ 241 242bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow) 243{ 244 AttribDeclaration *ad; 245 VarDeclaration *vd; 246 TemplateMixin *tm; 247 TupleDeclaration *td; 248 249 //printf("Dsymbol_toElem() %s\n", s->toChars()); 250 ad = s->isAttribDeclaration(); 251 if (ad) 252 { 253 Dsymbols *decl = ad->include(NULL, NULL); 254 if (decl && decl->dim) 255 { 256 for (size_t i = 0; i < decl->dim; i++) 257 { 258 s = (*decl)[i]; 259 if (Dsymbol_canThrow(s, func, mustNotThrow)) 260 return true; 261 } 262 } 263 } 264 else if ((vd = s->isVarDeclaration()) != NULL) 265 { 266 s = s->toAlias(); 267 if (s != vd) 268 return Dsymbol_canThrow(s, func, mustNotThrow); 269 if (vd->storage_class & STCmanifest) 270 ; 271 else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) 272 ; 273 else 274 { 275 if (vd->_init) 276 { 277 ExpInitializer *ie = vd->_init->isExpInitializer(); 278 if (ie && canThrow(ie->exp, func, mustNotThrow)) 279 return true; 280 } 281 if (vd->needsScopeDtor()) 282 return canThrow(vd->edtor, func, mustNotThrow); 283 } 284 } 285 else if ((tm = s->isTemplateMixin()) != NULL) 286 { 287 //printf("%s\n", tm->toChars()); 288 if (tm->members) 289 { 290 for (size_t i = 0; i < tm->members->dim; i++) 291 { 292 Dsymbol *sm = (*tm->members)[i]; 293 if (Dsymbol_canThrow(sm, func, mustNotThrow)) 294 return true; 295 } 296 } 297 } 298 else if ((td = s->isTupleDeclaration()) != NULL) 299 { 300 for (size_t i = 0; i < td->objects->dim; i++) 301 { 302 RootObject *o = (*td->objects)[i]; 303 if (o->dyncast() == DYNCAST_EXPRESSION) 304 { 305 Expression *eo = (Expression *)o; 306 if (eo->op == TOKdsymbol) 307 { 308 DsymbolExp *se = (DsymbolExp *)eo; 309 if (Dsymbol_canThrow(se->s, func, mustNotThrow)) 310 return true; 311 } 312 } 313 } 314 } 315 return false; 316} 317