]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/canthrow.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / gcc / d / dmd / canthrow.d
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.astenums;
21 import dmd.declaration;
22 import dmd.dsymbol;
23 import dmd.expression;
24 import dmd.func;
25 import dmd.globals;
26 import dmd.init;
27 import dmd.mtype;
28 import dmd.root.rootobject;
29 import dmd.tokens;
30 import dmd.visitor;
31
32 /********************************************
33 * Returns true if the expression may throw exceptions.
34 * If 'mustNotThrow' is true, generate an error if it throws
35 */
36 extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
37 {
38 //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
39 // stop walking if we determine this expression can throw
40 extern (C++) final class CanThrow : StoppableVisitor
41 {
42 alias visit = typeof(super).visit;
43 FuncDeclaration func;
44 bool mustNotThrow;
45
46 public:
47 extern (D) this(FuncDeclaration func, bool mustNotThrow)
48 {
49 this.func = func;
50 this.mustNotThrow = mustNotThrow;
51 }
52
53 void checkFuncThrows(Expression e, FuncDeclaration f)
54 {
55 auto tf = f.type.toBasetype().isTypeFunction();
56 if (tf && !tf.isnothrow)
57 {
58 if (mustNotThrow)
59 {
60 e.error("%s `%s` is not `nothrow`",
61 f.kind(), f.toPrettyChars());
62
63 e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
64 }
65 stop = true; // if any function throws, then the whole expression throws
66 }
67 }
68
69 override void visit(Expression)
70 {
71 }
72
73 override void visit(DeclarationExp de)
74 {
75 stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
76 }
77
78 override void visit(CallExp ce)
79 {
80 if (ce.inDebugStatement)
81 return;
82
83 if (global.errors && !ce.e1.type)
84 return; // error recovery
85 /* If calling a function or delegate that is typed as nothrow,
86 * then this expression cannot throw.
87 * Note that pure functions can throw.
88 */
89 if (ce.f && ce.f == func)
90 return;
91 Type t = ce.e1.type.toBasetype();
92 auto tf = t.isTypeFunction();
93 if (tf && tf.isnothrow)
94 return;
95 else
96 {
97 auto td = t.isTypeDelegate();
98 if (td && td.nextOf().isTypeFunction().isnothrow)
99 return;
100 }
101
102 if (ce.f)
103 checkFuncThrows(ce, ce.f);
104 else if (mustNotThrow)
105 {
106 auto e1 = ce.e1;
107 if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp)
108 e1 = pe.e1;
109 ce.error("`%s` is not `nothrow`", e1.toChars());
110 }
111 stop = true;
112 }
113
114 override void visit(NewExp ne)
115 {
116 if (ne.member)
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 }