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-2021 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.dsymbol; 22 import dmd.expression; 23 import dmd.func; 24 import dmd.globals; 25 import dmd.init; 26 import dmd.mtype; 27 import dmd.root.rootobject; 28 import dmd.tokens; 29 import dmd.visitor; 30 31 /******************************************** 32 * Returns true if the expression may throw exceptions. 33 * If 'mustNotThrow' is true, generate an error if it throws 34 */ 35 extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) 36 { 37 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); 38 // stop walking if we determine this expression can throw 39 extern (C++) final class CanThrow : StoppableVisitor 40 { 41 alias visit = typeof(super).visit; 42 FuncDeclaration func; 43 bool mustNotThrow; 44 45 public: 46 extern (D) this(FuncDeclaration func, bool mustNotThrow) 47 { 48 this.func = func; 49 this.mustNotThrow = mustNotThrow; 50 } 51 52 void checkFuncThrows(Expression e, FuncDeclaration f) 53 { 54 auto tf = f.type.toBasetype().isTypeFunction(); 55 if (tf && !tf.isnothrow) 56 { 57 if (mustNotThrow) 58 { 59 e.error("%s `%s` is not `nothrow`", 60 f.kind(), f.toPrettyChars()); 61 62 e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); 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 (ce.inDebugStatement) 80 return; 81 82 if (global.errors && !ce.e1.type) 83 return; // error recovery 84 /* If calling a function or delegate that is typed as nothrow, 85 * then this expression cannot throw. 86 * Note that pure functions can throw. 87 */ 88 if (ce.f && ce.f == func) 89 return; 90 Type t = ce.e1.type.toBasetype(); 91 auto tf = t.isTypeFunction(); 92 if (tf && tf.isnothrow) 93 return; 94 else 95 { 96 auto td = t.isTypeDelegate(); 97 if (td && td.nextOf().isTypeFunction().isnothrow) 98 return; 99 } 100 101 if (ce.f) 102 checkFuncThrows(ce, ce.f); 103 else if (mustNotThrow) 104 { 105 auto e1 = ce.e1; 106 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp) 107 e1 = pe.e1; 108 ce.error("`%s` is not `nothrow`", e1.toChars()); 109 } 110 stop = true; 111 } 112 113 override void visit(NewExp ne) 114 { 115 if (ne.member) 116 { 117 if (ne.allocator) 118 // https://issues.dlang.org/show_bug.cgi?id=14407 119 checkFuncThrows(ne, ne.allocator); 120 121 // See if constructor call can throw 122 checkFuncThrows(ne, ne.member); 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 = tb.isTypeClass().sym; 135 break; 136 137 case Tpointer: 138 case Tarray: 139 auto ts = tb.nextOf().baseElemOf().isTypeStruct(); 140 if (!ts) 141 return; 142 ad = ts.sym; 143 break; 144 145 default: 146 assert(0); // error should have been detected by semantic() 147 } 148 149 if (ad.dtor) 150 checkFuncThrows(de, ad.dtor); 151 } 152 153 override void visit(AssignExp ae) 154 { 155 // blit-init cannot throw 156 if (ae.op == TOK.blit) 157 return; 158 /* Element-wise assignment could invoke postblits. 159 */ 160 Type t; 161 if (ae.type.toBasetype().ty == Tsarray) 162 { 163 if (!ae.e2.isLvalue()) 164 return; 165 t = ae.type; 166 } 167 else if (auto se = ae.e1.isSliceExp()) 168 t = se.e1.type; 169 else 170 return; 171 172 if (auto ts = t.baseElemOf().isTypeStruct()) 173 if (auto postblit = ts.sym.postblit) 174 checkFuncThrows(ae, postblit); 175 } 176 177 override void visit(NewAnonClassExp) 178 { 179 assert(0); // should have been lowered by semantic() 180 } 181 } 182 183 scope CanThrow ct = new CanThrow(func, mustNotThrow); 184 return walkPostorder(e, ct); 185 } 186 187 /************************************** 188 * Does symbol, when initialized, throw? 189 * Mirrors logic in Dsymbol_toElem(). 190 */ 191 private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow) 192 { 193 int symbolDg(Dsymbol s) 194 { 195 return Dsymbol_canThrow(s, func, mustNotThrow); 196 } 197 198 //printf("Dsymbol_toElem() %s\n", s.toChars()); 199 if (auto vd = s.isVarDeclaration()) 200 { 201 s = s.toAlias(); 202 if (s != vd) 203 return Dsymbol_canThrow(s, func, mustNotThrow); 204 if (vd.storage_class & STC.manifest) 205 { 206 } 207 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared)) 208 { 209 } 210 else 211 { 212 if (vd._init) 213 { 214 if (auto ie = vd._init.isExpInitializer()) 215 if (canThrow(ie.exp, func, mustNotThrow)) 216 return true; 217 } 218 if (vd.needsScopeDtor()) 219 return canThrow(vd.edtor, func, mustNotThrow); 220 } 221 } 222 else if (auto ad = s.isAttribDeclaration()) 223 { 224 return ad.include(null).foreachDsymbol(&symbolDg) != 0; 225 } 226 else if (auto tm = s.isTemplateMixin()) 227 { 228 return tm.members.foreachDsymbol(&symbolDg) != 0; 229 } 230 else if (auto td = s.isTupleDeclaration()) 231 { 232 for (size_t i = 0; i < td.objects.dim; i++) 233 { 234 RootObject o = (*td.objects)[i]; 235 if (o.dyncast() == DYNCAST.expression) 236 { 237 Expression eo = cast(Expression)o; 238 if (auto se = eo.isDsymbolExp()) 239 { 240 if (Dsymbol_canThrow(se.s, func, mustNotThrow)) 241 return true; 242 } 243 } 244 } 245 } 246 return false; 247 }