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-2023 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 https://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.arraytypes; 18 import dmd.attrib; 19 import dmd.astenums; 20 import dmd.blockexit : BE, checkThrow; 21 import dmd.declaration; 22 import dmd.dsymbol; 23 import dmd.errorsink; 24 import dmd.expression; 25 import dmd.func; 26 import dmd.globals; 27 import dmd.init; 28 import dmd.mtype; 29 import dmd.postordervisitor; 30 import dmd.tokens; 31 import dmd.visitor; 32 33 /** 34 * Status indicating what kind of throwable might be caused by an expression. 35 * 36 * This is a subset of `BE` restricted to the values actually used by `canThrow`. 37 */ 38 enum CT : BE 39 { 40 /// Never throws an `Exception` or `Throwable` 41 none = BE.none, 42 43 /// Might throw an `Exception` 44 exception = BE.throw_, 45 46 // Might throw an `Error` 47 error = BE.errthrow, 48 } 49 50 /******************************************** 51 * If `eSink` is not null, generate an error if `e` throws 52 * Params: 53 * e = expression to check for throwing 54 * func = function 55 * eSink = if !null, then send error messages to eSink 56 * Returns: `CT.exception` or `CT.error` if the expression may throw exceptions. 57 */ 58 CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) 59 { 60 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, e.toChars()); 61 // stop walking if we determine this expression can throw 62 extern (C++) final class CanThrow : StoppableVisitor 63 { 64 alias visit = typeof(super).visit; 65 CT result; 66 67 public: 68 extern (D) this() scope @safe 69 { 70 } 71 72 void checkFuncThrows(Expression e, FuncDeclaration f) 73 { 74 auto tf = f.type.toBasetype().isTypeFunction(); 75 if (tf && !tf.isnothrow) 76 { 77 if (eSink) 78 { 79 eSink.error(e.loc, "%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); 80 if (!f.isDtorDeclaration()) 81 errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); 82 83 e.checkOverriddenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); 84 } 85 else if (func) 86 { 87 func.setThrowCall(e.loc, f); 88 } 89 result |= CT.exception; 90 } 91 } 92 93 override void visit(Expression) 94 { 95 } 96 97 override void visit(DeclarationExp de) 98 { 99 result |= Dsymbol_canThrow(de.declaration, func, eSink); 100 } 101 102 override void visit(CallExp ce) 103 { 104 if (ce.inDebugStatement) 105 return; 106 107 if (global.errors && !ce.e1.type) 108 return; // error recovery 109 110 if (ce.f && ce.arguments.length > 0) 111 { 112 Type tb = (*ce.arguments)[0].type.toBasetype(); 113 auto tbNext = tb.nextOf(); 114 if (tbNext) 115 { 116 auto ts = tbNext.baseElemOf().isTypeStruct(); 117 if (ts) 118 { 119 auto sd = ts.sym; 120 const id = ce.f.ident; 121 if (sd.postblit && isArrayConstruction(id)) 122 { 123 checkFuncThrows(ce, sd.postblit); 124 return; 125 } 126 } 127 } 128 } 129 130 /* If calling a function or delegate that is typed as nothrow, 131 * then this expression cannot throw. 132 * Note that pure functions can throw. 133 */ 134 if (ce.f && ce.f == func) 135 return; 136 const tf = ce.calledFunctionType(); 137 if (tf && tf.isnothrow) 138 return; 139 140 if (ce.f) 141 checkFuncThrows(ce, ce.f); 142 else if (eSink) 143 { 144 auto e1 = ce.e1; 145 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp) 146 e1 = pe.e1; 147 eSink.error(ce.loc, "`%s` is not `nothrow`", e1.toChars()); 148 } 149 result |= CT.exception; 150 } 151 152 override void visit(NewExp ne) 153 { 154 if (ne.member) 155 { 156 // See if constructor call can throw 157 checkFuncThrows(ne, ne.member); 158 } 159 // regard storage allocation failures as not recoverable 160 } 161 162 override void visit(DeleteExp de) 163 { 164 Type tb = de.e1.type.toBasetype(); 165 AggregateDeclaration ad = null; 166 switch (tb.ty) 167 { 168 case Tclass: 169 ad = tb.isTypeClass().sym; 170 break; 171 172 default: 173 assert(0); // error should have been detected by semantic() 174 } 175 176 if (ad.dtor) 177 checkFuncThrows(de, ad.dtor); 178 } 179 180 override void visit(AssignExp ae) 181 { 182 // blit-init cannot throw 183 if (ae.op == EXP.blit) 184 return; 185 /* Element-wise assignment could invoke postblits. 186 */ 187 Type t; 188 if (ae.type.toBasetype().ty == Tsarray) 189 { 190 if (!ae.e2.isLvalue()) 191 return; 192 t = ae.type; 193 } 194 else if (auto se = ae.e1.isSliceExp()) 195 t = se.e1.type; 196 else 197 return; 198 199 if (auto ts = t.baseElemOf().isTypeStruct()) 200 if (auto postblit = ts.sym.postblit) 201 checkFuncThrows(ae, postblit); 202 } 203 204 override void visit(ThrowExp te) 205 { 206 const res = checkThrow(te.loc, te.e1, func, eSink); 207 assert((res & ~(CT.exception | CT.error)) == 0); 208 result |= res; 209 } 210 211 override void visit(NewAnonClassExp) 212 { 213 assert(0); // should have been lowered by semantic() 214 } 215 } 216 217 scope CanThrow ct = new CanThrow(); 218 walkPostorder(e, ct); 219 return ct.result; 220 } 221 222 /************************************** 223 * Does symbol `s`, when initialized, throw? 224 * Mirrors logic in Dsymbol_toElem(). 225 */ 226 private CT Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, ErrorSink eSink) 227 { 228 CT result; 229 230 int symbolDg(Dsymbol s) 231 { 232 result |= Dsymbol_canThrow(s, func, eSink); 233 return 0; 234 } 235 236 //printf("Dsymbol_toElem() %s\n", s.toChars()); 237 if (auto vd = s.isVarDeclaration()) 238 { 239 s = s.toAlias(); 240 if (s != vd) 241 return Dsymbol_canThrow(s, func, eSink); 242 if (vd.storage_class & STC.manifest) 243 { 244 } 245 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared)) 246 { 247 } 248 else 249 { 250 if (vd._init) 251 { 252 if (auto ie = vd._init.isExpInitializer()) 253 result |= canThrow(ie.exp, func, eSink); 254 } 255 if (vd.needsScopeDtor()) 256 result |= canThrow(vd.edtor, func, eSink); 257 } 258 } 259 else if (auto ad = s.isAttribDeclaration()) 260 { 261 ad.include(null).foreachDsymbol(&symbolDg); 262 } 263 else if (auto tm = s.isTemplateMixin()) 264 { 265 tm.members.foreachDsymbol(&symbolDg); 266 } 267 else if (auto td = s.isTupleDeclaration()) 268 { 269 td.foreachVar(&symbolDg); 270 } 271 return result; 272 }