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 stop = true; // if any function throws, then the whole expression throws 65 } 66 } 67 68 override void visit(Expression) 69 { 70 } 71 72 override void visit(DeclarationExp de) 73 { 74 stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow); 75 } 76 77 override void visit(CallExp ce) 78 { 79 if (global.errors && !ce.e1.type) 80 return; // error recovery 81 /* If calling a function or delegate that is typed as nothrow, 82 * then this expression cannot throw. 83 * Note that pure functions can throw. 84 */ 85 if (ce.f && ce.f == func) 86 return; 87 Type t = ce.e1.type.toBasetype(); 88 auto tf = t.isTypeFunction(); 89 if (tf && tf.isnothrow) 90 return; 91 else 92 { 93 auto td = t.isTypeDelegate(); 94 if (td && td.nextOf().isTypeFunction().isnothrow) 95 return; 96 } 97 98 if (ce.f) 99 checkFuncThrows(ce, ce.f); 100 else if (mustNotThrow) 101 { 102 auto e1 = ce.e1; 103 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp) 104 e1 = pe.e1; 105 ce.error("`%s` is not `nothrow`", e1.toChars()); 106 } 107 stop = true; 108 } 109 110 override void visit(NewExp ne) 111 { 112 if (ne.member) 113 { 114 if (ne.allocator) 115 // https://issues.dlang.org/show_bug.cgi?id=14407 116 checkFuncThrows(ne, ne.allocator); 117 118 // See if constructor call can throw 119 checkFuncThrows(ne, ne.member); 120 } 121 // regard storage allocation failures as not recoverable 122 } 123 124 override void visit(DeleteExp de) 125 { 126 Type tb = de.e1.type.toBasetype(); 127 AggregateDeclaration ad = null; 128 switch (tb.ty) 129 { 130 case Tclass: 131 ad = tb.isTypeClass().sym; 132 break; 133 134 case Tpointer: 135 case Tarray: 136 auto ts = tb.nextOf().baseElemOf().isTypeStruct(); 137 if (!ts) 138 return; 139 ad = ts.sym; 140 break; 141 142 default: 143 assert(0); // error should have been detected by semantic() 144 } 145 146 if (ad.dtor) 147 checkFuncThrows(de, ad.dtor); 148 } 149 150 override void visit(AssignExp ae) 151 { 152 // blit-init cannot throw 153 if (ae.op == TOK.blit) 154 return; 155 /* Element-wise assignment could invoke postblits. 156 */ 157 Type t; 158 if (ae.type.toBasetype().ty == Tsarray) 159 { 160 if (!ae.e2.isLvalue()) 161 return; 162 t = ae.type; 163 } 164 else if (auto se = ae.e1.isSliceExp()) 165 t = se.e1.type; 166 else 167 return; 168 169 if (auto ts = t.baseElemOf().isTypeStruct()) 170 if (auto postblit = ts.sym.postblit) 171 checkFuncThrows(ae, postblit); 172 } 173 174 override void visit(NewAnonClassExp) 175 { 176 assert(0); // should have been lowered by semantic() 177 } 178 } 179 180 scope CanThrow ct = new CanThrow(func, mustNotThrow); 181 return walkPostorder(e, ct); 182 } 183 184 /************************************** 185 * Does symbol, when initialized, throw? 186 * Mirrors logic in Dsymbol_toElem(). 187 */ 188 private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow) 189 { 190 int symbolDg(Dsymbol s) 191 { 192 return Dsymbol_canThrow(s, func, mustNotThrow); 193 } 194 195 //printf("Dsymbol_toElem() %s\n", s.toChars()); 196 if (auto vd = s.isVarDeclaration()) 197 { 198 s = s.toAlias(); 199 if (s != vd) 200 return Dsymbol_canThrow(s, func, mustNotThrow); 201 if (vd.storage_class & STC.manifest) 202 { 203 } 204 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared)) 205 { 206 } 207 else 208 { 209 if (vd._init) 210 { 211 if (auto ie = vd._init.isExpInitializer()) 212 if (canThrow(ie.exp, func, mustNotThrow)) 213 return true; 214 } 215 if (vd.needsScopeDtor()) 216 return canThrow(vd.edtor, func, mustNotThrow); 217 } 218 } 219 else if (auto ad = s.isAttribDeclaration()) 220 { 221 return ad.include(null).foreachDsymbol(&symbolDg) != 0; 222 } 223 else if (auto tm = s.isTemplateMixin()) 224 { 225 return tm.members.foreachDsymbol(&symbolDg) != 0; 226 } 227 else if (auto td = s.isTupleDeclaration()) 228 { 229 for (size_t i = 0; i < td.objects.dim; i++) 230 { 231 RootObject o = (*td.objects)[i]; 232 if (o.dyncast() == DYNCAST.expression) 233 { 234 Expression eo = cast(Expression)o; 235 if (auto se = eo.isDsymbolExp()) 236 { 237 if (Dsymbol_canThrow(se.s, func, mustNotThrow)) 238 return true; 239 } 240 } 241 } 242 } 243 return false; 244 }