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