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