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