d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / optimize.d
blobf86abde5c3e2b88156a658a19adccf07d7a6c161
1 /**
2 * Perform constant folding.
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://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
12 module dmd.optimize;
14 import core.stdc.stdio;
16 import dmd.astenums;
17 import dmd.constfold;
18 import dmd.ctfeexpr;
19 import dmd.dcast;
20 import dmd.dclass;
21 import dmd.declaration;
22 import dmd.dsymbol;
23 import dmd.dsymbolsem;
24 import dmd.errors;
25 import dmd.expression;
26 import dmd.expressionsem;
27 import dmd.globals;
28 import dmd.hdrgen;
29 import dmd.init;
30 import dmd.location;
31 import dmd.mtype;
32 import dmd.printast;
33 import dmd.root.ctfloat;
34 import dmd.sideeffect;
35 import dmd.tokens;
36 import dmd.visitor;
38 /*************************************
39 * If variable has a const initializer,
40 * return that initializer.
41 * Returns:
42 * initializer if there is one,
43 * null if not,
44 * ErrorExp if error
46 Expression expandVar(int result, VarDeclaration v)
48 //printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");
50 /********
51 * Params:
52 * e = initializer expression
54 Expression initializerReturn(Expression e)
56 if (e.type != v.type)
58 e = e.castTo(null, v.type);
60 v.inuse++;
61 e = e.optimize(result);
62 v.inuse--;
63 //if (e) printf("\te = %p, %s, e.type = %d, %s\n", e, e.toChars(), e.type.ty, e.type.toChars());
64 return e;
67 static Expression nullReturn()
69 return null;
72 static Expression errorReturn()
74 return ErrorExp.get();
77 if (!v)
78 return nullReturn();
79 if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run
80 v.dsymbolSemantic(null);
81 if (v.type &&
82 (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest))
84 Type tb = v.type.toBasetype();
85 if (v.storage_class & STC.manifest ||
86 tb.isscalar() ||
87 ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct)))
89 if (v._init)
91 if (v.inuse)
93 if (v.storage_class & STC.manifest)
95 .error(v.loc, "%s `%s` recursive initialization of constant", v.kind, v.toPrettyChars);
96 return errorReturn();
98 return nullReturn();
100 Expression ei = v.getConstInitializer();
101 if (!ei)
103 if (v.storage_class & STC.manifest)
105 .error(v.loc, "%s `%s` enum cannot be initialized with `%s`", v.kind, v.toPrettyChars, dmd.hdrgen.toChars(v._init));
106 return errorReturn();
108 return nullReturn();
110 if (ei.op == EXP.construct || ei.op == EXP.blit)
112 AssignExp ae = cast(AssignExp)ei;
113 ei = ae.e2;
114 if (ei.isConst() == 1)
117 else if (ei.op == EXP.string_)
119 // https://issues.dlang.org/show_bug.cgi?id=14459
120 // Do not constfold the string literal
121 // if it's typed as a C string, because the value expansion
122 // will drop the pointer identity.
123 if (!(result & WANTexpand) && ei.type.toBasetype().ty == Tpointer)
124 return nullReturn();
126 else
127 return nullReturn();
128 if (ei.type == v.type)
130 // const variable initialized with const expression
132 else if (ei.implicitConvTo(v.type) >= MATCH.constant)
134 // const var initialized with non-const expression
135 ei = ei.implicitCastTo(null, v.type);
136 ei = ei.expressionSemantic(null);
138 else
139 return nullReturn();
141 else if (!(v.storage_class & STC.manifest) &&
142 ei.isConst() != 1 &&
143 ei.op != EXP.string_ &&
144 ei.op != EXP.address)
146 return nullReturn();
149 if (!ei.type)
151 return nullReturn();
153 else
155 // Should remove the copy() operation by
156 // making all mods to expressions copy-on-write
157 return initializerReturn(ei.copy());
160 else
162 // v does not have an initializer
163 version (all)
165 return nullReturn();
167 else
169 // BUG: what if const is initialized in constructor?
170 auto e = v.type.defaultInit();
171 e.loc = e1.loc;
172 return initializerReturn(e);
175 assert(0);
178 return nullReturn();
181 private Expression fromConstInitializer(int result, Expression e1)
183 //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
184 //static int xx; if (xx++ == 10) assert(0);
185 Expression e = e1;
186 if (auto ve = e1.isVarExp())
188 VarDeclaration v = ve.var.isVarDeclaration();
189 e = expandVar(result, v);
190 if (e)
192 // If it is a comma expression involving a declaration, we mustn't
193 // perform a copy -- we'd get two declarations of the same variable.
194 // See https://issues.dlang.org/show_bug.cgi?id=4465.
195 if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp())
196 e = e1;
197 else if (e.type != e1.type && e1.type && e1.type.ty != Tident)
199 // Type 'paint' operation
200 e = e.copy();
201 e.type = e1.type;
203 e.loc = e1.loc;
205 else
207 e = e1;
210 return e;
213 /***
214 * It is possible for constant folding to change an array expression of
215 * unknown length, into one where the length is known.
216 * If the expression 'arr' is a literal, set lengthVar to be its length.
217 * Params:
218 * lengthVar = variable declaration for the `.length` property
219 * arr = String, ArrayLiteral, or of TypeSArray
221 package void setLengthVarIfKnown(VarDeclaration lengthVar, Expression arr)
223 if (!lengthVar)
224 return;
225 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
226 return; // we have previously calculated the length
227 dinteger_t len;
228 if (auto se = arr.isStringExp())
229 len = se.len;
230 else if (auto ale = arr.isArrayLiteralExp())
231 len = ale.elements.length;
232 else
234 auto tsa = arr.type.toBasetype().isTypeSArray();
235 if (!tsa)
236 return; // we don't know the length yet
237 len = tsa.dim.toInteger();
239 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
240 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
241 lengthVar.storage_class |= STC.static_ | STC.const_;
244 /***
245 * Same as above, but determines the length from 'type'.
246 * Params:
247 * lengthVar = variable declaration for the `.length` property
248 * type = TypeSArray
250 package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type)
252 if (!lengthVar)
253 return;
254 if (lengthVar._init && !lengthVar._init.isVoidInitializer())
255 return; // we have previously calculated the length
256 auto tsa = type.toBasetype().isTypeSArray();
257 if (!tsa)
258 return; // we don't know the length yet
259 const len = tsa.dim.toInteger();
260 Expression dollar = new IntegerExp(Loc.initial, len, Type.tsize_t);
261 lengthVar._init = new ExpInitializer(Loc.initial, dollar);
262 lengthVar.storage_class |= STC.static_ | STC.const_;
265 /*********************************
266 * Constant fold an Expression.
267 * Params:
268 * e = expression to const fold; this may get modified in-place
269 * result = WANTvalue, WANTexpand, or both
270 * keepLvalue = `e` is an lvalue, and keep it as an lvalue since it is
271 * an argument to a `ref` or `out` parameter, or the operand of `&` operator
272 * Returns:
273 * Constant folded version of `e`
275 Expression optimize(Expression e, int result, bool keepLvalue = false)
277 //printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue);
278 Expression ret = e;
280 void errorReturn()
282 ret = ErrorExp.get();
285 /* Returns: true if error
287 bool expOptimize(ref Expression e, int flags, bool keepLvalue = false)
289 if (!e)
290 return false;
291 Expression ex = optimize(e, flags, keepLvalue);
292 if (ex.op == EXP.error)
294 ret = ex; // store error result
295 return true;
297 else
299 e = ex; // modify original
300 return false;
304 bool unaOptimize(UnaExp e, int flags)
306 return expOptimize(e.e1, flags);
309 bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
311 return expOptimize(e.e1, flags, keepLhsLvalue) |
312 expOptimize(e.e2, flags);
315 void visitExp(Expression e)
317 //printf("Expression::optimize(result = x%x) %s\n", result, e.toChars());
320 void visitVar(VarExp e)
322 VarDeclaration v = e.var.isVarDeclaration();
324 if (!(keepLvalue && v && !(v.storage_class & STC.manifest)))
325 ret = fromConstInitializer(result, e);
327 // if unoptimized, try to optimize the dtor expression
328 // (e.g., might be a LogicalExp with constant lhs)
329 if (ret == e && v && v.edtor)
331 // prevent infinite recursion (`<var>.~this()`)
332 if (!v.inuse)
334 v.inuse++;
335 expOptimize(v.edtor, WANTvalue);
336 v.inuse--;
341 void visitTuple(TupleExp e)
343 expOptimize(e.e0, WANTvalue);
344 foreach (ref ex; (*e.exps)[])
346 expOptimize(ex, WANTvalue);
350 void visitArrayLiteral(ArrayLiteralExp e)
352 if (e.elements)
354 expOptimize(e.basis, result & WANTexpand);
355 foreach (ref ex; (*e.elements)[])
357 expOptimize(ex, result & WANTexpand);
362 void visitAssocArrayLiteral(AssocArrayLiteralExp e)
364 assert(e.keys.length == e.values.length);
365 foreach (i, ref ekey; (*e.keys)[])
367 expOptimize(ekey, result & WANTexpand);
368 expOptimize((*e.values)[i], result & WANTexpand);
372 void visitStructLiteral(StructLiteralExp e)
374 if (e.stageflags & stageOptimize)
375 return;
376 const old = e.stageflags;
377 e.stageflags |= stageOptimize;
378 if (e.elements)
380 foreach (ref ex; (*e.elements)[])
382 expOptimize(ex, result & WANTexpand);
385 e.stageflags = old;
388 void visitUna(UnaExp e)
390 //printf("UnaExp::optimize() %s\n", e.toChars());
391 if (unaOptimize(e, result))
392 return;
395 void visitNeg(NegExp e)
397 if (unaOptimize(e, result))
398 return;
399 if (e.e1.isConst() == 1)
401 ret = Neg(e.type, e.e1).copy();
405 void visitCom(ComExp e)
407 if (unaOptimize(e, result))
408 return;
409 if (e.e1.isConst() == 1)
411 ret = Com(e.type, e.e1).copy();
415 void visitNop(NotExp e)
417 if (unaOptimize(e, result))
418 return;
419 if (e.e1.isConst() == 1)
421 ret = Not(e.type, e.e1).copy();
425 void visitSymOff(SymOffExp e)
427 assert(e.var);
430 void visitAddr(AddrExp e)
432 //printf("AddrExp::optimize(result = %d, keepLvalue = %d) %s\n", result, keepLvalue, e.toChars());
433 /* Rewrite &(a,b) as (a,&b)
435 if (auto ce = e.e1.isCommaExp())
437 auto ae = new AddrExp(e.loc, ce.e2, e.type);
438 ret = new CommaExp(ce.loc, ce.e1, ae);
439 ret.type = e.type;
440 return;
442 // Keep lvalue-ness
443 if (expOptimize(e.e1, result, true))
444 return; // error return
446 // Convert &*ex to ex
447 if (auto pe = e.e1.isPtrExp())
449 Expression ex = pe.e1;
450 if (e.type.equals(ex.type))
451 ret = ex;
452 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
454 ret = ex.copy();
455 ret.type = e.type;
457 return;
459 if (auto ve = e.e1.isVarExp())
461 if (!ve.var.isReference() && !ve.var.isImportedSymbol())
463 ret = new SymOffExp(e.loc, ve.var, 0, ve.hasOverloads);
464 ret.type = e.type;
465 return;
468 if (e.e1.isDotVarExp())
470 /******************************
471 * Run down the left side of the a.b.c expression to determine the
472 * leftmost variable being addressed (`a`), and accumulate the offsets of the `.b` and `.c`.
473 * Params:
474 * e = the DotVarExp or VarExp
475 * var = set to the VarExp at the end, or null if doesn't end in VarExp
476 * eint = set to the IntegerExp at the end, or null if doesn't end in IntegerExp
477 * offset = accumulation of all the .var offsets encountered
478 * Returns: true on error
480 static bool getVarAndOffset(Expression e, out VarDeclaration var, out IntegerExp eint, ref uint offset)
482 if (e.type.size() == SIZE_INVALID) // trigger computation of v.offset
483 return true;
485 if (auto dve = e.isDotVarExp())
487 auto v = dve.var.isVarDeclaration();
488 if (!v || !v.isField() || v.isBitFieldDeclaration())
489 return false;
491 if (getVarAndOffset(dve.e1, var, eint, offset))
492 return true;
493 offset += v.offset;
495 else if (auto ve = e.isVarExp())
497 if (!ve.var.isReference() &&
498 !ve.var.isImportedSymbol() &&
499 ve.var.isDataseg() &&
500 ve.var.isCsymbol())
502 var = ve.var.isVarDeclaration();
505 else if (auto ep = e.isPtrExp())
507 if (auto ei = ep.e1.isIntegerExp())
509 eint = ei;
511 else if (auto se = ep.e1.isSymOffExp())
513 if (!se.var.isReference() &&
514 !se.var.isImportedSymbol() &&
515 se.var.isDataseg())
517 var = se.var.isVarDeclaration();
518 offset += se.offset;
522 else if (auto ei = e.isIndexExp())
524 if (auto ve = ei.e1.isVarExp())
526 if (!ve.var.isReference() &&
527 !ve.var.isImportedSymbol() &&
528 ve.var.isDataseg() &&
529 ve.var.isCsymbol())
531 if (auto ie = ei.e2.isIntegerExp())
533 var = ve.var.isVarDeclaration();
534 offset += ie.toInteger() * ve.type.toBasetype().nextOf().size();
539 return false;
542 uint offset;
543 VarDeclaration var;
544 IntegerExp eint;
545 if (getVarAndOffset(e.e1, var, eint, offset))
547 ret = ErrorExp.get();
548 return;
550 if (var)
552 ret = new SymOffExp(e.loc, var, offset, false);
553 ret.type = e.type;
554 return;
556 if (eint)
558 ret = new IntegerExp(e.loc, eint.toInteger() + offset, e.type);
559 return;
562 else if (auto ae = e.e1.isIndexExp())
564 if (ae.e2.isIntegerExp() && ae.e1.isIndexExp())
566 /* Rewrite `(a[i])[index]` to `(&a[i]) + index*size`
568 sinteger_t index = ae.e2.toInteger();
569 auto ae1 = ae.e1.isIndexExp(); // ae1 is a[i]
570 if (auto ts = ae1.type.isTypeSArray())
572 sinteger_t dim = ts.dim.toInteger();
574 if (index < 0 || index > dim)
576 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim);
577 return errorReturn();
580 import core.checkedint : mulu;
581 bool overflow;
582 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // offset = index*size
583 if (overflow)
585 error(e.loc, "array offset overflow");
586 return errorReturn();
589 Expression ex = new AddrExp(ae1.loc, ae1); // &a[i]
590 ex.type = ae1.type.pointerTo();
592 Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type));
593 add.type = e.type;
594 ret = optimize(add, result, keepLvalue);
595 return;
599 // Convert &array[n] to &array+n
600 if (ae.e2.isIntegerExp() && ae.e1.isVarExp())
602 sinteger_t index = ae.e2.toInteger();
603 VarExp ve = ae.e1.isVarExp();
604 if (ve.type.isTypeSArray() && !ve.var.isImportedSymbol())
606 TypeSArray ts = ve.type.isTypeSArray();
607 sinteger_t dim = ts.dim.toInteger();
608 if (index < 0 || index >= dim)
610 /* 0 for C static arrays means size is unknown, no need to check,
611 * and address one past the end is OK, too
613 if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
615 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim);
616 return errorReturn();
620 import core.checkedint : mulu;
621 bool overflow;
622 const offset = mulu(index, ts.nextOf().size(e.loc), overflow);
623 if (overflow)
625 error(e.loc, "array offset overflow");
626 return errorReturn();
629 ret = new SymOffExp(e.loc, ve.var, offset);
630 ret.type = e.type;
631 return;
634 // Convert &((a.b)[index]) to (&a.b)+index*elementsize
635 else if (ae.e2.isIntegerExp() && ae.e1.isDotVarExp())
637 sinteger_t index = ae.e2.toInteger();
638 DotVarExp ve = ae.e1.isDotVarExp();
639 if (ve.type.isTypeSArray() && ve.var.isField() && ve.e1.isPtrExp())
641 TypeSArray ts = ve.type.isTypeSArray();
642 sinteger_t dim = ts.dim.toInteger();
643 if (index < 0 || index >= dim)
645 /* 0 for C static arrays means size is unknown, no need to check,
646 * and address one past the end is OK, too
648 if (!((dim == 0 || dim == index) && ve.var.isCsymbol()))
650 error(e.loc, "array index %lld is out of bounds `[0..%lld]`", index, dim);
651 return errorReturn();
655 import core.checkedint : mulu;
656 bool overflow;
657 const offset = mulu(index, ts.nextOf().size(e.loc), overflow); // index*elementsize
658 if (overflow)
660 error(e.loc, "array offset overflow");
661 return errorReturn();
664 auto pe = new AddrExp(e.loc, ve);
665 pe.type = e.type;
666 ret = new AddExp(e.loc, pe, new IntegerExp(e.loc, offset, Type.tsize_t));
667 ret.type = e.type;
668 return;
674 void visitPtr(PtrExp e)
676 //printf("PtrExp::optimize(result = x%x) %s\n", result, e.toChars());
677 if (expOptimize(e.e1, result))
678 return;
679 // Convert *&ex to ex
680 // But only if there is no type punning involved
681 if (auto ey = e.e1.isAddrExp())
683 Expression ex = ey.e1;
684 if (e.type.equals(ex.type))
685 ret = ex;
686 else if (e.type.toBasetype().equivalent(ex.type.toBasetype()))
688 ret = ex.copy();
689 ret.type = e.type;
692 if (keepLvalue)
693 return;
694 // Constant fold *(&structliteral + offset)
695 if (e.e1.op == EXP.add)
697 Expression ex = Ptr(e.type, e.e1).copy();
698 if (!CTFEExp.isCantExp(ex))
700 ret = ex;
701 return;
704 if (auto se = e.e1.isSymOffExp())
706 VarDeclaration v = se.var.isVarDeclaration();
707 Expression ex = expandVar(result, v);
708 if (ex && ex.isStructLiteralExp())
710 StructLiteralExp sle = ex.isStructLiteralExp();
711 ex = sle.getField(e.type, cast(uint)se.offset);
712 if (ex && !CTFEExp.isCantExp(ex))
714 ret = ex;
715 return;
721 void visitDotVar(DotVarExp e)
723 //printf("DotVarExp::optimize(result = x%x) %s\n", result, e.toChars());
724 if (expOptimize(e.e1, result))
725 return;
726 if (keepLvalue)
727 return;
728 Expression ex = e.e1;
729 if (auto ve = ex.isVarExp())
731 VarDeclaration v = ve.var.isVarDeclaration();
732 ex = expandVar(result, v);
734 if (ex && ex.isStructLiteralExp())
736 StructLiteralExp sle = ex.isStructLiteralExp();
737 VarDeclaration vf = e.var.isVarDeclaration();
738 if (vf && !vf.overlapped)
740 /* https://issues.dlang.org/show_bug.cgi?id=13021
741 * Prevent optimization if vf has overlapped fields.
743 ex = sle.getField(e.type, vf.offset);
744 if (ex && !CTFEExp.isCantExp(ex))
746 ret = ex;
747 return;
753 void visitNew(NewExp e)
755 expOptimize(e.thisexp, WANTvalue);
756 // Optimize parameters
757 if (e.arguments)
759 foreach (ref arg; (*e.arguments)[])
761 expOptimize(arg, WANTvalue);
766 void visitCall(CallExp e)
768 //printf("CallExp::optimize(result = %d) %s\n", result, e.toChars());
769 // Optimize parameters with keeping lvalue-ness
770 if (expOptimize(e.e1, result))
771 return;
772 if (e.arguments)
774 // t1 can apparently be void for __ArrayDtor(T) calls
775 if (auto tf = e.calledFunctionType())
777 foreach (i, ref arg; (*e.arguments)[])
779 Parameter p = tf.parameterList[i];
780 bool keep = p && p.isReference();
781 expOptimize(arg, WANTvalue, keep);
787 void visitCast(CastExp e)
789 //printf("CastExp::optimize(result = %d) %s\n", result, e.toChars());
790 //printf("from %s to %s\n", e.type.toChars(), e.to.toChars());
791 //printf("from %s\n", e.type.toChars());
792 //printf("e1.type %s\n", e.e1.type.toChars());
793 //printf("type = %p\n", e.type);
794 assert(e.type);
795 const op1 = e.e1.op;
796 Expression e1old = e.e1;
797 if (expOptimize(e.e1, result, keepLvalue))
798 return;
799 if (!keepLvalue)
800 e.e1 = fromConstInitializer(result, e.e1);
801 if (e.e1 == e1old && e.e1.op == EXP.arrayLiteral && e.type.toBasetype().ty == Tpointer && e.e1.type.toBasetype().ty != Tsarray)
803 // Casting this will result in the same expression, and
804 // infinite loop because of Expression::implicitCastTo()
805 return; // no change
807 if ((e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral) &&
808 (e.type.ty == Tpointer || e.type.ty == Tarray))
810 const esz = e.type.nextOf().size(e.loc);
811 const e1sz = e.e1.type.toBasetype().nextOf().size(e.e1.loc);
812 if (esz == SIZE_INVALID || e1sz == SIZE_INVALID)
813 return errorReturn();
815 if (e1sz == esz)
817 // https://issues.dlang.org/show_bug.cgi?id=12937
818 // If target type is void array, trying to paint
819 // e.e1 with that type will cause infinite recursive optimization.
820 if (e.type.nextOf().ty == Tvoid)
821 return;
822 ret = e.e1.castTo(null, e.type);
823 //printf(" returning1 %s\n", ret.toChars());
824 return;
828 // Returning e.e1 with changing its type
829 void returnE_e1()
831 ret = (e1old == e.e1 ? e.e1.copy() : e.e1);
832 ret.type = e.type;
835 if (e.e1.op == EXP.structLiteral && e.e1.type.implicitConvTo(e.type) >= MATCH.constant)
837 //printf(" returning2 %s\n", e.e1.toChars());
838 return returnE_e1();
840 /* The first test here is to prevent infinite loops
842 if (op1 != EXP.arrayLiteral && e.e1.op == EXP.arrayLiteral)
844 ret = e.e1.castTo(null, e.to);
845 return;
847 if (e.e1.op == EXP.null_ && (e.type.ty == Tpointer || e.type.ty == Tclass || e.type.ty == Tarray))
849 //printf(" returning3 %s\n", e.e1.toChars());
850 return returnE_e1();
852 if (e.type.ty == Tclass && e.e1.type.ty == Tclass)
854 import dmd.astenums : Sizeok;
856 // See if we can remove an unnecessary cast
857 ClassDeclaration cdfrom = e.e1.type.isClassHandle();
858 ClassDeclaration cdto = e.type.isClassHandle();
859 if (cdfrom.errors || cdto.errors)
860 return errorReturn();
861 if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
862 return returnE_e1(); // can always convert a class to Object
863 // Need to determine correct offset before optimizing away the cast.
864 // https://issues.dlang.org/show_bug.cgi?id=16980
865 if (cdfrom.size(e.loc) == SIZE_INVALID)
866 return errorReturn();
867 assert(cdfrom.sizeok == Sizeok.done);
868 assert(cdto.sizeok == Sizeok.done || !cdto.isBaseOf(cdfrom, null));
869 int offset;
870 if (cdto.isBaseOf(cdfrom, &offset) && offset == 0)
872 //printf(" returning4 %s\n", e.e1.toChars());
873 return returnE_e1();
876 if (e.e1.type.mutableOf().unSharedOf().equals(e.to.mutableOf().unSharedOf()))
878 //printf(" returning5 %s\n", e.e1.toChars());
879 return returnE_e1();
881 if (e.e1.isConst())
883 if (e.e1.op == EXP.symbolOffset)
885 if (e.type.toBasetype().ty != Tsarray)
887 const esz = e.type.size(e.loc);
888 const e1sz = e.e1.type.size(e.e1.loc);
889 if (esz == SIZE_INVALID ||
890 e1sz == SIZE_INVALID)
891 return errorReturn();
893 if (esz == e1sz)
894 return returnE_e1();
896 return;
898 if (e.to.toBasetype().ty != Tvoid)
900 if (e.e1.type.equals(e.type) && e.type.equals(e.to))
901 ret = e.e1;
902 else
903 ret = Cast(e.loc, e.type, e.to, e.e1).copy();
906 //printf(" returning6 %s\n", ret.toChars());
909 void visitBinAssign(BinAssignExp e)
911 //printf("BinAssignExp::optimize(result = %d) %s\n", result, e.toChars());
912 if (binOptimize(e, result, /*keepLhsLvalue*/ true))
913 return;
914 if (e.op == EXP.leftShiftAssign || e.op == EXP.rightShiftAssign || e.op == EXP.unsignedRightShiftAssign)
916 if (e.e2.isConst() == 1)
918 sinteger_t i2 = e.e2.toInteger();
919 uinteger_t sz = e.e1.type.size(e.e1.loc);
920 assert(sz != SIZE_INVALID);
921 sz *= 8;
922 if (i2 < 0 || i2 >= sz)
924 error(e.loc, "shift assign by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
925 return errorReturn();
931 void visitCatAssign(CatAssignExp e)
933 if (auto lowering = e.lowering)
934 optimize(lowering, result, keepLvalue);
935 else
936 visitBinAssign(e);
939 void visitBin(BinExp e)
941 //printf("BinExp::optimize(result = %d) %s\n", result, e.toChars());
942 const keepLhsLvalue = e.op == EXP.construct || e.op == EXP.blit || e.op == EXP.assign
943 || e.op == EXP.plusPlus || e.op == EXP.minusMinus
944 || e.op == EXP.prePlusPlus || e.op == EXP.preMinusMinus;
945 binOptimize(e, result, keepLhsLvalue);
948 void visitAdd(AddExp e)
950 //printf("AddExp::optimize(%s)\n", e.toChars());
951 if (binOptimize(e, result))
952 return;
953 if (e.e1.isConst() && e.e2.isConst())
955 if (e.e1.op == EXP.symbolOffset && e.e2.op == EXP.symbolOffset)
956 return;
957 ret = Add(e.loc, e.type, e.e1, e.e2).copy();
961 void visitMin(MinExp e)
963 //printf("MinExp::optimize(%s)\n", e.toChars());
964 if (binOptimize(e, result))
965 return;
966 if (e.e1.isConst() && e.e2.isConst())
968 if (e.e2.op == EXP.symbolOffset)
969 return;
970 ret = Min(e.loc, e.type, e.e1, e.e2).copy();
974 void visitMul(MulExp e)
976 //printf("MulExp::optimize(result = %d) %s\n", result, e.toChars());
977 if (binOptimize(e, result))
978 return;
979 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
981 ret = Mul(e.loc, e.type, e.e1, e.e2).copy();
985 void visitDiv(DivExp e)
987 //printf("DivExp::optimize(%s)\n", e.toChars());
988 if (binOptimize(e, result))
989 return;
990 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
992 ret = Div(e.loc, e.type, e.e1, e.e2).copy();
996 void visitMod(ModExp e)
998 if (binOptimize(e, result))
999 return;
1000 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1002 ret = Mod(e.loc, e.type, e.e1, e.e2).copy();
1006 extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift)
1008 if (binOptimize(e, result))
1009 return;
1010 if (e.e2.isConst() == 1)
1012 sinteger_t i2 = e.e2.toInteger();
1013 uinteger_t sz = e.e1.type.size(e.e1.loc);
1014 assert(sz != SIZE_INVALID);
1015 sz *= 8;
1016 if (i2 < 0 || i2 >= sz)
1018 error(e.loc, "shift by %lld is outside the range `0..%llu`", i2, cast(ulong)sz - 1);
1019 return errorReturn();
1021 if (e.e1.isConst() == 1)
1022 ret = (*shift)(e.loc, e.type, e.e1, e.e2).copy();
1026 void visitShl(ShlExp e)
1028 //printf("ShlExp::optimize(result = %d) %s\n", result, e.toChars());
1029 shift_optimize(e, &Shl);
1032 void visitShr(ShrExp e)
1034 //printf("ShrExp::optimize(result = %d) %s\n", result, e.toChars());
1035 shift_optimize(e, &Shr);
1038 void visitUshr(UshrExp e)
1040 //printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
1041 shift_optimize(e, &Ushr);
1044 void visitAnd(AndExp e)
1046 if (binOptimize(e, result))
1047 return;
1048 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1049 ret = And(e.loc, e.type, e.e1, e.e2).copy();
1052 void visitOr(OrExp e)
1054 if (binOptimize(e, result))
1055 return;
1056 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1057 ret = Or(e.loc, e.type, e.e1, e.e2).copy();
1060 void visitXor(XorExp e)
1062 if (binOptimize(e, result))
1063 return;
1064 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1065 ret = Xor(e.loc, e.type, e.e1, e.e2).copy();
1068 void visitPow(PowExp e)
1070 if (binOptimize(e, result))
1071 return;
1072 // All negative integral powers are illegal.
1073 if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0)
1075 error(e.loc, "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());
1076 return errorReturn();
1078 // If e2 *could* have been an integer, make it one.
1079 if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal()))
1081 // This only applies to floating point, or positive integral powers.
1082 if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0)
1083 e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64);
1085 if (e.e1.isConst() == 1 && e.e2.isConst() == 1)
1087 Expression ex = Pow(e.loc, e.type, e.e1, e.e2).copy();
1088 if (!CTFEExp.isCantExp(ex))
1090 ret = ex;
1091 return;
1096 void visitComma(CommaExp e)
1098 //printf("CommaExp::optimize(result = %d) %s\n", result, e.toChars());
1099 // Comma needs special treatment, because it may
1100 // contain compiler-generated declarations. We can interpret them, but
1101 // otherwise we must NOT attempt to constant-fold them.
1102 // In particular, if the comma returns a temporary variable, it needs
1103 // to be an lvalue (this is particularly important for struct constructors)
1104 expOptimize(e.e1, WANTvalue);
1105 expOptimize(e.e2, result, keepLvalue);
1106 if (ret.op == EXP.error)
1107 return;
1108 if (!e.e1 || e.e1.op == EXP.int64 || e.e1.op == EXP.float64 || !hasSideEffect(e.e1))
1110 ret = e.e2;
1111 if (ret)
1112 ret.type = e.type;
1114 //printf("-CommaExp::optimize(result = %d) %s\n", result, e.e.toChars());
1117 void visitArrayLength(ArrayLengthExp e)
1119 //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e.toChars());
1120 if (unaOptimize(e, WANTexpand))
1121 return;
1122 // CTFE interpret static immutable arrays (to get better diagnostics)
1123 if (auto ve = e.e1.isVarExp())
1125 VarDeclaration v = ve.var.isVarDeclaration();
1126 if (v && (v.storage_class & STC.static_) && (v.storage_class & STC.immutable_) && v._init)
1128 if (Expression ci = v.getConstInitializer())
1129 e.e1 = ci;
1132 if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray || e.e1.op == EXP.null_)
1134 ret = ArrayLength(e.type, e.e1).copy();
1138 void visitEqual(EqualExp e)
1140 //printf("EqualExp::optimize(result = %x) %s\n", result, e.toChars());
1141 if (binOptimize(e, WANTvalue))
1142 return;
1143 Expression e1 = fromConstInitializer(result, e.e1);
1144 Expression e2 = fromConstInitializer(result, e.e2);
1145 if (e1.op == EXP.error)
1147 ret = e1;
1148 return;
1150 if (e2.op == EXP.error)
1152 ret = e2;
1153 return;
1155 ret = Equal(e.op, e.loc, e.type, e1, e2).copy();
1156 if (CTFEExp.isCantExp(ret))
1157 ret = e;
1160 void visitIdentity(IdentityExp e)
1162 //printf("IdentityExp::optimize(result = %d) %s\n", result, e.toChars());
1163 if (binOptimize(e, WANTvalue))
1164 return;
1165 if ((e.e1.isConst() && e.e2.isConst()) || (e.e1.op == EXP.null_ && e.e2.op == EXP.null_))
1167 ret = Identity(e.op, e.loc, e.type, e.e1, e.e2).copy();
1168 if (CTFEExp.isCantExp(ret))
1169 ret = e;
1173 void visitIndex(IndexExp e)
1175 //printf("IndexExp::optimize(result = %d) %s\n", result, e.toChars());
1176 if (expOptimize(e.e1, result & WANTexpand))
1177 return;
1178 Expression ex = fromConstInitializer(result, e.e1);
1179 // We might know $ now
1180 setLengthVarIfKnown(e.lengthVar, ex);
1181 if (expOptimize(e.e2, WANTvalue))
1182 return;
1183 // Don't optimize to an array literal element directly in case an lvalue is requested
1184 if (keepLvalue && ex.op == EXP.arrayLiteral)
1185 return;
1186 ret = Index(e.type, ex, e.e2, e.indexIsInBounds).copy();
1187 if (CTFEExp.isCantExp(ret) || (!ret.isErrorExp() && keepLvalue && !ret.isLvalue()))
1188 ret = e;
1191 void visitSlice(SliceExp e)
1193 //printf("SliceExp::optimize(result = %d) %s\n", result, e.toChars());
1194 if (expOptimize(e.e1, result & WANTexpand))
1195 return;
1196 if (!e.lwr)
1198 if (e.e1.op == EXP.string_)
1200 // Convert slice of string literal into dynamic array
1201 Type t = e.e1.type.toBasetype();
1202 if (Type tn = t.nextOf())
1203 ret = e.e1.castTo(null, tn.arrayOf());
1206 else
1208 e.e1 = fromConstInitializer(result, e.e1);
1209 // We might know $ now
1210 setLengthVarIfKnown(e.lengthVar, e.e1);
1211 expOptimize(e.lwr, WANTvalue);
1212 expOptimize(e.upr, WANTvalue);
1213 if (ret.op == EXP.error)
1214 return;
1215 ret = Slice(e.type, e.e1, e.lwr, e.upr).copy();
1216 if (CTFEExp.isCantExp(ret))
1217 ret = e;
1219 // https://issues.dlang.org/show_bug.cgi?id=14649
1220 // Leave the slice form so it might be
1221 // a part of array operation.
1222 // Assume that the backend codegen will handle the form `e[]`
1223 // as an equal to `e` itself.
1224 if (ret.op == EXP.string_)
1226 e.e1 = ret;
1227 e.lwr = null;
1228 e.upr = null;
1229 ret = e;
1231 //printf("-SliceExp::optimize() %s\n", ret.toChars());
1234 void visitLogical(LogicalExp e)
1236 //printf("LogicalExp::optimize(%d) %s\n", result, e.toChars());
1237 if (expOptimize(e.e1, WANTvalue))
1238 return;
1239 const oror = e.op == EXP.orOr;
1240 if (e.e1.toBool().hasValue(oror))
1242 // Replace with (e1, oror)
1243 ret = IntegerExp.createBool(oror);
1244 ret = Expression.combine(e.e1, ret);
1245 if (e.type.toBasetype().ty == Tvoid)
1247 ret = new CastExp(e.loc, ret, Type.tvoid);
1248 ret.type = e.type;
1250 ret = optimize(ret, result, false);
1251 return;
1253 expOptimize(e.e2, WANTvalue);
1254 if (e.e1.isConst())
1256 const e1Opt = e.e1.toBool();
1257 if (e.e2.isConst())
1259 bool n1 = e1Opt.get();
1260 bool n2 = e.e2.toBool().get();
1261 ret = new IntegerExp(e.loc, oror ? (n1 || n2) : (n1 && n2), e.type);
1263 else if (e1Opt.hasValue(!oror))
1265 if (e.type.toBasetype().ty == Tvoid)
1266 ret = e.e2;
1267 else
1269 ret = new CastExp(e.loc, e.e2, e.type);
1270 ret.type = e.type;
1276 void visitCmp(CmpExp e)
1278 //printf("CmpExp::optimize() %s\n", e.toChars());
1279 if (binOptimize(e, WANTvalue))
1280 return;
1281 Expression e1 = fromConstInitializer(result, e.e1);
1282 Expression e2 = fromConstInitializer(result, e.e2);
1283 ret = Cmp(e.op, e.loc, e.type, e1, e2).copy();
1284 if (CTFEExp.isCantExp(ret))
1285 ret = e;
1288 void visitCat(CatExp e)
1290 //printf("CatExp::optimize(%d) %s\n", result, e.toChars());
1291 if (binOptimize(e, result))
1292 return;
1294 if (e.type == Type.tstring)
1295 if (auto ce1 = e.e1.isCatExp())
1297 // https://issues.dlang.org/show_bug.cgi?id=12798
1298 // optimize ((expr ~ str1) ~ str2)
1299 // https://issues.dlang.org/show_bug.cgi?id=24078
1300 // This optimization is only valid if `expr` is a string.
1301 // Otherwise it leads to:
1302 // `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"`
1303 scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2);
1304 cex.type = e.type;
1305 Expression ex = optimize(cex, result, false);
1306 if (ex != cex)
1308 e.e1 = ce1.e1;
1309 e.e2 = ex;
1312 // optimize "str"[] -> "str"
1313 if (auto se1 = e.e1.isSliceExp())
1315 if (se1.e1.op == EXP.string_ && !se1.lwr)
1316 e.e1 = se1.e1;
1318 if (auto se2 = e.e2.isSliceExp())
1320 if (se2.e1.op == EXP.string_ && !se2.lwr)
1321 e.e2 = se2.e1;
1323 ret = Cat(e.loc, e.type, e.e1, e.e2).copy();
1324 if (CTFEExp.isCantExp(ret))
1325 ret = e;
1328 void visitCond(CondExp e)
1330 if (expOptimize(e.econd, WANTvalue))
1331 return;
1332 const opt = e.econd.toBool();
1333 if (opt.hasValue(true))
1334 ret = optimize(e.e1, result, keepLvalue);
1335 else if (opt.hasValue(false))
1336 ret = optimize(e.e2, result, keepLvalue);
1337 else
1339 expOptimize(e.e1, result, keepLvalue);
1340 expOptimize(e.e2, result, keepLvalue);
1344 // Optimize the expression until it can no longer be simplified.
1345 size_t b;
1346 while (1)
1348 if (b++ == global.recursionLimit)
1350 error(e.loc, "infinite loop while optimizing expression");
1351 fatal();
1354 auto ex = ret;
1355 switch (ex.op)
1357 case EXP.variable: visitVar(ex.isVarExp()); break;
1358 case EXP.tuple: visitTuple(ex.isTupleExp()); break;
1359 case EXP.arrayLiteral: visitArrayLiteral(ex.isArrayLiteralExp()); break;
1360 case EXP.assocArrayLiteral: visitAssocArrayLiteral(ex.isAssocArrayLiteralExp()); break;
1361 case EXP.structLiteral: visitStructLiteral(ex.isStructLiteralExp()); break;
1363 case EXP.import_:
1364 case EXP.assert_:
1365 case EXP.dotIdentifier:
1366 case EXP.dotTemplateDeclaration:
1367 case EXP.dotTemplateInstance:
1368 case EXP.delegate_:
1369 case EXP.dotType:
1370 case EXP.uadd:
1371 case EXP.delete_:
1372 case EXP.vector:
1373 case EXP.vectorArray:
1374 case EXP.array:
1375 case EXP.delegatePointer:
1376 case EXP.delegateFunctionPointer:
1377 case EXP.preMinusMinus:
1378 case EXP.prePlusPlus: visitUna(cast(UnaExp)ex); break;
1380 case EXP.negate: visitNeg(ex.isNegExp()); break;
1381 case EXP.tilde: visitCom(ex.isComExp()); break;
1382 case EXP.not: visitNop(ex.isNotExp()); break;
1383 case EXP.symbolOffset: visitSymOff(ex.isSymOffExp()); break;
1384 case EXP.address: visitAddr(ex.isAddrExp()); break;
1385 case EXP.star: visitPtr(ex.isPtrExp()); break;
1386 case EXP.dotVariable: visitDotVar(ex.isDotVarExp()); break;
1387 case EXP.new_: visitNew(ex.isNewExp()); break;
1388 case EXP.call: visitCall(ex.isCallExp()); break;
1389 case EXP.cast_: visitCast(ex.isCastExp()); break;
1391 case EXP.addAssign:
1392 case EXP.minAssign:
1393 case EXP.mulAssign:
1394 case EXP.divAssign:
1395 case EXP.modAssign:
1396 case EXP.andAssign:
1397 case EXP.orAssign:
1398 case EXP.xorAssign:
1399 case EXP.powAssign:
1400 case EXP.leftShiftAssign:
1401 case EXP.rightShiftAssign:
1402 case EXP.unsignedRightShiftAssign:
1403 case EXP.concatenateDcharAssign: visitBinAssign(ex.isBinAssignExp()); break;
1404 case EXP.concatenateElemAssign:
1405 case EXP.concatenateAssign: visitCatAssign(cast(CatAssignExp) ex); break;
1407 case EXP.minusMinus:
1408 case EXP.plusPlus:
1409 case EXP.assign:
1410 case EXP.construct:
1411 case EXP.blit:
1412 case EXP.in_:
1413 case EXP.remove:
1414 case EXP.dot: visitBin(cast(BinExp)ex); break;
1416 case EXP.add: visitAdd(ex.isAddExp()); break;
1417 case EXP.min: visitMin(ex.isMinExp()); break;
1418 case EXP.mul: visitMul(ex.isMulExp()); break;
1419 case EXP.div: visitDiv(ex.isDivExp()); break;
1420 case EXP.mod: visitMod(ex.isModExp()); break;
1421 case EXP.leftShift: visitShl(ex.isShlExp()); break;
1422 case EXP.rightShift: visitShr(ex.isShrExp()); break;
1423 case EXP.unsignedRightShift: visitUshr(ex.isUshrExp()); break;
1424 case EXP.and: visitAnd(ex.isAndExp()); break;
1425 case EXP.or: visitOr(ex.isOrExp()); break;
1426 case EXP.xor: visitXor(ex.isXorExp()); break;
1427 case EXP.pow: visitPow(ex.isPowExp()); break;
1428 case EXP.comma: visitComma(ex.isCommaExp()); break;
1429 case EXP.arrayLength: visitArrayLength(ex.isArrayLengthExp()); break;
1430 case EXP.notEqual:
1431 case EXP.equal: visitEqual(ex.isEqualExp()); break;
1432 case EXP.notIdentity:
1433 case EXP.identity: visitIdentity(ex.isIdentityExp()); break;
1434 case EXP.index: visitIndex(ex.isIndexExp()); break;
1435 case EXP.slice: visitSlice(ex.isSliceExp()); break;
1436 case EXP.andAnd:
1437 case EXP.orOr: visitLogical(ex.isLogicalExp()); break;
1438 case EXP.lessThan:
1439 case EXP.lessOrEqual:
1440 case EXP.greaterThan:
1441 case EXP.greaterOrEqual: visitCmp(cast(CmpExp)ex); break;
1442 case EXP.concatenate: visitCat(ex.isCatExp()); break;
1443 case EXP.question: visitCond(ex.isCondExp()); break;
1445 default: visitExp(ex); break;
1448 if (ex == ret)
1449 break;
1451 return ret;