1 /** 2 * Perform checks for `nothrow`. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions) 5 * 6 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d, _canthrow.d) 10 * Documentation: https://dlang.org/phobos/dmd_canthrow.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/canthrow.d 12 */ 13 14 module dmd.canthrow; 15 16 import dmd.aggregate; 17 import dmd.apply; 18 import dmd.arraytypes; 19 import dmd.attrib; 20 import dmd.declaration; 21 import dmd.dstruct; 22 import dmd.dsymbol; 23 import dmd.dtemplate; 24 import dmd.expression; 25 import dmd.func; 26 import dmd.globals; 27 import dmd.init; 28 import dmd.mtype; 29 import dmd.root.rootobject; 30 import dmd.tokens; 31 import dmd.visitor; 32 33 /******************************************** 34 * Returns true if the expression may throw exceptions. 35 * If 'mustNotThrow' is true, generate an error if it throws 36 */ 37 extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) 38 { 39 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); 40 // stop walking if we determine this expression can throw 41 extern (C++) final class CanThrow : StoppableVisitor 42 { 43 alias visit = typeof(super).visit; 44 FuncDeclaration func; 45 bool mustNotThrow; 46 47 public: 48 extern (D) this(FuncDeclaration func, bool mustNotThrow) 49 { 50 this.func = func; 51 this.mustNotThrow = mustNotThrow; 52 } 53 54 void checkFuncThrows(Expression e, FuncDeclaration f) 55 { 56 auto tf = f.type.toBasetype().isTypeFunction(); 57 if (tf && !tf.isnothrow) 58 { 59 if (mustNotThrow) 60 { 61 e.error("%s `%s` is not `nothrow`", 62 f.kind(), f.toPrettyChars()); 63 64 e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); 65 } 66 stop = true; // if any function throws, then the whole expression throws 67 } 68 } 69 70 override void visit(Expression) 71 { 72 } 73 74 override void visit(DeclarationExp de) 75 { 76 stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow); 77 } 78 79 override void visit(CallExp ce) 80 { 81 if (ce.inDebugStatement) 82 return; 83 84 if (global.errors && !ce.e1.type) 85 return; // error recovery 86 /* If calling a function or delegate that is typed as nothrow, 87 * then this expression cannot throw. 88 * Note that pure functions can throw. 89 */ 90 if (ce.f && ce.f == func) 91 return; 92 Type t = ce.e1.type.toBasetype(); 93 auto tf = t.isTypeFunction(); 94 if (tf && tf.isnothrow) 95 return; 96 else 97 { 98 auto td = t.isTypeDelegate(); 99 if (td && td.nextOf().isTypeFunction().isnothrow) 100 return; 101 } 102 103 if (ce.f) 104 checkFuncThrows(ce, ce.f); 105 else if (mustNotThrow) 106 { 107 auto e1 = ce.e1; 108 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp) 109 e1 = pe.e1; 110 ce.error("`%s` is not `nothrow`", e1.toChars()); 111 } 112 stop = true; 113 } 114 115 override void visit(NewExp ne) 116 { 117 if (ne.member) 118 { 119 if (ne.allocator) 120 // https://issues.dlang.org/show_bug.cgi?id=14407 121 checkFuncThrows(ne, ne.allocator); 122 123 // See if constructor call can throw 124 checkFuncThrows(ne, ne.member); 125 } 126 // regard storage allocation failures as not recoverable 127 } 128 129 override void visit(DeleteExp de) 130 { 131 Type tb = de.e1.type.toBasetype(); 132 AggregateDeclaration ad = null; 133 switch (tb.ty) 134 { 135 case Tclass: 136 ad = tb.isTypeClass().sym; 137 break; 138 139 case Tpointer: 140 case Tarray: 141 auto ts = tb.nextOf().baseElemOf().isTypeStruct(); 142 if (!ts) 143 return; 144 ad = ts.sym; 145 break; 146 147 default: 148 assert(0); // error should have been detected by semantic() 149 } 150 151 if (ad.dtor) 152 checkFuncThrows(de, ad.dtor); 153 } 154 155 override void visit(AssignExp ae) 156 { 157 // blit-init cannot throw 158 if (ae.op == TOK.blit) 159 return; 160 /* Element-wise assignment could invoke postblits. 161 */ 162 Type t; 163 if (ae.type.toBasetype().ty == Tsarray) 164 { 165 if (!ae.e2.isLvalue()) 166 return; 167 t = ae.type; 168 } 169 else if (auto se = ae.e1.isSliceExp()) 170 t = se.e1.type; 171 else 172 return; 173 174 if (auto ts = t.baseElemOf().isTypeStruct()) 175 if (auto postblit = ts.sym.postblit) 176 checkFuncThrows(ae, postblit); 177 } 178 179 override void visit(NewAnonClassExp) 180 { 181 assert(0); // should have been lowered by semantic() 182 } 183 } 184 185 scope CanThrow ct = new CanThrow(func, mustNotThrow); 186 return walkPostorder(e, ct); 187 } 188 189 /************************************** 190 * Does symbol, when initialized, throw? 191 * Mirrors logic in Dsymbol_toElem(). 192 */ 193 private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow) 194 { 195 int symbolDg(Dsymbol s) 196 { 197 return Dsymbol_canThrow(s, func, mustNotThrow); 198 } 199 200 //printf("Dsymbol_toElem() %s\n", s.toChars()); 201 if (auto vd = s.isVarDeclaration()) 202 { 203 s = s.toAlias(); 204 if (s != vd) 205 return Dsymbol_canThrow(s, func, mustNotThrow); 206 if (vd.storage_class & STC.manifest) 207 { 208 } 209 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared)) 210 { 211 } 212 else 213 { 214 if (vd._init) 215 { 216 if (auto ie = vd._init.isExpInitializer()) 217 if (canThrow(ie.exp, func, mustNotThrow)) 218 return true; 219 } 220 if (vd.needsScopeDtor()) 221 return canThrow(vd.edtor, func, mustNotThrow); 222 } 223 } 224 else if (auto ad = s.isAttribDeclaration()) 225 { 226 return ad.include(null).foreachDsymbol(&symbolDg) != 0; 227 } 228 else if (auto tm = s.isTemplateMixin()) 229 { 230 return tm.members.foreachDsymbol(&symbolDg) != 0; 231 } 232 else if (auto td = s.isTupleDeclaration()) 233 { 234 for (size_t i = 0; i < td.objects.dim; i++) 235 { 236 RootObject o = (*td.objects)[i]; 237 if (o.dyncast() == DYNCAST.expression) 238 { 239 Expression eo = cast(Expression)o; 240 if (auto se = eo.isDsymbolExp()) 241 { 242 if (Dsymbol_canThrow(se.s, func, mustNotThrow)) 243 return true; 244 } 245 } 246 } 247 } 248 return false; 249 }