1 
2 /* Compiler implementation of the D programming language
3  * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4  * written by Walter Bright
5  * http://www.digitalmars.com
6  * Distributed under the Boost Software License, Version 1.0.
7  * http://www.boost.org/LICENSE_1_0.txt
8  * https://github.com/D-Programming-Language/dmd/blob/master/src/sideeffect.c
9  */
10 
11 #include "root/dsystem.h"
12 
13 #include "mars.h"
14 #include "init.h"
15 #include "expression.h"
16 #include "template.h"
17 #include "statement.h"
18 #include "mtype.h"
19 #include "utf.h"
20 #include "declaration.h"
21 #include "aggregate.h"
22 #include "scope.h"
23 #include "attrib.h"
24 #include "tokens.h"
25 
26 bool walkPostorder(Expression *e, StoppableVisitor *v);
27 bool lambdaHasSideEffect(Expression *e);
28 Expression *semantic(Expression *e, Scope *sc);
29 
30 /**************************************************
31  * Front-end expression rewriting should create temporary variables for
32  * non trivial sub-expressions in order to:
33  *  1. save evaluation order
34  *  2. prevent sharing of sub-expression in AST
35  */
isTrivialExp(Expression * e)36 bool isTrivialExp(Expression *e)
37 {
38     class IsTrivialExp : public StoppableVisitor
39     {
40     public:
41         IsTrivialExp() {}
42 
43         void visit(Expression *e)
44         {
45             /* Bugzilla 11201: CallExp is always non trivial expression,
46              * especially for inlining.
47              */
48             if (e->op == TOKcall)
49             {
50                 stop = true;
51                 return;
52             }
53 
54             // stop walking if we determine this expression has side effects
55             stop = lambdaHasSideEffect(e);
56         }
57     };
58 
59     IsTrivialExp v;
60     return walkPostorder(e, &v) == false;
61 }
62 
63 /********************************************
64  * Determine if Expression has any side effects.
65  */
66 
hasSideEffect(Expression * e)67 bool hasSideEffect(Expression *e)
68 {
69     class LambdaHasSideEffect : public StoppableVisitor
70     {
71     public:
72         LambdaHasSideEffect() {}
73 
74         void visit(Expression *e)
75         {
76             // stop walking if we determine this expression has side effects
77             stop = lambdaHasSideEffect(e);
78         }
79     };
80 
81     LambdaHasSideEffect v;
82     return walkPostorder(e, &v);
83 }
84 
85 /********************************************
86  * Determine if the call of f, or function type or delegate type t1, has any side effects.
87  * Returns:
88  *      0   has any side effects
89  *      1   nothrow + constant purity
90  *      2   nothrow + strong purity
91  */
92 
callSideEffectLevel(FuncDeclaration * f)93 int callSideEffectLevel(FuncDeclaration *f)
94 {
95     /* Bugzilla 12760: ctor call always has side effects.
96      */
97     if (f->isCtorDeclaration())
98         return 0;
99 
100     assert(f->type->ty == Tfunction);
101     TypeFunction *tf = (TypeFunction *)f->type;
102     if (tf->isnothrow)
103     {
104         PURE purity = f->isPure();
105         if (purity == PUREstrong)
106             return 2;
107         if (purity == PUREconst)
108             return 1;
109     }
110     return 0;
111 }
112 
callSideEffectLevel(Type * t)113 int callSideEffectLevel(Type *t)
114 {
115     t = t->toBasetype();
116 
117     TypeFunction *tf;
118     if (t->ty == Tdelegate)
119         tf = (TypeFunction *)((TypeDelegate *)t)->next;
120     else
121     {
122         assert(t->ty == Tfunction);
123         tf = (TypeFunction *)t;
124     }
125 
126     tf->purityLevel();
127     PURE purity = tf->purity;
128     if (t->ty == Tdelegate && purity > PUREweak)
129     {
130         if (tf->isMutable())
131             purity = PUREweak;
132         else if (!tf->isImmutable())
133             purity = PUREconst;
134     }
135 
136     if (tf->isnothrow)
137     {
138         if (purity == PUREstrong)
139             return 2;
140         if (purity == PUREconst)
141             return 1;
142     }
143     return 0;
144 }
145 
lambdaHasSideEffect(Expression * e)146 bool lambdaHasSideEffect(Expression *e)
147 {
148     switch (e->op)
149     {
150         // Sort the cases by most frequently used first
151         case TOKassign:
152         case TOKplusplus:
153         case TOKminusminus:
154         case TOKdeclaration:
155         case TOKconstruct:
156         case TOKblit:
157         case TOKaddass:
158         case TOKminass:
159         case TOKcatass:
160         case TOKmulass:
161         case TOKdivass:
162         case TOKmodass:
163         case TOKshlass:
164         case TOKshrass:
165         case TOKushrass:
166         case TOKandass:
167         case TOKorass:
168         case TOKxorass:
169         case TOKpowass:
170         case TOKin:
171         case TOKremove:
172         case TOKassert:
173         case TOKhalt:
174         case TOKdelete:
175         case TOKnew:
176         case TOKnewanonclass:
177             return true;
178 
179         case TOKcall:
180         {
181             CallExp *ce = (CallExp *)e;
182             /* Calling a function or delegate that is pure nothrow
183              * has no side effects.
184              */
185             if (ce->e1->type)
186             {
187                 Type *t = ce->e1->type->toBasetype();
188                 if (t->ty == Tdelegate)
189                     t = ((TypeDelegate *)t)->next;
190                 if (t->ty == Tfunction &&
191                     (ce->f ? callSideEffectLevel(ce->f)
192                            : callSideEffectLevel(ce->e1->type)) > 0)
193                 {
194                 }
195                 else
196                     return true;
197             }
198             break;
199         }
200 
201         case TOKcast:
202         {
203             CastExp *ce = (CastExp *)e;
204             /* if:
205              *  cast(classtype)func()  // because it may throw
206              */
207             if (ce->to->ty == Tclass && ce->e1->op == TOKcall && ce->e1->type->ty == Tclass)
208                 return true;
209             break;
210         }
211 
212         default:
213             break;
214     }
215     return false;
216 }
217 
218 
219 /***********************************
220  * The result of this expression will be discarded.
221  * Print error messages if the operation has no side effects (and hence is meaningless).
222  * Returns:
223  *      true if expression has no side effects
224  */
discardValue(Expression * e)225 bool discardValue(Expression *e)
226 {
227     if (lambdaHasSideEffect(e))     // check side-effect shallowly
228         return false;
229     switch (e->op)
230     {
231         case TOKcast:
232         {
233             CastExp *ce = (CastExp *)e;
234             if (ce->to->equals(Type::tvoid))
235             {
236                 /*
237                  * Don't complain about an expression with no effect if it was cast to void
238                  */
239                 return false;
240             }
241             break;          // complain
242         }
243 
244         case TOKerror:
245             return false;
246 
247         case TOKvar:
248         {
249             VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration();
250             if (v && (v->storage_class & STCtemp))
251             {
252                 // Bugzilla 5810: Don't complain about an internal generated variable.
253                 return false;
254             }
255             break;
256         }
257         case TOKcall:
258             /* Issue 3882: */
259             if (global.params.warnings != DIAGNOSTICoff && !global.gag)
260             {
261                 CallExp *ce = (CallExp *)e;
262                 if (e->type->ty == Tvoid)
263                 {
264                     /* Don't complain about calling void-returning functions with no side-effect,
265                      * because purity and nothrow are inferred, and because some of the
266                      * runtime library depends on it. Needs more investigation.
267                      *
268                      * One possible solution is to restrict this message to only be called in hierarchies that
269                      * never call assert (and or not called from inside unittest blocks)
270                      */
271                 }
272                 else if (ce->e1->type)
273                 {
274                     Type *t = ce->e1->type->toBasetype();
275                     if (t->ty == Tdelegate)
276                         t = ((TypeDelegate *)t)->next;
277                     if (t->ty == Tfunction &&
278                         (ce->f ? callSideEffectLevel(ce->f)
279                                : callSideEffectLevel(ce->e1->type)) > 0)
280                     {
281                         const char *s;
282                         if (ce->f)
283                             s = ce->f->toPrettyChars();
284                         else if (ce->e1->op == TOKstar)
285                         {
286                             // print 'fp' if ce->e1 is (*fp)
287                             s = ((PtrExp *)ce->e1)->e1->toChars();
288                         }
289                         else
290                             s = ce->e1->toChars();
291 
292                         e->warning("calling %s without side effects discards return value of type %s, prepend a cast(void) if intentional",
293                                    s, e->type->toChars());
294                     }
295                 }
296             }
297             return false;
298 
299         case TOKscope:
300             e->error("%s has no effect", e->toChars());
301             return true;
302 
303         case TOKandand:
304         {
305             AndAndExp *aae = (AndAndExp *)e;
306             return discardValue(aae->e2);
307         }
308 
309         case TOKoror:
310         {
311             OrOrExp *ooe = (OrOrExp *)e;
312             return discardValue(ooe->e2);
313         }
314 
315         case TOKquestion:
316         {
317             CondExp *ce = (CondExp *)e;
318 
319             /* Bugzilla 6178 & 14089: Either CondExp::e1 or e2 may have
320              * redundant expression to make those types common. For example:
321              *
322              *  struct S { this(int n); int v; alias v this; }
323              *  S[int] aa;
324              *  aa[1] = 0;
325              *
326              * The last assignment statement will be rewitten to:
327              *
328              *  1 in aa ? aa[1].value = 0 : (aa[1] = 0, aa[1].this(0)).value;
329              *
330              * The last DotVarExp is necessary to take assigned value.
331              *
332              *  int value = (aa[1] = 0);    // value = aa[1].value
333              *
334              * To avoid false error, discardValue() should be called only when
335              * the both tops of e1 and e2 have actually no side effects.
336              */
337             if (!lambdaHasSideEffect(ce->e1) &&
338                 !lambdaHasSideEffect(ce->e2))
339             {
340                 return discardValue(ce->e1) |
341                        discardValue(ce->e2);
342             }
343             return false;
344         }
345 
346         case TOKcomma:
347         {
348             CommaExp *ce = (CommaExp *)e;
349             /* Check for compiler-generated code of the form  auto __tmp, e, __tmp;
350              * In such cases, only check e for side effect (it's OK for __tmp to have
351              * no side effect).
352              * See Bugzilla 4231 for discussion
353              */
354             CommaExp *firstComma = ce;
355             while (firstComma->e1->op == TOKcomma)
356                 firstComma = (CommaExp *)firstComma->e1;
357             if (firstComma->e1->op == TOKdeclaration &&
358                 ce->e2->op == TOKvar &&
359                 ((DeclarationExp *)firstComma->e1)->declaration == ((VarExp*)ce->e2)->var)
360             {
361                 return false;
362             }
363             // Don't check e1 until we cast(void) the a,b code generation
364             //discardValue(ce->e1);
365             return discardValue(ce->e2);
366         }
367 
368         case TOKtuple:
369             /* Pass without complaint if any of the tuple elements have side effects.
370              * Ideally any tuple elements with no side effects should raise an error,
371              * this needs more investigation as to what is the right thing to do.
372              */
373             if (!hasSideEffect(e))
374                 break;
375             return false;
376 
377         default:
378             break;
379     }
380     e->error("%s has no effect in expression (%s)", Token::toChars(e->op), e->toChars());
381     return true;
382 }
383 
384 /**************************************************
385  * Build a temporary variable to copy the value of e into.
386  * Params:
387  *  stc = storage classes will be added to the made temporary variable
388  *  name = name for temporary variable
389  *  e = original expression
390  * Returns:
391  *  Newly created temporary variable.
392  */
copyToTemp(StorageClass stc,const char * name,Expression * e)393 VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e)
394 {
395     assert(name && name[0] == '_' && name[1] == '_');
396     Identifier *id = Identifier::generateId(name);
397     ExpInitializer *ez = new ExpInitializer(e->loc, e);
398     VarDeclaration *vd = new VarDeclaration(e->loc, e->type, id, ez);
399     vd->storage_class = stc;
400     vd->storage_class |= STCtemp;
401     vd->storage_class |= STCctfe; // temporary is always CTFEable
402     return vd;
403 }
404 
405 /**************************************************
406  * Build a temporary variable to extract e's evaluation, if e is not trivial.
407  * Params:
408  *  sc = scope
409  *  name = name for temporary variable
410  *  e0 = a new side effect part will be appended to it.
411  *  e = original expression
412  *  alwaysCopy = if true, build new temporary variable even if e is trivial.
413  * Returns:
414  *  When e is trivial and alwaysCopy == false, e itself is returned.
415  *  Otherwise, a new VarExp is returned.
416  * Note:
417  *  e's lvalue-ness will be handled well by STCref or STCrvalue.
418  */
419 Expression *extractSideEffect(Scope *sc, const char *name,
420     Expression **e0, Expression *e, bool alwaysCopy = false)
421 {
422     if (!alwaysCopy && isTrivialExp(e))
423         return e;
424 
425     VarDeclaration *vd = copyToTemp(0, name, e);
426     if (e->isLvalue())
427         vd->storage_class |= STCref;
428     else
429         vd->storage_class |= STCrvalue;
430 
431     Expression *de = new DeclarationExp(vd->loc, vd);
432     Expression *ve = new VarExp(vd->loc, vd);
433     de = semantic(de, sc);
434     ve = semantic(ve, sc);
435 
436     *e0 = Expression::combine(*e0, de);
437     return ve;
438 }
439