1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _canthrow.d)
9  */
10 
11 module ddmd.canthrow;
12 
13 import ddmd.aggregate;
14 import ddmd.apply;
15 import ddmd.arraytypes;
16 import ddmd.attrib;
17 import ddmd.declaration;
18 import ddmd.dstruct;
19 import ddmd.dsymbol;
20 import ddmd.dtemplate;
21 import ddmd.expression;
22 import ddmd.func;
23 import ddmd.globals;
24 import ddmd.init;
25 import ddmd.mtype;
26 import ddmd.root.rootobject;
27 import ddmd.tokens;
28 import ddmd.visitor;
29 
30 /********************************************
31  * Returns true if the expression may throw exceptions.
32  * If 'mustNotThrow' is true, generate an error if it throws
33  */
34 extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
35 {
36     //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
37     // stop walking if we determine this expression can throw
38     extern (C++) final class CanThrow : StoppableVisitor
39     {
40         alias visit = super.visit;
41         FuncDeclaration func;
42         bool mustNotThrow;
43 
44     public:
45         extern (D) this(FuncDeclaration func, bool mustNotThrow)
46         {
47             this.func = func;
48             this.mustNotThrow = mustNotThrow;
49         }
50 
51         override void visit(Expression)
52         {
53         }
54 
55         override void visit(DeclarationExp de)
56         {
57             stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
58         }
59 
60         override void visit(CallExp ce)
61         {
62             if (global.errors && !ce.e1.type)
63                 return; // error recovery
64             /* If calling a function or delegate that is typed as nothrow,
65              * then this expression cannot throw.
66              * Note that pure functions can throw.
67              */
68             Type t = ce.e1.type.toBasetype();
69             if (ce.f && ce.f == func)
70                 return;
71             if (t.ty == Tfunction && (cast(TypeFunction)t).isnothrow)
72                 return;
73             if (t.ty == Tdelegate && (cast(TypeFunction)(cast(TypeDelegate)t).next).isnothrow)
74                 return;
75 
76             if (mustNotThrow)
77             {
78                 if (ce.f)
79                 {
80                     ce.error("%s '%s' is not nothrow",
81                         ce.f.kind(), ce.f.toPrettyChars());
82                 }
83                 else
84                 {
85                     auto e1 = ce.e1;
86                     if (e1.op == TOKstar)   // print 'fp' if e1 is (*fp)
87                         e1 = (cast(PtrExp)e1).e1;
88                     ce.error("'%s' is not nothrow", e1.toChars());
89                 }
90             }
91             stop = true;
92         }
93 
94         override void visit(NewExp ne)
95         {
96             if (ne.member)
97             {
98                 if (ne.allocator)
99                 {
100                     // Bugzilla 14407
101                     Type t = ne.allocator.type.toBasetype();
102                     if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
103                     {
104                         if (mustNotThrow)
105                         {
106                             ne.error("%s '%s' is not nothrow",
107                                 ne.allocator.kind(), ne.allocator.toPrettyChars());
108                         }
109                         stop = true;
110                     }
111                 }
112                 // See if constructor call can throw
113                 Type t = ne.member.type.toBasetype();
114                 if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
115                 {
116                     if (mustNotThrow)
117                     {
118                         ne.error("%s '%s' is not nothrow",
119                             ne.member.kind(), ne.member.toPrettyChars());
120                     }
121                     stop = true;
122                 }
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 = (cast(TypeClass)tb).sym;
135                 break;
136 
137             case Tpointer:
138                 tb = (cast(TypePointer)tb).next.toBasetype();
139                 if (tb.ty == Tstruct)
140                     ad = (cast(TypeStruct)tb).sym;
141                 break;
142 
143             case Tarray:
144                 Type tv = tb.nextOf().baseElemOf();
145                 if (tv.ty == Tstruct)
146                     ad = (cast(TypeStruct)tv).sym;
147                 break;
148 
149             default:
150                 break;
151             }
152             if (!ad)
153                 return;
154 
155             if (ad.dtor)
156             {
157                 Type t = ad.dtor.type.toBasetype();
158                 if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
159                 {
160                     if (mustNotThrow)
161                     {
162                         de.error("%s '%s' is not nothrow",
163                             ad.dtor.kind(), ad.dtor.toPrettyChars());
164                     }
165                     stop = true;
166                 }
167             }
168             if (ad.aggDelete && tb.ty != Tarray)
169             {
170                 Type t = ad.aggDelete.type;
171                 if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
172                 {
173                     if (mustNotThrow)
174                     {
175                         de.error("%s '%s' is not nothrow",
176                             ad.aggDelete.kind(), ad.aggDelete.toPrettyChars());
177                     }
178                     stop = true;
179                 }
180             }
181         }
182 
183         override void visit(AssignExp ae)
184         {
185             // blit-init cannot throw
186             if (ae.op == TOKblit)
187                 return;
188             /* Element-wise assignment could invoke postblits.
189              */
190             Type t;
191             if (ae.type.toBasetype().ty == Tsarray)
192             {
193                 if (!ae.e2.isLvalue())
194                     return;
195                 t = ae.type;
196             }
197             else if (ae.e1.op == TOKslice)
198                 t = (cast(SliceExp)ae.e1).e1.type;
199             else
200                 return;
201             Type tv = t.baseElemOf();
202             if (tv.ty != Tstruct)
203                 return;
204             StructDeclaration sd = (cast(TypeStruct)tv).sym;
205             if (!sd.postblit || sd.postblit.type.ty != Tfunction)
206                 return;
207             if ((cast(TypeFunction)sd.postblit.type).isnothrow)
208             {
209             }
210             else
211             {
212                 if (mustNotThrow)
213                 {
214                     ae.error("%s '%s' is not nothrow",
215                         sd.postblit.kind(), sd.postblit.toPrettyChars());
216                 }
217                 stop = true;
218             }
219         }
220 
221         override void visit(NewAnonClassExp)
222         {
223             assert(0); // should have been lowered by semantic()
224         }
225     }
226 
227     scope CanThrow ct = new CanThrow(func, mustNotThrow);
228     return walkPostorder(e, ct);
229 }
230 
231 /**************************************
232  * Does symbol, when initialized, throw?
233  * Mirrors logic in Dsymbol_toElem().
234  */
235 extern (C++) bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
236 {
237     AttribDeclaration ad;
238     VarDeclaration vd;
239     TemplateMixin tm;
240     TupleDeclaration td;
241     //printf("Dsymbol_toElem() %s\n", s->toChars());
242     ad = s.isAttribDeclaration();
243     if (ad)
244     {
245         Dsymbols* decl = ad.include(null, null);
246         if (decl && decl.dim)
247         {
248             for (size_t i = 0; i < decl.dim; i++)
249             {
250                 s = (*decl)[i];
251                 if (Dsymbol_canThrow(s, func, mustNotThrow))
252                     return true;
253             }
254         }
255     }
256     else if ((vd = s.isVarDeclaration()) !is null)
257     {
258         s = s.toAlias();
259         if (s != vd)
260             return Dsymbol_canThrow(s, func, mustNotThrow);
261         if (vd.storage_class & STCmanifest)
262         {
263         }
264         else if (vd.isStatic() || vd.storage_class & (STCextern | STCtls | STCgshared))
265         {
266         }
267         else
268         {
269             if (vd._init)
270             {
271                 ExpInitializer ie = vd._init.isExpInitializer();
272                 if (ie && canThrow(ie.exp, func, mustNotThrow))
273                     return true;
274             }
275             if (vd.needsScopeDtor())
276                 return canThrow(vd.edtor, func, mustNotThrow);
277         }
278     }
279     else if ((tm = s.isTemplateMixin()) !is null)
280     {
281         //printf("%s\n", tm->toChars());
282         if (tm.members)
283         {
284             for (size_t i = 0; i < tm.members.dim; i++)
285             {
286                 Dsymbol sm = (*tm.members)[i];
287                 if (Dsymbol_canThrow(sm, func, mustNotThrow))
288                     return true;
289             }
290         }
291     }
292     else if ((td = s.isTupleDeclaration()) !is null)
293     {
294         for (size_t i = 0; i < td.objects.dim; i++)
295         {
296             RootObject o = (*td.objects)[i];
297             if (o.dyncast() == DYNCAST_EXPRESSION)
298             {
299                 Expression eo = cast(Expression)o;
300                 if (eo.op == TOKdsymbol)
301                 {
302                     DsymbolExp se = cast(DsymbolExp)eo;
303                     if (Dsymbol_canThrow(se.s, func, mustNotThrow))
304                         return true;
305                 }
306             }
307         }
308     }
309     return false;
310 }