2 * Perform checks for `nothrow`.
4 * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions)
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
18 import dmd.arraytypes;
21 import dmd.blockexit : BE, checkThrow;
22 import dmd.declaration;
24 import dmd.expression;
29 import dmd.root.rootobject;
34 * Status indicating what kind of throwable might be caused by an expression.
36 * This is a subset of `BE` restricted to the values actually used by `canThrow`.
40 /// Never throws an `Exception` or `Throwable`
43 /// Might throw an `Exception`
44 exception = BE.throw_,
46 // Might throw an `Error`
50 /********************************************
51 * Returns true if the expression may throw exceptions.
52 * If 'mustNotThrow' is true, generate an error if it throws
54 extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
56 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
57 // stop walking if we determine this expression can throw
58 extern (C++) final class CanThrow : StoppableVisitor
60 alias visit = typeof(super).visit;
66 extern (D) this(FuncDeclaration func, bool mustNotThrow) scope
69 this.mustNotThrow = mustNotThrow;
72 void checkFuncThrows(Expression e, FuncDeclaration f)
74 auto tf = f.type.toBasetype().isTypeFunction();
75 if (tf && !tf.isnothrow)
79 e.error("%s `%s` is not `nothrow`",
80 f.kind(), f.toPrettyChars());
82 e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
84 result |= CT.exception;
88 override void visit(Expression)
92 override void visit(DeclarationExp de)
94 result |= Dsymbol_canThrow(de.declaration, func, mustNotThrow);
97 override void visit(CallExp ce)
99 if (ce.inDebugStatement)
102 if (global.errors && !ce.e1.type)
103 return; // error recovery
105 if (ce.f && ce.arguments.length > 0)
107 Type tb = (*ce.arguments)[0].type.toBasetype();
108 auto tbNext = tb.nextOf();
111 auto ts = tbNext.baseElemOf().isTypeStruct();
115 const id = ce.f.ident;
116 if (sd.postblit && isArrayConstructionOrAssign(id))
118 checkFuncThrows(ce, sd.postblit);
125 /* If calling a function or delegate that is typed as nothrow,
126 * then this expression cannot throw.
127 * Note that pure functions can throw.
129 if (ce.f && ce.f == func)
131 Type t = ce.e1.type.toBasetype();
132 auto tf = t.isTypeFunction();
133 if (tf && tf.isnothrow)
137 auto td = t.isTypeDelegate();
138 if (td && td.nextOf().isTypeFunction().isnothrow)
143 checkFuncThrows(ce, ce.f);
144 else if (mustNotThrow)
147 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp)
149 ce.error("`%s` is not `nothrow`", e1.toChars());
151 result |= CT.exception;
154 override void visit(NewExp ne)
158 // See if constructor call can throw
159 checkFuncThrows(ne, ne.member);
161 // regard storage allocation failures as not recoverable
164 override void visit(DeleteExp de)
166 Type tb = de.e1.type.toBasetype();
167 AggregateDeclaration ad = null;
171 ad = tb.isTypeClass().sym;
175 assert(0); // error should have been detected by semantic()
179 checkFuncThrows(de, ad.dtor);
182 override void visit(AssignExp ae)
184 // blit-init cannot throw
185 if (ae.op == EXP.blit)
187 /* Element-wise assignment could invoke postblits.
190 if (ae.type.toBasetype().ty == Tsarray)
192 if (!ae.e2.isLvalue())
196 else if (auto se = ae.e1.isSliceExp())
201 if (auto ts = t.baseElemOf().isTypeStruct())
202 if (auto postblit = ts.sym.postblit)
203 checkFuncThrows(ae, postblit);
206 override void visit(ThrowExp te)
208 const res = checkThrow(te.loc, te.e1, mustNotThrow);
209 assert((res & ~(CT.exception | CT.error)) == 0);
213 override void visit(NewAnonClassExp)
215 assert(0); // should have been lowered by semantic()
219 scope CanThrow ct = new CanThrow(func, mustNotThrow);
220 walkPostorder(e, ct);
224 /**************************************
225 * Does symbol, when initialized, throw?
226 * Mirrors logic in Dsymbol_toElem().
228 private CT Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
232 int symbolDg(Dsymbol s)
234 result |= Dsymbol_canThrow(s, func, mustNotThrow);
238 //printf("Dsymbol_toElem() %s\n", s.toChars());
239 if (auto vd = s.isVarDeclaration())
243 return Dsymbol_canThrow(s, func, mustNotThrow);
244 if (vd.storage_class & STC.manifest)
247 else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared))
254 if (auto ie = vd._init.isExpInitializer())
255 result |= canThrow(ie.exp, func, mustNotThrow);
257 if (vd.needsScopeDtor())
258 result |= canThrow(vd.edtor, func, mustNotThrow);
261 else if (auto ad = s.isAttribDeclaration())
263 ad.include(null).foreachDsymbol(&symbolDg);
265 else if (auto tm = s.isTemplateMixin())
267 tm.members.foreachDsymbol(&symbolDg);
269 else if (auto td = s.isTupleDeclaration())
271 td.foreachVar(&symbolDg);