xref: /netbsd/external/gpl3/gcc/dist/gcc/d/dmd/canthrow.d (revision f0fbc68b)
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-2022 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.apply;
18 import dmd.arraytypes;
19 import dmd.attrib;
20 import dmd.astenums;
21 import dmd.blockexit : BE, checkThrow;
22 import dmd.declaration;
23 import dmd.dsymbol;
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  * 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  * Returns true if the expression may throw exceptions.
52  * If 'mustNotThrow' is true, generate an error if it throws
53  */
canThrow(Expression e,FuncDeclaration func,bool mustNotThrow)54 extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
55 {
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
59     {
60         alias visit = typeof(super).visit;
61         FuncDeclaration func;
62         bool mustNotThrow;
63         CT result;
64 
65     public:
66         extern (D) this(FuncDeclaration func, bool mustNotThrow)
67         {
68             this.func = func;
69             this.mustNotThrow = mustNotThrow;
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 (mustNotThrow)
78                 {
79                     e.error("%s `%s` is not `nothrow`",
80                         f.kind(), f.toPrettyChars());
81 
82                     e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
83                 }
84                 result |= CT.exception;
85             }
86         }
87 
88         override void visit(Expression)
89         {
90         }
91 
92         override void visit(DeclarationExp de)
93         {
94             result |= Dsymbol_canThrow(de.declaration, func, mustNotThrow);
95         }
96 
97         override void visit(CallExp ce)
98         {
99             if (ce.inDebugStatement)
100                 return;
101 
102             if (global.errors && !ce.e1.type)
103                 return; // error recovery
104 
105             if (ce.f && ce.arguments.dim > 0)
106             {
107                 Type tb = (*ce.arguments)[0].type.toBasetype();
108                 auto tbNext = tb.nextOf();
109                 if (tbNext)
110                 {
111                     auto ts = tbNext.baseElemOf().isTypeStruct();
112                     if (ts)
113                     {
114                         import dmd.id : Id;
115 
116                         auto sd = ts.sym;
117                         if (sd.postblit &&
118                             (ce.f.ident == Id._d_arrayctor || ce.f.ident == Id._d_arraysetctor))
119                         {
120                             checkFuncThrows(ce, sd.postblit);
121                             return;
122                         }
123                     }
124                 }
125             }
126 
127             /* If calling a function or delegate that is typed as nothrow,
128              * then this expression cannot throw.
129              * Note that pure functions can throw.
130              */
131             if (ce.f && ce.f == func)
132                 return;
133             Type t = ce.e1.type.toBasetype();
134             auto tf = t.isTypeFunction();
135             if (tf && tf.isnothrow)
136                 return;
137             else
138             {
139                 auto td = t.isTypeDelegate();
140                 if (td && td.nextOf().isTypeFunction().isnothrow)
141                     return;
142             }
143 
144             if (ce.f)
145                 checkFuncThrows(ce, ce.f);
146             else if (mustNotThrow)
147             {
148                 auto e1 = ce.e1;
149                 if (auto pe = e1.isPtrExp())   // print 'fp' if e1 is (*fp)
150                     e1 = pe.e1;
151                 ce.error("`%s` is not `nothrow`", e1.toChars());
152             }
153             result |= CT.exception;
154         }
155 
156         override void visit(NewExp ne)
157         {
158             if (ne.member)
159             {
160                 // See if constructor call can throw
161                 checkFuncThrows(ne, ne.member);
162             }
163             // regard storage allocation failures as not recoverable
164         }
165 
166         override void visit(DeleteExp de)
167         {
168             Type tb = de.e1.type.toBasetype();
169             AggregateDeclaration ad = null;
170             switch (tb.ty)
171             {
172             case Tclass:
173                 ad = tb.isTypeClass().sym;
174                 break;
175 
176             default:
177                 assert(0);  // error should have been detected by semantic()
178             }
179 
180             if (ad.dtor)
181                 checkFuncThrows(de, ad.dtor);
182         }
183 
184         override void visit(AssignExp ae)
185         {
186             // blit-init cannot throw
187             if (ae.op == EXP.blit)
188                 return;
189             /* Element-wise assignment could invoke postblits.
190              */
191             Type t;
192             if (ae.type.toBasetype().ty == Tsarray)
193             {
194                 if (!ae.e2.isLvalue())
195                     return;
196                 t = ae.type;
197             }
198             else if (auto se = ae.e1.isSliceExp())
199                 t = se.e1.type;
200             else
201                 return;
202 
203             if (auto ts = t.baseElemOf().isTypeStruct())
204                 if (auto postblit = ts.sym.postblit)
205                     checkFuncThrows(ae, postblit);
206         }
207 
208         override void visit(ThrowExp te)
209         {
210             const res = checkThrow(te.loc, te.e1, mustNotThrow);
211             assert((res & ~(CT.exception | CT.error)) == 0);
212             result |= res;
213         }
214 
215         override void visit(NewAnonClassExp)
216         {
217             assert(0); // should have been lowered by semantic()
218         }
219     }
220 
221     scope CanThrow ct = new CanThrow(func, mustNotThrow);
222     walkPostorder(e, ct);
223     return ct.result;
224 }
225 
226 /**************************************
227  * Does symbol, when initialized, throw?
228  * Mirrors logic in Dsymbol_toElem().
229  */
Dsymbol_canThrow(Dsymbol s,FuncDeclaration func,bool mustNotThrow)230 private CT Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
231 {
232     CT result;
233 
234     int symbolDg(Dsymbol s)
235     {
236         result |= Dsymbol_canThrow(s, func, mustNotThrow);
237         return 0;
238     }
239 
240     //printf("Dsymbol_toElem() %s\n", s.toChars());
241     if (auto vd = s.isVarDeclaration())
242     {
243         s = s.toAlias();
244         if (s != vd)
245             return Dsymbol_canThrow(s, func, mustNotThrow);
246         if (vd.storage_class & STC.manifest)
247         {
248         }
249         else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.gshared))
250         {
251         }
252         else
253         {
254             if (vd._init)
255             {
256                 if (auto ie = vd._init.isExpInitializer())
257                     result |= canThrow(ie.exp, func, mustNotThrow);
258             }
259             if (vd.needsScopeDtor())
260                 result |= canThrow(vd.edtor, func, mustNotThrow);
261         }
262     }
263     else if (auto ad = s.isAttribDeclaration())
264     {
265         ad.include(null).foreachDsymbol(&symbolDg);
266     }
267     else if (auto tm = s.isTemplateMixin())
268     {
269         tm.members.foreachDsymbol(&symbolDg);
270     }
271     else if (auto td = s.isTupleDeclaration())
272     {
273         for (size_t i = 0; i < td.objects.dim; i++)
274         {
275             RootObject o = (*td.objects)[i];
276             if (o.dyncast() == DYNCAST.expression)
277             {
278                 Expression eo = cast(Expression)o;
279                 if (auto se = eo.isDsymbolExp())
280                 {
281                     result |= Dsymbol_canThrow(se.s, func, mustNotThrow);
282                 }
283             }
284         }
285     }
286     return result;
287 }
288