1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _canthrow.d) 9 */ 10 11 module ddmd.canthrow; 12 13 import ddmd.aggregate; 14 import ddmd.apply; 15 import ddmd.arraytypes; 16 import ddmd.attrib; 17 import ddmd.declaration; 18 import ddmd.dstruct; 19 import ddmd.dsymbol; 20 import ddmd.dtemplate; 21 import ddmd.expression; 22 import ddmd.func; 23 import ddmd.globals; 24 import ddmd.init; 25 import ddmd.mtype; 26 import ddmd.root.rootobject; 27 import ddmd.tokens; 28 import ddmd.visitor; 29 30 /******************************************** 31 * Returns true if the expression may throw exceptions. 32 * If 'mustNotThrow' is true, generate an error if it throws 33 */ 34 extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) 35 { 36 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); 37 // stop walking if we determine this expression can throw 38 extern (C++) final class CanThrow : StoppableVisitor 39 { 40 alias visit = super.visit; 41 FuncDeclaration func; 42 bool mustNotThrow; 43 44 public: 45 extern (D) this(FuncDeclaration func, bool mustNotThrow) 46 { 47 this.func = func; 48 this.mustNotThrow = mustNotThrow; 49 } 50 51 override void visit(Expression) 52 { 53 } 54 55 override void visit(DeclarationExp de) 56 { 57 stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow); 58 } 59 60 override void visit(CallExp ce) 61 { 62 if (global.errors && !ce.e1.type) 63 return; // error recovery 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 && (cast(TypeFunction)t).isnothrow) 72 return; 73 if (t.ty == Tdelegate && (cast(TypeFunction)(cast(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 auto e1 = ce.e1; 86 if (e1.op == TOKstar) // print 'fp' if e1 is (*fp) 87 e1 = (cast(PtrExp)e1).e1; 88 ce.error("'%s' is not nothrow", e1.toChars()); 89 } 90 } 91 stop = true; 92 } 93 94 override 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 && !(cast(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 && !(cast(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 override 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 = (cast(TypeClass)tb).sym; 135 break; 136 137 case Tpointer: 138 tb = (cast(TypePointer)tb).next.toBasetype(); 139 if (tb.ty == Tstruct) 140 ad = (cast(TypeStruct)tb).sym; 141 break; 142 143 case Tarray: 144 Type tv = tb.nextOf().baseElemOf(); 145 if (tv.ty == Tstruct) 146 ad = (cast(TypeStruct)tv).sym; 147 break; 148 149 default: 150 break; 151 } 152 if (!ad) 153 return; 154 155 if (ad.dtor) 156 { 157 Type t = ad.dtor.type.toBasetype(); 158 if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow) 159 { 160 if (mustNotThrow) 161 { 162 de.error("%s '%s' is not nothrow", 163 ad.dtor.kind(), ad.dtor.toPrettyChars()); 164 } 165 stop = true; 166 } 167 } 168 if (ad.aggDelete && tb.ty != Tarray) 169 { 170 Type t = ad.aggDelete.type; 171 if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow) 172 { 173 if (mustNotThrow) 174 { 175 de.error("%s '%s' is not nothrow", 176 ad.aggDelete.kind(), ad.aggDelete.toPrettyChars()); 177 } 178 stop = true; 179 } 180 } 181 } 182 183 override void visit(AssignExp ae) 184 { 185 // blit-init cannot throw 186 if (ae.op == TOKblit) 187 return; 188 /* Element-wise assignment could invoke postblits. 189 */ 190 Type t; 191 if (ae.type.toBasetype().ty == Tsarray) 192 { 193 if (!ae.e2.isLvalue()) 194 return; 195 t = ae.type; 196 } 197 else if (ae.e1.op == TOKslice) 198 t = (cast(SliceExp)ae.e1).e1.type; 199 else 200 return; 201 Type tv = t.baseElemOf(); 202 if (tv.ty != Tstruct) 203 return; 204 StructDeclaration sd = (cast(TypeStruct)tv).sym; 205 if (!sd.postblit || sd.postblit.type.ty != Tfunction) 206 return; 207 if ((cast(TypeFunction)sd.postblit.type).isnothrow) 208 { 209 } 210 else 211 { 212 if (mustNotThrow) 213 { 214 ae.error("%s '%s' is not nothrow", 215 sd.postblit.kind(), sd.postblit.toPrettyChars()); 216 } 217 stop = true; 218 } 219 } 220 221 override void visit(NewAnonClassExp) 222 { 223 assert(0); // should have been lowered by semantic() 224 } 225 } 226 227 scope CanThrow ct = new CanThrow(func, mustNotThrow); 228 return walkPostorder(e, ct); 229 } 230 231 /************************************** 232 * Does symbol, when initialized, throw? 233 * Mirrors logic in Dsymbol_toElem(). 234 */ 235 extern (C++) bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow) 236 { 237 AttribDeclaration ad; 238 VarDeclaration vd; 239 TemplateMixin tm; 240 TupleDeclaration td; 241 //printf("Dsymbol_toElem() %s\n", s->toChars()); 242 ad = s.isAttribDeclaration(); 243 if (ad) 244 { 245 Dsymbols* decl = ad.include(null, null); 246 if (decl && decl.dim) 247 { 248 for (size_t i = 0; i < decl.dim; i++) 249 { 250 s = (*decl)[i]; 251 if (Dsymbol_canThrow(s, func, mustNotThrow)) 252 return true; 253 } 254 } 255 } 256 else if ((vd = s.isVarDeclaration()) !is null) 257 { 258 s = s.toAlias(); 259 if (s != vd) 260 return Dsymbol_canThrow(s, func, mustNotThrow); 261 if (vd.storage_class & STCmanifest) 262 { 263 } 264 else if (vd.isStatic() || vd.storage_class & (STCextern | STCtls | STCgshared)) 265 { 266 } 267 else 268 { 269 if (vd._init) 270 { 271 ExpInitializer ie = vd._init.isExpInitializer(); 272 if (ie && canThrow(ie.exp, func, mustNotThrow)) 273 return true; 274 } 275 if (vd.needsScopeDtor()) 276 return canThrow(vd.edtor, func, mustNotThrow); 277 } 278 } 279 else if ((tm = s.isTemplateMixin()) !is null) 280 { 281 //printf("%s\n", tm->toChars()); 282 if (tm.members) 283 { 284 for (size_t i = 0; i < tm.members.dim; i++) 285 { 286 Dsymbol sm = (*tm.members)[i]; 287 if (Dsymbol_canThrow(sm, func, mustNotThrow)) 288 return true; 289 } 290 } 291 } 292 else if ((td = s.isTupleDeclaration()) !is null) 293 { 294 for (size_t i = 0; i < td.objects.dim; i++) 295 { 296 RootObject o = (*td.objects)[i]; 297 if (o.dyncast() == DYNCAST_EXPRESSION) 298 { 299 Expression eo = cast(Expression)o; 300 if (eo.op == TOKdsymbol) 301 { 302 DsymbolExp se = cast(DsymbolExp)eo; 303 if (Dsymbol_canThrow(se.s, func, mustNotThrow)) 304 return true; 305 } 306 } 307 } 308 } 309 return false; 310 }