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