]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/d/dmd/optimize.d
d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[thirdparty/gcc.git] / gcc / d / dmd / optimize.d
CommitLineData
5fee5ec3
IB
1/**
2 * Perform constant folding.
3 *
4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d)
8 * Documentation: https://dlang.org/phobos/dmd_optimize.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d
10 */
11
12module dmd.optimize;
13
14import core.stdc.stdio;
15
16import dmd.astenums;
17import dmd.constfold;
18import dmd.ctfeexpr;
19import dmd.dclass;
20import dmd.declaration;
21import dmd.dsymbol;
22import dmd.dsymbolsem;
23import dmd.errors;
24import dmd.expression;
25import dmd.expressionsem;
26import dmd.globals;
27import dmd.init;
28import dmd.mtype;
29import dmd.root.ctfloat;
30import dmd.sideeffect;
31import dmd.tokens;
32import dmd.visitor;
33
34/*************************************
35 * If variable has a const initializer,
36 * return that initializer.
37 * Returns:
38 * initializer if there is one,
39 * null if not,
40 * ErrorExp if error
41 */
42Expression expandVar(int result, VarDeclaration v)
43{
44 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
45
46 /********
47 * Params:
48 * e = initializer expression
49 */
50 Expression initializerReturn(Expression e)
51 {
52 if (e.type != v.type)
53 {
54 e = e.castTo(null, v.type);
55 }
56 v.inuse++;
57 e = e.optimize(result);
58 v.inuse--;
59 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
60 return e;
61 }
62
63 static Expression nullReturn()
64 {
65 return null;
66 }
67
68 static Expression errorReturn()
69 {
70 return ErrorExp.get();
71 }
72
73 if (!v)
74 return nullReturn();
75 if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
76 v.dsymbolSemantic(null);
77 if (v.type &&
78 (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
79 {
80 Type tb = v.type.toBasetype();
81 if (v.storage_class & STC.manifest ||
82 tb.isscalar() ||
83 ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
84 {
85 if (v._init)
86 {
87 if (v.inuse)
88 {
89 if (v.storage_class & STC.manifest)
90 {
91 v.error("recursive initialization of constant");
92 return errorReturn();
93 }
94 return nullReturn();
95 }
96 Expression ei = v.getConstInitializer();
97 if (!ei)
98 {
99 if (v.storage_class & STC.manifest)
100 {
101 v.error("enum cannot be initialized with `%s`", v._init.toChars());
102 return errorReturn();
103 }
104 return nullReturn();
105 }
9c7d5e88 106 if (ei.op == EXP.construct || ei.op == EXP.blit)
5fee5ec3
IB
107 {
108 AssignExp ae = cast(AssignExp)ei;
109 ei = ae.e2;
110 if (ei.isConst() == 1)
111 {
112 }
9c7d5e88 113 else if (ei.op == EXP.string_)
5fee5ec3
IB
114 {
115 // https://issues.dlang.org/show_bug.cgi?id=14459
116 // Do not constfold the string literal
117 // if it's typed as a C string, because the value expansion
118 // will drop the pointer identity.
119 if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
120 return nullReturn();
121 }
122 else
123 return nullReturn();
124 if (ei.type == v.type)
125 {
126 // const variable initialized with const expression
127 }
128 else if (ei.implicitConvTo(v.type) >= MATCH.constant)
129 {
130 // const var initialized with non-const expression
131 ei = ei.implicitCastTo(null, v.type);
132 ei = ei.expressionSemantic(null);
133 }
134 else
135 return nullReturn();
136 }
137 else if (!(v.storage_class & STC.manifest) &&
138 ei.isConst() != 1 &&
9c7d5e88
IB
139 ei.op != EXP.string_ &&
140 ei.op != EXP.address)
5fee5ec3
IB
141 {
142 return nullReturn();
143 }
144
145 if (!ei.type)
146 {
147 return nullReturn();
148 }
149 else
150 {
151 // Should remove the copy() operation by
152 // making all mods to expressions copy-on-write
153 return initializerReturn(ei.copy());
154 }
155 }
156 else
157 {
158 // v does not have an initializer
159 version (all)
160 {
161 return nullReturn();
162 }
163 else
164 {
165 // BUG: what if const is initialized in constructor?
166 auto e = v.type.defaultInit();
167 e.loc = e1.loc;
168 return initializerReturn(e);
169 }
170 }
171 assert(0);
172 }
173 }
174 return nullReturn();
175}
176
177private Expression fromConstInitializer(int result, Expression e1)
178{
179 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
180 //static int xx; if (xx++ == 10) assert(0);
181 Expression e = e1;
9c7d5e88 182 if (auto ve = e1.isVarExp())
5fee5ec3 183 {
5fee5ec3
IB
184 VarDeclaration v = ve.var.isVarDeclaration();
185 e = expandVar(result, v);
186 if (e)
187 {
188 // If it is a comma expression involving a declaration, we mustn't
189 // perform a copy -- we'd get two declarations of the same variable.
190 // See bugzilla 4465.
9c7d5e88 191 if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
5fee5ec3
IB
192 e = e1;
193 else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
194 {
195 // Type 'paint' operation
196 e = e.copy();
197 e.type = e1.type;
198 }
199 e.loc = e1.loc;
200 }
201 else
202 {
203 e = e1;
204 }
205 }
206 return e;
207}
208
9c7d5e88
IB
209/***
210 * It is possible for constant folding to change an array expression of
5fee5ec3
IB
211 * unknown length, into one where the length is known.
212 * If the expression 'arr' is a literal, set lengthVar to be its length.
9c7d5e88
IB
213 * Params:
214 * lengthVar = variable declaration for the `.length` property
215 * arr = String, ArrayLiteral, or of TypeSArray
5fee5ec3
IB
216 */
217package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
218{
219 if (!lengthVar)
220 return;
221 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
222 return; // we have previously calculated the length
9c7d5e88
IB
223 d_uns64 len;
224 if (auto se = arr.isStringExp())
225 len = se.len;
226 else if (auto ale = arr.isArrayLiteralExp())
227 len = ale.elements.dim;
5fee5ec3
IB
228 else
229 {
9c7d5e88
IB
230 auto tsa = arr.type.toBasetype().isTypeSArray();
231 if (!tsa)
5fee5ec3 232 return; // we don't know the length yet
9c7d5e88 233 len = tsa.dim.toInteger();
5fee5ec3
IB
234 }
235 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
236 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
237 lengthVar.storage_class |= STC.static_ | STC.const_;
238}
239
9c7d5e88
IB
240/***
241 * Same as above, but determines the length from 'type'.
242 * Params:
243 * lengthVar = variable declaration for the `.length` property
244 * type = TypeSArray
245 */
5fee5ec3
IB
246package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
247{
248 if (!lengthVar)
249 return;
250 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
251 return; // we have previously calculated the length
9c7d5e88
IB
252 auto tsa = type.toBasetype().isTypeSArray();
253 if (!tsa)
5fee5ec3 254 return; // we don't know the length yet
9c7d5e88 255 d_uns64 len = tsa.dim.toInteger();
5fee5ec3
IB
256 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
257 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
258 lengthVar.storage_class |= STC.static_ | STC.const_;
259}
260
261/*********************************
262 * Constant fold an Expression.
263 * Params:
264 * e = expression to const fold; this may get modified in-place
265 * result = WANTvalue, WANTexpand, or both
266 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
267 * an argument to a `ref` or `out` parameter, or the operand of `&` operator
268 * Returns:
269 * Constant folded version of `e`
270 */
271Expression Expression_optimize(Expression e, int result, bool keepLvalue)
272{
9c7d5e88 273 Expression ret = e;
5fee5ec3 274
9c7d5e88
IB
275 void error()
276 {
277 ret = ErrorExp.get();
278 }
5fee5ec3 279
9c7d5e88
IB
280 /* Returns: true if error
281 */
282 bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
283 {
284 if (!e)
285 return false;
286 Expression ex = Expression_optimize(e, flags, keepLvalue);
287 if (ex.op == EXP.error)
5fee5ec3 288 {
9c7d5e88
IB
289 ret = ex; // store error result
290 return true;
5fee5ec3 291 }
9c7d5e88 292 else
5fee5ec3 293 {
9c7d5e88
IB
294 e = ex; // modify original
295 return false;
5fee5ec3 296 }
9c7d5e88 297 }
5fee5ec3 298
9c7d5e88
IB
299 bool unaOptimize(UnaExp e, int flags)
300 {
301 return expOptimize(e.e1, flags);
302 }
5fee5ec3 303
9c7d5e88
IB
304 bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
305 {
306 return expOptimize(e.e1, flags, keepLhsLvalue) |
307 expOptimize(e.e2, flags);
308 }
5fee5ec3 309
9c7d5e88
IB
310 void visitExp(Expression e)
311 {
312 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
313 }
5fee5ec3 314
9c7d5e88
IB
315 void visitVar(VarExp e)
316 {
317 VarDeclaration v = e.var.isVarDeclaration();
5fee5ec3 318
9c7d5e88
IB
319 if (!(keepLvalue && v && !(v.storage_class & STC.manifest)))
320 ret = fromConstInitializer(result, e);
5fee5ec3 321
9c7d5e88
IB
322 // if unoptimized, try to optimize the dtor expression
323 // (e.g., might be a LogicalExp with constant lhs)
324 if (ret == e && v && v.edtor)
325 {
326 // prevent infinite recursion (`<var>.~this()`)
327 if (!v.inuse)
5fee5ec3 328 {
9c7d5e88
IB
329 v.inuse++;
330 expOptimize(v.edtor, WANTvalue);
331 v.inuse--;
5fee5ec3
IB
332 }
333 }
9c7d5e88 334 }
5fee5ec3 335
9c7d5e88
IB
336 void visitTuple(TupleExp e)
337 {
338 expOptimize(e.e0, WANTvalue);
339 for (size_t i = 0; i < e.exps.dim; i++)
5fee5ec3 340 {
9c7d5e88 341 expOptimize((*e.exps)[i], WANTvalue);
5fee5ec3 342 }
9c7d5e88 343 }
5fee5ec3 344
9c7d5e88
IB
345 void visitArrayLiteral(ArrayLiteralExp e)
346 {
347 if (e.elements)
5fee5ec3 348 {
9c7d5e88
IB
349 expOptimize(e.basis, result & WANTexpand);
350 for (size_t i = 0; i < e.elements.dim; i++)
5fee5ec3 351 {
9c7d5e88 352 expOptimize((*e.elements)[i], result & WANTexpand);
5fee5ec3
IB
353 }
354 }
9c7d5e88 355 }
5fee5ec3 356
9c7d5e88
IB
357 void visitAssocArrayLiteral(AssocArrayLiteralExp e)
358 {
359 assert(e.keys.dim == e.values.dim);
360 for (size_t i = 0; i < e.keys.dim; i++)
5fee5ec3 361 {
9c7d5e88
IB
362 expOptimize((*e.keys)[i], result & WANTexpand);
363 expOptimize((*e.values)[i], result & WANTexpand);
5fee5ec3 364 }
9c7d5e88 365 }
5fee5ec3 366
9c7d5e88
IB
367 void visitStructLiteral(StructLiteralExp e)
368 {
369 if (e.stageflags & stageOptimize)
370 return;
371 int old = e.stageflags;
372 e.stageflags |= stageOptimize;
373 if (e.elements)
5fee5ec3 374 {
9c7d5e88 375 for (size_t i = 0; i < e.elements.dim; i++)
5fee5ec3 376 {
9c7d5e88 377 expOptimize((*e.elements)[i], result & WANTexpand);
5fee5ec3 378 }
5fee5ec3 379 }
9c7d5e88
IB
380 e.stageflags = old;
381 }
5fee5ec3 382
9c7d5e88
IB
383 void visitUna(UnaExp e)
384 {
385 //printf("UnaExp::optimize() %s\n", e.toChars());
386 if (unaOptimize(e, result))
387 return;
388 }
5fee5ec3 389
9c7d5e88
IB
390 void visitNeg(NegExp e)
391 {
392 if (unaOptimize(e, result))
393 return;
394 if (e.e1.isConst() == 1)
5fee5ec3 395 {
9c7d5e88 396 ret = Neg(e.type, e.e1).copy();
5fee5ec3 397 }
9c7d5e88 398 }
5fee5ec3 399
9c7d5e88
IB
400 void visitCom(ComExp e)
401 {
402 if (unaOptimize(e, result))
403 return;
404 if (e.e1.isConst() == 1)
5fee5ec3 405 {
9c7d5e88 406 ret = Com(e.type, e.e1).copy();
5fee5ec3 407 }
9c7d5e88 408 }
5fee5ec3 409
9c7d5e88
IB
410 void visitNop(NotExp e)
411 {
412 if (unaOptimize(e, result))
413 return;
414 if (e.e1.isConst() == 1)
5fee5ec3 415 {
9c7d5e88 416 ret = Not(e.type, e.e1).copy();
5fee5ec3 417 }
9c7d5e88 418 }
5fee5ec3 419
9c7d5e88
IB
420 void visitSymOff(SymOffExp e)
421 {
422 assert(e.var);
423 }
424
425 void visitAddr(AddrExp e)
426 {
427 //printf("AddrExp::optimize(result = %d) %s\n", result, e.toChars());
428 /* Rewrite &(a,b) as (a,&b)
429 */
430 if (auto ce = e.e1.isCommaExp())
5fee5ec3 431 {
9c7d5e88
IB
432 auto ae = new AddrExp(e.loc, ce.e2, e.type);
433 ret = new CommaExp(ce.loc, ce.e1, ae);
434 ret.type = e.type;
435 return;
5fee5ec3 436 }
9c7d5e88
IB
437 // Keep lvalue-ness
438 if (expOptimize(e.e1, result, true))
439 return;
440 // Convert &*ex to ex
441 if (auto pe = e.e1.isPtrExp())
5fee5ec3 442 {
9c7d5e88
IB
443 Expression ex = pe.e1;
444 if (e.type.equals(ex.type))
445 ret = ex;
446 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
5fee5ec3 447 {
9c7d5e88 448 ret = ex.copy();
5fee5ec3 449 ret.type = e.type;
5fee5ec3 450 }
9c7d5e88 451 return;
5fee5ec3 452 }
9c7d5e88 453 if (auto ve = e.e1.isVarExp())
5fee5ec3 454 {
9c7d5e88 455 if (!ve.var.isReference() && !ve.var.isImportedSymbol())
5fee5ec3 456 {
9c7d5e88
IB
457 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
458 ret.type = e.type;
5fee5ec3 459 return;
5fee5ec3 460 }
9c7d5e88
IB
461 }
462 if (auto ae = e.e1.isIndexExp())
463 {
464 // Convert &array[n] to &array+n
465 if (ae.e2.op == EXP.int64 && ae.e1.isVarExp())
5fee5ec3 466 {
9c7d5e88
IB
467 sinteger_t index = ae.e2.toInteger();
468 VarExp ve = ae.e1.isVarExp();
469 if (ve.type.isTypeSArray() && !ve.var.isImportedSymbol())
5fee5ec3 470 {
9c7d5e88
IB
471 TypeSArray ts = ve.type.isTypeSArray();
472 sinteger_t dim = ts.dim.toInteger();
473 if (index < 0 || index >= dim)
5fee5ec3 474 {
9c7d5e88
IB
475 e.error("array index %lld is out of bounds `[0..%lld]`", index, dim);
476 return error();
5fee5ec3 477 }
5fee5ec3 478
9c7d5e88
IB
479 import core.checkedint : mulu;
480 bool overflow;
481 const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
482 if (overflow)
5fee5ec3 483 {
9c7d5e88
IB
484 e.error("array offset overflow");
485 return error();
5fee5ec3 486 }
9c7d5e88
IB
487
488 ret = new SymOffExp(e.loc, ve.var, offset);
489 ret.type = e.type;
490 return;
5fee5ec3
IB
491 }
492 }
493 }
9c7d5e88 494 }
5fee5ec3 495
9c7d5e88
IB
496 void visitPtr(PtrExp e)
497 {
498 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
499 if (expOptimize(e.e1, result))
500 return;
501 // Convert *&ex to ex
502 // But only if there is no type punning involved
503 if (auto ey = e.e1.isAddrExp())
5fee5ec3 504 {
9c7d5e88
IB
505 Expression ex = ey.e1;
506 if (e.type.equals(ex.type))
507 ret = ex;
508 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
5fee5ec3 509 {
9c7d5e88
IB
510 ret = ex.copy();
511 ret.type = e.type;
5fee5ec3 512 }
9c7d5e88
IB
513 }
514 if (keepLvalue)
515 return;
516 // Constant fold *(&structliteral + offset)
517 if (e.e1.op == EXP.add)
518 {
519 Expression ex = Ptr(e.type, e.e1).copy();
520 if (!CTFEExp.isCantExp(ex))
5fee5ec3 521 {
9c7d5e88
IB
522 ret = ex;
523 return;
5fee5ec3
IB
524 }
525 }
9c7d5e88 526 if (auto se = e.e1.isSymOffExp())
5fee5ec3 527 {
9c7d5e88
IB
528 VarDeclaration v = se.var.isVarDeclaration();
529 Expression ex = expandVar(result, v);
530 if (ex && ex.isStructLiteralExp())
5fee5ec3 531 {
9c7d5e88
IB
532 StructLiteralExp sle = ex.isStructLiteralExp();
533 ex = sle.getField(e.type, cast(uint)se.offset);
534 if (ex && !CTFEExp.isCantExp(ex))
5fee5ec3 535 {
9c7d5e88
IB
536 ret = ex;
537 return;
5fee5ec3
IB
538 }
539 }
540 }
9c7d5e88 541 }
5fee5ec3 542
9c7d5e88
IB
543 void visitDotVar(DotVarExp e)
544 {
545 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
546 if (expOptimize(e.e1, result))
547 return;
548 if (keepLvalue)
549 return;
550 Expression ex = e.e1;
551 if (auto ve = ex.isVarExp())
5fee5ec3 552 {
9c7d5e88
IB
553 VarDeclaration v = ve.var.isVarDeclaration();
554 ex = expandVar(result, v);
555 }
556 if (ex && ex.isStructLiteralExp())
557 {
558 StructLiteralExp sle = ex.isStructLiteralExp();
559 VarDeclaration vf = e.var.isVarDeclaration();
560 if (vf && !vf.overlapped)
5fee5ec3 561 {
9c7d5e88
IB
562 /* https://issues.dlang.org/show_bug.cgi?id=13021
563 * Prevent optimization if vf has overlapped fields.
564 */
565 ex = sle.getField(e.type, vf.offset);
566 if (ex && !CTFEExp.isCantExp(ex))
5fee5ec3 567 {
9c7d5e88 568 ret = ex;
5fee5ec3
IB
569 return;
570 }
571 }
9c7d5e88
IB
572 }
573 }
5fee5ec3 574
9c7d5e88
IB
575 void visitNew(NewExp e)
576 {
577 expOptimize(e.thisexp, WANTvalue);
578 // Optimize parameters
579 if (e.newargs)
580 {
581 for (size_t i = 0; i < e.newargs.dim; i++)
5fee5ec3 582 {
9c7d5e88 583 expOptimize((*e.newargs)[i], WANTvalue);
5fee5ec3 584 }
9c7d5e88
IB
585 }
586 if (e.arguments)
587 {
588 for (size_t i = 0; i < e.arguments.dim; i++)
5fee5ec3 589 {
9c7d5e88 590 expOptimize((*e.arguments)[i], WANTvalue);
5fee5ec3 591 }
5fee5ec3 592 }
9c7d5e88 593 }
5fee5ec3 594
9c7d5e88
IB
595 void visitCall(CallExp e)
596 {
597 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
598 // Optimize parameters with keeping lvalue-ness
599 if (expOptimize(e.e1, result))
600 return;
601 if (e.arguments)
5fee5ec3 602 {
9c7d5e88
IB
603 Type t1 = e.e1.type.toBasetype();
604 if (t1.ty == Tdelegate)
605 t1 = t1.nextOf();
606 // t1 can apparently be void for __ArrayDtor(T) calls
607 if (auto tf = t1.isTypeFunction())
5fee5ec3 608 {
9c7d5e88 609 for (size_t i = 0; i < e.arguments.dim; i++)
5fee5ec3 610 {
9c7d5e88
IB
611 Parameter p = tf.parameterList[i];
612 bool keep = p && p.isReference();
613 expOptimize((*e.arguments)[i], WANTvalue, keep);
5fee5ec3
IB
614 }
615 }
616 }
9c7d5e88 617 }
5fee5ec3 618
9c7d5e88
IB
619 void visitCast(CastExp e)
620 {
621 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
622 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
623 //printf("from %s\n", e.type.toChars());
624 //printf("e1.type %s\n", e.e1.type.toChars());
625 //printf("type = %p\n", e.type);
626 assert(e.type);
627 const op1 = e.e1.op;
628 Expression e1old = e.e1;
629 if (expOptimize(e.e1, result, keepLvalue))
630 return;
631 if (!keepLvalue)
632 e.e1 = fromConstInitializer(result, e.e1);
633 if (e.e1 == e1old && e.e1.op == EXP.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
5fee5ec3 634 {
9c7d5e88
IB
635 // Casting this will result in the same expression, and
636 // infinite loop because of Expression::implicitCastTo()
637 return; // no change
5fee5ec3 638 }
9c7d5e88
IB
639 if ((e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral) &&
640 (e.type.ty == Tpointer || e.type.ty == Tarray))
5fee5ec3 641 {
9c7d5e88
IB
642 const esz = e.type.nextOf().size(e.loc);
643 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
644 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
645 return error();
646
647 if (e1sz == esz)
5fee5ec3 648 {
9c7d5e88
IB
649 // https://issues.dlang.org/show_bug.cgi?id=12937
650 // If target type is void array, trying to paint
651 // e.e1 with that type will cause infinite recursive optimization.
652 if (e.type.nextOf().ty == Tvoid)
5fee5ec3 653 return;
9c7d5e88
IB
654 ret = e.e1.castTo(null, e.type);
655 //printf(" returning1 %s\n", ret.toChars());
656 return;
5fee5ec3
IB
657 }
658 }
659
9c7d5e88 660 if (e.e1.op == EXP.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
5fee5ec3 661 {
9c7d5e88
IB
662 //printf(" returning2 %s\n", e.e1.toChars());
663 L1:
664 // Returning e1 with changing its type
665 ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
666 ret.type = e.type;
667 return;
5fee5ec3 668 }
9c7d5e88
IB
669 /* The first test here is to prevent infinite loops
670 */
671 if (op1 != EXP.arrayLiteral && e.e1.op == EXP.arrayLiteral)
5fee5ec3 672 {
9c7d5e88
IB
673 ret = e.e1.castTo(null, e.to);
674 return;
5fee5ec3 675 }
9c7d5e88 676 if (e.e1.op == EXP.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
5fee5ec3 677 {
9c7d5e88
IB
678 //printf(" returning3 %s\n", e.e1.toChars());
679 goto L1;
680 }
681 if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
682 {
683 import dmd.astenums : Sizeok;
684
685 // See if we can remove an unnecessary cast
686 ClassDeclaration cdfrom = e.e1.type.isClassHandle();
687 ClassDeclaration cdto = e.type.isClassHandle();
688 if (cdfrom.errors || cdto.errors)
689 return error();
690 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
691 goto L1; // can always convert a class to Object
692 // Need to determine correct offset before optimizing away the cast.
693 // https://issues.dlang.org/show_bug.cgi?id=16980
694 cdfrom.size(e.loc);
695 assert(cdfrom.sizeok == Sizeok.done);
696 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
697 int offset;
698 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
5fee5ec3 699 {
9c7d5e88
IB
700 //printf(" returning4 %s\n", e.e1.toChars());
701 goto L1;
5fee5ec3
IB
702 }
703 }
9c7d5e88 704 if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf()))
5fee5ec3 705 {
9c7d5e88
IB
706 //printf(" returning5 %s\n", e.e1.toChars());
707 goto L1;
708 }
709 if (e.e1.isConst())
710 {
711 if (e.e1.op == EXP.symbolOffset)
712 {
713 if (e.type.toBasetype().ty != Tsarray)
714 {
715 const esz = e.type.size(e.loc);
716 const e1sz = e.e1.type.size(e.e1.loc);
717 if (esz == SIZE_INVALID ||
718 e1sz == SIZE_INVALID)
719 return error();
720
721 if (esz == e1sz)
722 goto L1;
723 }
5fee5ec3 724 return;
9c7d5e88
IB
725 }
726 if (e.to.toBasetype().ty != Tvoid)
5fee5ec3 727 {
9c7d5e88
IB
728 if (e.e1.type.equals(e.type) && e.type.equals(e.to))
729 ret = e.e1;
730 else
731 ret = Cast(e.loc, e.type, e.to, e.e1).copy();
5fee5ec3
IB
732 }
733 }
9c7d5e88
IB
734 //printf(" returning6 %s\n", ret.toChars());
735 }
5fee5ec3 736
9c7d5e88
IB
737 void visitBinAssign(BinAssignExp e)
738 {
739 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
740 if (binOptimize(e, result, /*keepLhsLvalue*/ true))
741 return;
742 if (e.op == EXP.leftShiftAssign || e.op == EXP.rightShiftAssign || e.op == EXP.unsignedRightShiftAssign)
5fee5ec3 743 {
5fee5ec3
IB
744 if (e.e2.isConst() == 1)
745 {
746 sinteger_t i2 = e.e2.toInteger();
747 d_uns64 sz = e.e1.type.size(e.e1.loc);
748 assert(sz != SIZE_INVALID);
749 sz *= 8;
750 if (i2 < 0 || i2 >= sz)
751 {
9c7d5e88 752 e.error("shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
5fee5ec3
IB
753 return error();
754 }
5fee5ec3
IB
755 }
756 }
9c7d5e88 757 }
5fee5ec3 758
9c7d5e88
IB
759 void visitBin(BinExp e)
760 {
761 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
762 const keepLhsLvalue = e.op == EXP.construct || e.op == EXP.blit || e.op == EXP.assign
763 || e.op == EXP.plusPlus || e.op == EXP.minusMinus
764 || e.op == EXP.prePlusPlus || e.op == EXP.preMinusMinus;
765 binOptimize(e, result, keepLhsLvalue);
766 }
5fee5ec3 767
9c7d5e88
IB
768 void visitAdd(AddExp e)
769 {
770 //printf("AddExp::optimize(%s)\n", e.toChars());
771 if (binOptimize(e, result))
772 return;
773 if (e.e1.isConst() && e.e2.isConst())
5fee5ec3 774 {
9c7d5e88
IB
775 if (e.e1.op == EXP.symbolOffset && e.e2.op == EXP.symbolOffset)
776 return;
777 ret = Add(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 778 }
9c7d5e88 779 }
5fee5ec3 780
9c7d5e88
IB
781 void visitMin(MinExp e)
782 {
783 if (binOptimize(e, result))
784 return;
785 if (e.e1.isConst() && e.e2.isConst())
5fee5ec3 786 {
9c7d5e88
IB
787 if (e.e2.op == EXP.symbolOffset)
788 return;
789 ret = Min(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 790 }
9c7d5e88 791 }
5fee5ec3 792
9c7d5e88
IB
793 void visitMul(MulExp e)
794 {
795 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
796 if (binOptimize(e, result))
797 return;
798 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
5fee5ec3 799 {
9c7d5e88 800 ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 801 }
9c7d5e88 802 }
5fee5ec3 803
9c7d5e88
IB
804 void visitDiv(DivExp e)
805 {
806 //printf("DivExp::optimize(%s)\n", e.toChars());
807 if (binOptimize(e, result))
808 return;
809 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
5fee5ec3 810 {
9c7d5e88 811 ret = Div(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 812 }
9c7d5e88 813 }
5fee5ec3 814
9c7d5e88
IB
815 void visitMod(ModExp e)
816 {
817 if (binOptimize(e, result))
818 return;
819 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
5fee5ec3 820 {
9c7d5e88 821 ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 822 }
9c7d5e88 823 }
5fee5ec3 824
9c7d5e88
IB
825 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
826 {
827 if (binOptimize(e, result))
828 return;
829 if (e.e2.isConst() == 1)
5fee5ec3 830 {
9c7d5e88
IB
831 sinteger_t i2 = e.e2.toInteger();
832 d_uns64 sz = e.e1.type.size(e.e1.loc);
833 assert(sz != SIZE_INVALID);
834 sz *= 8;
835 if (i2 < 0 || i2 >= sz)
5fee5ec3 836 {
9c7d5e88 837 e.error("shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
5fee5ec3
IB
838 return error();
839 }
9c7d5e88
IB
840 if (e.e1.isConst() == 1)
841 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
5fee5ec3 842 }
9c7d5e88
IB
843 }
844
845 void visitShl(ShlExp e)
846 {
847 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
848 shift_optimize(e, &Shl);
849 }
850
851 void visitShr(ShrExp e)
852 {
853 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
854 shift_optimize(e, &Shr);
855 }
856
857 void visitUshr(UshrExp e)
858 {
859 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
860 shift_optimize(e, &Ushr);
861 }
5fee5ec3 862
9c7d5e88
IB
863 void visitAnd(AndExp e)
864 {
865 if (binOptimize(e, result))
866 return;
867 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
868 ret = And(e.loc, e.type, e.e1, e.e2).copy();
869 }
870
871 void visitOr(OrExp e)
872 {
873 if (binOptimize(e, result))
874 return;
875 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
876 ret = Or(e.loc, e.type, e.e1, e.e2).copy();
877 }
878
879 void visitXor(XorExp e)
880 {
881 if (binOptimize(e, result))
882 return;
883 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
884 ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
885 }
886
887 void visitPow(PowExp e)
888 {
889 if (binOptimize(e, result))
890 return;
891 // All negative integral powers are illegal.
892 if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
5fee5ec3 893 {
9c7d5e88
IB
894 e.error("cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars());
895 return error();
5fee5ec3 896 }
9c7d5e88
IB
897 // If e2 *could* have been an integer, make it one.
898 if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
5fee5ec3 899 {
9c7d5e88
IB
900 // This only applies to floating point, or positive integral powers.
901 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
902 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
5fee5ec3 903 }
9c7d5e88 904 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
5fee5ec3 905 {
9c7d5e88
IB
906 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
907 if (!CTFEExp.isCantExp(ex))
5fee5ec3 908 {
9c7d5e88 909 ret = ex;
5fee5ec3
IB
910 return;
911 }
5fee5ec3 912 }
9c7d5e88 913 }
5fee5ec3 914
9c7d5e88
IB
915 void visitComma(CommaExp e)
916 {
917 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
918 // Comma needs special treatment, because it may
919 // contain compiler-generated declarations. We can interpret them, but
920 // otherwise we must NOT attempt to constant-fold them.
921 // In particular, if the comma returns a temporary variable, it needs
922 // to be an lvalue (this is particularly important for struct constructors)
923 expOptimize(e.e1, WANTvalue);
924 expOptimize(e.e2, result, keepLvalue);
925 if (ret.op == EXP.error)
926 return;
927 if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1))
5fee5ec3 928 {
9c7d5e88
IB
929 ret = e.e2;
930 if (ret)
931 ret.type = e.type;
932 }
933 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
934 }
935
936 void visitArrayLength(ArrayLengthExp e)
937 {
938 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
939 if (unaOptimize(e, WANTexpand))
940 return;
941 // CTFE interpret static immutable arrays (to get better diagnostics)
942 if (auto ve = e.e1.isVarExp())
943 {
944 VarDeclaration v = ve.var.isVarDeclaration();
945 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
5fee5ec3 946 {
9c7d5e88
IB
947 if (Expression ci = v.getConstInitializer())
948 e.e1 = ci;
5fee5ec3
IB
949 }
950 }
9c7d5e88
IB
951 if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray)
952 {
953 ret = ArrayLength(e.type, e.e1).copy();
954 }
955 }
5fee5ec3 956
9c7d5e88
IB
957 void visitEqual(EqualExp e)
958 {
959 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
960 if (binOptimize(e, WANTvalue))
961 return;
962 Expression e1 = fromConstInitializer(result, e.e1);
963 Expression e2 = fromConstInitializer(result, e.e2);
964 if (e1.op == EXP.error)
5fee5ec3 965 {
9c7d5e88
IB
966 ret = e1;
967 return;
968 }
969 if (e2.op == EXP.error)
970 {
971 ret = e2;
972 return;
5fee5ec3 973 }
9c7d5e88
IB
974 ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
975 if (CTFEExp.isCantExp(ret))
976 ret = e;
977 }
5fee5ec3 978
9c7d5e88
IB
979 void visitIdentity(IdentityExp e)
980 {
981 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
982 if (binOptimize(e, WANTvalue))
983 return;
984 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == EXP.null_ && e.e2.op == EXP.null_))
5fee5ec3 985 {
9c7d5e88
IB
986 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
987 if (CTFEExp.isCantExp(ret))
5fee5ec3 988 ret = e;
5fee5ec3 989 }
9c7d5e88
IB
990 }
991
992 void visitIndex(IndexExp e)
993 {
994 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
995 if (expOptimize(e.e1, result & WANTexpand))
996 return;
997 Expression ex = fromConstInitializer(result, e.e1);
998 // We might know $ now
999 setLengthVarIfKnown(e.lengthVar, ex);
1000 if (expOptimize(e.e2, WANTvalue))
1001 return;
1002 // Don't optimize to an array literal element directly in case an lvalue is requested
1003 if (keepLvalue && ex.op == EXP.arrayLiteral)
1004 return;
1005 ret = Index(e.type, ex, e.e2).copy();
1006 if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue()))
1007 ret = e;
1008 }
5fee5ec3 1009
9c7d5e88
IB
1010 void visitSlice(SliceExp e)
1011 {
1012 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1013 if (expOptimize(e.e1, result & WANTexpand))
1014 return;
1015 if (!e.lwr)
5fee5ec3 1016 {
9c7d5e88 1017 if (e.e1.op == EXP.string_)
5fee5ec3 1018 {
9c7d5e88
IB
1019 // Convert slice of string literal into dynamic array
1020 Type t = e.e1.type.toBasetype();
1021 if (Type tn = t.nextOf())
1022 ret = e.e1.castTo(null, tn.arrayOf());
5fee5ec3
IB
1023 }
1024 }
9c7d5e88 1025 else
5fee5ec3 1026 {
9c7d5e88
IB
1027 e.e1 = fromConstInitializer(result, e.e1);
1028 // We might know $ now
1029 setLengthVarIfKnown(e.lengthVar, e.e1);
1030 expOptimize(e.lwr, WANTvalue);
1031 expOptimize(e.upr, WANTvalue);
1032 if (ret.op == EXP.error)
5fee5ec3 1033 return;
9c7d5e88 1034 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
5fee5ec3
IB
1035 if (CTFEExp.isCantExp(ret))
1036 ret = e;
1037 }
9c7d5e88
IB
1038 // https://issues.dlang.org/show_bug.cgi?id=14649
1039 // Leave the slice form so it might be
1040 // a part of array operation.
1041 // Assume that the backend codegen will handle the form `e[]`
1042 // as an equal to `e` itself.
1043 if (ret.op == EXP.string_)
1044 {
1045 e.e1 = ret;
1046 e.lwr = null;
1047 e.upr = null;
1048 ret = e;
1049 }
1050 //printf("-SliceExp::optimize() %s\n", ret.toChars());
1051 }
5fee5ec3 1052
9c7d5e88
IB
1053 void visitLogical(LogicalExp e)
1054 {
1055 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1056 if (expOptimize(e.e1, WANTvalue))
1057 return;
1058 const oror = e.op == EXP.orOr;
1059 if (e.e1.toBool().hasValue(oror))
5fee5ec3 1060 {
9c7d5e88
IB
1061 // Replace with (e1, oror)
1062 ret = IntegerExp.createBool(oror);
1063 ret = Expression.combine(e.e1, ret);
1064 if (e.type.toBasetype().ty == Tvoid)
5fee5ec3 1065 {
9c7d5e88
IB
1066 ret = new CastExp(e.loc, ret, Type.tvoid);
1067 ret.type = e.type;
5fee5ec3 1068 }
9c7d5e88
IB
1069 ret = Expression_optimize(ret, result, false);
1070 return;
1071 }
1072 expOptimize(e.e2, WANTvalue);
1073 if (e.e1.isConst())
1074 {
1075 const e1Opt = e.e1.toBool();
1076 if (e.e2.isConst())
5fee5ec3 1077 {
9c7d5e88
IB
1078 bool n1 = e1Opt.hasValue(true);
1079 bool n2 = e.e2.toBool().hasValue(true);
1080 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
5fee5ec3 1081 }
9c7d5e88 1082 else if (e1Opt.hasValue(!oror))
5fee5ec3 1083 {
9c7d5e88
IB
1084 if (e.type.toBasetype().ty == Tvoid)
1085 ret = e.e2;
1086 else
1087 {
1088 ret = new CastExp(e.loc, e.e2, e.type);
1089 ret.type = e.type;
1090 }
5fee5ec3 1091 }
5fee5ec3 1092 }
9c7d5e88 1093 }
5fee5ec3 1094
9c7d5e88
IB
1095 void visitCmp(CmpExp e)
1096 {
1097 //printf("CmpExp::optimize() %s\n", e.toChars());
1098 if (binOptimize(e, WANTvalue))
1099 return;
1100 Expression e1 = fromConstInitializer(result, e.e1);
1101 Expression e2 = fromConstInitializer(result, e.e2);
1102 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
1103 if (CTFEExp.isCantExp(ret))
1104 ret = e;
1105 }
1106
1107 void visitCat(CatExp e)
1108 {
1109 //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1110 if (binOptimize(e, result))
1111 return;
1112 if (auto ce1 = e.e1.isCatExp())
5fee5ec3 1113 {
9c7d5e88
IB
1114 // https://issues.dlang.org/show_bug.cgi?id=12798
1115 // optimize ((expr ~ str1) ~ str2)
1116 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
1117 cex.type = e.type;
1118 Expression ex = Expression_optimize(cex, result, false);
1119 if (ex != cex)
5fee5ec3 1120 {
9c7d5e88
IB
1121 e.e1 = ce1.e1;
1122 e.e2 = ex;
5fee5ec3
IB
1123 }
1124 }
9c7d5e88
IB
1125 // optimize "str"[] -> "str"
1126 if (auto se1 = e.e1.isSliceExp())
1127 {
1128 if (se1.e1.op == EXP.string_ && !se1.lwr)
1129 e.e1 = se1.e1;
1130 }
1131 if (auto se2 = e.e2.isSliceExp())
1132 {
1133 if (se2.e1.op == EXP.string_ && !se2.lwr)
1134 e.e2 = se2.e1;
1135 }
1136 ret = Cat(e.loc, e.type, e.e1, e.e2).copy();
1137 if (CTFEExp.isCantExp(ret))
1138 ret = e;
5fee5ec3
IB
1139 }
1140
9c7d5e88
IB
1141 void visitCond(CondExp e)
1142 {
1143 if (expOptimize(e.econd, WANTvalue))
1144 return;
1145 const opt = e.econd.toBool();
1146 if (opt.hasValue(true))
1147 ret = Expression_optimize(e.e1, result, keepLvalue);
1148 else if (opt.hasValue(false))
1149 ret = Expression_optimize(e.e2, result, keepLvalue);
1150 else
1151 {
1152 expOptimize(e.e1, result, keepLvalue);
1153 expOptimize(e.e2, result, keepLvalue);
1154 }
1155 }
5fee5ec3
IB
1156
1157 // Optimize the expression until it can no longer be simplified.
1158 size_t b;
1159 while (1)
1160 {
1161 if (b++ == global.recursionLimit)
1162 {
1163 e.error("infinite loop while optimizing expression");
1164 fatal();
1165 }
9c7d5e88
IB
1166
1167 auto ex = ret;
1168 switch (ex.op)
1169 {
1170 case EXP.variable: visitVar(ex.isVarExp()); break;
1171 case EXP.tuple: visitTuple(ex.isTupleExp()); break;
1172 case EXP.arrayLiteral: visitArrayLiteral(ex.isArrayLiteralExp()); break;
1173 case EXP.assocArrayLiteral: visitAssocArrayLiteral(ex.isAssocArrayLiteralExp()); break;
1174 case EXP.structLiteral: visitStructLiteral(ex.isStructLiteralExp()); break;
1175
1176 case EXP.import_:
1177 case EXP.assert_:
1178 case EXP.dotIdentifier:
1179 case EXP.dotTemplateDeclaration:
1180 case EXP.dotTemplateInstance:
1181 case EXP.delegate_:
1182 case EXP.dotType:
1183 case EXP.uadd:
1184 case EXP.delete_:
1185 case EXP.vector:
1186 case EXP.vectorArray:
1187 case EXP.array:
1188 case EXP.delegatePointer:
1189 case EXP.delegateFunctionPointer:
1190 case EXP.preMinusMinus:
1191 case EXP.prePlusPlus: visitUna(cast(UnaExp)ex); break;
1192
1193 case EXP.negate: visitNeg(ex.isNegExp()); break;
1194 case EXP.tilde: visitCom(ex.isComExp()); break;
1195 case EXP.not: visitNop(ex.isNotExp()); break;
1196 case EXP.symbolOffset: visitSymOff(ex.isSymOffExp()); break;
1197 case EXP.address: visitAddr(ex.isAddrExp()); break;
1198 case EXP.star: visitPtr(ex.isPtrExp()); break;
1199 case EXP.dotVariable: visitDotVar(ex.isDotVarExp()); break;
1200 case EXP.new_: visitNew(ex.isNewExp()); break;
1201 case EXP.call: visitCall(ex.isCallExp()); break;
1202 case EXP.cast_: visitCast(ex.isCastExp()); break;
1203
1204 case EXP.addAssign:
1205 case EXP.minAssign:
1206 case EXP.mulAssign:
1207 case EXP.divAssign:
1208 case EXP.modAssign:
1209 case EXP.andAssign:
1210 case EXP.orAssign:
1211 case EXP.xorAssign:
1212 case EXP.powAssign:
1213 case EXP.leftShiftAssign:
1214 case EXP.rightShiftAssign:
1215 case EXP.unsignedRightShiftAssign:
1216 case EXP.concatenateElemAssign:
1217 case EXP.concatenateDcharAssign:
1218 case EXP.concatenateAssign: visitBinAssign(ex.isBinAssignExp()); break;
1219
1220 case EXP.minusMinus:
1221 case EXP.plusPlus:
1222 case EXP.assign:
1223 case EXP.construct:
1224 case EXP.blit:
1225 case EXP.in_:
1226 case EXP.remove:
1227 case EXP.dot: visitBin(cast(BinExp)ex); break;
1228
1229 case EXP.add: visitAdd(ex.isAddExp()); break;
1230 case EXP.min: visitMin(ex.isMinExp()); break;
1231 case EXP.mul: visitMul(ex.isMulExp()); break;
1232 case EXP.div: visitDiv(ex.isDivExp()); break;
1233 case EXP.mod: visitMod(ex.isModExp()); break;
1234 case EXP.leftShift: visitShl(ex.isShlExp()); break;
1235 case EXP.rightShift: visitShr(ex.isShrExp()); break;
1236 case EXP.unsignedRightShift: visitUshr(ex.isUshrExp()); break;
1237 case EXP.and: visitAnd(ex.isAndExp()); break;
1238 case EXP.or: visitOr(ex.isOrExp()); break;
1239 case EXP.xor: visitXor(ex.isXorExp()); break;
1240 case EXP.pow: visitPow(ex.isPowExp()); break;
1241 case EXP.comma: visitComma(ex.isCommaExp()); break;
1242 case EXP.arrayLength: visitArrayLength(ex.isArrayLengthExp()); break;
1243 case EXP.notEqual:
1244 case EXP.equal: visitEqual(ex.isEqualExp()); break;
1245 case EXP.notIdentity:
1246 case EXP.identity: visitIdentity(ex.isIdentityExp()); break;
1247 case EXP.index: visitIndex(ex.isIndexExp()); break;
1248 case EXP.slice: visitSlice(ex.isSliceExp()); break;
1249 case EXP.andAnd:
1250 case EXP.orOr: visitLogical(ex.isLogicalExp()); break;
1251 case EXP.lessThan:
1252 case EXP.lessOrEqual:
1253 case EXP.greaterThan:
1254 case EXP.greaterOrEqual: visitCmp(cast(CmpExp)ex); break;
1255 case EXP.concatenate: visitCat(ex.isCatExp()); break;
1256 case EXP.question: visitCond(ex.isCondExp()); break;
1257
1258 default: visitExp(ex); break;
1259 }
1260
1261 if (ex == ret)
5fee5ec3
IB
1262 break;
1263 }
9c7d5e88 1264 return ret;
5fee5ec3 1265}