1 /**
2 * Defines the bulk of the classes which represent the AST at the expression level.
3 *
4 * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
5 *
6 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expression.d, _expression.d)
10 * Documentation: https://dlang.org/phobos/dmd_expression.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expression.d
12 */
13
14 module dmd.expression;
15
16 import core.stdc.stdarg;
17 import core.stdc.stdio;
18 import core.stdc.string;
19
20 import dmd.aggregate;
21 import dmd.aliasthis;
22 import dmd.apply;
23 import dmd.arrayop;
24 import dmd.arraytypes;
25 import dmd.astenums;
26 import dmd.ast_node;
27 import dmd.gluelayer;
28 import dmd.constfold;
29 import dmd.ctfeexpr;
30 import dmd.ctorflow;
31 import dmd.dcast;
32 import dmd.dclass;
33 import dmd.declaration;
34 import dmd.delegatize;
35 import dmd.dimport;
36 import dmd.dinterpret;
37 import dmd.dmodule;
38 import dmd.dscope;
39 import dmd.dstruct;
40 import dmd.dsymbol;
41 import dmd.dsymbolsem;
42 import dmd.dtemplate;
43 import dmd.errors;
44 import dmd.escape;
45 import dmd.expressionsem;
46 import dmd.func;
47 import dmd.globals;
48 import dmd.hdrgen;
49 import dmd.id;
50 import dmd.identifier;
51 import dmd.init;
52 import dmd.inline;
53 import dmd.mtype;
54 import dmd.nspace;
55 import dmd.objc;
56 import dmd.opover;
57 import dmd.optimize;
58 import dmd.root.complex;
59 import dmd.root.ctfloat;
60 import dmd.root.filename;
61 import dmd.common.outbuffer;
62 import dmd.root.optional;
63 import dmd.root.rmem;
64 import dmd.root.rootobject;
65 import dmd.root.string;
66 import dmd.root.utf;
67 import dmd.safe;
68 import dmd.sideeffect;
69 import dmd.target;
70 import dmd.tokens;
71 import dmd.typesem;
72 import dmd.visitor;
73
74 enum LOGSEMANTIC = false;
75
76 void emplaceExp(T : Expression, Args...)(void* p, Args args)
77 {
78 static if (__VERSION__ < 2099)
79 const init = typeid(T).initializer;
80 else
81 const init = __traits(initSymbol, T);
82 p[0 .. __traits(classInstanceSize, T)] = init[];
83 (cast(T)p).__ctor(args);
84 }
85
86 void emplaceExp(T : UnionExp)(T* p, Expression e)
87 {
88 memcpy(p, cast(void*)e, e.size);
89 }
90
91 /// Return value for `checkModifiable`
92 enum Modifiable
93 {
94 /// Not modifiable
95 no,
96 /// Modifiable (the type is mutable)
97 yes,
98 /// Modifiable because it is initialization
99 initialization,
100 }
101 /**
102 * Specifies how the checkModify deals with certain situations
103 */
104 enum ModifyFlags
105 {
106 /// Issue error messages on invalid modifications of the variable
107 none,
108 /// No errors are emitted for invalid modifications
109 noError = 0x1,
110 /// The modification occurs for a subfield of the current variable
111 fieldAssign = 0x2,
112 }
113
114 /****************************************
115 * Find the first non-comma expression.
116 * Params:
117 * e = Expressions connected by commas
118 * Returns:
119 * left-most non-comma expression
120 */
firstComma(inout Expression e)121 inout(Expression) firstComma(inout Expression e)
122 {
123 Expression ex = cast()e;
124 while (ex.op == EXP.comma)
125 ex = (cast(CommaExp)ex).e1;
126 return cast(inout)ex;
127
128 }
129
130 /****************************************
131 * Find the last non-comma expression.
132 * Params:
133 * e = Expressions connected by commas
134 * Returns:
135 * right-most non-comma expression
136 */
137
lastComma(inout Expression e)138 inout(Expression) lastComma(inout Expression e)
139 {
140 Expression ex = cast()e;
141 while (ex.op == EXP.comma)
142 ex = (cast(CommaExp)ex).e2;
143 return cast(inout)ex;
144
145 }
146
147 /*****************************************
148 * Determine if `this` is available by walking up the enclosing
149 * scopes until a function is found.
150 *
151 * Params:
152 * sc = where to start looking for the enclosing function
153 * Returns:
154 * Found function if it satisfies `isThis()`, otherwise `null`
155 */
hasThis(Scope * sc)156 FuncDeclaration hasThis(Scope* sc)
157 {
158 //printf("hasThis()\n");
159 Dsymbol p = sc.parent;
160 while (p && p.isTemplateMixin())
161 p = p.parent;
162 FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null;
163 //printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : "");
164
165 // Go upwards until we find the enclosing member function
166 FuncDeclaration fd = fdthis;
167 while (1)
168 {
169 if (!fd)
170 {
171 return null;
172 }
173 if (!fd.isNested() || fd.isThis() || (fd.hasDualContext() && fd.isMember2()))
174 break;
175
176 Dsymbol parent = fd.parent;
177 while (1)
178 {
179 if (!parent)
180 return null;
181 TemplateInstance ti = parent.isTemplateInstance();
182 if (ti)
183 parent = ti.parent;
184 else
185 break;
186 }
187 fd = parent.isFuncDeclaration();
188 }
189
190 if (!fd.isThis() && !(fd.hasDualContext() && fd.isMember2()))
191 {
192 return null;
193 }
194
195 assert(fd.vthis);
196 return fd;
197
198 }
199
200 /***********************************
201 * Determine if a `this` is needed to access `d`.
202 * Params:
203 * sc = context
204 * d = declaration to check
205 * Returns:
206 * true means a `this` is needed
207 */
isNeedThisScope(Scope * sc,Declaration d)208 bool isNeedThisScope(Scope* sc, Declaration d)
209 {
210 if (sc.intypeof == 1)
211 return false;
212
213 AggregateDeclaration ad = d.isThis();
214 if (!ad)
215 return false;
216 //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
217
218 for (Dsymbol s = sc.parent; s; s = s.toParentLocal())
219 {
220 //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
221 if (AggregateDeclaration ad2 = s.isAggregateDeclaration())
222 {
223 if (ad2 == ad)
224 return false;
225 else if (ad2.isNested())
226 continue;
227 else
228 return true;
229 }
230 if (FuncDeclaration f = s.isFuncDeclaration())
231 {
232 if (f.isMemberLocal())
233 break;
234 }
235 }
236 return true;
237 }
238
239 /******************************
240 * check e is exp.opDispatch!(tiargs) or not
241 * It's used to switch to UFCS the semantic analysis path
242 */
isDotOpDispatch(Expression e)243 bool isDotOpDispatch(Expression e)
244 {
245 if (auto dtie = e.isDotTemplateInstanceExp())
246 return dtie.ti.name == Id.opDispatch;
247 return false;
248 }
249
250 /****************************************
251 * Expand tuples.
252 * Input:
253 * exps aray of Expressions
254 * Output:
255 * exps rewritten in place
256 */
expandTuples(Expressions * exps)257 extern (C++) void expandTuples(Expressions* exps)
258 {
259 //printf("expandTuples()\n");
260 if (exps is null)
261 return;
262
263 for (size_t i = 0; i < exps.dim; i++)
264 {
265 Expression arg = (*exps)[i];
266 if (!arg)
267 continue;
268
269 // Look for tuple with 0 members
270 if (auto e = arg.isTypeExp())
271 {
272 if (auto tt = e.type.toBasetype().isTypeTuple())
273 {
274 if (!tt.arguments || tt.arguments.dim == 0)
275 {
276 exps.remove(i);
277 if (i == exps.dim)
278 return;
279 }
280 else // Expand a TypeTuple
281 {
282 exps.remove(i);
283 auto texps = new Expressions(tt.arguments.length);
284 foreach (j, a; *tt.arguments)
285 (*texps)[j] = new TypeExp(e.loc, a.type);
286 exps.insert(i, texps);
287 }
288 i--;
289 continue;
290 }
291 }
292
293 // Inline expand all the tuples
294 while (arg.op == EXP.tuple)
295 {
296 TupleExp te = cast(TupleExp)arg;
297 exps.remove(i); // remove arg
298 exps.insert(i, te.exps); // replace with tuple contents
299 if (i == exps.dim)
300 return; // empty tuple, no more arguments
301 (*exps)[i] = Expression.combine(te.e0, (*exps)[i]);
302 arg = (*exps)[i];
303 }
304 }
305 }
306
307 /****************************************
308 * Expand alias this tuples.
309 */
isAliasThisTuple(Expression e)310 TupleDeclaration isAliasThisTuple(Expression e)
311 {
312 if (!e.type)
313 return null;
314
315 Type t = e.type.toBasetype();
316 while (true)
317 {
318 if (Dsymbol s = t.toDsymbol(null))
319 {
320 if (auto ad = s.isAggregateDeclaration())
321 {
322 s = ad.aliasthis ? ad.aliasthis.sym : null;
323 if (s && s.isVarDeclaration())
324 {
325 TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration();
326 if (td && td.isexp)
327 return td;
328 }
329 if (Type att = t.aliasthisOf())
330 {
331 t = att;
332 continue;
333 }
334 }
335 }
336 return null;
337 }
338 }
339
340 int expandAliasThisTuples(Expressions* exps, size_t starti = 0)
341 {
342 if (!exps || exps.dim == 0)
343 return -1;
344
345 for (size_t u = starti; u < exps.dim; u++)
346 {
347 Expression exp = (*exps)[u];
348 if (TupleDeclaration td = exp.isAliasThisTuple)
349 {
350 exps.remove(u);
351 size_t i;
352 td.foreachVar((s)
353 {
354 auto d = s.isDeclaration();
355 auto e = new DotVarExp(exp.loc, exp, d);
356 assert(d.type);
357 e.type = d.type;
358 exps.insert(u + i, e);
359 ++i;
360 });
version(none)361 version (none)
362 {
363 printf("expansion ->\n");
364 foreach (e; exps)
365 {
366 printf("\texps[%d] e = %s %s\n", i, EXPtoString(e.op), e.toChars());
367 }
368 }
369 return cast(int)u;
370 }
371 }
372 return -1;
373 }
374
375 /****************************************
376 * If `s` is a function template, i.e. the only member of a template
377 * and that member is a function, return that template.
378 * Params:
379 * s = symbol that might be a function template
380 * Returns:
381 * template for that function, otherwise null
382 */
getFuncTemplateDecl(Dsymbol s)383 TemplateDeclaration getFuncTemplateDecl(Dsymbol s)
384 {
385 FuncDeclaration f = s.isFuncDeclaration();
386 if (f && f.parent)
387 {
388 if (auto ti = f.parent.isTemplateInstance())
389 {
390 if (!ti.isTemplateMixin() && ti.tempdecl)
391 {
392 auto td = ti.tempdecl.isTemplateDeclaration();
393 if (td.onemember && td.ident == f.ident)
394 {
395 return td;
396 }
397 }
398 }
399 }
400 return null;
401 }
402
403 /************************************************
404 * If we want the value of this expression, but do not want to call
405 * the destructor on it.
406 */
valueNoDtor(Expression e)407 Expression valueNoDtor(Expression e)
408 {
409 auto ex = lastComma(e);
410
411 if (auto ce = ex.isCallExp())
412 {
413 /* The struct value returned from the function is transferred
414 * so do not call the destructor on it.
415 * Recognize:
416 * ((S _ctmp = S.init), _ctmp).this(...)
417 * and make sure the destructor is not called on _ctmp
418 * BUG: if ex is a CommaExp, we should go down the right side.
419 */
420 if (auto dve = ce.e1.isDotVarExp())
421 {
422 if (dve.var.isCtorDeclaration())
423 {
424 // It's a constructor call
425 if (auto comma = dve.e1.isCommaExp())
426 {
427 if (auto ve = comma.e2.isVarExp())
428 {
429 VarDeclaration ctmp = ve.var.isVarDeclaration();
430 if (ctmp)
431 {
432 ctmp.storage_class |= STC.nodtor;
433 assert(!ce.isLvalue());
434 }
435 }
436 }
437 }
438 }
439 }
440 else if (auto ve = ex.isVarExp())
441 {
442 auto vtmp = ve.var.isVarDeclaration();
443 if (vtmp && (vtmp.storage_class & STC.rvalue))
444 {
445 vtmp.storage_class |= STC.nodtor;
446 }
447 }
448 return e;
449 }
450
451 /*********************************************
452 * If e is an instance of a struct, and that struct has a copy constructor,
453 * rewrite e as:
454 * (tmp = e),tmp
455 * Input:
456 * sc = just used to specify the scope of created temporary variable
457 * destinationType = the type of the object on which the copy constructor is called;
458 * may be null if the struct defines a postblit
459 */
callCpCtor(Scope * sc,Expression e,Type destinationType)460 private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
461 {
462 if (auto ts = e.type.baseElemOf().isTypeStruct())
463 {
464 StructDeclaration sd = ts.sym;
465 if (sd.postblit || sd.hasCopyCtor)
466 {
467 /* Create a variable tmp, and replace the argument e with:
468 * (tmp = e),tmp
469 * and let AssignExp() handle the construction.
470 * This is not the most efficient, ideally tmp would be constructed
471 * directly onto the stack.
472 */
473 auto tmp = copyToTemp(STC.rvalue, "__copytmp", e);
474 if (sd.hasCopyCtor && destinationType)
475 {
476 // https://issues.dlang.org/show_bug.cgi?id=22619
477 // If the destination type is inout we can preserve it
478 // only if inside an inout function; if we are not inside
479 // an inout function, then we will preserve the type of
480 // the source
481 if (destinationType.hasWild && !(sc.func.storage_class & STC.wild))
482 tmp.type = e.type;
483 else
484 tmp.type = destinationType;
485 }
486 tmp.storage_class |= STC.nodtor;
487 tmp.dsymbolSemantic(sc);
488 Expression de = new DeclarationExp(e.loc, tmp);
489 Expression ve = new VarExp(e.loc, tmp);
490 de.type = Type.tvoid;
491 ve.type = e.type;
492 return Expression.combine(de, ve);
493 }
494 }
495 return e;
496 }
497
498 /************************************************
499 * Handle the postblit call on lvalue, or the move of rvalue.
500 *
501 * Params:
502 * sc = the scope where the expression is encountered
503 * e = the expression the needs to be moved or copied (source)
504 * t = if the struct defines a copy constructor, the type of the destination
505 *
506 * Returns:
507 * The expression that copy constructs or moves the value.
508 */
509 extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
510 {
511 if (auto ce = e.isCondExp())
512 {
513 ce.e1 = doCopyOrMove(sc, ce.e1);
514 ce.e2 = doCopyOrMove(sc, ce.e2);
515 }
516 else
517 {
518 e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e);
519 }
520 return e;
521 }
522
523 /****************************************************************/
524 /* A type meant as a union of all the Expression types,
525 * to serve essentially as a Variant that will sit on the stack
526 * during CTFE to reduce memory consumption.
527 */
528 extern (C++) struct UnionExp
529 {
530 // yes, default constructor does nothing
thisUnionExp531 extern (D) this(Expression e)
532 {
533 memcpy(&this, cast(void*)e, e.size);
534 }
535
536 /* Extract pointer to Expression
537 */
538 extern (C++) Expression exp() return
539 {
540 return cast(Expression)&u;
541 }
542
543 /* Convert to an allocated Expression
544 */
545 extern (C++) Expression copy()
546 {
547 Expression e = exp();
548 //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
549 assert(e.size <= u.sizeof);
550 switch (e.op)
551 {
552 case EXP.cantExpression: return CTFEExp.cantexp;
553 case EXP.voidExpression: return CTFEExp.voidexp;
554 case EXP.break_: return CTFEExp.breakexp;
555 case EXP.continue_: return CTFEExp.continueexp;
556 case EXP.goto_: return CTFEExp.gotoexp;
557 default: return e.copy();
558 }
559 }
560
561 private:
562 // Ensure that the union is suitably aligned.
563 align(8) union __AnonStruct__u
564 {
565 char[__traits(classInstanceSize, Expression)] exp;
566 char[__traits(classInstanceSize, IntegerExp)] integerexp;
567 char[__traits(classInstanceSize, ErrorExp)] errorexp;
568 char[__traits(classInstanceSize, RealExp)] realexp;
569 char[__traits(classInstanceSize, ComplexExp)] complexexp;
570 char[__traits(classInstanceSize, SymOffExp)] symoffexp;
571 char[__traits(classInstanceSize, StringExp)] stringexp;
572 char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp;
573 char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp;
574 char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp;
575 char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp;
576 char[__traits(classInstanceSize, NullExp)] nullexp;
577 char[__traits(classInstanceSize, DotVarExp)] dotvarexp;
578 char[__traits(classInstanceSize, AddrExp)] addrexp;
579 char[__traits(classInstanceSize, IndexExp)] indexexp;
580 char[__traits(classInstanceSize, SliceExp)] sliceexp;
581 char[__traits(classInstanceSize, VectorExp)] vectorexp;
582 }
583
584 __AnonStruct__u u;
585 }
586
587 /********************************
588 * Test to see if two reals are the same.
589 * Regard NaN's as equivalent.
590 * Regard +0 and -0 as different.
591 * Params:
592 * x1 = first operand
593 * x2 = second operand
594 * Returns:
595 * true if x1 is x2
596 * else false
597 */
RealIdentical(real_t x1,real_t x2)598 bool RealIdentical(real_t x1, real_t x2)
599 {
600 return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2);
601 }
602
603 /************************ TypeDotIdExp ************************************/
604 /* Things like:
605 * int.size
606 * foo.size
607 * (foo).size
608 * cast(foo).size
609 */
typeDotIdExp(const ref Loc loc,Type type,Identifier ident)610 DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident)
611 {
612 return new DotIdExp(loc, new TypeExp(loc, type), ident);
613 }
614
615 /***************************************************
616 * Given an Expression, find the variable it really is.
617 *
618 * For example, `a[index]` is really `a`, and `s.f` is really `s`.
619 * Params:
620 * e = Expression to look at
621 * Returns:
622 * variable if there is one, null if not
623 */
expToVariable(Expression e)624 VarDeclaration expToVariable(Expression e)
625 {
626 while (1)
627 {
628 switch (e.op)
629 {
630 case EXP.variable:
631 return (cast(VarExp)e).var.isVarDeclaration();
632
633 case EXP.dotVariable:
634 e = (cast(DotVarExp)e).e1;
635 continue;
636
637 case EXP.index:
638 {
639 IndexExp ei = cast(IndexExp)e;
640 e = ei.e1;
641 Type ti = e.type.toBasetype();
642 if (ti.ty == Tsarray)
643 continue;
644 return null;
645 }
646
647 case EXP.slice:
648 {
649 SliceExp ei = cast(SliceExp)e;
650 e = ei.e1;
651 Type ti = e.type.toBasetype();
652 if (ti.ty == Tsarray)
653 continue;
654 return null;
655 }
656
657 case EXP.this_:
658 case EXP.super_:
659 return (cast(ThisExp)e).var.isVarDeclaration();
660
661 default:
662 return null;
663 }
664 }
665 }
666
667 enum OwnedBy : ubyte
668 {
669 code, // normal code expression in AST
670 ctfe, // value expression for CTFE
671 cache, // constant value cached for CTFE
672 }
673
674 enum WANTvalue = 0; // default
675 enum WANTexpand = 1; // expand const/immutable variables if possible
676
677 /***********************************************************
678 * https://dlang.org/spec/expression.html#expression
679 */
680 extern (C++) abstract class Expression : ASTNode
681 {
682 const EXP op; // to minimize use of dynamic_cast
683 ubyte size; // # of bytes in Expression so we can copy() it
684 ubyte parens; // if this is a parenthesized expression
685 Type type; // !=null means that semantic() has been run
686 Loc loc; // file location
687
this(const ref Loc loc,EXP op,int size)688 extern (D) this(const ref Loc loc, EXP op, int size)
689 {
690 //printf("Expression::Expression(op = %d) this = %p\n", op, this);
691 this.loc = loc;
692 this.op = op;
693 this.size = cast(ubyte)size;
694 }
695
_init()696 static void _init()
697 {
698 CTFEExp.cantexp = new CTFEExp(EXP.cantExpression);
699 CTFEExp.voidexp = new CTFEExp(EXP.voidExpression);
700 CTFEExp.breakexp = new CTFEExp(EXP.break_);
701 CTFEExp.continueexp = new CTFEExp(EXP.continue_);
702 CTFEExp.gotoexp = new CTFEExp(EXP.goto_);
703 CTFEExp.showcontext = new CTFEExp(EXP.showCtfeContext);
704 }
705
706 /**
707 * Deinitializes the global state of the compiler.
708 *
709 * This can be used to restore the state set by `_init` to its original
710 * state.
711 */
deinitialize()712 static void deinitialize()
713 {
714 CTFEExp.cantexp = CTFEExp.cantexp.init;
715 CTFEExp.voidexp = CTFEExp.voidexp.init;
716 CTFEExp.breakexp = CTFEExp.breakexp.init;
717 CTFEExp.continueexp = CTFEExp.continueexp.init;
718 CTFEExp.gotoexp = CTFEExp.gotoexp.init;
719 CTFEExp.showcontext = CTFEExp.showcontext.init;
720 }
721
722 /*********************************
723 * Does *not* do a deep copy.
724 */
copy()725 final Expression copy()
726 {
727 Expression e;
728 if (!size)
729 {
730 debug
731 {
732 fprintf(stderr, "No expression copy for: %s\n", toChars());
733 printf("op = %d\n", op);
734 }
735 assert(0);
736 }
737
738 // memory never freed, so can use the faster bump-pointer-allocation
739 e = cast(Expression)allocmemory(size);
740 //printf("Expression::copy(op = %d) e = %p\n", op, e);
741 return cast(Expression)memcpy(cast(void*)e, cast(void*)this, size);
742 }
743
syntaxCopy()744 Expression syntaxCopy()
745 {
746 //printf("Expression::syntaxCopy()\n");
747 //print();
748 return copy();
749 }
750
751 // kludge for template.isExpression()
dyncast()752 override final DYNCAST dyncast() const
753 {
754 return DYNCAST.expression;
755 }
756
toChars()757 override const(char)* toChars() const
758 {
759 OutBuffer buf;
760 HdrGenState hgs;
761 toCBuffer(this, &buf, &hgs);
762 return buf.extractChars();
763 }
764
765 static if (__VERSION__ < 2092)
766 {
error(const (char)* format,...)767 final void error(const(char)* format, ...) const
768 {
769 if (type != Type.terror)
770 {
771 va_list ap;
772 va_start(ap, format);
773 .verror(loc, format, ap);
774 va_end(ap);
775 }
776 }
777
errorSupplemental(const (char)* format,...)778 final void errorSupplemental(const(char)* format, ...)
779 {
780 if (type == Type.terror)
781 return;
782
783 va_list ap;
784 va_start(ap, format);
785 .verrorSupplemental(loc, format, ap);
786 va_end(ap);
787 }
788
warning(const (char)* format,...)789 final void warning(const(char)* format, ...) const
790 {
791 if (type != Type.terror)
792 {
793 va_list ap;
794 va_start(ap, format);
795 .vwarning(loc, format, ap);
796 va_end(ap);
797 }
798 }
799
deprecation(const (char)* format,...)800 final void deprecation(const(char)* format, ...) const
801 {
802 if (type != Type.terror)
803 {
804 va_list ap;
805 va_start(ap, format);
806 .vdeprecation(loc, format, ap);
807 va_end(ap);
808 }
809 }
810 }
811 else
812 {
pragma(printf)813 pragma(printf) final void error(const(char)* format, ...) const
814 {
815 if (type != Type.terror)
816 {
817 va_list ap;
818 va_start(ap, format);
819 .verror(loc, format, ap);
820 va_end(ap);
821 }
822 }
823
pragma(printf)824 pragma(printf) final void errorSupplemental(const(char)* format, ...)
825 {
826 if (type == Type.terror)
827 return;
828
829 va_list ap;
830 va_start(ap, format);
831 .verrorSupplemental(loc, format, ap);
832 va_end(ap);
833 }
834
pragma(printf)835 pragma(printf) final void warning(const(char)* format, ...) const
836 {
837 if (type != Type.terror)
838 {
839 va_list ap;
840 va_start(ap, format);
841 .vwarning(loc, format, ap);
842 va_end(ap);
843 }
844 }
845
pragma(printf)846 pragma(printf) final void deprecation(const(char)* format, ...) const
847 {
848 if (type != Type.terror)
849 {
850 va_list ap;
851 va_start(ap, format);
852 .vdeprecation(loc, format, ap);
853 va_end(ap);
854 }
855 }
856 }
857
858 /**********************************
859 * Combine e1 and e2 by CommaExp if both are not NULL.
860 */
combine(Expression e1,Expression e2)861 extern (D) static Expression combine(Expression e1, Expression e2)
862 {
863 if (e1)
864 {
865 if (e2)
866 {
867 e1 = new CommaExp(e1.loc, e1, e2);
868 e1.type = e2.type;
869 }
870 }
871 else
872 e1 = e2;
873 return e1;
874 }
875
combine(Expression e1,Expression e2,Expression e3)876 extern (D) static Expression combine(Expression e1, Expression e2, Expression e3)
877 {
878 return combine(combine(e1, e2), e3);
879 }
880
combine(Expression e1,Expression e2,Expression e3,Expression e4)881 extern (D) static Expression combine(Expression e1, Expression e2, Expression e3, Expression e4)
882 {
883 return combine(combine(e1, e2), combine(e3, e4));
884 }
885
886 /**********************************
887 * If 'e' is a tree of commas, returns the rightmost expression
888 * by stripping off it from the tree. The remained part of the tree
889 * is returned via e0.
890 * Otherwise 'e' is directly returned and e0 is set to NULL.
891 */
extractLast(Expression e,out Expression e0)892 extern (D) static Expression extractLast(Expression e, out Expression e0)
893 {
894 if (e.op != EXP.comma)
895 {
896 return e;
897 }
898
899 CommaExp ce = cast(CommaExp)e;
900 if (ce.e2.op != EXP.comma)
901 {
902 e0 = ce.e1;
903 return ce.e2;
904 }
905 else
906 {
907 e0 = e;
908
909 Expression* pce = &ce.e2;
910 while ((cast(CommaExp)(*pce)).e2.op == EXP.comma)
911 {
912 pce = &(cast(CommaExp)(*pce)).e2;
913 }
914 assert((*pce).op == EXP.comma);
915 ce = cast(CommaExp)(*pce);
916 *pce = ce.e1;
917
918 return ce.e2;
919 }
920 }
921
arraySyntaxCopy(Expressions * exps)922 extern (D) static Expressions* arraySyntaxCopy(Expressions* exps)
923 {
924 Expressions* a = null;
925 if (exps)
926 {
927 a = new Expressions(exps.dim);
928 foreach (i, e; *exps)
929 {
930 (*a)[i] = e ? e.syntaxCopy() : null;
931 }
932 }
933 return a;
934 }
935
toInteger()936 dinteger_t toInteger()
937 {
938 //printf("Expression %s\n", EXPtoString(op).ptr);
939 error("integer constant expression expected instead of `%s`", toChars());
940 return 0;
941 }
942
toUInteger()943 uinteger_t toUInteger()
944 {
945 //printf("Expression %s\n", EXPtoString(op).ptr);
946 return cast(uinteger_t)toInteger();
947 }
948
toReal()949 real_t toReal()
950 {
951 error("floating point constant expression expected instead of `%s`", toChars());
952 return CTFloat.zero;
953 }
954
toImaginary()955 real_t toImaginary()
956 {
957 error("floating point constant expression expected instead of `%s`", toChars());
958 return CTFloat.zero;
959 }
960
toComplex()961 complex_t toComplex()
962 {
963 error("floating point constant expression expected instead of `%s`", toChars());
964 return complex_t(CTFloat.zero);
965 }
966
toStringExp()967 StringExp toStringExp()
968 {
969 return null;
970 }
971
972 /***************************************
973 * Return !=0 if expression is an lvalue.
974 */
isLvalue()975 bool isLvalue()
976 {
977 return false;
978 }
979
980 /*******************************
981 * Give error if we're not an lvalue.
982 * If we can, convert expression to be an lvalue.
983 */
toLvalue(Scope * sc,Expression e)984 Expression toLvalue(Scope* sc, Expression e)
985 {
986 if (!e)
987 e = this;
988 else if (!loc.isValid())
989 loc = e.loc;
990
991 if (e.op == EXP.type)
992 error("`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind());
993 else
994 error("`%s` is not an lvalue and cannot be modified", e.toChars());
995
996 return ErrorExp.get();
997 }
998
modifiableLvalue(Scope * sc,Expression e)999 Expression modifiableLvalue(Scope* sc, Expression e)
1000 {
1001 //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars());
1002 // See if this expression is a modifiable lvalue (i.e. not const)
1003 if (checkModifiable(this, sc) == Modifiable.yes)
1004 {
1005 assert(type);
1006 if (!type.isMutable())
1007 {
1008 if (auto dve = this.isDotVarExp())
1009 {
1010 if (isNeedThisScope(sc, dve.var))
1011 for (Dsymbol s = sc.func; s; s = s.toParentLocal())
1012 {
1013 FuncDeclaration ff = s.isFuncDeclaration();
1014 if (!ff)
1015 break;
1016 if (!ff.type.isMutable)
1017 {
1018 error("cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod));
1019 return ErrorExp.get();
1020 }
1021 }
1022 }
1023 error("cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars());
1024 return ErrorExp.get();
1025 }
1026 else if (!type.isAssignable())
1027 {
1028 error("cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members",
1029 toChars(), type.toChars());
1030 return ErrorExp.get();
1031 }
1032 }
1033 return toLvalue(sc, e);
1034 }
1035
implicitCastTo(Scope * sc,Type t)1036 final Expression implicitCastTo(Scope* sc, Type t)
1037 {
1038 return .implicitCastTo(this, sc, t);
1039 }
1040
implicitConvTo(Type t)1041 final MATCH implicitConvTo(Type t)
1042 {
1043 return .implicitConvTo(this, t);
1044 }
1045
castTo(Scope * sc,Type t)1046 final Expression castTo(Scope* sc, Type t)
1047 {
1048 return .castTo(this, sc, t);
1049 }
1050
1051 /****************************************
1052 * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
1053 */
resolveLoc(const ref Loc loc,Scope * sc)1054 Expression resolveLoc(const ref Loc loc, Scope* sc)
1055 {
1056 this.loc = loc;
1057 return this;
1058 }
1059
1060 /****************************************
1061 * Check that the expression has a valid type.
1062 * If not, generates an error "... has no type".
1063 * Returns:
1064 * true if the expression is not valid.
1065 * Note:
1066 * When this function returns true, `checkValue()` should also return true.
1067 */
checkType()1068 bool checkType()
1069 {
1070 return false;
1071 }
1072
1073 /****************************************
1074 * Check that the expression has a valid value.
1075 * If not, generates an error "... has no value".
1076 * Returns:
1077 * true if the expression is not valid or has void type.
1078 */
checkValue()1079 bool checkValue()
1080 {
1081 if (type && type.toBasetype().ty == Tvoid)
1082 {
1083 error("expression `%s` is `void` and has no value", toChars());
1084 //print(); assert(0);
1085 if (!global.gag)
1086 type = Type.terror;
1087 return true;
1088 }
1089 return false;
1090 }
1091
checkScalar()1092 extern (D) final bool checkScalar()
1093 {
1094 if (op == EXP.error)
1095 return true;
1096 if (type.toBasetype().ty == Terror)
1097 return true;
1098 if (!type.isscalar())
1099 {
1100 error("`%s` is not a scalar, it is a `%s`", toChars(), type.toChars());
1101 return true;
1102 }
1103 return checkValue();
1104 }
1105
checkNoBool()1106 extern (D) final bool checkNoBool()
1107 {
1108 if (op == EXP.error)
1109 return true;
1110 if (type.toBasetype().ty == Terror)
1111 return true;
1112 if (type.toBasetype().ty == Tbool)
1113 {
1114 error("operation not allowed on `bool` `%s`", toChars());
1115 return true;
1116 }
1117 return false;
1118 }
1119
checkIntegral()1120 extern (D) final bool checkIntegral()
1121 {
1122 if (op == EXP.error)
1123 return true;
1124 if (type.toBasetype().ty == Terror)
1125 return true;
1126 if (!type.isintegral())
1127 {
1128 error("`%s` is not of integral type, it is a `%s`", toChars(), type.toChars());
1129 return true;
1130 }
1131 return checkValue();
1132 }
1133
checkArithmetic()1134 extern (D) final bool checkArithmetic()
1135 {
1136 if (op == EXP.error)
1137 return true;
1138 if (type.toBasetype().ty == Terror)
1139 return true;
1140 if (!type.isintegral() && !type.isfloating())
1141 {
1142 error("`%s` is not of arithmetic type, it is a `%s`", toChars(), type.toChars());
1143 return true;
1144 }
1145 return checkValue();
1146 }
1147
checkDeprecated(Scope * sc,Dsymbol s)1148 final bool checkDeprecated(Scope* sc, Dsymbol s)
1149 {
1150 return s.checkDeprecated(loc, sc);
1151 }
1152
checkDisabled(Scope * sc,Dsymbol s)1153 extern (D) final bool checkDisabled(Scope* sc, Dsymbol s)
1154 {
1155 if (auto d = s.isDeclaration())
1156 {
1157 return d.checkDisabled(loc, sc);
1158 }
1159
1160 return false;
1161 }
1162
1163 /*********************************************
1164 * Calling function f.
1165 * Check the purity, i.e. if we're in a pure function
1166 * we can only call other pure functions.
1167 * Returns true if error occurs.
1168 */
checkPurity(Scope * sc,FuncDeclaration f)1169 extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f)
1170 {
1171 if (!sc.func)
1172 return false;
1173 if (sc.func == f)
1174 return false;
1175 if (sc.intypeof == 1)
1176 return false;
1177 if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
1178 return false;
1179
1180 // If the call has a pure parent, then the called func must be pure.
1181 if (!f.isPure() && checkImpure(sc))
1182 {
1183 error("`pure` %s `%s` cannot call impure %s `%s`",
1184 sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
1185 f.toPrettyChars());
1186
1187 checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure");
1188 return true;
1189 }
1190 return false;
1191 }
1192
1193 /**
1194 * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one
1195 * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but
1196 * the generated dtor is not).
1197 * In that case the method will identify and print all members causing the attribute
1198 * missmatch.
1199 *
1200 * Params:
1201 * sc = scope
1202 * f = potential `DtorDeclaration`
1203 * check = current check (e.g. whether it's pure)
1204 * checkName = the kind of check (e.g. `"pure"`)
1205 */
checkOverridenDtor(Scope * sc,FuncDeclaration f,scope bool function (DtorDeclaration)check,const string checkName)1206 extern (D) final void checkOverridenDtor(Scope* sc, FuncDeclaration f,
1207 scope bool function(DtorDeclaration) check, const string checkName
1208 ) {
1209 auto dd = f.isDtorDeclaration();
1210 if (!dd || !dd.isGenerated())
1211 return;
1212
1213 // DtorDeclaration without parents should fail at an earlier stage
1214 auto ad = cast(AggregateDeclaration) f.toParent2();
1215 assert(ad);
1216
1217 if (ad.userDtors.dim)
1218 {
1219 if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
1220 return;
1221
1222 // Sanity check
1223 assert(!check(ad.fieldDtor));
1224 }
1225
1226 dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
1227 dd.isGenerated() ? "generated " : "".ptr,
1228 ad.toChars,
1229 cast(int) checkName.length, checkName.ptr);
1230
1231 // Search for the offending fields
1232 foreach (field; ad.fields)
1233 {
1234 // Only structs may define automatically called destructors
1235 auto ts = field.type.isTypeStruct();
1236 if (!ts)
1237 {
1238 // But they might be part of a static array
1239 auto ta = field.type.isTypeSArray();
1240 if (!ta)
1241 continue;
1242
1243 ts = ta.baseElemOf().isTypeStruct();
1244 if (!ts)
1245 continue;
1246 }
1247
1248 auto fieldSym = ts.toDsymbol(sc);
1249 assert(fieldSym); // Resolving ts must succeed because missing defs. should error before
1250
1251 auto fieldSd = fieldSym.isStructDeclaration();
1252 assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR
1253
1254 if (fieldSd.dtor && !check(fieldSd.dtor))
1255 {
1256 field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars());
1257
1258 if (fieldSd.dtor.isGenerated())
1259 checkOverridenDtor(sc, fieldSd.dtor, check, checkName);
1260 else
1261 fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here",
1262 cast(int) checkName.length, checkName.ptr, fieldSd.toChars());
1263 }
1264 }
1265 }
1266
1267 /*******************************************
1268 * Accessing variable v.
1269 * Check for purity and safety violations.
1270 * Returns true if error occurs.
1271 */
checkPurity(Scope * sc,VarDeclaration v)1272 extern (D) final bool checkPurity(Scope* sc, VarDeclaration v)
1273 {
1274 //printf("v = %s %s\n", v.type.toChars(), v.toChars());
1275 /* Look for purity and safety violations when accessing variable v
1276 * from current function.
1277 */
1278 if (!sc.func)
1279 return false;
1280 if (sc.intypeof == 1)
1281 return false; // allow violations inside typeof(expression)
1282 if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
1283 return false; // allow violations inside compile-time evaluated expressions and debug conditionals
1284 if (v.ident == Id.ctfe)
1285 return false; // magic variable never violates pure and safe
1286 if (v.isImmutable())
1287 return false; // always safe and pure to access immutables...
1288 if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf()))
1289 return false; // or const global/parameter values which have no mutable indirections
1290 if (v.storage_class & STC.manifest)
1291 return false; // ...or manifest constants
1292
1293 // accessing empty structs is pure
1294 if (v.type.ty == Tstruct)
1295 {
1296 StructDeclaration sd = (cast(TypeStruct)v.type).sym;
1297 if (sd.members) // not opaque
1298 {
1299 sd.determineSize(v.loc);
1300 if (sd.hasNoFields)
1301 return false;
1302 }
1303 }
1304
1305 bool err = false;
1306 if (v.isDataseg())
1307 {
1308 // https://issues.dlang.org/show_bug.cgi?id=7533
1309 // Accessing implicit generated __gate is pure.
1310 if (v.ident == Id.gate)
1311 return false;
1312
1313 if (checkImpure(sc))
1314 {
1315 error("`pure` %s `%s` cannot access mutable static data `%s`",
1316 sc.func.kind(), sc.func.toPrettyChars(), v.toChars());
1317 err = true;
1318 }
1319 }
1320 else
1321 {
1322 /* Given:
1323 * void f() {
1324 * int fx;
1325 * pure void g() {
1326 * int gx;
1327 * /+pure+/ void h() {
1328 * int hx;
1329 * /+pure+/ void i() { }
1330 * }
1331 * }
1332 * }
1333 * i() can modify hx and gx but not fx
1334 */
1335
1336 Dsymbol vparent = v.toParent2();
1337 for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent))
1338 {
1339 if (s == vparent)
1340 break;
1341
1342 if (AggregateDeclaration ad = s.isAggregateDeclaration())
1343 {
1344 if (ad.isNested())
1345 continue;
1346 break;
1347 }
1348 FuncDeclaration ff = s.isFuncDeclaration();
1349 if (!ff)
1350 break;
1351 if (ff.isNested() || ff.isThis())
1352 {
1353 if (ff.type.isImmutable() ||
1354 ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod))
1355 {
1356 OutBuffer ffbuf;
1357 OutBuffer vbuf;
1358 MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod);
1359 MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod);
1360 error("%s%s `%s` cannot access %sdata `%s`",
1361 ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars());
1362 err = true;
1363 break;
1364 }
1365 continue;
1366 }
1367 break;
1368 }
1369 }
1370
1371 /* Do not allow safe functions to access __gshared data
1372 */
1373 if (v.storage_class & STC.gshared)
1374 {
1375 if (sc.func.setUnsafe())
1376 {
1377 error("`@safe` %s `%s` cannot access `__gshared` data `%s`",
1378 sc.func.kind(), sc.func.toChars(), v.toChars());
1379 err = true;
1380 }
1381 }
1382
1383 return err;
1384 }
1385
1386 /*
1387 Check if sc.func is impure or can be made impure.
1388 Returns true on error, i.e. if sc.func is pure and cannot be made impure.
1389 */
checkImpure(Scope * sc)1390 private static bool checkImpure(Scope* sc)
1391 {
1392 return sc.func && (sc.flags & SCOPE.compile
1393 ? sc.func.isPureBypassingInference() >= PURE.weak
1394 : sc.func.setImpure());
1395 }
1396
1397 /*********************************************
1398 * Calling function f.
1399 * Check the safety, i.e. if we're in a @safe function
1400 * we can only call @safe or @trusted functions.
1401 * Returns true if error occurs.
1402 */
checkSafety(Scope * sc,FuncDeclaration f)1403 extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f)
1404 {
1405 if (!sc.func)
1406 return false;
1407 if (sc.func == f)
1408 return false;
1409 if (sc.intypeof == 1)
1410 return false;
1411 if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
1412 return false;
1413
1414 if (!f.isSafe() && !f.isTrusted())
1415 {
1416 if (sc.flags & SCOPE.compile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe())
1417 {
1418 if (!loc.isValid()) // e.g. implicitly generated dtor
1419 loc = sc.func.loc;
1420
1421 const prettyChars = f.toPrettyChars();
1422 error("`@safe` %s `%s` cannot call `@system` %s `%s`",
1423 sc.func.kind(), sc.func.toPrettyChars(), f.kind(),
1424 prettyChars);
1425 .errorSupplemental(f.loc, "`%s` is declared here", prettyChars);
1426
1427 checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system");
1428
1429 return true;
1430 }
1431 }
1432 return false;
1433 }
1434
1435 /*********************************************
1436 * Calling function f.
1437 * Check the @nogc-ness, i.e. if we're in a @nogc function
1438 * we can only call other @nogc functions.
1439 * Returns true if error occurs.
1440 */
checkNogc(Scope * sc,FuncDeclaration f)1441 extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f)
1442 {
1443 if (!sc.func)
1444 return false;
1445 if (sc.func == f)
1446 return false;
1447 if (sc.intypeof == 1)
1448 return false;
1449 if (sc.flags & (SCOPE.ctfe | SCOPE.debug_))
1450 return false;
1451
1452 if (!f.isNogc())
1453 {
1454 if (sc.flags & SCOPE.compile ? sc.func.isNogcBypassingInference() : sc.func.setGC())
1455 {
1456 if (loc.linnum == 0) // e.g. implicitly generated dtor
1457 loc = sc.func.loc;
1458
1459 // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
1460 // so don't print anything to avoid double error messages.
1461 if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT))
1462 error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
1463 sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());
1464
1465 checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc");
1466
1467 return true;
1468 }
1469 }
1470 return false;
1471 }
1472
1473 /********************************************
1474 * Check that the postblit is callable if t is an array of structs.
1475 * Returns true if error happens.
1476 */
checkPostblit(Scope * sc,Type t)1477 extern (D) final bool checkPostblit(Scope* sc, Type t)
1478 {
1479 if (auto ts = t.baseElemOf().isTypeStruct())
1480 {
1481 if (global.params.useTypeInfo && Type.dtypeinfo)
1482 {
1483 // https://issues.dlang.org/show_bug.cgi?id=11395
1484 // Require TypeInfo generation for array concatenation
1485 semanticTypeInfo(sc, t);
1486 }
1487
1488 StructDeclaration sd = ts.sym;
1489 if (sd.postblit)
1490 {
1491 if (sd.postblit.checkDisabled(loc, sc))
1492 return true;
1493
1494 //checkDeprecated(sc, sd.postblit); // necessary?
1495 checkPurity(sc, sd.postblit);
1496 checkSafety(sc, sd.postblit);
1497 checkNogc(sc, sd.postblit);
1498 //checkAccess(sd, loc, sc, sd.postblit); // necessary?
1499 return false;
1500 }
1501 }
1502 return false;
1503 }
1504
checkRightThis(Scope * sc)1505 extern (D) final bool checkRightThis(Scope* sc)
1506 {
1507 if (op == EXP.error)
1508 return true;
1509 if (op == EXP.variable && type.ty != Terror)
1510 {
1511 VarExp ve = cast(VarExp)this;
1512 if (isNeedThisScope(sc, ve.var))
1513 {
1514 //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
1515 // sc.intypeof, sc.getStructClassScope(), func, fdthis);
1516 error("need `this` for `%s` of type `%s`", ve.var.toChars(), ve.var.type.toChars());
1517 return true;
1518 }
1519 }
1520 return false;
1521 }
1522
1523 /*******************************
1524 * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
1525 * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
1526 * Returns true if error occurs.
1527 */
1528 extern (D) final bool checkReadModifyWrite(EXP rmwOp, Expression ex = null)
1529 {
1530 //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : "");
1531 if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass())
1532 return false;
1533
1534 // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
1535 switch (rmwOp)
1536 {
1537 case EXP.plusPlus:
1538 case EXP.prePlusPlus:
1539 rmwOp = EXP.addAssign;
1540 break;
1541 case EXP.minusMinus:
1542 case EXP.preMinusMinus:
1543 rmwOp = EXP.minAssign;
1544 break;
1545 default:
1546 break;
1547 }
1548
1549 error("read-modify-write operations are not allowed for `shared` variables");
1550 errorSupplemental("Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead",
1551 EXPtoString(rmwOp).ptr, toChars(), ex ? ex.toChars() : "1");
1552 return true;
1553 }
1554
1555 /************************************************
1556 * Destructors are attached to VarDeclarations.
1557 * Hence, if expression returns a temp that needs a destructor,
1558 * make sure and create a VarDeclaration for that temp.
1559 */
addDtorHook(Scope * sc)1560 Expression addDtorHook(Scope* sc)
1561 {
1562 return this;
1563 }
1564
1565 /******************************
1566 * Take address of expression.
1567 */
addressOf()1568 final Expression addressOf()
1569 {
1570 //printf("Expression::addressOf()\n");
1571 debug
1572 {
1573 assert(op == EXP.error || isLvalue());
1574 }
1575 Expression e = new AddrExp(loc, this, type.pointerTo());
1576 return e;
1577 }
1578
1579 /******************************
1580 * If this is a reference, dereference it.
1581 */
deref()1582 final Expression deref()
1583 {
1584 //printf("Expression::deref()\n");
1585 // type could be null if forward referencing an 'auto' variable
1586 if (type)
1587 if (auto tr = type.isTypeReference())
1588 {
1589 Expression e = new PtrExp(loc, this, tr.next);
1590 return e;
1591 }
1592 return this;
1593 }
1594
1595 final Expression optimize(int result, bool keepLvalue = false)
1596 {
1597 return Expression_optimize(this, result, keepLvalue);
1598 }
1599
1600 // Entry point for CTFE.
1601 // A compile-time result is required. Give an error if not possible
ctfeInterpret()1602 final Expression ctfeInterpret()
1603 {
1604 return .ctfeInterpret(this);
1605 }
1606
isConst()1607 final int isConst()
1608 {
1609 return .isConst(this);
1610 }
1611
1612 /// Statically evaluate this expression to a `bool` if possible
1613 /// Returns: an optional thath either contains the value or is empty
1614 Optional!bool toBool()
1615 {
1616 return typeof(return)();
1617 }
1618
hasCode()1619 bool hasCode()
1620 {
1621 return true;
1622 }
1623
1624 final pure inout nothrow @nogc @safe
1625 {
isIntegerExp()1626 inout(IntegerExp) isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; }
isErrorExp()1627 inout(ErrorExp) isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; }
isVoidInitExp()1628 inout(VoidInitExp) isVoidInitExp() { return op == EXP.void_ ? cast(typeof(return))this : null; }
isRealExp()1629 inout(RealExp) isRealExp() { return op == EXP.float64 ? cast(typeof(return))this : null; }
isComplexExp()1630 inout(ComplexExp) isComplexExp() { return op == EXP.complex80 ? cast(typeof(return))this : null; }
isIdentifierExp()1631 inout(IdentifierExp) isIdentifierExp() { return op == EXP.identifier ? cast(typeof(return))this : null; }
isDollarExp()1632 inout(DollarExp) isDollarExp() { return op == EXP.dollar ? cast(typeof(return))this : null; }
isDsymbolExp()1633 inout(DsymbolExp) isDsymbolExp() { return op == EXP.dSymbol ? cast(typeof(return))this : null; }
isThisExp()1634 inout(ThisExp) isThisExp() { return op == EXP.this_ ? cast(typeof(return))this : null; }
isSuperExp()1635 inout(SuperExp) isSuperExp() { return op == EXP.super_ ? cast(typeof(return))this : null; }
isNullExp()1636 inout(NullExp) isNullExp() { return op == EXP.null_ ? cast(typeof(return))this : null; }
isStringExp()1637 inout(StringExp) isStringExp() { return op == EXP.string_ ? cast(typeof(return))this : null; }
isTupleExp()1638 inout(TupleExp) isTupleExp() { return op == EXP.tuple ? cast(typeof(return))this : null; }
isArrayLiteralExp()1639 inout(ArrayLiteralExp) isArrayLiteralExp() { return op == EXP.arrayLiteral ? cast(typeof(return))this : null; }
isAssocArrayLiteralExp()1640 inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == EXP.assocArrayLiteral ? cast(typeof(return))this : null; }
isStructLiteralExp()1641 inout(StructLiteralExp) isStructLiteralExp() { return op == EXP.structLiteral ? cast(typeof(return))this : null; }
isCompoundLiteralExp()1642 inout(CompoundLiteralExp) isCompoundLiteralExp() { return op == EXP.compoundLiteral ? cast(typeof(return))this : null; }
isTypeExp()1643 inout(TypeExp) isTypeExp() { return op == EXP.type ? cast(typeof(return))this : null; }
isScopeExp()1644 inout(ScopeExp) isScopeExp() { return op == EXP.scope_ ? cast(typeof(return))this : null; }
isTemplateExp()1645 inout(TemplateExp) isTemplateExp() { return op == EXP.template_ ? cast(typeof(return))this : null; }
isNewExp()1646 inout(NewExp) isNewExp() { return op == EXP.new_ ? cast(typeof(return))this : null; }
isNewAnonClassExp()1647 inout(NewAnonClassExp) isNewAnonClassExp() { return op == EXP.newAnonymousClass ? cast(typeof(return))this : null; }
isSymOffExp()1648 inout(SymOffExp) isSymOffExp() { return op == EXP.symbolOffset ? cast(typeof(return))this : null; }
isVarExp()1649 inout(VarExp) isVarExp() { return op == EXP.variable ? cast(typeof(return))this : null; }
isOverExp()1650 inout(OverExp) isOverExp() { return op == EXP.overloadSet ? cast(typeof(return))this : null; }
isFuncExp()1651 inout(FuncExp) isFuncExp() { return op == EXP.function_ ? cast(typeof(return))this : null; }
isDeclarationExp()1652 inout(DeclarationExp) isDeclarationExp() { return op == EXP.declaration ? cast(typeof(return))this : null; }
isTypeidExp()1653 inout(TypeidExp) isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; }
isTraitsExp()1654 inout(TraitsExp) isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; }
isHaltExp()1655 inout(HaltExp) isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; }
isExp()1656 inout(IsExp) isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; }
isMixinExp()1657 inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; }
isImportExp()1658 inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; }
isAssertExp()1659 inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; }
isThrowExp()1660 inout(ThrowExp) isThrowExp() { return op == EXP.throw_ ? cast(typeof(return))this : null; }
isDotIdExp()1661 inout(DotIdExp) isDotIdExp() { return op == EXP.dotIdentifier ? cast(typeof(return))this : null; }
isDotTemplateExp()1662 inout(DotTemplateExp) isDotTemplateExp() { return op == EXP.dotTemplateDeclaration ? cast(typeof(return))this : null; }
isDotVarExp()1663 inout(DotVarExp) isDotVarExp() { return op == EXP.dotVariable ? cast(typeof(return))this : null; }
isDotTemplateInstanceExp()1664 inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == EXP.dotTemplateInstance ? cast(typeof(return))this : null; }
isDelegateExp()1665 inout(DelegateExp) isDelegateExp() { return op == EXP.delegate_ ? cast(typeof(return))this : null; }
isDotTypeExp()1666 inout(DotTypeExp) isDotTypeExp() { return op == EXP.dotType ? cast(typeof(return))this : null; }
isCallExp()1667 inout(CallExp) isCallExp() { return op == EXP.call ? cast(typeof(return))this : null; }
isAddrExp()1668 inout(AddrExp) isAddrExp() { return op == EXP.address ? cast(typeof(return))this : null; }
isPtrExp()1669 inout(PtrExp) isPtrExp() { return op == EXP.star ? cast(typeof(return))this : null; }
isNegExp()1670 inout(NegExp) isNegExp() { return op == EXP.negate ? cast(typeof(return))this : null; }
isUAddExp()1671 inout(UAddExp) isUAddExp() { return op == EXP.uadd ? cast(typeof(return))this : null; }
isComExp()1672 inout(ComExp) isComExp() { return op == EXP.tilde ? cast(typeof(return))this : null; }
isNotExp()1673 inout(NotExp) isNotExp() { return op == EXP.not ? cast(typeof(return))this : null; }
isDeleteExp()1674 inout(DeleteExp) isDeleteExp() { return op == EXP.delete_ ? cast(typeof(return))this : null; }
isCastExp()1675 inout(CastExp) isCastExp() { return op == EXP.cast_ ? cast(typeof(return))this : null; }
isVectorExp()1676 inout(VectorExp) isVectorExp() { return op == EXP.vector ? cast(typeof(return))this : null; }
isVectorArrayExp()1677 inout(VectorArrayExp) isVectorArrayExp() { return op == EXP.vectorArray ? cast(typeof(return))this : null; }
isSliceExp()1678 inout(SliceExp) isSliceExp() { return op == EXP.slice ? cast(typeof(return))this : null; }
isArrayLengthExp()1679 inout(ArrayLengthExp) isArrayLengthExp() { return op == EXP.arrayLength ? cast(typeof(return))this : null; }
isArrayExp()1680 inout(ArrayExp) isArrayExp() { return op == EXP.array ? cast(typeof(return))this : null; }
isDotExp()1681 inout(DotExp) isDotExp() { return op == EXP.dot ? cast(typeof(return))this : null; }
isCommaExp()1682 inout(CommaExp) isCommaExp() { return op == EXP.comma ? cast(typeof(return))this : null; }
isIntervalExp()1683 inout(IntervalExp) isIntervalExp() { return op == EXP.interval ? cast(typeof(return))this : null; }
isDelegatePtrExp()1684 inout(DelegatePtrExp) isDelegatePtrExp() { return op == EXP.delegatePointer ? cast(typeof(return))this : null; }
isDelegateFuncptrExp()1685 inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == EXP.delegateFunctionPointer ? cast(typeof(return))this : null; }
isIndexExp()1686 inout(IndexExp) isIndexExp() { return op == EXP.index ? cast(typeof(return))this : null; }
isPostExp()1687 inout(PostExp) isPostExp() { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; }
isPreExp()1688 inout(PreExp) isPreExp() { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; }
isAssignExp()1689 inout(AssignExp) isAssignExp() { return op == EXP.assign ? cast(typeof(return))this : null; }
isConstructExp()1690 inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; }
isBlitExp()1691 inout(BlitExp) isBlitExp() { return op == EXP.blit ? cast(typeof(return))this : null; }
isAddAssignExp()1692 inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; }
isMinAssignExp()1693 inout(MinAssignExp) isMinAssignExp() { return op == EXP.minAssign ? cast(typeof(return))this : null; }
isMulAssignExp()1694 inout(MulAssignExp) isMulAssignExp() { return op == EXP.mulAssign ? cast(typeof(return))this : null; }
1695
isDivAssignExp()1696 inout(DivAssignExp) isDivAssignExp() { return op == EXP.divAssign ? cast(typeof(return))this : null; }
isModAssignExp()1697 inout(ModAssignExp) isModAssignExp() { return op == EXP.modAssign ? cast(typeof(return))this : null; }
isAndAssignExp()1698 inout(AndAssignExp) isAndAssignExp() { return op == EXP.andAssign ? cast(typeof(return))this : null; }
isOrAssignExp()1699 inout(OrAssignExp) isOrAssignExp() { return op == EXP.orAssign ? cast(typeof(return))this : null; }
isXorAssignExp()1700 inout(XorAssignExp) isXorAssignExp() { return op == EXP.xorAssign ? cast(typeof(return))this : null; }
isPowAssignExp()1701 inout(PowAssignExp) isPowAssignExp() { return op == EXP.powAssign ? cast(typeof(return))this : null; }
1702
isShlAssignExp()1703 inout(ShlAssignExp) isShlAssignExp() { return op == EXP.leftShiftAssign ? cast(typeof(return))this : null; }
isShrAssignExp()1704 inout(ShrAssignExp) isShrAssignExp() { return op == EXP.rightShiftAssign ? cast(typeof(return))this : null; }
isUshrAssignExp()1705 inout(UshrAssignExp) isUshrAssignExp() { return op == EXP.unsignedRightShiftAssign ? cast(typeof(return))this : null; }
1706
isCatAssignExp()1707 inout(CatAssignExp) isCatAssignExp() { return op == EXP.concatenateAssign
1708 ? cast(typeof(return))this
1709 : null; }
1710
isCatElemAssignExp()1711 inout(CatElemAssignExp) isCatElemAssignExp() { return op == EXP.concatenateElemAssign
1712 ? cast(typeof(return))this
1713 : null; }
1714
isCatDcharAssignExp()1715 inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == EXP.concatenateDcharAssign
1716 ? cast(typeof(return))this
1717 : null; }
1718
isAddExp()1719 inout(AddExp) isAddExp() { return op == EXP.add ? cast(typeof(return))this : null; }
isMinExp()1720 inout(MinExp) isMinExp() { return op == EXP.min ? cast(typeof(return))this : null; }
isCatExp()1721 inout(CatExp) isCatExp() { return op == EXP.concatenate ? cast(typeof(return))this : null; }
isMulExp()1722 inout(MulExp) isMulExp() { return op == EXP.mul ? cast(typeof(return))this : null; }
isDivExp()1723 inout(DivExp) isDivExp() { return op == EXP.div ? cast(typeof(return))this : null; }
isModExp()1724 inout(ModExp) isModExp() { return op == EXP.mod ? cast(typeof(return))this : null; }
isPowExp()1725 inout(PowExp) isPowExp() { return op == EXP.pow ? cast(typeof(return))this : null; }
isShlExp()1726 inout(ShlExp) isShlExp() { return op == EXP.leftShift ? cast(typeof(return))this : null; }
isShrExp()1727 inout(ShrExp) isShrExp() { return op == EXP.rightShift ? cast(typeof(return))this : null; }
isUshrExp()1728 inout(UshrExp) isUshrExp() { return op == EXP.unsignedRightShift ? cast(typeof(return))this : null; }
isAndExp()1729 inout(AndExp) isAndExp() { return op == EXP.and ? cast(typeof(return))this : null; }
isOrExp()1730 inout(OrExp) isOrExp() { return op == EXP.or ? cast(typeof(return))this : null; }
isXorExp()1731 inout(XorExp) isXorExp() { return op == EXP.xor ? cast(typeof(return))this : null; }
isLogicalExp()1732 inout(LogicalExp) isLogicalExp() { return (op == EXP.andAnd || op == EXP.orOr) ? cast(typeof(return))this : null; }
1733 //inout(CmpExp) isCmpExp() { return op == EXP. ? cast(typeof(return))this : null; }
isInExp()1734 inout(InExp) isInExp() { return op == EXP.in_ ? cast(typeof(return))this : null; }
isRemoveExp()1735 inout(RemoveExp) isRemoveExp() { return op == EXP.remove ? cast(typeof(return))this : null; }
isEqualExp()1736 inout(EqualExp) isEqualExp() { return (op == EXP.equal || op == EXP.notEqual) ? cast(typeof(return))this : null; }
isIdentityExp()1737 inout(IdentityExp) isIdentityExp() { return (op == EXP.identity || op == EXP.notIdentity) ? cast(typeof(return))this : null; }
isCondExp()1738 inout(CondExp) isCondExp() { return op == EXP.question ? cast(typeof(return))this : null; }
isGenericExp()1739 inout(GenericExp) isGenericExp() { return op == EXP._Generic ? cast(typeof(return))this : null; }
isDefaultInitExp()1740 inout(DefaultInitExp) isDefaultInitExp() { return isDefaultInitOp(op) ? cast(typeof(return))this: null; }
isFileInitExp()1741 inout(FileInitExp) isFileInitExp() { return (op == EXP.file || op == EXP.fileFullPath) ? cast(typeof(return))this : null; }
isLineInitExp()1742 inout(LineInitExp) isLineInitExp() { return op == EXP.line ? cast(typeof(return))this : null; }
isModuleInitExp()1743 inout(ModuleInitExp) isModuleInitExp() { return op == EXP.moduleString ? cast(typeof(return))this : null; }
isFuncInitExp()1744 inout(FuncInitExp) isFuncInitExp() { return op == EXP.functionString ? cast(typeof(return))this : null; }
isPrettyFuncInitExp()1745 inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == EXP.prettyFunction ? cast(typeof(return))this : null; }
isObjcClassReferenceExp()1746 inout(ObjcClassReferenceExp) isObjcClassReferenceExp() { return op == EXP.objcClassReference ? cast(typeof(return))this : null; }
isClassReferenceExp()1747 inout(ClassReferenceExp) isClassReferenceExp() { return op == EXP.classReference ? cast(typeof(return))this : null; }
isThrownExceptionExp()1748 inout(ThrownExceptionExp) isThrownExceptionExp() { return op == EXP.thrownException ? cast(typeof(return))this : null; }
1749
isUnaExp()1750 inout(UnaExp) isUnaExp() pure inout nothrow @nogc
1751 {
1752 return exptab[op] & EXPFLAGS.unary ? cast(typeof(return))this : null;
1753 }
1754
isBinExp()1755 inout(BinExp) isBinExp() pure inout nothrow @nogc
1756 {
1757 return exptab[op] & EXPFLAGS.binary ? cast(typeof(return))this : null;
1758 }
1759
isBinAssignExp()1760 inout(BinAssignExp) isBinAssignExp() pure inout nothrow @nogc
1761 {
1762 return exptab[op] & EXPFLAGS.binaryAssign ? cast(typeof(return))this : null;
1763 }
1764 }
1765
accept(Visitor v)1766 override void accept(Visitor v)
1767 {
1768 v.visit(this);
1769 }
1770 }
1771
1772 /***********************************************************
1773 * A compile-time known integer value
1774 */
1775 extern (C++) final class IntegerExp : Expression
1776 {
1777 private dinteger_t value;
1778
this(const ref Loc loc,dinteger_t value,Type type)1779 extern (D) this(const ref Loc loc, dinteger_t value, Type type)
1780 {
1781 super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp));
1782 //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
1783 assert(type);
1784 if (!type.isscalar())
1785 {
1786 //printf("%s, loc = %d\n", toChars(), loc.linnum);
1787 if (type.ty != Terror)
1788 error("integral constant must be scalar type, not `%s`", type.toChars());
1789 type = Type.terror;
1790 }
1791 this.type = type;
1792 this.value = normalize(type.toBasetype().ty, value);
1793 }
1794
this(dinteger_t value)1795 extern (D) this(dinteger_t value)
1796 {
1797 super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp));
1798 this.type = Type.tint32;
1799 this.value = cast(int)value;
1800 }
1801
create(const ref Loc loc,dinteger_t value,Type type)1802 static IntegerExp create(const ref Loc loc, dinteger_t value, Type type)
1803 {
1804 return new IntegerExp(loc, value, type);
1805 }
1806
1807 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,dinteger_t value,Type type)1808 static void emplace(UnionExp* pue, const ref Loc loc, dinteger_t value, Type type)
1809 {
1810 emplaceExp!(IntegerExp)(pue, loc, value, type);
1811 }
1812
equals(const RootObject o)1813 override bool equals(const RootObject o) const
1814 {
1815 if (this == o)
1816 return true;
1817 if (auto ne = (cast(Expression)o).isIntegerExp())
1818 {
1819 if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value)
1820 {
1821 return true;
1822 }
1823 }
1824 return false;
1825 }
1826
toInteger()1827 override dinteger_t toInteger()
1828 {
1829 // normalize() is necessary until we fix all the paints of 'type'
1830 return value = normalize(type.toBasetype().ty, value);
1831 }
1832
toReal()1833 override real_t toReal()
1834 {
1835 // normalize() is necessary until we fix all the paints of 'type'
1836 const ty = type.toBasetype().ty;
1837 const val = normalize(ty, value);
1838 value = val;
1839 return (ty == Tuns64)
1840 ? real_t(cast(ulong)val)
1841 : real_t(cast(long)val);
1842 }
1843
toImaginary()1844 override real_t toImaginary()
1845 {
1846 return CTFloat.zero;
1847 }
1848
toComplex()1849 override complex_t toComplex()
1850 {
1851 return complex_t(toReal());
1852 }
1853
1854 override Optional!bool toBool()
1855 {
1856 bool r = toInteger() != 0;
1857 return typeof(return)(r);
1858 }
1859
toLvalue(Scope * sc,Expression e)1860 override Expression toLvalue(Scope* sc, Expression e)
1861 {
1862 if (!e)
1863 e = this;
1864 else if (!loc.isValid())
1865 loc = e.loc;
1866 e.error("cannot modify constant `%s`", e.toChars());
1867 return ErrorExp.get();
1868 }
1869
accept(Visitor v)1870 override void accept(Visitor v)
1871 {
1872 v.visit(this);
1873 }
1874
getInteger()1875 dinteger_t getInteger()
1876 {
1877 return value;
1878 }
1879
setInteger(dinteger_t value)1880 void setInteger(dinteger_t value)
1881 {
1882 this.value = normalize(type.toBasetype().ty, value);
1883 }
1884
normalize(TY ty,dinteger_t value)1885 extern (D) static dinteger_t normalize(TY ty, dinteger_t value)
1886 {
1887 /* 'Normalize' the value of the integer to be in range of the type
1888 */
1889 dinteger_t result;
1890 switch (ty)
1891 {
1892 case Tbool:
1893 result = (value != 0);
1894 break;
1895
1896 case Tint8:
1897 result = cast(byte)value;
1898 break;
1899
1900 case Tchar:
1901 case Tuns8:
1902 result = cast(ubyte)value;
1903 break;
1904
1905 case Tint16:
1906 result = cast(short)value;
1907 break;
1908
1909 case Twchar:
1910 case Tuns16:
1911 result = cast(ushort)value;
1912 break;
1913
1914 case Tint32:
1915 result = cast(int)value;
1916 break;
1917
1918 case Tdchar:
1919 case Tuns32:
1920 result = cast(uint)value;
1921 break;
1922
1923 case Tint64:
1924 result = cast(long)value;
1925 break;
1926
1927 case Tuns64:
1928 result = cast(ulong)value;
1929 break;
1930
1931 case Tpointer:
1932 if (target.ptrsize == 8)
1933 goto case Tuns64;
1934 if (target.ptrsize == 4)
1935 goto case Tuns32;
1936 if (target.ptrsize == 2)
1937 goto case Tuns16;
1938 assert(0);
1939
1940 default:
1941 break;
1942 }
1943 return result;
1944 }
1945
syntaxCopy()1946 override IntegerExp syntaxCopy()
1947 {
1948 return this;
1949 }
1950
1951 /**
1952 * Use this instead of creating new instances for commonly used literals
1953 * such as 0 or 1.
1954 *
1955 * Parameters:
1956 * v = The value of the expression
1957 * Returns:
1958 * A static instance of the expression, typed as `Tint32`.
1959 */
literal(int v)1960 static IntegerExp literal(int v)()
1961 {
1962 __gshared IntegerExp theConstant;
1963 if (!theConstant)
1964 theConstant = new IntegerExp(v);
1965 return theConstant;
1966 }
1967
1968 /**
1969 * Use this instead of creating new instances for commonly used bools.
1970 *
1971 * Parameters:
1972 * b = The value of the expression
1973 * Returns:
1974 * A static instance of the expression, typed as `Type.tbool`.
1975 */
createBool(bool b)1976 static IntegerExp createBool(bool b)
1977 {
1978 __gshared IntegerExp trueExp, falseExp;
1979 if (!trueExp)
1980 {
1981 trueExp = new IntegerExp(Loc.initial, 1, Type.tbool);
1982 falseExp = new IntegerExp(Loc.initial, 0, Type.tbool);
1983 }
1984 return b ? trueExp : falseExp;
1985 }
1986 }
1987
1988 /***********************************************************
1989 * Use this expression for error recovery.
1990 *
1991 * It should behave as a 'sink' to prevent further cascaded error messages.
1992 */
1993 extern (C++) final class ErrorExp : Expression
1994 {
this()1995 private extern (D) this()
1996 {
1997 super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp));
1998 type = Type.terror;
1999 }
2000
get()2001 static ErrorExp get ()
2002 {
2003 if (errorexp is null)
2004 errorexp = new ErrorExp();
2005
2006 if (global.errors == 0 && global.gaggedErrors == 0)
2007 {
2008 /* Unfortunately, errors can still leak out of gagged errors,
2009 * and we need to set the error count to prevent bogus code
2010 * generation. At least give a message.
2011 */
2012 .error(Loc.initial, "unknown, please file report on issues.dlang.org");
2013 }
2014
2015 return errorexp;
2016 }
2017
toLvalue(Scope * sc,Expression e)2018 override Expression toLvalue(Scope* sc, Expression e)
2019 {
2020 return this;
2021 }
2022
accept(Visitor v)2023 override void accept(Visitor v)
2024 {
2025 v.visit(this);
2026 }
2027
2028 extern (C++) __gshared ErrorExp errorexp; // handy shared value
2029 }
2030
2031
2032 /***********************************************************
2033 * An uninitialized value,
2034 * generated from void initializers.
2035 *
2036 * https://dlang.org/spec/declaration.html#void_init
2037 */
2038 extern (C++) final class VoidInitExp : Expression
2039 {
2040 VarDeclaration var; /// the variable from where the void value came from, null if not known
2041 /// Useful for error messages
2042
this(VarDeclaration var)2043 extern (D) this(VarDeclaration var)
2044 {
2045 super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp));
2046 this.var = var;
2047 this.type = var.type;
2048 }
2049
toChars()2050 override const(char)* toChars() const
2051 {
2052 return "void";
2053 }
2054
accept(Visitor v)2055 override void accept(Visitor v)
2056 {
2057 v.visit(this);
2058 }
2059 }
2060
2061
2062 /***********************************************************
2063 * A compile-time known floating point number
2064 */
2065 extern (C++) final class RealExp : Expression
2066 {
2067 real_t value;
2068
this(const ref Loc loc,real_t value,Type type)2069 extern (D) this(const ref Loc loc, real_t value, Type type)
2070 {
2071 super(loc, EXP.float64, __traits(classInstanceSize, RealExp));
2072 //printf("RealExp::RealExp(%Lg)\n", value);
2073 this.value = value;
2074 this.type = type;
2075 }
2076
create(const ref Loc loc,real_t value,Type type)2077 static RealExp create(const ref Loc loc, real_t value, Type type)
2078 {
2079 return new RealExp(loc, value, type);
2080 }
2081
2082 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,real_t value,Type type)2083 static void emplace(UnionExp* pue, const ref Loc loc, real_t value, Type type)
2084 {
2085 emplaceExp!(RealExp)(pue, loc, value, type);
2086 }
2087
equals(const RootObject o)2088 override bool equals(const RootObject o) const
2089 {
2090 if (this == o)
2091 return true;
2092 if (auto ne = (cast(Expression)o).isRealExp())
2093 {
2094 if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(value, ne.value))
2095 {
2096 return true;
2097 }
2098 }
2099 return false;
2100 }
2101
toInteger()2102 override dinteger_t toInteger()
2103 {
2104 return cast(sinteger_t)toReal();
2105 }
2106
toUInteger()2107 override uinteger_t toUInteger()
2108 {
2109 return cast(uinteger_t)toReal();
2110 }
2111
toReal()2112 override real_t toReal()
2113 {
2114 return type.isreal() ? value : CTFloat.zero;
2115 }
2116
toImaginary()2117 override real_t toImaginary()
2118 {
2119 return type.isreal() ? CTFloat.zero : value;
2120 }
2121
toComplex()2122 override complex_t toComplex()
2123 {
2124 return complex_t(toReal(), toImaginary());
2125 }
2126
2127 override Optional!bool toBool()
2128 {
2129 return typeof(return)(!!value);
2130 }
2131
accept(Visitor v)2132 override void accept(Visitor v)
2133 {
2134 v.visit(this);
2135 }
2136 }
2137
2138 /***********************************************************
2139 * A compile-time complex number (deprecated)
2140 */
2141 extern (C++) final class ComplexExp : Expression
2142 {
2143 complex_t value;
2144
this(const ref Loc loc,complex_t value,Type type)2145 extern (D) this(const ref Loc loc, complex_t value, Type type)
2146 {
2147 super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp));
2148 this.value = value;
2149 this.type = type;
2150 //printf("ComplexExp::ComplexExp(%s)\n", toChars());
2151 }
2152
create(const ref Loc loc,complex_t value,Type type)2153 static ComplexExp create(const ref Loc loc, complex_t value, Type type)
2154 {
2155 return new ComplexExp(loc, value, type);
2156 }
2157
2158 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,complex_t value,Type type)2159 static void emplace(UnionExp* pue, const ref Loc loc, complex_t value, Type type)
2160 {
2161 emplaceExp!(ComplexExp)(pue, loc, value, type);
2162 }
2163
equals(const RootObject o)2164 override bool equals(const RootObject o) const
2165 {
2166 if (this == o)
2167 return true;
2168 if (auto ne = (cast(Expression)o).isComplexExp())
2169 {
2170 if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(creall(value), creall(ne.value)) && RealIdentical(cimagl(value), cimagl(ne.value)))
2171 {
2172 return true;
2173 }
2174 }
2175 return false;
2176 }
2177
toInteger()2178 override dinteger_t toInteger()
2179 {
2180 return cast(sinteger_t)toReal();
2181 }
2182
toUInteger()2183 override uinteger_t toUInteger()
2184 {
2185 return cast(uinteger_t)toReal();
2186 }
2187
toReal()2188 override real_t toReal()
2189 {
2190 return creall(value);
2191 }
2192
toImaginary()2193 override real_t toImaginary()
2194 {
2195 return cimagl(value);
2196 }
2197
toComplex()2198 override complex_t toComplex()
2199 {
2200 return value;
2201 }
2202
2203 override Optional!bool toBool()
2204 {
2205 return typeof(return)(!!value);
2206 }
2207
accept(Visitor v)2208 override void accept(Visitor v)
2209 {
2210 v.visit(this);
2211 }
2212 }
2213
2214 /***********************************************************
2215 * An identifier in the context of an expression (as opposed to a declaration)
2216 *
2217 * ---
2218 * int x; // VarDeclaration with Identifier
2219 * x++; // PostExp with IdentifierExp
2220 * ---
2221 */
2222 extern (C++) class IdentifierExp : Expression
2223 {
2224 Identifier ident;
2225
this(const ref Loc loc,Identifier ident)2226 extern (D) this(const ref Loc loc, Identifier ident)
2227 {
2228 super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp));
2229 this.ident = ident;
2230 }
2231
create(const ref Loc loc,Identifier ident)2232 static IdentifierExp create(const ref Loc loc, Identifier ident)
2233 {
2234 return new IdentifierExp(loc, ident);
2235 }
2236
isLvalue()2237 override final bool isLvalue()
2238 {
2239 return true;
2240 }
2241
toLvalue(Scope * sc,Expression e)2242 override final Expression toLvalue(Scope* sc, Expression e)
2243 {
2244 return this;
2245 }
2246
accept(Visitor v)2247 override void accept(Visitor v)
2248 {
2249 v.visit(this);
2250 }
2251 }
2252
2253 /***********************************************************
2254 * The dollar operator used when indexing or slicing an array. E.g `a[$]`, `a[1 .. $]` etc.
2255 *
2256 * https://dlang.org/spec/arrays.html#array-length
2257 */
2258 extern (C++) final class DollarExp : IdentifierExp
2259 {
this(const ref Loc loc)2260 extern (D) this(const ref Loc loc)
2261 {
2262 super(loc, Id.dollar);
2263 }
2264
accept(Visitor v)2265 override void accept(Visitor v)
2266 {
2267 v.visit(this);
2268 }
2269 }
2270
2271 /***********************************************************
2272 * Won't be generated by parser.
2273 */
2274 extern (C++) final class DsymbolExp : Expression
2275 {
2276 Dsymbol s;
2277 bool hasOverloads;
2278
2279 extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true)
2280 {
2281 super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp));
2282 this.s = s;
2283 this.hasOverloads = hasOverloads;
2284 }
2285
isLvalue()2286 override bool isLvalue()
2287 {
2288 return true;
2289 }
2290
toLvalue(Scope * sc,Expression e)2291 override Expression toLvalue(Scope* sc, Expression e)
2292 {
2293 return this;
2294 }
2295
accept(Visitor v)2296 override void accept(Visitor v)
2297 {
2298 v.visit(this);
2299 }
2300 }
2301
2302 /***********************************************************
2303 * https://dlang.org/spec/expression.html#this
2304 */
2305 extern (C++) class ThisExp : Expression
2306 {
2307 VarDeclaration var;
2308
this(const ref Loc loc)2309 extern (D) this(const ref Loc loc)
2310 {
2311 super(loc, EXP.this_, __traits(classInstanceSize, ThisExp));
2312 //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
2313 }
2314
this(const ref Loc loc,const EXP tok)2315 this(const ref Loc loc, const EXP tok)
2316 {
2317 super(loc, tok, __traits(classInstanceSize, ThisExp));
2318 //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
2319 }
2320
syntaxCopy()2321 override ThisExp syntaxCopy()
2322 {
2323 auto r = cast(ThisExp) super.syntaxCopy();
2324 // require new semantic (possibly new `var` etc.)
2325 r.type = null;
2326 r.var = null;
2327 return r;
2328 }
2329
2330 override Optional!bool toBool()
2331 {
2332 // `this` is never null (what about structs?)
2333 return typeof(return)(true);
2334 }
2335
isLvalue()2336 override final bool isLvalue()
2337 {
2338 // Class `this` should be an rvalue; struct `this` should be an lvalue.
2339 return type.toBasetype().ty != Tclass;
2340 }
2341
toLvalue(Scope * sc,Expression e)2342 override final Expression toLvalue(Scope* sc, Expression e)
2343 {
2344 if (type.toBasetype().ty == Tclass)
2345 {
2346 // Class `this` is an rvalue; struct `this` is an lvalue.
2347 return Expression.toLvalue(sc, e);
2348 }
2349 return this;
2350 }
2351
accept(Visitor v)2352 override void accept(Visitor v)
2353 {
2354 v.visit(this);
2355 }
2356 }
2357
2358 /***********************************************************
2359 * https://dlang.org/spec/expression.html#super
2360 */
2361 extern (C++) final class SuperExp : ThisExp
2362 {
this(const ref Loc loc)2363 extern (D) this(const ref Loc loc)
2364 {
2365 super(loc, EXP.super_);
2366 }
2367
accept(Visitor v)2368 override void accept(Visitor v)
2369 {
2370 v.visit(this);
2371 }
2372 }
2373
2374 /***********************************************************
2375 * A compile-time known `null` value
2376 *
2377 * https://dlang.org/spec/expression.html#null
2378 */
2379 extern (C++) final class NullExp : Expression
2380 {
2381 extern (D) this(const ref Loc loc, Type type = null)
2382 {
2383 super(loc, EXP.null_, __traits(classInstanceSize, NullExp));
2384 this.type = type;
2385 }
2386
equals(const RootObject o)2387 override bool equals(const RootObject o) const
2388 {
2389 if (auto e = o.isExpression())
2390 {
2391 if (e.op == EXP.null_ && type.equals(e.type))
2392 {
2393 return true;
2394 }
2395 }
2396 return false;
2397 }
2398
2399 override Optional!bool toBool()
2400 {
2401 // null in any type is false
2402 return typeof(return)(false);
2403 }
2404
toStringExp()2405 override StringExp toStringExp()
2406 {
2407 if (implicitConvTo(Type.tstring))
2408 {
2409 auto se = new StringExp(loc, (cast(char*)mem.xcalloc(1, 1))[0 .. 0]);
2410 se.type = Type.tstring;
2411 return se;
2412 }
2413 return null;
2414 }
2415
accept(Visitor v)2416 override void accept(Visitor v)
2417 {
2418 v.visit(this);
2419 }
2420 }
2421
2422 /***********************************************************
2423 * https://dlang.org/spec/expression.html#string_literals
2424 */
2425 extern (C++) final class StringExp : Expression
2426 {
2427 private union
2428 {
2429 char* string; // if sz == 1
2430 wchar* wstring; // if sz == 2
2431 dchar* dstring; // if sz == 4
2432 } // (const if ownedByCtfe == OwnedBy.code)
2433 size_t len; // number of code units
2434 ubyte sz = 1; // 1: char, 2: wchar, 4: dchar
2435 ubyte committed; // !=0 if type is committed
2436 enum char NoPostfix = 0;
2437 char postfix = NoPostfix; // 'c', 'w', 'd'
2438 OwnedBy ownedByCtfe = OwnedBy.code;
2439
this(const ref Loc loc,const (void)[]string)2440 extern (D) this(const ref Loc loc, const(void)[] string)
2441 {
2442 super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
2443 this.string = cast(char*)string.ptr; // note that this.string should be const
2444 this.len = string.length;
2445 this.sz = 1; // work around LDC bug #1286
2446 }
2447
2448 extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix)
2449 {
2450 super(loc, EXP.string_, __traits(classInstanceSize, StringExp));
2451 this.string = cast(char*)string.ptr; // note that this.string should be const
2452 this.len = len;
2453 this.sz = sz;
2454 this.postfix = postfix;
2455 }
2456
create(const ref Loc loc,const (char)* s)2457 static StringExp create(const ref Loc loc, const(char)* s)
2458 {
2459 return new StringExp(loc, s.toDString());
2460 }
2461
create(const ref Loc loc,const (void)* string,size_t len)2462 static StringExp create(const ref Loc loc, const(void)* string, size_t len)
2463 {
2464 return new StringExp(loc, string[0 .. len]);
2465 }
2466
2467 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,const (char)* s)2468 static void emplace(UnionExp* pue, const ref Loc loc, const(char)* s)
2469 {
2470 emplaceExp!(StringExp)(pue, loc, s.toDString());
2471 }
2472
emplace(UnionExp * pue,const ref Loc loc,const (void)[]string)2473 extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string)
2474 {
2475 emplaceExp!(StringExp)(pue, loc, string);
2476 }
2477
emplace(UnionExp * pue,const ref Loc loc,const (void)[]string,size_t len,ubyte sz,char postfix)2478 extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
2479 {
2480 emplaceExp!(StringExp)(pue, loc, string, len, sz, postfix);
2481 }
2482
equals(const RootObject o)2483 override bool equals(const RootObject o) const
2484 {
2485 //printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
2486 if (auto e = o.isExpression())
2487 {
2488 if (auto se = e.isStringExp())
2489 {
2490 return compare(se) == 0;
2491 }
2492 }
2493 return false;
2494 }
2495
2496 /**********************************
2497 * Return the number of code units the string would be if it were re-encoded
2498 * as tynto.
2499 * Params:
2500 * tynto = code unit type of the target encoding
2501 * Returns:
2502 * number of code units
2503 */
2504 size_t numberOfCodeUnits(int tynto = 0) const
2505 {
2506 int encSize;
2507 switch (tynto)
2508 {
2509 case 0: return len;
2510 case Tchar: encSize = 1; break;
2511 case Twchar: encSize = 2; break;
2512 case Tdchar: encSize = 4; break;
2513 default:
2514 assert(0);
2515 }
2516 if (sz == encSize)
2517 return len;
2518
2519 size_t result = 0;
2520 dchar c;
2521
2522 switch (sz)
2523 {
2524 case 1:
2525 for (size_t u = 0; u < len;)
2526 {
2527 if (const s = utf_decodeChar(string[0 .. len], u, c))
2528 {
2529 error("%.*s", cast(int)s.length, s.ptr);
2530 return 0;
2531 }
2532 result += utf_codeLength(encSize, c);
2533 }
2534 break;
2535
2536 case 2:
2537 for (size_t u = 0; u < len;)
2538 {
2539 if (const s = utf_decodeWchar(wstring[0 .. len], u, c))
2540 {
2541 error("%.*s", cast(int)s.length, s.ptr);
2542 return 0;
2543 }
2544 result += utf_codeLength(encSize, c);
2545 }
2546 break;
2547
2548 case 4:
2549 foreach (u; 0 .. len)
2550 {
2551 result += utf_codeLength(encSize, dstring[u]);
2552 }
2553 break;
2554
2555 default:
2556 assert(0);
2557 }
2558 return result;
2559 }
2560
2561 /**********************************************
2562 * Write the contents of the string to dest.
2563 * Use numberOfCodeUnits() to determine size of result.
2564 * Params:
2565 * dest = destination
2566 * tyto = encoding type of the result
2567 * zero = add terminating 0
2568 */
2569 void writeTo(void* dest, bool zero, int tyto = 0) const
2570 {
2571 int encSize;
2572 switch (tyto)
2573 {
2574 case 0: encSize = sz; break;
2575 case Tchar: encSize = 1; break;
2576 case Twchar: encSize = 2; break;
2577 case Tdchar: encSize = 4; break;
2578 default:
2579 assert(0);
2580 }
2581 if (sz == encSize)
2582 {
2583 memcpy(dest, string, len * sz);
2584 if (zero)
2585 memset(dest + len * sz, 0, sz);
2586 }
2587 else
2588 assert(0);
2589 }
2590
2591 /*********************************************
2592 * Get the code unit at index i
2593 * Params:
2594 * i = index
2595 * Returns:
2596 * code unit at index i
2597 */
getCodeUnit(size_t i)2598 dchar getCodeUnit(size_t i) const pure
2599 {
2600 assert(i < len);
2601 final switch (sz)
2602 {
2603 case 1:
2604 return string[i];
2605 case 2:
2606 return wstring[i];
2607 case 4:
2608 return dstring[i];
2609 }
2610 }
2611
2612 /*********************************************
2613 * Set the code unit at index i to c
2614 * Params:
2615 * i = index
2616 * c = code unit to set it to
2617 */
setCodeUnit(size_t i,dchar c)2618 void setCodeUnit(size_t i, dchar c)
2619 {
2620 assert(i < len);
2621 final switch (sz)
2622 {
2623 case 1:
2624 string[i] = cast(char)c;
2625 break;
2626 case 2:
2627 wstring[i] = cast(wchar)c;
2628 break;
2629 case 4:
2630 dstring[i] = c;
2631 break;
2632 }
2633 }
2634
toStringExp()2635 override StringExp toStringExp()
2636 {
2637 return this;
2638 }
2639
2640 /****************************************
2641 * Convert string to char[].
2642 */
toUTF8(Scope * sc)2643 StringExp toUTF8(Scope* sc)
2644 {
2645 if (sz != 1)
2646 {
2647 // Convert to UTF-8 string
2648 committed = 0;
2649 Expression e = castTo(sc, Type.tchar.arrayOf());
2650 e = e.optimize(WANTvalue);
2651 auto se = e.isStringExp();
2652 assert(se.sz == 1);
2653 return se;
2654 }
2655 return this;
2656 }
2657
2658 /**
2659 * Compare two `StringExp` by length, then value
2660 *
2661 * The comparison is not the usual C-style comparison as seen with
2662 * `strcmp` or `memcmp`, but instead first compare based on the length.
2663 * This allows both faster lookup and sorting when comparing sparse data.
2664 *
2665 * This ordering scheme is relied on by the string-switching feature.
2666 * Code in Druntime's `core.internal.switch_` relies on this ordering
2667 * when doing a binary search among case statements.
2668 *
2669 * Both `StringExp` should be of the same encoding.
2670 *
2671 * Params:
2672 * se2 = String expression to compare `this` to
2673 *
2674 * Returns:
2675 * `0` when `this` is equal to se2, a value greater than `0` if
2676 * `this` should be considered greater than `se2`,
2677 * and a value less than `0` if `this` is lesser than `se2`.
2678 */
compare(const StringExp se2)2679 int compare(const StringExp se2) const nothrow pure @nogc
2680 {
2681 //printf("StringExp::compare()\n");
2682 const len1 = len;
2683 const len2 = se2.len;
2684
2685 assert(this.sz == se2.sz, "Comparing string expressions of different sizes");
2686 //printf("sz = %d, len1 = %d, len2 = %d\n", sz, cast(int)len1, cast(int)len2);
2687 if (len1 == len2)
2688 {
2689 switch (sz)
2690 {
2691 case 1:
2692 return memcmp(string, se2.string, len1);
2693
2694 case 2:
2695 {
2696 wchar* s1 = cast(wchar*)string;
2697 wchar* s2 = cast(wchar*)se2.string;
2698 foreach (u; 0 .. len)
2699 {
2700 if (s1[u] != s2[u])
2701 return s1[u] - s2[u];
2702 }
2703 }
2704 break;
2705 case 4:
2706 {
2707 dchar* s1 = cast(dchar*)string;
2708 dchar* s2 = cast(dchar*)se2.string;
2709 foreach (u; 0 .. len)
2710 {
2711 if (s1[u] != s2[u])
2712 return s1[u] - s2[u];
2713 }
2714 }
2715 break;
2716 default:
2717 assert(0);
2718 }
2719 }
2720 return cast(int)(len1 - len2);
2721 }
2722
2723 override Optional!bool toBool()
2724 {
2725 // Keep the old behaviour for this refactoring
2726 // Should probably match language spec instead and check for length
2727 return typeof(return)(true);
2728 }
2729
isLvalue()2730 override bool isLvalue()
2731 {
2732 /* string literal is rvalue in default, but
2733 * conversion to reference of static array is only allowed.
2734 */
2735 return (type && type.toBasetype().ty == Tsarray);
2736 }
2737
toLvalue(Scope * sc,Expression e)2738 override Expression toLvalue(Scope* sc, Expression e)
2739 {
2740 //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
2741 return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
2742 }
2743
modifiableLvalue(Scope * sc,Expression e)2744 override Expression modifiableLvalue(Scope* sc, Expression e)
2745 {
2746 error("cannot modify string literal `%s`", toChars());
2747 return ErrorExp.get();
2748 }
2749
2750 /********************************
2751 * Convert string contents to a 0 terminated string,
2752 * allocated by mem.xmalloc().
2753 */
toStringz()2754 extern (D) const(char)[] toStringz() const
2755 {
2756 auto nbytes = len * sz;
2757 char* s = cast(char*)mem.xmalloc(nbytes + sz);
2758 writeTo(s, true);
2759 return s[0 .. nbytes];
2760 }
2761
peekString()2762 extern (D) const(char)[] peekString() const
2763 {
2764 assert(sz == 1);
2765 return this.string[0 .. len];
2766 }
2767
peekWstring()2768 extern (D) const(wchar)[] peekWstring() const
2769 {
2770 assert(sz == 2);
2771 return this.wstring[0 .. len];
2772 }
2773
peekDstring()2774 extern (D) const(dchar)[] peekDstring() const
2775 {
2776 assert(sz == 4);
2777 return this.dstring[0 .. len];
2778 }
2779
2780 /*******************
2781 * Get a slice of the data.
2782 */
peekData()2783 extern (D) const(ubyte)[] peekData() const
2784 {
2785 return cast(const(ubyte)[])this.string[0 .. len * sz];
2786 }
2787
2788 /*******************
2789 * Borrow a slice of the data, so the caller can modify
2790 * it in-place (!)
2791 */
borrowData()2792 extern (D) ubyte[] borrowData()
2793 {
2794 return cast(ubyte[])this.string[0 .. len * sz];
2795 }
2796
2797 /***********************
2798 * Set new string data.
2799 * `this` becomes the new owner of the data.
2800 */
setData(void * s,size_t len,ubyte sz)2801 extern (D) void setData(void* s, size_t len, ubyte sz)
2802 {
2803 this.string = cast(char*)s;
2804 this.len = len;
2805 this.sz = sz;
2806 }
2807
accept(Visitor v)2808 override void accept(Visitor v)
2809 {
2810 v.visit(this);
2811 }
2812 }
2813
2814 /***********************************************************
2815 * A sequence of expressions
2816 *
2817 * ---
2818 * alias AliasSeq(T...) = T;
2819 * alias Tup = AliasSeq!(3, int, "abc");
2820 * ---
2821 */
2822 extern (C++) final class TupleExp : Expression
2823 {
2824 /* Tuple-field access may need to take out its side effect part.
2825 * For example:
2826 * foo().tupleof
2827 * is rewritten as:
2828 * (ref __tup = foo(); tuple(__tup.field0, __tup.field1, ...))
2829 * The declaration of temporary variable __tup will be stored in TupleExp.e0.
2830 */
2831 Expression e0;
2832
2833 Expressions* exps;
2834
this(const ref Loc loc,Expression e0,Expressions * exps)2835 extern (D) this(const ref Loc loc, Expression e0, Expressions* exps)
2836 {
2837 super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
2838 //printf("TupleExp(this = %p)\n", this);
2839 this.e0 = e0;
2840 this.exps = exps;
2841 }
2842
this(const ref Loc loc,Expressions * exps)2843 extern (D) this(const ref Loc loc, Expressions* exps)
2844 {
2845 super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
2846 //printf("TupleExp(this = %p)\n", this);
2847 this.exps = exps;
2848 }
2849
this(const ref Loc loc,TupleDeclaration tup)2850 extern (D) this(const ref Loc loc, TupleDeclaration tup)
2851 {
2852 super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp));
2853 this.exps = new Expressions();
2854
2855 this.exps.reserve(tup.objects.dim);
2856 foreach (o; *tup.objects)
2857 {
2858 if (Dsymbol s = getDsymbol(o))
2859 {
2860 /* If tuple element represents a symbol, translate to DsymbolExp
2861 * to supply implicit 'this' if needed later.
2862 */
2863 Expression e = new DsymbolExp(loc, s);
2864 this.exps.push(e);
2865 }
2866 else if (auto eo = o.isExpression())
2867 {
2868 auto e = eo.copy();
2869 e.loc = loc; // https://issues.dlang.org/show_bug.cgi?id=15669
2870 this.exps.push(e);
2871 }
2872 else if (auto t = o.isType())
2873 {
2874 Expression e = new TypeExp(loc, t);
2875 this.exps.push(e);
2876 }
2877 else
2878 {
2879 error("`%s` is not an expression", o.toChars());
2880 }
2881 }
2882 }
2883
create(const ref Loc loc,Expressions * exps)2884 static TupleExp create(const ref Loc loc, Expressions* exps)
2885 {
2886 return new TupleExp(loc, exps);
2887 }
2888
syntaxCopy()2889 override TupleExp syntaxCopy()
2890 {
2891 return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps));
2892 }
2893
equals(const RootObject o)2894 override bool equals(const RootObject o) const
2895 {
2896 if (this == o)
2897 return true;
2898 if (auto e = o.isExpression())
2899 if (auto te = e.isTupleExp())
2900 {
2901 if (exps.dim != te.exps.dim)
2902 return false;
2903 if (e0 && !e0.equals(te.e0) || !e0 && te.e0)
2904 return false;
2905 foreach (i, e1; *exps)
2906 {
2907 auto e2 = (*te.exps)[i];
2908 if (!e1.equals(e2))
2909 return false;
2910 }
2911 return true;
2912 }
2913 return false;
2914 }
2915
accept(Visitor v)2916 override void accept(Visitor v)
2917 {
2918 v.visit(this);
2919 }
2920 }
2921
2922 /***********************************************************
2923 * [ e1, e2, e3, ... ]
2924 *
2925 * https://dlang.org/spec/expression.html#array_literals
2926 */
2927 extern (C++) final class ArrayLiteralExp : Expression
2928 {
2929 /** If !is null, elements[] can be sparse and basis is used for the
2930 * "default" element value. In other words, non-null elements[i] overrides
2931 * this 'basis' value.
2932 */
2933 Expression basis;
2934
2935 Expressions* elements;
2936 OwnedBy ownedByCtfe = OwnedBy.code;
2937
2938
this(const ref Loc loc,Type type,Expressions * elements)2939 extern (D) this(const ref Loc loc, Type type, Expressions* elements)
2940 {
2941 super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
2942 this.type = type;
2943 this.elements = elements;
2944 }
2945
this(const ref Loc loc,Type type,Expression e)2946 extern (D) this(const ref Loc loc, Type type, Expression e)
2947 {
2948 super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
2949 this.type = type;
2950 elements = new Expressions();
2951 elements.push(e);
2952 }
2953
this(const ref Loc loc,Type type,Expression basis,Expressions * elements)2954 extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements)
2955 {
2956 super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp));
2957 this.type = type;
2958 this.basis = basis;
2959 this.elements = elements;
2960 }
2961
create(const ref Loc loc,Expressions * elements)2962 static ArrayLiteralExp create(const ref Loc loc, Expressions* elements)
2963 {
2964 return new ArrayLiteralExp(loc, null, elements);
2965 }
2966
2967 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,Expressions * elements)2968 static void emplace(UnionExp* pue, const ref Loc loc, Expressions* elements)
2969 {
2970 emplaceExp!(ArrayLiteralExp)(pue, loc, null, elements);
2971 }
2972
syntaxCopy()2973 override ArrayLiteralExp syntaxCopy()
2974 {
2975 return new ArrayLiteralExp(loc,
2976 null,
2977 basis ? basis.syntaxCopy() : null,
2978 arraySyntaxCopy(elements));
2979 }
2980
equals(const RootObject o)2981 override bool equals(const RootObject o) const
2982 {
2983 if (this == o)
2984 return true;
2985 auto e = o.isExpression();
2986 if (!e)
2987 return false;
2988 if (auto ae = e.isArrayLiteralExp())
2989 {
2990 if (elements.dim != ae.elements.dim)
2991 return false;
2992 if (elements.dim == 0 && !type.equals(ae.type))
2993 {
2994 return false;
2995 }
2996
2997 foreach (i, e1; *elements)
2998 {
2999 auto e2 = (*ae.elements)[i];
3000 auto e1x = e1 ? e1 : basis;
3001 auto e2x = e2 ? e2 : ae.basis;
3002
3003 if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x)))
3004 return false;
3005 }
3006 return true;
3007 }
3008 return false;
3009 }
3010
getElement(size_t i)3011 Expression getElement(size_t i)
3012 {
3013 return this[i];
3014 }
3015
opIndex(size_t i)3016 Expression opIndex(size_t i)
3017 {
3018 auto el = (*elements)[i];
3019 return el ? el : basis;
3020 }
3021
3022 override Optional!bool toBool()
3023 {
3024 size_t dim = elements ? elements.dim : 0;
3025 return typeof(return)(dim != 0);
3026 }
3027
toStringExp()3028 override StringExp toStringExp()
3029 {
3030 TY telem = type.nextOf().toBasetype().ty;
3031 if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.dim == 0)))
3032 {
3033 ubyte sz = 1;
3034 if (telem == Twchar)
3035 sz = 2;
3036 else if (telem == Tdchar)
3037 sz = 4;
3038
3039 OutBuffer buf;
3040 if (elements)
3041 {
3042 foreach (i; 0 .. elements.dim)
3043 {
3044 auto ch = this[i];
3045 if (ch.op != EXP.int64)
3046 return null;
3047 if (sz == 1)
3048 buf.writeByte(cast(uint)ch.toInteger());
3049 else if (sz == 2)
3050 buf.writeword(cast(uint)ch.toInteger());
3051 else
3052 buf.write4(cast(uint)ch.toInteger());
3053 }
3054 }
3055 char prefix;
3056 if (sz == 1)
3057 {
3058 prefix = 'c';
3059 buf.writeByte(0);
3060 }
3061 else if (sz == 2)
3062 {
3063 prefix = 'w';
3064 buf.writeword(0);
3065 }
3066 else
3067 {
3068 prefix = 'd';
3069 buf.write4(0);
3070 }
3071
3072 const size_t len = buf.length / sz - 1;
3073 auto se = new StringExp(loc, buf.extractSlice()[0 .. len * sz], len, sz, prefix);
3074 se.sz = sz;
3075 se.type = type;
3076 return se;
3077 }
3078 return null;
3079 }
3080
accept(Visitor v)3081 override void accept(Visitor v)
3082 {
3083 v.visit(this);
3084 }
3085 }
3086
3087 /***********************************************************
3088 * [ key0 : value0, key1 : value1, ... ]
3089 *
3090 * https://dlang.org/spec/expression.html#associative_array_literals
3091 */
3092 extern (C++) final class AssocArrayLiteralExp : Expression
3093 {
3094 Expressions* keys;
3095 Expressions* values;
3096
3097 OwnedBy ownedByCtfe = OwnedBy.code;
3098
this(const ref Loc loc,Expressions * keys,Expressions * values)3099 extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values)
3100 {
3101 super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp));
3102 assert(keys.dim == values.dim);
3103 this.keys = keys;
3104 this.values = values;
3105 }
3106
equals(const RootObject o)3107 override bool equals(const RootObject o) const
3108 {
3109 if (this == o)
3110 return true;
3111 auto e = o.isExpression();
3112 if (!e)
3113 return false;
3114 if (auto ae = e.isAssocArrayLiteralExp())
3115 {
3116 if (keys.dim != ae.keys.dim)
3117 return false;
3118 size_t count = 0;
3119 foreach (i, key; *keys)
3120 {
3121 foreach (j, akey; *ae.keys)
3122 {
3123 if (key.equals(akey))
3124 {
3125 if (!(*values)[i].equals((*ae.values)[j]))
3126 return false;
3127 ++count;
3128 }
3129 }
3130 }
3131 return count == keys.dim;
3132 }
3133 return false;
3134 }
3135
syntaxCopy()3136 override AssocArrayLiteralExp syntaxCopy()
3137 {
3138 return new AssocArrayLiteralExp(loc, arraySyntaxCopy(keys), arraySyntaxCopy(values));
3139 }
3140
3141 override Optional!bool toBool()
3142 {
3143 size_t dim = keys.dim;
3144 return typeof(return)(dim != 0);
3145 }
3146
accept(Visitor v)3147 override void accept(Visitor v)
3148 {
3149 v.visit(this);
3150 }
3151 }
3152
3153 enum stageScrub = 0x1; /// scrubReturnValue is running
3154 enum stageSearchPointers = 0x2; /// hasNonConstPointers is running
3155 enum stageOptimize = 0x4; /// optimize is running
3156 enum stageApply = 0x8; /// apply is running
3157 enum stageInlineScan = 0x10; /// inlineScan is running
3158 enum stageToCBuffer = 0x20; /// toCBuffer is running
3159
3160 /***********************************************************
3161 * sd( e1, e2, e3, ... )
3162 */
3163 extern (C++) final class StructLiteralExp : Expression
3164 {
3165 StructDeclaration sd; /// which aggregate this is for
3166 Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip
3167 Type stype; /// final type of result (can be different from sd's type)
3168
3169 Symbol* sym; /// back end symbol to initialize with literal
3170
3171 /** pointer to the origin instance of the expression.
3172 * once a new expression is created, origin is set to 'this'.
3173 * anytime when an expression copy is created, 'origin' pointer is set to
3174 * 'origin' pointer value of the original expression.
3175 */
3176 StructLiteralExp origin;
3177
3178 /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer.
3179 StructLiteralExp inlinecopy;
3180
3181 /** anytime when recursive function is calling, 'stageflags' marks with bit flag of
3182 * current stage and unmarks before return from this function.
3183 * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline'
3184 * (with infinite recursion) of this expression.
3185 */
3186 int stageflags;
3187
3188 bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol
3189 bool isOriginal = false; /// used when moving instances to indicate `this is this.origin`
3190 OwnedBy ownedByCtfe = OwnedBy.code;
3191
3192 extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null)
3193 {
3194 super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp));
3195 this.sd = sd;
3196 if (!elements)
3197 elements = new Expressions();
3198 this.elements = elements;
3199 this.stype = stype;
3200 this.origin = this;
3201 //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
3202 }
3203
3204 static StructLiteralExp create(const ref Loc loc, StructDeclaration sd, void* elements, Type stype = null)
3205 {
3206 return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
3207 }
3208
equals(const RootObject o)3209 override bool equals(const RootObject o) const
3210 {
3211 if (this == o)
3212 return true;
3213 auto e = o.isExpression();
3214 if (!e)
3215 return false;
3216 if (auto se = e.isStructLiteralExp())
3217 {
3218 if (!type.equals(se.type))
3219 return false;
3220 if (elements.dim != se.elements.dim)
3221 return false;
3222 foreach (i, e1; *elements)
3223 {
3224 auto e2 = (*se.elements)[i];
3225 if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
3226 return false;
3227 }
3228 return true;
3229 }
3230 return false;
3231 }
3232
syntaxCopy()3233 override StructLiteralExp syntaxCopy()
3234 {
3235 auto exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
3236 exp.origin = this;
3237 return exp;
3238 }
3239
3240 /**************************************
3241 * Gets expression at offset of type.
3242 * Returns NULL if not found.
3243 */
getField(Type type,uint offset)3244 Expression getField(Type type, uint offset)
3245 {
3246 //printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
3247 // /*toChars()*/"", type.toChars(), offset);
3248 Expression e = null;
3249 int i = getFieldIndex(type, offset);
3250
3251 if (i != -1)
3252 {
3253 //printf("\ti = %d\n", i);
3254 if (i >= sd.nonHiddenFields())
3255 return null;
3256
3257 assert(i < elements.dim);
3258 e = (*elements)[i];
3259 if (e)
3260 {
3261 //printf("e = %s, e.type = %s\n", e.toChars(), e.type.toChars());
3262
3263 /* If type is a static array, and e is an initializer for that array,
3264 * then the field initializer should be an array literal of e.
3265 */
3266 auto tsa = type.isTypeSArray();
3267 if (tsa && e.type.castMod(0) != type.castMod(0))
3268 {
3269 const length = cast(size_t)tsa.dim.toInteger();
3270 auto z = new Expressions(length);
3271 foreach (ref q; *z)
3272 q = e.copy();
3273 e = new ArrayLiteralExp(loc, type, z);
3274 }
3275 else
3276 {
3277 e = e.copy();
3278 e.type = type;
3279 }
3280 if (useStaticInit && e.type.needsNested())
3281 if (auto se = e.isStructLiteralExp())
3282 {
3283 se.useStaticInit = true;
3284 }
3285 }
3286 }
3287 return e;
3288 }
3289
3290 /************************************
3291 * Get index of field.
3292 * Returns -1 if not found.
3293 */
getFieldIndex(Type type,uint offset)3294 int getFieldIndex(Type type, uint offset)
3295 {
3296 /* Find which field offset is by looking at the field offsets
3297 */
3298 if (elements.dim)
3299 {
3300 const sz = type.size();
3301 if (sz == SIZE_INVALID)
3302 return -1;
3303 foreach (i, v; sd.fields)
3304 {
3305 if (offset == v.offset && sz == v.type.size())
3306 {
3307 /* context fields might not be filled. */
3308 if (i >= sd.nonHiddenFields())
3309 return cast(int)i;
3310 if (auto e = (*elements)[i])
3311 {
3312 return cast(int)i;
3313 }
3314 break;
3315 }
3316 }
3317 }
3318 return -1;
3319 }
3320
addDtorHook(Scope * sc)3321 override Expression addDtorHook(Scope* sc)
3322 {
3323 /* If struct requires a destructor, rewrite as:
3324 * (S tmp = S()),tmp
3325 * so that the destructor can be hung on tmp.
3326 */
3327 if (sd.dtor && sc.func)
3328 {
3329 /* Make an identifier for the temporary of the form:
3330 * __sl%s%d, where %s is the struct name
3331 */
3332 char[10] buf = void;
3333 const prefix = "__sl";
3334 const ident = sd.ident.toString;
3335 const fullLen = prefix.length + ident.length;
3336 const len = fullLen < buf.length ? fullLen : buf.length;
3337 buf[0 .. prefix.length] = prefix;
3338 buf[prefix.length .. len] = ident[0 .. len - prefix.length];
3339
3340 auto tmp = copyToTemp(0, buf[0 .. len], this);
3341 Expression ae = new DeclarationExp(loc, tmp);
3342 Expression e = new CommaExp(loc, ae, new VarExp(loc, tmp));
3343 e = e.expressionSemantic(sc);
3344 return e;
3345 }
3346 return this;
3347 }
3348
toLvalue(Scope * sc,Expression e)3349 override Expression toLvalue(Scope* sc, Expression e)
3350 {
3351 if (sc.flags & SCOPE.Cfile)
3352 return this; // C struct literals are lvalues
3353 else
3354 return Expression.toLvalue(sc, e);
3355 }
3356
accept(Visitor v)3357 override void accept(Visitor v)
3358 {
3359 v.visit(this);
3360 }
3361 }
3362
3363 /***********************************************************
3364 * C11 6.5.2.5
3365 * ( type-name ) { initializer-list }
3366 */
3367 extern (C++) final class CompoundLiteralExp : Expression
3368 {
3369 Initializer initializer; /// initializer-list
3370
this(const ref Loc loc,Type type_name,Initializer initializer)3371 extern (D) this(const ref Loc loc, Type type_name, Initializer initializer)
3372 {
3373 super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp));
3374 super.type = type_name;
3375 this.initializer = initializer;
3376 //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars());
3377 }
3378
accept(Visitor v)3379 override void accept(Visitor v)
3380 {
3381 v.visit(this);
3382 }
3383 }
3384
3385 /***********************************************************
3386 * Mainly just a placeholder
3387 */
3388 extern (C++) final class TypeExp : Expression
3389 {
this(const ref Loc loc,Type type)3390 extern (D) this(const ref Loc loc, Type type)
3391 {
3392 super(loc, EXP.type, __traits(classInstanceSize, TypeExp));
3393 //printf("TypeExp::TypeExp(%s)\n", type.toChars());
3394 this.type = type;
3395 }
3396
syntaxCopy()3397 override TypeExp syntaxCopy()
3398 {
3399 return new TypeExp(loc, type.syntaxCopy());
3400 }
3401
checkType()3402 override bool checkType()
3403 {
3404 error("type `%s` is not an expression", toChars());
3405 return true;
3406 }
3407
checkValue()3408 override bool checkValue()
3409 {
3410 error("type `%s` has no value", toChars());
3411 return true;
3412 }
3413
accept(Visitor v)3414 override void accept(Visitor v)
3415 {
3416 v.visit(this);
3417 }
3418 }
3419
3420 /***********************************************************
3421 * Mainly just a placeholder of
3422 * Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
3423 *
3424 * A template instance that requires IFTI:
3425 * foo!tiargs(fargs) // foo!tiargs
3426 * is left until CallExp::semantic() or resolveProperties()
3427 */
3428 extern (C++) final class ScopeExp : Expression
3429 {
3430 ScopeDsymbol sds;
3431
this(const ref Loc loc,ScopeDsymbol sds)3432 extern (D) this(const ref Loc loc, ScopeDsymbol sds)
3433 {
3434 super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp));
3435 //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars());
3436 //static int count; if (++count == 38) *(char*)0=0;
3437 this.sds = sds;
3438 assert(!sds.isTemplateDeclaration()); // instead, you should use TemplateExp
3439 }
3440
syntaxCopy()3441 override ScopeExp syntaxCopy()
3442 {
3443 return new ScopeExp(loc, sds.syntaxCopy(null));
3444 }
3445
checkType()3446 override bool checkType()
3447 {
3448 if (sds.isPackage())
3449 {
3450 error("%s `%s` has no type", sds.kind(), sds.toChars());
3451 return true;
3452 }
3453 if (auto ti = sds.isTemplateInstance())
3454 {
3455 //assert(ti.needsTypeInference(sc));
3456 if (ti.tempdecl &&
3457 ti.semantictiargsdone &&
3458 ti.semanticRun == PASS.initial)
3459 {
3460 error("partial %s `%s` has no type", sds.kind(), toChars());
3461 return true;
3462 }
3463 }
3464 return false;
3465 }
3466
checkValue()3467 override bool checkValue()
3468 {
3469 error("%s `%s` has no value", sds.kind(), sds.toChars());
3470 return true;
3471 }
3472
accept(Visitor v)3473 override void accept(Visitor v)
3474 {
3475 v.visit(this);
3476 }
3477 }
3478
3479 /***********************************************************
3480 * Mainly just a placeholder
3481 */
3482 extern (C++) final class TemplateExp : Expression
3483 {
3484 TemplateDeclaration td;
3485 FuncDeclaration fd;
3486
3487 extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null)
3488 {
3489 super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp));
3490 //printf("TemplateExp(): %s\n", td.toChars());
3491 this.td = td;
3492 this.fd = fd;
3493 }
3494
isLvalue()3495 override bool isLvalue()
3496 {
3497 return fd !is null;
3498 }
3499
toLvalue(Scope * sc,Expression e)3500 override Expression toLvalue(Scope* sc, Expression e)
3501 {
3502 if (!fd)
3503 return Expression.toLvalue(sc, e);
3504
3505 assert(sc);
3506 return symbolToExp(fd, loc, sc, true);
3507 }
3508
checkType()3509 override bool checkType()
3510 {
3511 error("%s `%s` has no type", td.kind(), toChars());
3512 return true;
3513 }
3514
checkValue()3515 override bool checkValue()
3516 {
3517 error("%s `%s` has no value", td.kind(), toChars());
3518 return true;
3519 }
3520
accept(Visitor v)3521 override void accept(Visitor v)
3522 {
3523 v.visit(this);
3524 }
3525 }
3526
3527 /***********************************************************
3528 * newtype(arguments)
3529 */
3530 extern (C++) final class NewExp : Expression
3531 {
3532 Expression thisexp; // if !=null, 'this' for class being allocated
3533 Type newtype;
3534 Expressions* arguments; // Array of Expression's
3535
3536 Expression argprefix; // expression to be evaluated just before arguments[]
3537 CtorDeclaration member; // constructor function
3538 bool onstack; // allocate on stack
3539 bool thrownew; // this NewExp is the expression of a ThrowStatement
3540
this(const ref Loc loc,Expression thisexp,Type newtype,Expressions * arguments)3541 extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments)
3542 {
3543 super(loc, EXP.new_, __traits(classInstanceSize, NewExp));
3544 this.thisexp = thisexp;
3545 this.newtype = newtype;
3546 this.arguments = arguments;
3547 }
3548
create(const ref Loc loc,Expression thisexp,Type newtype,Expressions * arguments)3549 static NewExp create(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments)
3550 {
3551 return new NewExp(loc, thisexp, newtype, arguments);
3552 }
3553
syntaxCopy()3554 override NewExp syntaxCopy()
3555 {
3556 return new NewExp(loc,
3557 thisexp ? thisexp.syntaxCopy() : null,
3558 newtype.syntaxCopy(),
3559 arraySyntaxCopy(arguments));
3560 }
3561
accept(Visitor v)3562 override void accept(Visitor v)
3563 {
3564 v.visit(this);
3565 }
3566 }
3567
3568 /***********************************************************
3569 * class baseclasses { } (arguments)
3570 */
3571 extern (C++) final class NewAnonClassExp : Expression
3572 {
3573 Expression thisexp; // if !=null, 'this' for class being allocated
3574 ClassDeclaration cd; // class being instantiated
3575 Expressions* arguments; // Array of Expression's to call class constructor
3576
this(const ref Loc loc,Expression thisexp,ClassDeclaration cd,Expressions * arguments)3577 extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments)
3578 {
3579 super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
3580 this.thisexp = thisexp;
3581 this.cd = cd;
3582 this.arguments = arguments;
3583 }
3584
syntaxCopy()3585 override NewAnonClassExp syntaxCopy()
3586 {
3587 return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, cd.syntaxCopy(null), arraySyntaxCopy(arguments));
3588 }
3589
accept(Visitor v)3590 override void accept(Visitor v)
3591 {
3592 v.visit(this);
3593 }
3594 }
3595
3596 /***********************************************************
3597 */
3598 extern (C++) class SymbolExp : Expression
3599 {
3600 Declaration var;
3601 Dsymbol originalScope; // original scope before inlining
3602 bool hasOverloads;
3603
this(const ref Loc loc,EXP op,int size,Declaration var,bool hasOverloads)3604 extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads)
3605 {
3606 super(loc, op, size);
3607 assert(var);
3608 this.var = var;
3609 this.hasOverloads = hasOverloads;
3610 }
3611
accept(Visitor v)3612 override void accept(Visitor v)
3613 {
3614 v.visit(this);
3615 }
3616 }
3617
3618 /***********************************************************
3619 * Offset from symbol
3620 */
3621 extern (C++) final class SymOffExp : SymbolExp
3622 {
3623 dinteger_t offset;
3624
3625 extern (D) this(const ref Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true)
3626 {
3627 if (auto v = var.isVarDeclaration())
3628 {
3629 // FIXME: This error report will never be handled anyone.
3630 // It should be done before the SymOffExp construction.
3631 if (v.needThis())
3632 .error(loc, "need `this` for address of `%s`", v.toChars());
3633 hasOverloads = false;
3634 }
3635 super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads);
3636 this.offset = offset;
3637 }
3638
3639 override Optional!bool toBool()
3640 {
3641 return typeof(return)(true);
3642 }
3643
accept(Visitor v)3644 override void accept(Visitor v)
3645 {
3646 v.visit(this);
3647 }
3648 }
3649
3650 /***********************************************************
3651 * Variable
3652 */
3653 extern (C++) final class VarExp : SymbolExp
3654 {
3655 bool delegateWasExtracted;
3656 extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true)
3657 {
3658 if (var.isVarDeclaration())
3659 hasOverloads = false;
3660
3661 super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads);
3662 //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars());
3663 //if (strcmp(var.ident.toChars(), "func") == 0) assert(0);
3664 this.type = var.type;
3665 }
3666
3667 static VarExp create(const ref Loc loc, Declaration var, bool hasOverloads = true)
3668 {
3669 return new VarExp(loc, var, hasOverloads);
3670 }
3671
equals(const RootObject o)3672 override bool equals(const RootObject o) const
3673 {
3674 if (this == o)
3675 return true;
3676 if (auto ne = o.isExpression().isVarExp())
3677 {
3678 if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && var == ne.var)
3679 {
3680 return true;
3681 }
3682 }
3683 return false;
3684 }
3685
isLvalue()3686 override bool isLvalue()
3687 {
3688 if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest))
3689 return false;
3690 return true;
3691 }
3692
toLvalue(Scope * sc,Expression e)3693 override Expression toLvalue(Scope* sc, Expression e)
3694 {
3695 if (var.storage_class & STC.manifest)
3696 {
3697 error("manifest constant `%s` cannot be modified", var.toChars());
3698 return ErrorExp.get();
3699 }
3700 if (var.storage_class & STC.lazy_ && !delegateWasExtracted)
3701 {
3702 error("lazy variable `%s` cannot be modified", var.toChars());
3703 return ErrorExp.get();
3704 }
3705 if (var.ident == Id.ctfe)
3706 {
3707 error("cannot modify compiler-generated variable `__ctfe`");
3708 return ErrorExp.get();
3709 }
3710 if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574
3711 {
3712 error("cannot modify operator `$`");
3713 return ErrorExp.get();
3714 }
3715 return this;
3716 }
3717
modifiableLvalue(Scope * sc,Expression e)3718 override Expression modifiableLvalue(Scope* sc, Expression e)
3719 {
3720 //printf("VarExp::modifiableLvalue('%s')\n", var.toChars());
3721 if (var.storage_class & STC.manifest)
3722 {
3723 error("cannot modify manifest constant `%s`", toChars());
3724 return ErrorExp.get();
3725 }
3726 // See if this expression is a modifiable lvalue (i.e. not const)
3727 return Expression.modifiableLvalue(sc, e);
3728 }
3729
accept(Visitor v)3730 override void accept(Visitor v)
3731 {
3732 v.visit(this);
3733 }
3734 }
3735
3736 /***********************************************************
3737 * Overload Set
3738 */
3739 extern (C++) final class OverExp : Expression
3740 {
3741 OverloadSet vars;
3742
this(const ref Loc loc,OverloadSet s)3743 extern (D) this(const ref Loc loc, OverloadSet s)
3744 {
3745 super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp));
3746 //printf("OverExp(this = %p, '%s')\n", this, var.toChars());
3747 vars = s;
3748 type = Type.tvoid;
3749 }
3750
isLvalue()3751 override bool isLvalue()
3752 {
3753 return true;
3754 }
3755
toLvalue(Scope * sc,Expression e)3756 override Expression toLvalue(Scope* sc, Expression e)
3757 {
3758 return this;
3759 }
3760
accept(Visitor v)3761 override void accept(Visitor v)
3762 {
3763 v.visit(this);
3764 }
3765 }
3766
3767 /***********************************************************
3768 * Function/Delegate literal
3769 */
3770
3771 extern (C++) final class FuncExp : Expression
3772 {
3773 FuncLiteralDeclaration fd;
3774 TemplateDeclaration td;
3775 TOK tok; // TOK.reserved, TOK.delegate_, TOK.function_
3776
this(const ref Loc loc,Dsymbol s)3777 extern (D) this(const ref Loc loc, Dsymbol s)
3778 {
3779 super(loc, EXP.function_, __traits(classInstanceSize, FuncExp));
3780 this.td = s.isTemplateDeclaration();
3781 this.fd = s.isFuncLiteralDeclaration();
3782 if (td)
3783 {
3784 assert(td.literal);
3785 assert(td.members && td.members.dim == 1);
3786 fd = (*td.members)[0].isFuncLiteralDeclaration();
3787 }
3788 tok = fd.tok; // save original kind of function/delegate/(infer)
3789 assert(fd.fbody);
3790 }
3791
equals(const RootObject o)3792 override bool equals(const RootObject o) const
3793 {
3794 if (this == o)
3795 return true;
3796 auto e = o.isExpression();
3797 if (!e)
3798 return false;
3799 if (auto fe = e.isFuncExp())
3800 {
3801 return fd == fe.fd;
3802 }
3803 return false;
3804 }
3805
genIdent(Scope * sc)3806 extern (D) void genIdent(Scope* sc)
3807 {
3808 if (fd.ident == Id.empty)
3809 {
3810 const(char)[] s;
3811 if (fd.fes)
3812 s = "__foreachbody";
3813 else if (fd.tok == TOK.reserved)
3814 s = "__lambda";
3815 else if (fd.tok == TOK.delegate_)
3816 s = "__dgliteral";
3817 else
3818 s = "__funcliteral";
3819
3820 DsymbolTable symtab;
3821 if (FuncDeclaration func = sc.parent.isFuncDeclaration())
3822 {
3823 if (func.localsymtab is null)
3824 {
3825 // Inside template constraint, symtab is not set yet.
3826 // Initialize it lazily.
3827 func.localsymtab = new DsymbolTable();
3828 }
3829 symtab = func.localsymtab;
3830 }
3831 else
3832 {
3833 ScopeDsymbol sds = sc.parent.isScopeDsymbol();
3834 if (!sds.symtab)
3835 {
3836 // Inside template constraint, symtab may not be set yet.
3837 // Initialize it lazily.
3838 assert(sds.isTemplateInstance());
3839 sds.symtab = new DsymbolTable();
3840 }
3841 symtab = sds.symtab;
3842 }
3843 assert(symtab);
3844 Identifier id = Identifier.generateId(s, symtab.length() + 1);
3845 fd.ident = id;
3846 if (td)
3847 td.ident = id;
3848 symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd);
3849 }
3850 }
3851
syntaxCopy()3852 override FuncExp syntaxCopy()
3853 {
3854 if (td)
3855 return new FuncExp(loc, td.syntaxCopy(null));
3856 else if (fd.semanticRun == PASS.initial)
3857 return new FuncExp(loc, fd.syntaxCopy(null));
3858 else // https://issues.dlang.org/show_bug.cgi?id=13481
3859 // Prevent multiple semantic analysis of lambda body.
3860 return new FuncExp(loc, fd);
3861 }
3862
3863 extern (D) MATCH matchType(Type to, Scope* sc, FuncExp* presult, int flag = 0)
3864 {
3865
cannotInfer(Expression e,Type to,int flag)3866 static MATCH cannotInfer(Expression e, Type to, int flag)
3867 {
3868 if (!flag)
3869 e.error("cannot infer parameter types from `%s`", to.toChars());
3870 return MATCH.nomatch;
3871 }
3872
3873 //printf("FuncExp::matchType('%s'), to=%s\n", type ? type.toChars() : "null", to.toChars());
3874 if (presult)
3875 *presult = null;
3876
3877 TypeFunction tof = null;
3878 if (to.ty == Tdelegate)
3879 {
3880 if (tok == TOK.function_)
3881 {
3882 if (!flag)
3883 error("cannot match function literal to delegate type `%s`", to.toChars());
3884 return MATCH.nomatch;
3885 }
3886 tof = cast(TypeFunction)to.nextOf();
3887 }
3888 else if (to.ty == Tpointer && (tof = to.nextOf().isTypeFunction()) !is null)
3889 {
3890 if (tok == TOK.delegate_)
3891 {
3892 if (!flag)
3893 error("cannot match delegate literal to function pointer type `%s`", to.toChars());
3894 return MATCH.nomatch;
3895 }
3896 }
3897
3898 if (td)
3899 {
3900 if (!tof)
3901 {
3902 return cannotInfer(this, to, flag);
3903 }
3904
3905 // Parameter types inference from 'tof'
3906 assert(td._scope);
3907 TypeFunction tf = fd.type.isTypeFunction();
3908 //printf("\ttof = %s\n", tof.toChars());
3909 //printf("\ttf = %s\n", tf.toChars());
3910 const dim = tf.parameterList.length;
3911
3912 if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
3913 return cannotInfer(this, to, flag);
3914
3915 auto tiargs = new Objects();
3916 tiargs.reserve(td.parameters.dim);
3917
3918 foreach (tp; *td.parameters)
3919 {
3920 size_t u = 0;
3921 foreach (i, p; tf.parameterList)
3922 {
3923 if (auto ti = p.type.isTypeIdentifier())
3924 if (ti && ti.ident == tp.ident)
3925 break;
3926
3927 ++u;
3928 }
3929 assert(u < dim);
3930 Parameter pto = tof.parameterList[u];
3931 Type t = pto.type;
3932 if (t.ty == Terror)
3933 return cannotInfer(this, to, flag);
3934 tiargs.push(t);
3935 }
3936
3937 // Set target of return type inference
3938 if (!tf.next && tof.next)
3939 fd.treq = to;
3940
3941 auto ti = new TemplateInstance(loc, td, tiargs);
3942 Expression ex = (new ScopeExp(loc, ti)).expressionSemantic(td._scope);
3943
3944 // Reset inference target for the later re-semantic
3945 fd.treq = null;
3946
3947 if (ex.op == EXP.error)
3948 return MATCH.nomatch;
3949 if (auto ef = ex.isFuncExp())
3950 return ef.matchType(to, sc, presult, flag);
3951 else
3952 return cannotInfer(this, to, flag);
3953 }
3954
3955 if (!tof || !tof.next)
3956 return MATCH.nomatch;
3957
3958 assert(type && type != Type.tvoid);
3959 if (fd.type.ty == Terror)
3960 return MATCH.nomatch;
3961 auto tfx = fd.type.isTypeFunction();
3962 bool convertMatch = (type.ty != to.ty);
3963
3964 if (fd.inferRetType && tfx.next.implicitConvTo(tof.next) == MATCH.convert)
3965 {
3966 /* If return type is inferred and covariant return,
3967 * tweak return statements to required return type.
3968 *
3969 * interface I {}
3970 * class C : Object, I{}
3971 *
3972 * I delegate() dg = delegate() { return new class C(); }
3973 */
3974 convertMatch = true;
3975
3976 auto tfy = new TypeFunction(tfx.parameterList, tof.next,
3977 tfx.linkage, STC.undefined_);
3978 tfy.mod = tfx.mod;
3979 tfy.trust = tfx.trust;
3980 tfy.isnothrow = tfx.isnothrow;
3981 tfy.isnogc = tfx.isnogc;
3982 tfy.purity = tfx.purity;
3983 tfy.isproperty = tfx.isproperty;
3984 tfy.isref = tfx.isref;
3985 tfy.isInOutParam = tfx.isInOutParam;
3986 tfy.isInOutQual = tfx.isInOutQual;
3987 tfy.deco = tfy.merge().deco;
3988
3989 tfx = tfy;
3990 }
3991 Type tx;
3992 if (tok == TOK.delegate_ ||
3993 tok == TOK.reserved && (type.ty == Tdelegate || type.ty == Tpointer && to.ty == Tdelegate))
3994 {
3995 // Allow conversion from implicit function pointer to delegate
3996 tx = new TypeDelegate(tfx);
3997 tx.deco = tx.merge().deco;
3998 }
3999 else
4000 {
4001 assert(tok == TOK.function_ || tok == TOK.reserved && type.ty == Tpointer || fd.errors);
4002 tx = tfx.pointerTo();
4003 }
4004 //printf("\ttx = %s, to = %s\n", tx.toChars(), to.toChars());
4005
4006 MATCH m = tx.implicitConvTo(to);
4007 if (m > MATCH.nomatch)
4008 {
4009 // MATCH.exact: exact type match
4010 // MATCH.constant: covairiant type match (eg. attributes difference)
4011 // MATCH.convert: context conversion
4012 m = convertMatch ? MATCH.convert : tx.equals(to) ? MATCH.exact : MATCH.constant;
4013
4014 if (presult)
4015 {
4016 (*presult) = cast(FuncExp)copy();
4017 (*presult).type = to;
4018
4019 // https://issues.dlang.org/show_bug.cgi?id=12508
4020 // Tweak function body for covariant returns.
4021 (*presult).fd.modifyReturns(sc, tof.next);
4022 }
4023 }
4024 else if (!flag)
4025 {
4026 auto ts = toAutoQualChars(tx, to);
4027 error("cannot implicitly convert expression `%s` of type `%s` to `%s`",
4028 toChars(), ts[0], ts[1]);
4029 }
4030 return m;
4031 }
4032
toChars()4033 override const(char)* toChars() const
4034 {
4035 return fd.toChars();
4036 }
4037
checkType()4038 override bool checkType()
4039 {
4040 if (td)
4041 {
4042 error("template lambda has no type");
4043 return true;
4044 }
4045 return false;
4046 }
4047
checkValue()4048 override bool checkValue()
4049 {
4050 if (td)
4051 {
4052 error("template lambda has no value");
4053 return true;
4054 }
4055 return false;
4056 }
4057
accept(Visitor v)4058 override void accept(Visitor v)
4059 {
4060 v.visit(this);
4061 }
4062 }
4063
4064 /***********************************************************
4065 * Declaration of a symbol
4066 *
4067 * D grammar allows declarations only as statements. However in AST representation
4068 * it can be part of any expression. This is used, for example, during internal
4069 * syntax re-writes to inject hidden symbols.
4070 */
4071 extern (C++) final class DeclarationExp : Expression
4072 {
4073 Dsymbol declaration;
4074
this(const ref Loc loc,Dsymbol declaration)4075 extern (D) this(const ref Loc loc, Dsymbol declaration)
4076 {
4077 super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp));
4078 this.declaration = declaration;
4079 }
4080
syntaxCopy()4081 override DeclarationExp syntaxCopy()
4082 {
4083 return new DeclarationExp(loc, declaration.syntaxCopy(null));
4084 }
4085
hasCode()4086 override bool hasCode()
4087 {
4088 if (auto vd = declaration.isVarDeclaration())
4089 {
4090 return !(vd.storage_class & (STC.manifest | STC.static_));
4091 }
4092 return false;
4093 }
4094
accept(Visitor v)4095 override void accept(Visitor v)
4096 {
4097 v.visit(this);
4098 }
4099 }
4100
4101 /***********************************************************
4102 * typeid(int)
4103 */
4104 extern (C++) final class TypeidExp : Expression
4105 {
4106 RootObject obj;
4107
this(const ref Loc loc,RootObject o)4108 extern (D) this(const ref Loc loc, RootObject o)
4109 {
4110 super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp));
4111 this.obj = o;
4112 }
4113
syntaxCopy()4114 override TypeidExp syntaxCopy()
4115 {
4116 return new TypeidExp(loc, objectSyntaxCopy(obj));
4117 }
4118
accept(Visitor v)4119 override void accept(Visitor v)
4120 {
4121 v.visit(this);
4122 }
4123 }
4124
4125 /***********************************************************
4126 * __traits(identifier, args...)
4127 */
4128 extern (C++) final class TraitsExp : Expression
4129 {
4130 Identifier ident;
4131 Objects* args;
4132
this(const ref Loc loc,Identifier ident,Objects * args)4133 extern (D) this(const ref Loc loc, Identifier ident, Objects* args)
4134 {
4135 super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp));
4136 this.ident = ident;
4137 this.args = args;
4138 }
4139
syntaxCopy()4140 override TraitsExp syntaxCopy()
4141 {
4142 return new TraitsExp(loc, ident, TemplateInstance.arraySyntaxCopy(args));
4143 }
4144
accept(Visitor v)4145 override void accept(Visitor v)
4146 {
4147 v.visit(this);
4148 }
4149 }
4150
4151 /***********************************************************
4152 * Generates a halt instruction
4153 *
4154 * `assert(0)` gets rewritten to this with `CHECKACTION.halt`
4155 */
4156 extern (C++) final class HaltExp : Expression
4157 {
this(const ref Loc loc)4158 extern (D) this(const ref Loc loc)
4159 {
4160 super(loc, EXP.halt, __traits(classInstanceSize, HaltExp));
4161 }
4162
accept(Visitor v)4163 override void accept(Visitor v)
4164 {
4165 v.visit(this);
4166 }
4167 }
4168
4169 /***********************************************************
4170 * is(targ id tok tspec)
4171 * is(targ id == tok2)
4172 */
4173 extern (C++) final class IsExp : Expression
4174 {
4175 Type targ;
4176 Identifier id; // can be null
4177 Type tspec; // can be null
4178 TemplateParameters* parameters;
4179 TOK tok; // ':' or '=='
4180 TOK tok2; // 'struct', 'union', etc.
4181
this(const ref Loc loc,Type targ,Identifier id,TOK tok,Type tspec,TOK tok2,TemplateParameters * parameters)4182 extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters)
4183 {
4184 super(loc, EXP.is_, __traits(classInstanceSize, IsExp));
4185 this.targ = targ;
4186 this.id = id;
4187 this.tok = tok;
4188 this.tspec = tspec;
4189 this.tok2 = tok2;
4190 this.parameters = parameters;
4191 }
4192
syntaxCopy()4193 override IsExp syntaxCopy()
4194 {
4195 // This section is identical to that in TemplateDeclaration::syntaxCopy()
4196 TemplateParameters* p = null;
4197 if (parameters)
4198 {
4199 p = new TemplateParameters(parameters.dim);
4200 foreach (i, el; *parameters)
4201 (*p)[i] = el.syntaxCopy();
4202 }
4203 return new IsExp(loc, targ.syntaxCopy(), id, tok, tspec ? tspec.syntaxCopy() : null, tok2, p);
4204 }
4205
accept(Visitor v)4206 override void accept(Visitor v)
4207 {
4208 v.visit(this);
4209 }
4210 }
4211
4212 /***********************************************************
4213 * Base class for unary operators
4214 *
4215 * https://dlang.org/spec/expression.html#unary-expression
4216 */
4217 extern (C++) abstract class UnaExp : Expression
4218 {
4219 Expression e1;
4220 Type att1; // Save alias this type to detect recursion
4221
this(const ref Loc loc,EXP op,int size,Expression e1)4222 extern (D) this(const ref Loc loc, EXP op, int size, Expression e1)
4223 {
4224 super(loc, op, size);
4225 this.e1 = e1;
4226 }
4227
syntaxCopy()4228 override UnaExp syntaxCopy()
4229 {
4230 UnaExp e = cast(UnaExp)copy();
4231 e.type = null;
4232 e.e1 = e.e1.syntaxCopy();
4233 return e;
4234 }
4235
4236 /********************************
4237 * The type for a unary expression is incompatible.
4238 * Print error message.
4239 * Returns:
4240 * ErrorExp
4241 */
incompatibleTypes()4242 final Expression incompatibleTypes()
4243 {
4244 if (e1.type.toBasetype() == Type.terror)
4245 return e1;
4246
4247 if (e1.op == EXP.type)
4248 {
4249 error("incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr);
4250 }
4251 else
4252 {
4253 error("incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars());
4254 }
4255 return ErrorExp.get();
4256 }
4257
4258 /*********************
4259 * Mark the operand as will never be dereferenced,
4260 * which is useful info for @safe checks.
4261 * Do before semantic() on operands rewrites them.
4262 */
setNoderefOperand()4263 final void setNoderefOperand()
4264 {
4265 if (auto edi = e1.isDotIdExp())
4266 edi.noderef = true;
4267
4268 }
4269
resolveLoc(const ref Loc loc,Scope * sc)4270 override final Expression resolveLoc(const ref Loc loc, Scope* sc)
4271 {
4272 e1 = e1.resolveLoc(loc, sc);
4273 return this;
4274 }
4275
accept(Visitor v)4276 override void accept(Visitor v)
4277 {
4278 v.visit(this);
4279 }
4280 }
4281
4282 alias fp_t = UnionExp function(const ref Loc loc, Type, Expression, Expression);
4283 alias fp2_t = bool function(const ref Loc loc, EXP, Expression, Expression);
4284
4285 /***********************************************************
4286 * Base class for binary operators
4287 */
4288 extern (C++) abstract class BinExp : Expression
4289 {
4290 Expression e1;
4291 Expression e2;
4292 Type att1; // Save alias this type to detect recursion
4293 Type att2; // Save alias this type to detect recursion
4294
this(const ref Loc loc,EXP op,int size,Expression e1,Expression e2)4295 extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2)
4296 {
4297 super(loc, op, size);
4298 this.e1 = e1;
4299 this.e2 = e2;
4300 }
4301
syntaxCopy()4302 override BinExp syntaxCopy()
4303 {
4304 BinExp e = cast(BinExp)copy();
4305 e.type = null;
4306 e.e1 = e.e1.syntaxCopy();
4307 e.e2 = e.e2.syntaxCopy();
4308 return e;
4309 }
4310
4311 /********************************
4312 * The types for a binary expression are incompatible.
4313 * Print error message.
4314 * Returns:
4315 * ErrorExp
4316 */
incompatibleTypes()4317 final Expression incompatibleTypes()
4318 {
4319 if (e1.type.toBasetype() == Type.terror)
4320 return e1;
4321 if (e2.type.toBasetype() == Type.terror)
4322 return e2;
4323
4324 // CondExp uses 'a ? b : c' but we're comparing 'b : c'
4325 const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr;
4326 if (e1.op == EXP.type || e2.op == EXP.type)
4327 {
4328 error("incompatible types for `(%s) %s (%s)`: cannot use `%s` with types",
4329 e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr);
4330 }
4331 else if (e1.type.equals(e2.type))
4332 {
4333 error("incompatible types for `(%s) %s (%s)`: both operands are of type `%s`",
4334 e1.toChars(), thisOp, e2.toChars(), e1.type.toChars());
4335 }
4336 else
4337 {
4338 auto ts = toAutoQualChars(e1.type, e2.type);
4339 error("incompatible types for `(%s) %s (%s)`: `%s` and `%s`",
4340 e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]);
4341 }
4342 return ErrorExp.get();
4343 }
4344
checkOpAssignTypes(Scope * sc)4345 extern (D) final Expression checkOpAssignTypes(Scope* sc)
4346 {
4347 // At that point t1 and t2 are the merged types. type is the original type of the lhs.
4348 Type t1 = e1.type;
4349 Type t2 = e2.type;
4350
4351 // T opAssign floating yields a floating. Prevent truncating conversions (float to int).
4352 // See issue 3841.
4353 // Should we also prevent double to float (type.isfloating() && type.size() < t2.size()) ?
4354 if (op == EXP.addAssign || op == EXP.minAssign ||
4355 op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign ||
4356 op == EXP.powAssign)
4357 {
4358 if ((type.isintegral() && t2.isfloating()))
4359 {
4360 warning("`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars());
4361 }
4362 }
4363
4364 // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
4365 if (op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign)
4366 {
4367 // Any multiplication by an imaginary or complex number yields a complex result.
4368 // r *= c, i*=c, r*=i, i*=i are all forbidden operations.
4369 const(char)* opstr = EXPtoString(op).ptr;
4370 if (t1.isreal() && t2.iscomplex())
4371 {
4372 error("`%s %s %s` is undefined. Did you mean `%s %s %s.re`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
4373 return ErrorExp.get();
4374 }
4375 else if (t1.isimaginary() && t2.iscomplex())
4376 {
4377 error("`%s %s %s` is undefined. Did you mean `%s %s %s.im`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars());
4378 return ErrorExp.get();
4379 }
4380 else if ((t1.isreal() || t1.isimaginary()) && t2.isimaginary())
4381 {
4382 error("`%s %s %s` is an undefined operation", t1.toChars(), opstr, t2.toChars());
4383 return ErrorExp.get();
4384 }
4385 }
4386
4387 // generate an error if this is a nonsensical += or -=, eg real += imaginary
4388 if (op == EXP.addAssign || op == EXP.minAssign)
4389 {
4390 // Addition or subtraction of a real and an imaginary is a complex result.
4391 // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
4392 if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex())))
4393 {
4394 error("`%s %s %s` is undefined (result is complex)", t1.toChars(), EXPtoString(op).ptr, t2.toChars());
4395 return ErrorExp.get();
4396 }
4397 if (type.isreal() || type.isimaginary())
4398 {
4399 assert(global.errors || t2.isfloating());
4400 e2 = e2.castTo(sc, t1);
4401 }
4402 }
4403 if (op == EXP.mulAssign)
4404 {
4405 if (t2.isfloating())
4406 {
4407 if (t1.isreal())
4408 {
4409 if (t2.isimaginary() || t2.iscomplex())
4410 {
4411 e2 = e2.castTo(sc, t1);
4412 }
4413 }
4414 else if (t1.isimaginary())
4415 {
4416 if (t2.isimaginary() || t2.iscomplex())
4417 {
4418 switch (t1.ty)
4419 {
4420 case Timaginary32:
4421 t2 = Type.tfloat32;
4422 break;
4423
4424 case Timaginary64:
4425 t2 = Type.tfloat64;
4426 break;
4427
4428 case Timaginary80:
4429 t2 = Type.tfloat80;
4430 break;
4431
4432 default:
4433 assert(0);
4434 }
4435 e2 = e2.castTo(sc, t2);
4436 }
4437 }
4438 }
4439 }
4440 else if (op == EXP.divAssign)
4441 {
4442 if (t2.isimaginary())
4443 {
4444 if (t1.isreal())
4445 {
4446 // x/iv = i(-x/v)
4447 // Therefore, the result is 0
4448 e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1));
4449 e2.type = t1;
4450 Expression e = new AssignExp(loc, e1, e2);
4451 e.type = t1;
4452 return e;
4453 }
4454 else if (t1.isimaginary())
4455 {
4456 Type t3;
4457 switch (t1.ty)
4458 {
4459 case Timaginary32:
4460 t3 = Type.tfloat32;
4461 break;
4462
4463 case Timaginary64:
4464 t3 = Type.tfloat64;
4465 break;
4466
4467 case Timaginary80:
4468 t3 = Type.tfloat80;
4469 break;
4470
4471 default:
4472 assert(0);
4473 }
4474 e2 = e2.castTo(sc, t3);
4475 Expression e = new AssignExp(loc, e1, e2);
4476 e.type = t1;
4477 return e;
4478 }
4479 }
4480 }
4481 else if (op == EXP.modAssign)
4482 {
4483 if (t2.iscomplex())
4484 {
4485 error("cannot perform modulo complex arithmetic");
4486 return ErrorExp.get();
4487 }
4488 }
4489 return this;
4490 }
4491
checkIntegralBin()4492 extern (D) final bool checkIntegralBin()
4493 {
4494 bool r1 = e1.checkIntegral();
4495 bool r2 = e2.checkIntegral();
4496 return (r1 || r2);
4497 }
4498
checkArithmeticBin()4499 extern (D) final bool checkArithmeticBin()
4500 {
4501 bool r1 = e1.checkArithmetic();
4502 bool r2 = e2.checkArithmetic();
4503 return (r1 || r2);
4504 }
4505
checkSharedAccessBin(Scope * sc)4506 extern (D) final bool checkSharedAccessBin(Scope* sc)
4507 {
4508 const r1 = e1.checkSharedAccess(sc);
4509 const r2 = e2.checkSharedAccess(sc);
4510 return (r1 || r2);
4511 }
4512
4513 /*********************
4514 * Mark the operands as will never be dereferenced,
4515 * which is useful info for @safe checks.
4516 * Do before semantic() on operands rewrites them.
4517 */
setNoderefOperands()4518 final void setNoderefOperands()
4519 {
4520 if (auto edi = e1.isDotIdExp())
4521 edi.noderef = true;
4522 if (auto edi = e2.isDotIdExp())
4523 edi.noderef = true;
4524
4525 }
4526
reorderSettingAAElem(Scope * sc)4527 final Expression reorderSettingAAElem(Scope* sc)
4528 {
4529 BinExp be = this;
4530
4531 auto ie = be.e1.isIndexExp();
4532 if (!ie)
4533 return be;
4534 if (ie.e1.type.toBasetype().ty != Taarray)
4535 return be;
4536
4537 /* Fix evaluation order of setting AA element
4538 * https://issues.dlang.org/show_bug.cgi?id=3825
4539 * Rewrite:
4540 * aa[k1][k2][k3] op= val;
4541 * as:
4542 * auto ref __aatmp = aa;
4543 * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
4544 * auto ref __aaval = val;
4545 * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment
4546 */
4547
4548 Expression e0;
4549 while (1)
4550 {
4551 Expression de;
4552 ie.e2 = extractSideEffect(sc, "__aakey", de, ie.e2);
4553 e0 = Expression.combine(de, e0);
4554
4555 auto ie1 = ie.e1.isIndexExp();
4556 if (!ie1 ||
4557 ie1.e1.type.toBasetype().ty != Taarray)
4558 {
4559 break;
4560 }
4561 ie = ie1;
4562 }
4563 assert(ie.e1.type.toBasetype().ty == Taarray);
4564
4565 Expression de;
4566 ie.e1 = extractSideEffect(sc, "__aatmp", de, ie.e1);
4567 e0 = Expression.combine(de, e0);
4568
4569 be.e2 = extractSideEffect(sc, "__aaval", e0, be.e2, true);
4570
4571 //printf("-e0 = %s, be = %s\n", e0.toChars(), be.toChars());
4572 return Expression.combine(e0, be);
4573 }
4574
accept(Visitor v)4575 override void accept(Visitor v)
4576 {
4577 v.visit(this);
4578 }
4579 }
4580
4581 /***********************************************************
4582 * Binary operator assignment, `+=` `-=` `*=` etc.
4583 */
4584 extern (C++) class BinAssignExp : BinExp
4585 {
this(const ref Loc loc,EXP op,int size,Expression e1,Expression e2)4586 extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2)
4587 {
4588 super(loc, op, size, e1, e2);
4589 }
4590
isLvalue()4591 override final bool isLvalue()
4592 {
4593 return true;
4594 }
4595
toLvalue(Scope * sc,Expression ex)4596 override final Expression toLvalue(Scope* sc, Expression ex)
4597 {
4598 // Lvalue-ness will be handled in glue layer.
4599 return this;
4600 }
4601
modifiableLvalue(Scope * sc,Expression e)4602 override final Expression modifiableLvalue(Scope* sc, Expression e)
4603 {
4604 // should check e1.checkModifiable() ?
4605 return toLvalue(sc, this);
4606 }
4607
accept(Visitor v)4608 override void accept(Visitor v)
4609 {
4610 v.visit(this);
4611 }
4612 }
4613
4614 /***********************************************************
4615 * A string mixin, `mixin("x")`
4616 *
4617 * https://dlang.org/spec/expression.html#mixin_expressions
4618 */
4619 extern (C++) final class MixinExp : Expression
4620 {
4621 Expressions* exps;
4622
this(const ref Loc loc,Expressions * exps)4623 extern (D) this(const ref Loc loc, Expressions* exps)
4624 {
4625 super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp));
4626 this.exps = exps;
4627 }
4628
syntaxCopy()4629 override MixinExp syntaxCopy()
4630 {
4631 return new MixinExp(loc, arraySyntaxCopy(exps));
4632 }
4633
equals(const RootObject o)4634 override bool equals(const RootObject o) const
4635 {
4636 if (this == o)
4637 return true;
4638 auto e = o.isExpression();
4639 if (!e)
4640 return false;
4641 if (auto ce = e.isMixinExp())
4642 {
4643 if (exps.dim != ce.exps.dim)
4644 return false;
4645 foreach (i, e1; *exps)
4646 {
4647 auto e2 = (*ce.exps)[i];
4648 if (e1 != e2 && (!e1 || !e2 || !e1.equals(e2)))
4649 return false;
4650 }
4651 return true;
4652 }
4653 return false;
4654 }
4655
accept(Visitor v)4656 override void accept(Visitor v)
4657 {
4658 v.visit(this);
4659 }
4660 }
4661
4662 /***********************************************************
4663 * An import expression, `import("file.txt")`
4664 *
4665 * Not to be confused with module imports, `import std.stdio`, which is an `ImportStatement`
4666 *
4667 * https://dlang.org/spec/expression.html#import_expressions
4668 */
4669 extern (C++) final class ImportExp : UnaExp
4670 {
this(const ref Loc loc,Expression e)4671 extern (D) this(const ref Loc loc, Expression e)
4672 {
4673 super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e);
4674 }
4675
accept(Visitor v)4676 override void accept(Visitor v)
4677 {
4678 v.visit(this);
4679 }
4680 }
4681
4682 /***********************************************************
4683 * An assert expression, `assert(x == y)`
4684 *
4685 * https://dlang.org/spec/expression.html#assert_expressions
4686 */
4687 extern (C++) final class AssertExp : UnaExp
4688 {
4689 Expression msg;
4690
4691 extern (D) this(const ref Loc loc, Expression e, Expression msg = null)
4692 {
4693 super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e);
4694 this.msg = msg;
4695 }
4696
syntaxCopy()4697 override AssertExp syntaxCopy()
4698 {
4699 return new AssertExp(loc, e1.syntaxCopy(), msg ? msg.syntaxCopy() : null);
4700 }
4701
accept(Visitor v)4702 override void accept(Visitor v)
4703 {
4704 v.visit(this);
4705 }
4706 }
4707
4708 /***********************************************************
4709 * `throw <e1>` as proposed by DIP 1034.
4710 *
4711 * Replacement for the deprecated `ThrowStatement` that can be nested
4712 * in other expression.
4713 */
4714 extern (C++) final class ThrowExp : UnaExp
4715 {
this(const ref Loc loc,Expression e)4716 extern (D) this(const ref Loc loc, Expression e)
4717 {
4718 super(loc, EXP.throw_, __traits(classInstanceSize, ThrowExp), e);
4719 this.type = Type.tnoreturn;
4720 }
4721
syntaxCopy()4722 override ThrowExp syntaxCopy()
4723 {
4724 return new ThrowExp(loc, e1.syntaxCopy());
4725 }
4726
accept(Visitor v)4727 override void accept(Visitor v)
4728 {
4729 v.visit(this);
4730 }
4731 }
4732
4733 /***********************************************************
4734 */
4735 extern (C++) final class DotIdExp : UnaExp
4736 {
4737 Identifier ident;
4738 bool noderef; // true if the result of the expression will never be dereferenced
4739 bool wantsym; // do not replace Symbol with its initializer during semantic()
4740 bool arrow; // ImportC: if -> instead of .
4741
this(const ref Loc loc,Expression e,Identifier ident)4742 extern (D) this(const ref Loc loc, Expression e, Identifier ident)
4743 {
4744 super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
4745 this.ident = ident;
4746 }
4747
create(const ref Loc loc,Expression e,Identifier ident)4748 static DotIdExp create(const ref Loc loc, Expression e, Identifier ident)
4749 {
4750 return new DotIdExp(loc, e, ident);
4751 }
4752
accept(Visitor v)4753 override void accept(Visitor v)
4754 {
4755 v.visit(this);
4756 }
4757 }
4758
4759 /***********************************************************
4760 * Mainly just a placeholder
4761 */
4762 extern (C++) final class DotTemplateExp : UnaExp
4763 {
4764 TemplateDeclaration td;
4765
this(const ref Loc loc,Expression e,TemplateDeclaration td)4766 extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td)
4767 {
4768 super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e);
4769 this.td = td;
4770 }
4771
checkType()4772 override bool checkType()
4773 {
4774 error("%s `%s` has no type", td.kind(), toChars());
4775 return true;
4776 }
4777
checkValue()4778 override bool checkValue()
4779 {
4780 error("%s `%s` has no value", td.kind(), toChars());
4781 return true;
4782 }
4783
accept(Visitor v)4784 override void accept(Visitor v)
4785 {
4786 v.visit(this);
4787 }
4788 }
4789
4790 /***********************************************************
4791 */
4792 extern (C++) final class DotVarExp : UnaExp
4793 {
4794 Declaration var;
4795 bool hasOverloads;
4796
4797 extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true)
4798 {
4799 if (var.isVarDeclaration())
4800 hasOverloads = false;
4801
4802 super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e);
4803 //printf("DotVarExp()\n");
4804 this.var = var;
4805 this.hasOverloads = hasOverloads;
4806 }
4807
isLvalue()4808 override bool isLvalue()
4809 {
4810 if (e1.op != EXP.structLiteral)
4811 return true;
4812 auto vd = var.isVarDeclaration();
4813 return !(vd && vd.isField());
4814 }
4815
toLvalue(Scope * sc,Expression e)4816 override Expression toLvalue(Scope* sc, Expression e)
4817 {
4818 //printf("DotVarExp::toLvalue(%s)\n", toChars());
4819 if (sc && sc.flags & SCOPE.Cfile)
4820 {
4821 /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator
4822 * is an lvalue if the first expression is an lvalue.
4823 */
4824 if (!e1.isLvalue())
4825 return Expression.toLvalue(sc, e);
4826 }
4827 if (!isLvalue())
4828 return Expression.toLvalue(sc, e);
4829 if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor))
4830 {
4831 if (VarDeclaration vd = var.isVarDeclaration())
4832 {
4833 auto ad = vd.isMember2();
4834 if (ad && ad.fields.dim == sc.ctorflow.fieldinit.length)
4835 {
4836 foreach (i, f; ad.fields)
4837 {
4838 if (f == vd)
4839 {
4840 if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor))
4841 {
4842 /* If the address of vd is taken, assume it is thereby initialized
4843 * https://issues.dlang.org/show_bug.cgi?id=15869
4844 */
4845 modifyFieldVar(loc, sc, vd, e1);
4846 }
4847 break;
4848 }
4849 }
4850 }
4851 }
4852 }
4853 return this;
4854 }
4855
modifiableLvalue(Scope * sc,Expression e)4856 override Expression modifiableLvalue(Scope* sc, Expression e)
4857 {
4858 version (none)
4859 {
4860 printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
4861 printf("e1.type = %s\n", e1.type.toChars());
4862 printf("var.type = %s\n", var.type.toChars());
4863 }
4864
4865 return Expression.modifiableLvalue(sc, e);
4866 }
4867
accept(Visitor v)4868 override void accept(Visitor v)
4869 {
4870 v.visit(this);
4871 }
4872 }
4873
4874 /***********************************************************
4875 * foo.bar!(args)
4876 */
4877 extern (C++) final class DotTemplateInstanceExp : UnaExp
4878 {
4879 TemplateInstance ti;
4880
this(const ref Loc loc,Expression e,Identifier name,Objects * tiargs)4881 extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs)
4882 {
4883 super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
4884 //printf("DotTemplateInstanceExp()\n");
4885 this.ti = new TemplateInstance(loc, name, tiargs);
4886 }
4887
this(const ref Loc loc,Expression e,TemplateInstance ti)4888 extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti)
4889 {
4890 super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e);
4891 this.ti = ti;
4892 }
4893
syntaxCopy()4894 override DotTemplateInstanceExp syntaxCopy()
4895 {
4896 return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs));
4897 }
4898
findTempDecl(Scope * sc)4899 bool findTempDecl(Scope* sc)
4900 {
4901 static if (LOGSEMANTIC)
4902 {
4903 printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars());
4904 }
4905 if (ti.tempdecl)
4906 return true;
4907
4908 Expression e = new DotIdExp(loc, e1, ti.name);
4909 e = e.expressionSemantic(sc);
4910 if (e.op == EXP.dot)
4911 e = (cast(DotExp)e).e2;
4912
4913 Dsymbol s = null;
4914 switch (e.op)
4915 {
4916 case EXP.overloadSet:
4917 s = (cast(OverExp)e).vars;
4918 break;
4919
4920 case EXP.dotTemplateDeclaration:
4921 s = (cast(DotTemplateExp)e).td;
4922 break;
4923
4924 case EXP.scope_:
4925 s = (cast(ScopeExp)e).sds;
4926 break;
4927
4928 case EXP.dotVariable:
4929 s = (cast(DotVarExp)e).var;
4930 break;
4931
4932 case EXP.variable:
4933 s = (cast(VarExp)e).var;
4934 break;
4935
4936 default:
4937 return false;
4938 }
4939 return ti.updateTempDecl(sc, s);
4940 }
4941
checkType()4942 override bool checkType()
4943 {
4944 // Same logic as ScopeExp.checkType()
4945 if (ti.tempdecl &&
4946 ti.semantictiargsdone &&
4947 ti.semanticRun == PASS.initial)
4948 {
4949 error("partial %s `%s` has no type", ti.kind(), toChars());
4950 return true;
4951 }
4952 return false;
4953 }
4954
checkValue()4955 override bool checkValue()
4956 {
4957 if (ti.tempdecl &&
4958 ti.semantictiargsdone &&
4959 ti.semanticRun == PASS.initial)
4960
4961 error("partial %s `%s` has no value", ti.kind(), toChars());
4962 else
4963 error("%s `%s` has no value", ti.kind(), ti.toChars());
4964 return true;
4965 }
4966
accept(Visitor v)4967 override void accept(Visitor v)
4968 {
4969 v.visit(this);
4970 }
4971 }
4972
4973 /***********************************************************
4974 */
4975 extern (C++) final class DelegateExp : UnaExp
4976 {
4977 FuncDeclaration func;
4978 bool hasOverloads;
4979 VarDeclaration vthis2; // container for multi-context
4980
4981 extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null)
4982 {
4983 super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e);
4984 this.func = f;
4985 this.hasOverloads = hasOverloads;
4986 this.vthis2 = vthis2;
4987 }
4988
accept(Visitor v)4989 override void accept(Visitor v)
4990 {
4991 v.visit(this);
4992 }
4993 }
4994
4995 /***********************************************************
4996 */
4997 extern (C++) final class DotTypeExp : UnaExp
4998 {
4999 Dsymbol sym; // symbol that represents a type
5000
this(const ref Loc loc,Expression e,Dsymbol s)5001 extern (D) this(const ref Loc loc, Expression e, Dsymbol s)
5002 {
5003 super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e);
5004 this.sym = s;
5005 }
5006
accept(Visitor v)5007 override void accept(Visitor v)
5008 {
5009 v.visit(this);
5010 }
5011 }
5012
5013 /***********************************************************
5014 */
5015 extern (C++) final class CallExp : UnaExp
5016 {
5017 Expressions* arguments; // function arguments
5018 FuncDeclaration f; // symbol to call
5019 bool directcall; // true if a virtual call is devirtualized
5020 bool inDebugStatement; /// true if this was in a debug statement
5021 bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
5022 VarDeclaration vthis2; // container for multi-context
5023
this(const ref Loc loc,Expression e,Expressions * exps)5024 extern (D) this(const ref Loc loc, Expression e, Expressions* exps)
5025 {
5026 super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
5027 this.arguments = exps;
5028 }
5029
this(const ref Loc loc,Expression e)5030 extern (D) this(const ref Loc loc, Expression e)
5031 {
5032 super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
5033 }
5034
this(const ref Loc loc,Expression e,Expression earg1)5035 extern (D) this(const ref Loc loc, Expression e, Expression earg1)
5036 {
5037 super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
5038 this.arguments = new Expressions();
5039 if (earg1)
5040 this.arguments.push(earg1);
5041 }
5042
this(const ref Loc loc,Expression e,Expression earg1,Expression earg2)5043 extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2)
5044 {
5045 super(loc, EXP.call, __traits(classInstanceSize, CallExp), e);
5046 auto arguments = new Expressions(2);
5047 (*arguments)[0] = earg1;
5048 (*arguments)[1] = earg2;
5049 this.arguments = arguments;
5050 }
5051
5052 /***********************************************************
5053 * Instatiates a new function call expression
5054 * Params:
5055 * loc = location
5056 * fd = the declaration of the function to call
5057 * earg1 = the function argument
5058 */
this(const ref Loc loc,FuncDeclaration fd,Expression earg1)5059 extern(D) this(const ref Loc loc, FuncDeclaration fd, Expression earg1)
5060 {
5061 this(loc, new VarExp(loc, fd, false), earg1);
5062 this.f = fd;
5063 }
5064
create(const ref Loc loc,Expression e,Expressions * exps)5065 static CallExp create(const ref Loc loc, Expression e, Expressions* exps)
5066 {
5067 return new CallExp(loc, e, exps);
5068 }
5069
create(const ref Loc loc,Expression e)5070 static CallExp create(const ref Loc loc, Expression e)
5071 {
5072 return new CallExp(loc, e);
5073 }
5074
create(const ref Loc loc,Expression e,Expression earg1)5075 static CallExp create(const ref Loc loc, Expression e, Expression earg1)
5076 {
5077 return new CallExp(loc, e, earg1);
5078 }
5079
5080 /***********************************************************
5081 * Creates a new function call expression
5082 * Params:
5083 * loc = location
5084 * fd = the declaration of the function to call
5085 * earg1 = the function argument
5086 */
create(const ref Loc loc,FuncDeclaration fd,Expression earg1)5087 static CallExp create(const ref Loc loc, FuncDeclaration fd, Expression earg1)
5088 {
5089 return new CallExp(loc, fd, earg1);
5090 }
5091
syntaxCopy()5092 override CallExp syntaxCopy()
5093 {
5094 return new CallExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
5095 }
5096
isLvalue()5097 override bool isLvalue()
5098 {
5099 Type tb = e1.type.toBasetype();
5100 if (tb.ty == Tdelegate || tb.ty == Tpointer)
5101 tb = tb.nextOf();
5102 auto tf = tb.isTypeFunction();
5103 if (tf && tf.isref)
5104 {
5105 if (auto dve = e1.isDotVarExp())
5106 if (dve.var.isCtorDeclaration())
5107 return false;
5108 return true; // function returns a reference
5109 }
5110 return false;
5111 }
5112
toLvalue(Scope * sc,Expression e)5113 override Expression toLvalue(Scope* sc, Expression e)
5114 {
5115 if (isLvalue())
5116 return this;
5117 return Expression.toLvalue(sc, e);
5118 }
5119
addDtorHook(Scope * sc)5120 override Expression addDtorHook(Scope* sc)
5121 {
5122 /* Only need to add dtor hook if it's a type that needs destruction.
5123 * Use same logic as VarDeclaration::callScopeDtor()
5124 */
5125
5126 if (auto tf = e1.type.isTypeFunction())
5127 {
5128 if (tf.isref)
5129 return this;
5130 }
5131
5132 Type tv = type.baseElemOf();
5133 if (auto ts = tv.isTypeStruct())
5134 {
5135 StructDeclaration sd = ts.sym;
5136 if (sd.dtor)
5137 {
5138 /* Type needs destruction, so declare a tmp
5139 * which the back end will recognize and call dtor on
5140 */
5141 auto tmp = copyToTemp(0, "__tmpfordtor", this);
5142 auto de = new DeclarationExp(loc, tmp);
5143 auto ve = new VarExp(loc, tmp);
5144 Expression e = new CommaExp(loc, de, ve);
5145 e = e.expressionSemantic(sc);
5146 return e;
5147 }
5148 }
5149 return this;
5150 }
5151
accept(Visitor v)5152 override void accept(Visitor v)
5153 {
5154 v.visit(this);
5155 }
5156 }
5157
5158 FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null)
5159 {
5160 if (auto ae = e.isAddrExp())
5161 {
5162 auto ae1 = ae.e1;
5163 if (auto ve = ae1.isVarExp())
5164 {
5165 if (hasOverloads)
5166 *hasOverloads = ve.hasOverloads;
5167 return ve.var.isFuncDeclaration();
5168 }
5169 if (auto dve = ae1.isDotVarExp())
5170 {
5171 if (hasOverloads)
5172 *hasOverloads = dve.hasOverloads;
5173 return dve.var.isFuncDeclaration();
5174 }
5175 }
5176 else
5177 {
5178 if (auto soe = e.isSymOffExp())
5179 {
5180 if (hasOverloads)
5181 *hasOverloads = soe.hasOverloads;
5182 return soe.var.isFuncDeclaration();
5183 }
5184 if (auto dge = e.isDelegateExp())
5185 {
5186 if (hasOverloads)
5187 *hasOverloads = dge.hasOverloads;
5188 return dge.func.isFuncDeclaration();
5189 }
5190 }
5191 return null;
5192 }
5193
5194 /***********************************************************
5195 * The 'address of' operator, `&p`
5196 */
5197 extern (C++) final class AddrExp : UnaExp
5198 {
this(const ref Loc loc,Expression e)5199 extern (D) this(const ref Loc loc, Expression e)
5200 {
5201 super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e);
5202 }
5203
this(const ref Loc loc,Expression e,Type t)5204 extern (D) this(const ref Loc loc, Expression e, Type t)
5205 {
5206 this(loc, e);
5207 type = t;
5208 }
5209
accept(Visitor v)5210 override void accept(Visitor v)
5211 {
5212 v.visit(this);
5213 }
5214 }
5215
5216 /***********************************************************
5217 * The pointer dereference operator, `*p`
5218 */
5219 extern (C++) final class PtrExp : UnaExp
5220 {
this(const ref Loc loc,Expression e)5221 extern (D) this(const ref Loc loc, Expression e)
5222 {
5223 super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
5224 //if (e.type)
5225 // type = ((TypePointer *)e.type).next;
5226 }
5227
this(const ref Loc loc,Expression e,Type t)5228 extern (D) this(const ref Loc loc, Expression e, Type t)
5229 {
5230 super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e);
5231 type = t;
5232 }
5233
isLvalue()5234 override bool isLvalue()
5235 {
5236 return true;
5237 }
5238
toLvalue(Scope * sc,Expression e)5239 override Expression toLvalue(Scope* sc, Expression e)
5240 {
5241 return this;
5242 }
5243
modifiableLvalue(Scope * sc,Expression e)5244 override Expression modifiableLvalue(Scope* sc, Expression e)
5245 {
5246 //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
5247 Declaration var;
5248 if (auto se = e1.isSymOffExp())
5249 var = se.var;
5250 else if (auto ve = e1.isVarExp())
5251 var = ve.var;
5252 if (var && var.type.isFunction_Delegate_PtrToFunction())
5253 {
5254 if (var.type.isTypeFunction())
5255 error("function `%s` is not an lvalue and cannot be modified", var.toChars());
5256 else
5257 error("function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
5258 return ErrorExp.get();
5259 }
5260 return Expression.modifiableLvalue(sc, e);
5261 }
5262
accept(Visitor v)5263 override void accept(Visitor v)
5264 {
5265 v.visit(this);
5266 }
5267 }
5268
5269 /***********************************************************
5270 * The negation operator, `-x`
5271 */
5272 extern (C++) final class NegExp : UnaExp
5273 {
this(const ref Loc loc,Expression e)5274 extern (D) this(const ref Loc loc, Expression e)
5275 {
5276 super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e);
5277 }
5278
accept(Visitor v)5279 override void accept(Visitor v)
5280 {
5281 v.visit(this);
5282 }
5283 }
5284
5285 /***********************************************************
5286 * The unary add operator, `+x`
5287 */
5288 extern (C++) final class UAddExp : UnaExp
5289 {
this(const ref Loc loc,Expression e)5290 extern (D) this(const ref Loc loc, Expression e)
5291 {
5292 super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e);
5293 }
5294
accept(Visitor v)5295 override void accept(Visitor v)
5296 {
5297 v.visit(this);
5298 }
5299 }
5300
5301 /***********************************************************
5302 * The bitwise complement operator, `~x`
5303 */
5304 extern (C++) final class ComExp : UnaExp
5305 {
this(const ref Loc loc,Expression e)5306 extern (D) this(const ref Loc loc, Expression e)
5307 {
5308 super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e);
5309 }
5310
accept(Visitor v)5311 override void accept(Visitor v)
5312 {
5313 v.visit(this);
5314 }
5315 }
5316
5317 /***********************************************************
5318 * The logical not operator, `!x`
5319 */
5320 extern (C++) final class NotExp : UnaExp
5321 {
this(const ref Loc loc,Expression e)5322 extern (D) this(const ref Loc loc, Expression e)
5323 {
5324 super(loc, EXP.not, __traits(classInstanceSize, NotExp), e);
5325 }
5326
accept(Visitor v)5327 override void accept(Visitor v)
5328 {
5329 v.visit(this);
5330 }
5331 }
5332
5333 /***********************************************************
5334 * The delete operator, `delete x` (deprecated)
5335 *
5336 * https://dlang.org/spec/expression.html#delete_expressions
5337 */
5338 extern (C++) final class DeleteExp : UnaExp
5339 {
5340 bool isRAII; // true if called automatically as a result of scoped destruction
5341
this(const ref Loc loc,Expression e,bool isRAII)5342 extern (D) this(const ref Loc loc, Expression e, bool isRAII)
5343 {
5344 super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e);
5345 this.isRAII = isRAII;
5346 }
5347
accept(Visitor v)5348 override void accept(Visitor v)
5349 {
5350 v.visit(this);
5351 }
5352 }
5353
5354 /***********************************************************
5355 * The type cast operator, `cast(T) x`
5356 *
5357 * It's possible to cast to one type while painting to another type
5358 *
5359 * https://dlang.org/spec/expression.html#cast_expressions
5360 */
5361 extern (C++) final class CastExp : UnaExp
5362 {
5363 Type to; // type to cast to
5364 ubyte mod = cast(ubyte)~0; // MODxxxxx
5365
this(const ref Loc loc,Expression e,Type t)5366 extern (D) this(const ref Loc loc, Expression e, Type t)
5367 {
5368 super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
5369 this.to = t;
5370 }
5371
5372 /* For cast(const) and cast(immutable)
5373 */
this(const ref Loc loc,Expression e,ubyte mod)5374 extern (D) this(const ref Loc loc, Expression e, ubyte mod)
5375 {
5376 super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e);
5377 this.mod = mod;
5378 }
5379
syntaxCopy()5380 override CastExp syntaxCopy()
5381 {
5382 return to ? new CastExp(loc, e1.syntaxCopy(), to.syntaxCopy()) : new CastExp(loc, e1.syntaxCopy(), mod);
5383 }
5384
isLvalue()5385 override bool isLvalue()
5386 {
5387 //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars());
5388 if (!e1.isLvalue())
5389 return false;
5390 return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) ||
5391 e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf());
5392 }
5393
toLvalue(Scope * sc,Expression e)5394 override Expression toLvalue(Scope* sc, Expression e)
5395 {
5396 if (sc && sc.flags & SCOPE.Cfile)
5397 {
5398 /* C11 6.5.4-5: A cast does not yield an lvalue.
5399 */
5400 return Expression.toLvalue(sc, e);
5401 }
5402 if (isLvalue())
5403 return this;
5404 return Expression.toLvalue(sc, e);
5405 }
5406
addDtorHook(Scope * sc)5407 override Expression addDtorHook(Scope* sc)
5408 {
5409 if (to.toBasetype().ty == Tvoid) // look past the cast(void)
5410 e1 = e1.addDtorHook(sc);
5411 return this;
5412 }
5413
accept(Visitor v)5414 override void accept(Visitor v)
5415 {
5416 v.visit(this);
5417 }
5418 }
5419
5420 /***********************************************************
5421 */
5422 extern (C++) final class VectorExp : UnaExp
5423 {
5424 TypeVector to; // the target vector type before semantic()
5425 uint dim = ~0; // number of elements in the vector
5426 OwnedBy ownedByCtfe = OwnedBy.code;
5427
this(const ref Loc loc,Expression e,Type t)5428 extern (D) this(const ref Loc loc, Expression e, Type t)
5429 {
5430 super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e);
5431 assert(t.ty == Tvector);
5432 to = cast(TypeVector)t;
5433 }
5434
create(const ref Loc loc,Expression e,Type t)5435 static VectorExp create(const ref Loc loc, Expression e, Type t)
5436 {
5437 return new VectorExp(loc, e, t);
5438 }
5439
5440 // Same as create, but doesn't allocate memory.
emplace(UnionExp * pue,const ref Loc loc,Expression e,Type type)5441 static void emplace(UnionExp* pue, const ref Loc loc, Expression e, Type type)
5442 {
5443 emplaceExp!(VectorExp)(pue, loc, e, type);
5444 }
5445
syntaxCopy()5446 override VectorExp syntaxCopy()
5447 {
5448 return new VectorExp(loc, e1.syntaxCopy(), to.syntaxCopy());
5449 }
5450
accept(Visitor v)5451 override void accept(Visitor v)
5452 {
5453 v.visit(this);
5454 }
5455 }
5456
5457 /***********************************************************
5458 * e1.array property for vectors.
5459 *
5460 * https://dlang.org/spec/simd.html#properties
5461 */
5462 extern (C++) final class VectorArrayExp : UnaExp
5463 {
this(const ref Loc loc,Expression e1)5464 extern (D) this(const ref Loc loc, Expression e1)
5465 {
5466 super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1);
5467 }
5468
isLvalue()5469 override bool isLvalue()
5470 {
5471 return e1.isLvalue();
5472 }
5473
toLvalue(Scope * sc,Expression e)5474 override Expression toLvalue(Scope* sc, Expression e)
5475 {
5476 e1 = e1.toLvalue(sc, e);
5477 return this;
5478 }
5479
accept(Visitor v)5480 override void accept(Visitor v)
5481 {
5482 v.visit(this);
5483 }
5484 }
5485
5486 /***********************************************************
5487 * e1 [lwr .. upr]
5488 *
5489 * https://dlang.org/spec/expression.html#slice_expressions
5490 */
5491 extern (C++) final class SliceExp : UnaExp
5492 {
5493 Expression upr; // null if implicit 0
5494 Expression lwr; // null if implicit [length - 1]
5495
5496 VarDeclaration lengthVar;
5497 bool upperIsInBounds; // true if upr <= e1.length
5498 bool lowerIsLessThanUpper; // true if lwr <= upr
5499 bool arrayop; // an array operation, rather than a slice
5500
5501 /************************************************************/
this(const ref Loc loc,Expression e1,IntervalExp ie)5502 extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie)
5503 {
5504 super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
5505 this.upr = ie ? ie.upr : null;
5506 this.lwr = ie ? ie.lwr : null;
5507 }
5508
this(const ref Loc loc,Expression e1,Expression lwr,Expression upr)5509 extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr)
5510 {
5511 super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1);
5512 this.upr = upr;
5513 this.lwr = lwr;
5514 }
5515
syntaxCopy()5516 override SliceExp syntaxCopy()
5517 {
5518 auto se = new SliceExp(loc, e1.syntaxCopy(), lwr ? lwr.syntaxCopy() : null, upr ? upr.syntaxCopy() : null);
5519 se.lengthVar = this.lengthVar; // bug7871
5520 return se;
5521 }
5522
isLvalue()5523 override bool isLvalue()
5524 {
5525 /* slice expression is rvalue in default, but
5526 * conversion to reference of static array is only allowed.
5527 */
5528 return (type && type.toBasetype().ty == Tsarray);
5529 }
5530
toLvalue(Scope * sc,Expression e)5531 override Expression toLvalue(Scope* sc, Expression e)
5532 {
5533 //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
5534 return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e);
5535 }
5536
modifiableLvalue(Scope * sc,Expression e)5537 override Expression modifiableLvalue(Scope* sc, Expression e)
5538 {
5539 error("slice expression `%s` is not a modifiable lvalue", toChars());
5540 return this;
5541 }
5542
5543 override Optional!bool toBool()
5544 {
5545 return e1.toBool();
5546 }
5547
accept(Visitor v)5548 override void accept(Visitor v)
5549 {
5550 v.visit(this);
5551 }
5552 }
5553
5554 /***********************************************************
5555 * The `.length` property of an array
5556 */
5557 extern (C++) final class ArrayLengthExp : UnaExp
5558 {
this(const ref Loc loc,Expression e1)5559 extern (D) this(const ref Loc loc, Expression e1)
5560 {
5561 super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1);
5562 }
5563
accept(Visitor v)5564 override void accept(Visitor v)
5565 {
5566 v.visit(this);
5567 }
5568 }
5569
5570 /***********************************************************
5571 * e1 [ a0, a1, a2, a3 ,... ]
5572 *
5573 * https://dlang.org/spec/expression.html#index_expressions
5574 */
5575 extern (C++) final class ArrayExp : UnaExp
5576 {
5577 Expressions* arguments; // Array of Expression's a0..an
5578
5579 size_t currentDimension; // for opDollar
5580 VarDeclaration lengthVar;
5581
5582 extern (D) this(const ref Loc loc, Expression e1, Expression index = null)
5583 {
5584 super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
5585 arguments = new Expressions();
5586 if (index)
5587 arguments.push(index);
5588 }
5589
this(const ref Loc loc,Expression e1,Expressions * args)5590 extern (D) this(const ref Loc loc, Expression e1, Expressions* args)
5591 {
5592 super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1);
5593 arguments = args;
5594 }
5595
syntaxCopy()5596 override ArrayExp syntaxCopy()
5597 {
5598 auto ae = new ArrayExp(loc, e1.syntaxCopy(), arraySyntaxCopy(arguments));
5599 ae.lengthVar = this.lengthVar; // bug7871
5600 return ae;
5601 }
5602
isLvalue()5603 override bool isLvalue()
5604 {
5605 if (type && type.toBasetype().ty == Tvoid)
5606 return false;
5607 return true;
5608 }
5609
toLvalue(Scope * sc,Expression e)5610 override Expression toLvalue(Scope* sc, Expression e)
5611 {
5612 if (type && type.toBasetype().ty == Tvoid)
5613 error("`void`s have no value");
5614 return this;
5615 }
5616
accept(Visitor v)5617 override void accept(Visitor v)
5618 {
5619 v.visit(this);
5620 }
5621 }
5622
5623 /***********************************************************
5624 */
5625 extern (C++) final class DotExp : BinExp
5626 {
this(const ref Loc loc,Expression e1,Expression e2)5627 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
5628 {
5629 super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2);
5630 }
5631
accept(Visitor v)5632 override void accept(Visitor v)
5633 {
5634 v.visit(this);
5635 }
5636 }
5637
5638 /***********************************************************
5639 */
5640 extern (C++) final class CommaExp : BinExp
5641 {
5642 /// This is needed because AssignExp rewrites CommaExp, hence it needs
5643 /// to trigger the deprecation.
5644 const bool isGenerated;
5645
5646 /// Temporary variable to enable / disable deprecation of comma expression
5647 /// depending on the context.
5648 /// Since most constructor calls are rewritting, the only place where
5649 /// false will be passed will be from the parser.
5650 bool allowCommaExp;
5651
5652
5653 extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true)
5654 {
5655 super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2);
5656 allowCommaExp = isGenerated = generated;
5657 }
5658
isLvalue()5659 override bool isLvalue()
5660 {
5661 return e2.isLvalue();
5662 }
5663
toLvalue(Scope * sc,Expression e)5664 override Expression toLvalue(Scope* sc, Expression e)
5665 {
5666 e2 = e2.toLvalue(sc, null);
5667 return this;
5668 }
5669
modifiableLvalue(Scope * sc,Expression e)5670 override Expression modifiableLvalue(Scope* sc, Expression e)
5671 {
5672 e2 = e2.modifiableLvalue(sc, e);
5673 return this;
5674 }
5675
5676 override Optional!bool toBool()
5677 {
5678 return e2.toBool();
5679 }
5680
addDtorHook(Scope * sc)5681 override Expression addDtorHook(Scope* sc)
5682 {
5683 e2 = e2.addDtorHook(sc);
5684 return this;
5685 }
5686
accept(Visitor v)5687 override void accept(Visitor v)
5688 {
5689 v.visit(this);
5690 }
5691
5692 /**
5693 * If the argument is a CommaExp, set a flag to prevent deprecation messages
5694 *
5695 * It's impossible to know from CommaExp.semantic if the result will
5696 * be used, hence when there is a result (type != void), a deprecation
5697 * message is always emitted.
5698 * However, some construct can produce a result but won't use it
5699 * (ExpStatement and for loop increment). Those should call this function
5700 * to prevent unwanted deprecations to be emitted.
5701 *
5702 * Params:
5703 * exp = An expression that discards its result.
5704 * If the argument is null or not a CommaExp, nothing happens.
5705 */
allow(Expression exp)5706 static void allow(Expression exp)
5707 {
5708 if (exp)
5709 if (auto ce = exp.isCommaExp())
5710 ce.allowCommaExp = true;
5711 }
5712 }
5713
5714 /***********************************************************
5715 * Mainly just a placeholder
5716 */
5717 extern (C++) final class IntervalExp : Expression
5718 {
5719 Expression lwr;
5720 Expression upr;
5721
this(const ref Loc loc,Expression lwr,Expression upr)5722 extern (D) this(const ref Loc loc, Expression lwr, Expression upr)
5723 {
5724 super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp));
5725 this.lwr = lwr;
5726 this.upr = upr;
5727 }
5728
syntaxCopy()5729 override Expression syntaxCopy()
5730 {
5731 return new IntervalExp(loc, lwr.syntaxCopy(), upr.syntaxCopy());
5732 }
5733
accept(Visitor v)5734 override void accept(Visitor v)
5735 {
5736 v.visit(this);
5737 }
5738 }
5739
5740 /***********************************************************
5741 * The `dg.ptr` property, pointing to the delegate's 'context'
5742 *
5743 * c.f.`DelegateFuncptrExp` for the delegate's function pointer `dg.funcptr`
5744 */
5745 extern (C++) final class DelegatePtrExp : UnaExp
5746 {
this(const ref Loc loc,Expression e1)5747 extern (D) this(const ref Loc loc, Expression e1)
5748 {
5749 super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1);
5750 }
5751
isLvalue()5752 override bool isLvalue()
5753 {
5754 return e1.isLvalue();
5755 }
5756
toLvalue(Scope * sc,Expression e)5757 override Expression toLvalue(Scope* sc, Expression e)
5758 {
5759 e1 = e1.toLvalue(sc, e);
5760 return this;
5761 }
5762
modifiableLvalue(Scope * sc,Expression e)5763 override Expression modifiableLvalue(Scope* sc, Expression e)
5764 {
5765 if (sc.func.setUnsafe())
5766 {
5767 error("cannot modify delegate pointer in `@safe` code `%s`", toChars());
5768 return ErrorExp.get();
5769 }
5770 return Expression.modifiableLvalue(sc, e);
5771 }
5772
accept(Visitor v)5773 override void accept(Visitor v)
5774 {
5775 v.visit(this);
5776 }
5777 }
5778
5779 /***********************************************************
5780 * The `dg.funcptr` property, pointing to the delegate's function
5781 *
5782 * c.f.`DelegatePtrExp` for the delegate's function pointer `dg.ptr`
5783 */
5784 extern (C++) final class DelegateFuncptrExp : UnaExp
5785 {
this(const ref Loc loc,Expression e1)5786 extern (D) this(const ref Loc loc, Expression e1)
5787 {
5788 super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1);
5789 }
5790
isLvalue()5791 override bool isLvalue()
5792 {
5793 return e1.isLvalue();
5794 }
5795
toLvalue(Scope * sc,Expression e)5796 override Expression toLvalue(Scope* sc, Expression e)
5797 {
5798 e1 = e1.toLvalue(sc, e);
5799 return this;
5800 }
5801
modifiableLvalue(Scope * sc,Expression e)5802 override Expression modifiableLvalue(Scope* sc, Expression e)
5803 {
5804 if (sc.func.setUnsafe())
5805 {
5806 error("cannot modify delegate function pointer in `@safe` code `%s`", toChars());
5807 return ErrorExp.get();
5808 }
5809 return Expression.modifiableLvalue(sc, e);
5810 }
5811
accept(Visitor v)5812 override void accept(Visitor v)
5813 {
5814 v.visit(this);
5815 }
5816 }
5817
5818 /***********************************************************
5819 * e1 [ e2 ]
5820 */
5821 extern (C++) final class IndexExp : BinExp
5822 {
5823 VarDeclaration lengthVar;
5824 bool modifiable = false; // assume it is an rvalue
5825 bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1
5826
this(const ref Loc loc,Expression e1,Expression e2)5827 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
5828 {
5829 super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2);
5830 //printf("IndexExp::IndexExp('%s')\n", toChars());
5831 }
5832
this(const ref Loc loc,Expression e1,Expression e2,bool indexIsInBounds)5833 extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds)
5834 {
5835 super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2);
5836 this.indexIsInBounds = indexIsInBounds;
5837 //printf("IndexExp::IndexExp('%s')\n", toChars());
5838 }
5839
syntaxCopy()5840 override IndexExp syntaxCopy()
5841 {
5842 auto ie = new IndexExp(loc, e1.syntaxCopy(), e2.syntaxCopy());
5843 ie.lengthVar = this.lengthVar; // bug7871
5844 return ie;
5845 }
5846
isLvalue()5847 override bool isLvalue()
5848 {
5849 if (e1.op == EXP.assocArrayLiteral)
5850 return false;
5851 if (e1.type.ty == Tsarray ||
5852 (e1.op == EXP.index && e1.type.ty != Tarray))
5853 {
5854 return e1.isLvalue();
5855 }
5856 return true;
5857 }
5858
toLvalue(Scope * sc,Expression e)5859 override Expression toLvalue(Scope* sc, Expression e)
5860 {
5861 if (isLvalue())
5862 return this;
5863 return Expression.toLvalue(sc, e);
5864 }
5865
modifiableLvalue(Scope * sc,Expression e)5866 override Expression modifiableLvalue(Scope* sc, Expression e)
5867 {
5868 //printf("IndexExp::modifiableLvalue(%s)\n", toChars());
5869 Expression ex = markSettingAAElem();
5870 if (ex.op == EXP.error)
5871 return ex;
5872
5873 return Expression.modifiableLvalue(sc, e);
5874 }
5875
markSettingAAElem()5876 extern (D) Expression markSettingAAElem()
5877 {
5878 if (e1.type.toBasetype().ty == Taarray)
5879 {
5880 Type t2b = e2.type.toBasetype();
5881 if (t2b.ty == Tarray && t2b.nextOf().isMutable())
5882 {
5883 error("associative arrays can only be assigned values with immutable keys, not `%s`", e2.type.toChars());
5884 return ErrorExp.get();
5885 }
5886 modifiable = true;
5887
5888 if (auto ie = e1.isIndexExp())
5889 {
5890 Expression ex = ie.markSettingAAElem();
5891 if (ex.op == EXP.error)
5892 return ex;
5893 assert(ex == e1);
5894 }
5895 }
5896 return this;
5897 }
5898
accept(Visitor v)5899 override void accept(Visitor v)
5900 {
5901 v.visit(this);
5902 }
5903 }
5904
5905 /***********************************************************
5906 * The postfix increment/decrement operator, `i++` / `i--`
5907 */
5908 extern (C++) final class PostExp : BinExp
5909 {
this(EXP op,const ref Loc loc,Expression e)5910 extern (D) this(EXP op, const ref Loc loc, Expression e)
5911 {
5912 super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1);
5913 assert(op == EXP.minusMinus || op == EXP.plusPlus);
5914 }
5915
accept(Visitor v)5916 override void accept(Visitor v)
5917 {
5918 v.visit(this);
5919 }
5920 }
5921
5922 /***********************************************************
5923 * The prefix increment/decrement operator, `++i` / `--i`
5924 */
5925 extern (C++) final class PreExp : UnaExp
5926 {
this(EXP op,const ref Loc loc,Expression e)5927 extern (D) this(EXP op, const ref Loc loc, Expression e)
5928 {
5929 super(loc, op, __traits(classInstanceSize, PreExp), e);
5930 assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus);
5931 }
5932
accept(Visitor v)5933 override void accept(Visitor v)
5934 {
5935 v.visit(this);
5936 }
5937 }
5938
5939 enum MemorySet
5940 {
5941 none = 0, // simple assignment
5942 blockAssign = 1, // setting the contents of an array
5943 referenceInit = 2, // setting the reference of STC.ref_ variable
5944 }
5945
5946 /***********************************************************
5947 * The assignment / initialization operator, `=`
5948 *
5949 * Note: operator assignment `op=` has a different base class, `BinAssignExp`
5950 */
5951 extern (C++) class AssignExp : BinExp
5952 {
5953 MemorySet memset;
5954
5955 /************************************************************/
5956 /* op can be EXP.assign, EXP.construct, or EXP.blit */
this(const ref Loc loc,Expression e1,Expression e2)5957 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
5958 {
5959 super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2);
5960 }
5961
this(const ref Loc loc,EXP tok,Expression e1,Expression e2)5962 this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
5963 {
5964 super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2);
5965 }
5966
isLvalue()5967 override final bool isLvalue()
5968 {
5969 // Array-op 'x[] = y[]' should make an rvalue.
5970 // Setting array length 'x.length = v' should make an rvalue.
5971 if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
5972 {
5973 return false;
5974 }
5975 return true;
5976 }
5977
toLvalue(Scope * sc,Expression ex)5978 override final Expression toLvalue(Scope* sc, Expression ex)
5979 {
5980 if (e1.op == EXP.slice || e1.op == EXP.arrayLength)
5981 {
5982 return Expression.toLvalue(sc, ex);
5983 }
5984
5985 /* In front-end level, AssignExp should make an lvalue of e1.
5986 * Taking the address of e1 will be handled in low level layer,
5987 * so this function does nothing.
5988 */
5989 return this;
5990 }
5991
accept(Visitor v)5992 override void accept(Visitor v)
5993 {
5994 v.visit(this);
5995 }
5996 }
5997
5998 /***********************************************************
5999 */
6000 extern (C++) final class ConstructExp : AssignExp
6001 {
this(const ref Loc loc,Expression e1,Expression e2)6002 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6003 {
6004 super(loc, EXP.construct, e1, e2);
6005 }
6006
6007 // Internal use only. If `v` is a reference variable, the assignment
6008 // will become a reference initialization automatically.
this(const ref Loc loc,VarDeclaration v,Expression e2)6009 extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2)
6010 {
6011 auto ve = new VarExp(loc, v);
6012 assert(v.type && ve.type);
6013
6014 super(loc, EXP.construct, ve, e2);
6015
6016 if (v.isReference())
6017 memset = MemorySet.referenceInit;
6018 }
6019
accept(Visitor v)6020 override void accept(Visitor v)
6021 {
6022 v.visit(this);
6023 }
6024 }
6025
6026 /***********************************************************
6027 * A bit-for-bit copy from `e2` to `e1`
6028 */
6029 extern (C++) final class BlitExp : AssignExp
6030 {
this(const ref Loc loc,Expression e1,Expression e2)6031 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6032 {
6033 super(loc, EXP.blit, e1, e2);
6034 }
6035
6036 // Internal use only. If `v` is a reference variable, the assinment
6037 // will become a reference rebinding automatically.
this(const ref Loc loc,VarDeclaration v,Expression e2)6038 extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2)
6039 {
6040 auto ve = new VarExp(loc, v);
6041 assert(v.type && ve.type);
6042
6043 super(loc, EXP.blit, ve, e2);
6044
6045 if (v.isReference())
6046 memset = MemorySet.referenceInit;
6047 }
6048
accept(Visitor v)6049 override void accept(Visitor v)
6050 {
6051 v.visit(this);
6052 }
6053 }
6054
6055 /***********************************************************
6056 * `x += y`
6057 */
6058 extern (C++) final class AddAssignExp : BinAssignExp
6059 {
this(const ref Loc loc,Expression e1,Expression e2)6060 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6061 {
6062 super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2);
6063 }
6064
accept(Visitor v)6065 override void accept(Visitor v)
6066 {
6067 v.visit(this);
6068 }
6069 }
6070
6071 /***********************************************************
6072 * `x -= y`
6073 */
6074 extern (C++) final class MinAssignExp : BinAssignExp
6075 {
this(const ref Loc loc,Expression e1,Expression e2)6076 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6077 {
6078 super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2);
6079 }
6080
accept(Visitor v)6081 override void accept(Visitor v)
6082 {
6083 v.visit(this);
6084 }
6085 }
6086
6087 /***********************************************************
6088 * `x *= y`
6089 */
6090 extern (C++) final class MulAssignExp : BinAssignExp
6091 {
this(const ref Loc loc,Expression e1,Expression e2)6092 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6093 {
6094 super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2);
6095 }
6096
accept(Visitor v)6097 override void accept(Visitor v)
6098 {
6099 v.visit(this);
6100 }
6101 }
6102
6103 /***********************************************************
6104 * `x /= y`
6105 */
6106 extern (C++) final class DivAssignExp : BinAssignExp
6107 {
this(const ref Loc loc,Expression e1,Expression e2)6108 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6109 {
6110 super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2);
6111 }
6112
accept(Visitor v)6113 override void accept(Visitor v)
6114 {
6115 v.visit(this);
6116 }
6117 }
6118
6119 /***********************************************************
6120 * `x %= y`
6121 */
6122 extern (C++) final class ModAssignExp : BinAssignExp
6123 {
this(const ref Loc loc,Expression e1,Expression e2)6124 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6125 {
6126 super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2);
6127 }
6128
accept(Visitor v)6129 override void accept(Visitor v)
6130 {
6131 v.visit(this);
6132 }
6133 }
6134
6135 /***********************************************************
6136 * `x &= y`
6137 */
6138 extern (C++) final class AndAssignExp : BinAssignExp
6139 {
this(const ref Loc loc,Expression e1,Expression e2)6140 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6141 {
6142 super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2);
6143 }
6144
accept(Visitor v)6145 override void accept(Visitor v)
6146 {
6147 v.visit(this);
6148 }
6149 }
6150
6151 /***********************************************************
6152 * `x |= y`
6153 */
6154 extern (C++) final class OrAssignExp : BinAssignExp
6155 {
this(const ref Loc loc,Expression e1,Expression e2)6156 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6157 {
6158 super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2);
6159 }
6160
accept(Visitor v)6161 override void accept(Visitor v)
6162 {
6163 v.visit(this);
6164 }
6165 }
6166
6167 /***********************************************************
6168 * `x ^= y`
6169 */
6170 extern (C++) final class XorAssignExp : BinAssignExp
6171 {
this(const ref Loc loc,Expression e1,Expression e2)6172 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6173 {
6174 super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2);
6175 }
6176
accept(Visitor v)6177 override void accept(Visitor v)
6178 {
6179 v.visit(this);
6180 }
6181 }
6182
6183 /***********************************************************
6184 * `x ^^= y`
6185 */
6186 extern (C++) final class PowAssignExp : BinAssignExp
6187 {
this(const ref Loc loc,Expression e1,Expression e2)6188 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6189 {
6190 super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2);
6191 }
6192
accept(Visitor v)6193 override void accept(Visitor v)
6194 {
6195 v.visit(this);
6196 }
6197 }
6198
6199 /***********************************************************
6200 * `x <<= y`
6201 */
6202 extern (C++) final class ShlAssignExp : BinAssignExp
6203 {
this(const ref Loc loc,Expression e1,Expression e2)6204 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6205 {
6206 super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2);
6207 }
6208
accept(Visitor v)6209 override void accept(Visitor v)
6210 {
6211 v.visit(this);
6212 }
6213 }
6214
6215 /***********************************************************
6216 * `x >>= y`
6217 */
6218 extern (C++) final class ShrAssignExp : BinAssignExp
6219 {
this(const ref Loc loc,Expression e1,Expression e2)6220 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6221 {
6222 super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2);
6223 }
6224
accept(Visitor v)6225 override void accept(Visitor v)
6226 {
6227 v.visit(this);
6228 }
6229 }
6230
6231 /***********************************************************
6232 * `x >>>= y`
6233 */
6234 extern (C++) final class UshrAssignExp : BinAssignExp
6235 {
this(const ref Loc loc,Expression e1,Expression e2)6236 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6237 {
6238 super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2);
6239 }
6240
accept(Visitor v)6241 override void accept(Visitor v)
6242 {
6243 v.visit(this);
6244 }
6245 }
6246
6247 /***********************************************************
6248 * The `~=` operator.
6249 *
6250 * It can have one of the following operators:
6251 *
6252 * EXP.concatenateAssign - appending T[] to T[]
6253 * EXP.concatenateElemAssign - appending T to T[]
6254 * EXP.concatenateDcharAssign - appending dchar to T[]
6255 *
6256 * The parser initially sets it to EXP.concatenateAssign, and semantic() later decides which
6257 * of the three it will be set to.
6258 */
6259 extern (C++) class CatAssignExp : BinAssignExp
6260 {
this(const ref Loc loc,Expression e1,Expression e2)6261 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6262 {
6263 super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2);
6264 }
6265
this(const ref Loc loc,EXP tok,Expression e1,Expression e2)6266 extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2)
6267 {
6268 super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2);
6269 }
6270
accept(Visitor v)6271 override void accept(Visitor v)
6272 {
6273 v.visit(this);
6274 }
6275 }
6276
6277 /***********************************************************
6278 * The `~=` operator when appending a single element
6279 */
6280 extern (C++) final class CatElemAssignExp : CatAssignExp
6281 {
this(const ref Loc loc,Type type,Expression e1,Expression e2)6282 extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
6283 {
6284 super(loc, EXP.concatenateElemAssign, e1, e2);
6285 this.type = type;
6286 }
6287
accept(Visitor v)6288 override void accept(Visitor v)
6289 {
6290 v.visit(this);
6291 }
6292 }
6293
6294 /***********************************************************
6295 * The `~=` operator when appending a single `dchar`
6296 */
6297 extern (C++) final class CatDcharAssignExp : CatAssignExp
6298 {
this(const ref Loc loc,Type type,Expression e1,Expression e2)6299 extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2)
6300 {
6301 super(loc, EXP.concatenateDcharAssign, e1, e2);
6302 this.type = type;
6303 }
6304
accept(Visitor v)6305 override void accept(Visitor v)
6306 {
6307 v.visit(this);
6308 }
6309 }
6310
6311 /***********************************************************
6312 * The addition operator, `x + y`
6313 *
6314 * https://dlang.org/spec/expression.html#add_expressions
6315 */
6316 extern (C++) final class AddExp : BinExp
6317 {
this(const ref Loc loc,Expression e1,Expression e2)6318 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6319 {
6320 super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2);
6321 }
6322
accept(Visitor v)6323 override void accept(Visitor v)
6324 {
6325 v.visit(this);
6326 }
6327 }
6328
6329 /***********************************************************
6330 * The minus operator, `x - y`
6331 *
6332 * https://dlang.org/spec/expression.html#add_expressions
6333 */
6334 extern (C++) final class MinExp : BinExp
6335 {
this(const ref Loc loc,Expression e1,Expression e2)6336 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6337 {
6338 super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2);
6339 }
6340
accept(Visitor v)6341 override void accept(Visitor v)
6342 {
6343 v.visit(this);
6344 }
6345 }
6346
6347 /***********************************************************
6348 * The concatenation operator, `x ~ y`
6349 *
6350 * https://dlang.org/spec/expression.html#cat_expressions
6351 */
6352 extern (C++) final class CatExp : BinExp
6353 {
this(const ref Loc loc,Expression e1,Expression e2)6354 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6355 {
6356 super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2);
6357 }
6358
resolveLoc(const ref Loc loc,Scope * sc)6359 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6360 {
6361 e1 = e1.resolveLoc(loc, sc);
6362 e2 = e2.resolveLoc(loc, sc);
6363 return this;
6364 }
6365
accept(Visitor v)6366 override void accept(Visitor v)
6367 {
6368 v.visit(this);
6369 }
6370 }
6371
6372 /***********************************************************
6373 * The multiplication operator, `x * y`
6374 *
6375 * https://dlang.org/spec/expression.html#mul_expressions
6376 */
6377 extern (C++) final class MulExp : BinExp
6378 {
this(const ref Loc loc,Expression e1,Expression e2)6379 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6380 {
6381 super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2);
6382 }
6383
accept(Visitor v)6384 override void accept(Visitor v)
6385 {
6386 v.visit(this);
6387 }
6388 }
6389
6390 /***********************************************************
6391 * The division operator, `x / y`
6392 *
6393 * https://dlang.org/spec/expression.html#mul_expressions
6394 */
6395 extern (C++) final class DivExp : BinExp
6396 {
this(const ref Loc loc,Expression e1,Expression e2)6397 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6398 {
6399 super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2);
6400 }
6401
accept(Visitor v)6402 override void accept(Visitor v)
6403 {
6404 v.visit(this);
6405 }
6406 }
6407
6408 /***********************************************************
6409 * The modulo operator, `x % y`
6410 *
6411 * https://dlang.org/spec/expression.html#mul_expressions
6412 */
6413 extern (C++) final class ModExp : BinExp
6414 {
this(const ref Loc loc,Expression e1,Expression e2)6415 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6416 {
6417 super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2);
6418 }
6419
accept(Visitor v)6420 override void accept(Visitor v)
6421 {
6422 v.visit(this);
6423 }
6424 }
6425
6426 /***********************************************************
6427 * The 'power' operator, `x ^^ y`
6428 *
6429 * https://dlang.org/spec/expression.html#pow_expressions
6430 */
6431 extern (C++) final class PowExp : BinExp
6432 {
this(const ref Loc loc,Expression e1,Expression e2)6433 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6434 {
6435 super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2);
6436 }
6437
accept(Visitor v)6438 override void accept(Visitor v)
6439 {
6440 v.visit(this);
6441 }
6442 }
6443
6444 /***********************************************************
6445 * The 'shift left' operator, `x << y`
6446 *
6447 * https://dlang.org/spec/expression.html#shift_expressions
6448 */
6449 extern (C++) final class ShlExp : BinExp
6450 {
this(const ref Loc loc,Expression e1,Expression e2)6451 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6452 {
6453 super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2);
6454 }
6455
accept(Visitor v)6456 override void accept(Visitor v)
6457 {
6458 v.visit(this);
6459 }
6460 }
6461
6462 /***********************************************************
6463 * The 'shift right' operator, `x >> y`
6464 *
6465 * https://dlang.org/spec/expression.html#shift_expressions
6466 */
6467 extern (C++) final class ShrExp : BinExp
6468 {
this(const ref Loc loc,Expression e1,Expression e2)6469 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6470 {
6471 super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2);
6472 }
6473
accept(Visitor v)6474 override void accept(Visitor v)
6475 {
6476 v.visit(this);
6477 }
6478 }
6479
6480 /***********************************************************
6481 * The 'unsigned shift right' operator, `x >>> y`
6482 *
6483 * https://dlang.org/spec/expression.html#shift_expressions
6484 */
6485 extern (C++) final class UshrExp : BinExp
6486 {
this(const ref Loc loc,Expression e1,Expression e2)6487 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6488 {
6489 super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2);
6490 }
6491
accept(Visitor v)6492 override void accept(Visitor v)
6493 {
6494 v.visit(this);
6495 }
6496 }
6497
6498 /***********************************************************
6499 * The bitwise 'and' operator, `x & y`
6500 *
6501 * https://dlang.org/spec/expression.html#and_expressions
6502 */
6503 extern (C++) final class AndExp : BinExp
6504 {
this(const ref Loc loc,Expression e1,Expression e2)6505 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6506 {
6507 super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2);
6508 }
6509
accept(Visitor v)6510 override void accept(Visitor v)
6511 {
6512 v.visit(this);
6513 }
6514 }
6515
6516 /***********************************************************
6517 * The bitwise 'or' operator, `x | y`
6518 *
6519 * https://dlang.org/spec/expression.html#or_expressions
6520 */
6521 extern (C++) final class OrExp : BinExp
6522 {
this(const ref Loc loc,Expression e1,Expression e2)6523 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6524 {
6525 super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2);
6526 }
6527
accept(Visitor v)6528 override void accept(Visitor v)
6529 {
6530 v.visit(this);
6531 }
6532 }
6533
6534 /***********************************************************
6535 * The bitwise 'xor' operator, `x ^ y`
6536 *
6537 * https://dlang.org/spec/expression.html#xor_expressions
6538 */
6539 extern (C++) final class XorExp : BinExp
6540 {
this(const ref Loc loc,Expression e1,Expression e2)6541 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6542 {
6543 super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2);
6544 }
6545
accept(Visitor v)6546 override void accept(Visitor v)
6547 {
6548 v.visit(this);
6549 }
6550 }
6551
6552 /***********************************************************
6553 * The logical 'and' / 'or' operator, `X && Y` / `X || Y`
6554 *
6555 * https://dlang.org/spec/expression.html#andand_expressions
6556 * https://dlang.org/spec/expression.html#oror_expressions
6557 */
6558 extern (C++) final class LogicalExp : BinExp
6559 {
this(const ref Loc loc,EXP op,Expression e1,Expression e2)6560 extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2)
6561 {
6562 super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2);
6563 assert(op == EXP.andAnd || op == EXP.orOr);
6564 }
6565
accept(Visitor v)6566 override void accept(Visitor v)
6567 {
6568 v.visit(this);
6569 }
6570 }
6571
6572 /***********************************************************
6573 * A comparison operator, `<` `<=` `>` `>=`
6574 *
6575 * `op` is one of:
6576 * EXP.lessThan, EXP.lessOrEqual, EXP.greaterThan, EXP.greaterOrEqual
6577 *
6578 * https://dlang.org/spec/expression.html#relation_expressions
6579 */
6580 extern (C++) final class CmpExp : BinExp
6581 {
this(EXP op,const ref Loc loc,Expression e1,Expression e2)6582 extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
6583 {
6584 super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2);
6585 assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual);
6586 }
6587
accept(Visitor v)6588 override void accept(Visitor v)
6589 {
6590 v.visit(this);
6591 }
6592 }
6593
6594 /***********************************************************
6595 * The `in` operator, `"a" in ["a": 1]`
6596 *
6597 * Note: `x !in y` is rewritten to `!(x in y)` in the parser
6598 *
6599 * https://dlang.org/spec/expression.html#in_expressions
6600 */
6601 extern (C++) final class InExp : BinExp
6602 {
this(const ref Loc loc,Expression e1,Expression e2)6603 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6604 {
6605 super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2);
6606 }
6607
accept(Visitor v)6608 override void accept(Visitor v)
6609 {
6610 v.visit(this);
6611 }
6612 }
6613
6614 /***********************************************************
6615 * Associative array removal, `aa.remove(arg)`
6616 *
6617 * This deletes the key e1 from the associative array e2
6618 */
6619 extern (C++) final class RemoveExp : BinExp
6620 {
this(const ref Loc loc,Expression e1,Expression e2)6621 extern (D) this(const ref Loc loc, Expression e1, Expression e2)
6622 {
6623 super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2);
6624 type = Type.tbool;
6625 }
6626
accept(Visitor v)6627 override void accept(Visitor v)
6628 {
6629 v.visit(this);
6630 }
6631 }
6632
6633 /***********************************************************
6634 * `==` and `!=`
6635 *
6636 * EXP.equal and EXP.notEqual
6637 *
6638 * https://dlang.org/spec/expression.html#equality_expressions
6639 */
6640 extern (C++) final class EqualExp : BinExp
6641 {
this(EXP op,const ref Loc loc,Expression e1,Expression e2)6642 extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
6643 {
6644 super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2);
6645 assert(op == EXP.equal || op == EXP.notEqual);
6646 }
6647
accept(Visitor v)6648 override void accept(Visitor v)
6649 {
6650 v.visit(this);
6651 }
6652 }
6653
6654 /***********************************************************
6655 * `is` and `!is`
6656 *
6657 * EXP.identity and EXP.notIdentity
6658 *
6659 * https://dlang.org/spec/expression.html#identity_expressions
6660 */
6661 extern (C++) final class IdentityExp : BinExp
6662 {
this(EXP op,const ref Loc loc,Expression e1,Expression e2)6663 extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2)
6664 {
6665 super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2);
6666 assert(op == EXP.identity || op == EXP.notIdentity);
6667 }
6668
accept(Visitor v)6669 override void accept(Visitor v)
6670 {
6671 v.visit(this);
6672 }
6673 }
6674
6675 /***********************************************************
6676 * The ternary operator, `econd ? e1 : e2`
6677 *
6678 * https://dlang.org/spec/expression.html#conditional_expressions
6679 */
6680 extern (C++) final class CondExp : BinExp
6681 {
6682 Expression econd;
6683
this(const ref Loc loc,Expression econd,Expression e1,Expression e2)6684 extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2)
6685 {
6686 super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2);
6687 this.econd = econd;
6688 }
6689
syntaxCopy()6690 override CondExp syntaxCopy()
6691 {
6692 return new CondExp(loc, econd.syntaxCopy(), e1.syntaxCopy(), e2.syntaxCopy());
6693 }
6694
isLvalue()6695 override bool isLvalue()
6696 {
6697 return e1.isLvalue() && e2.isLvalue();
6698 }
6699
toLvalue(Scope * sc,Expression ex)6700 override Expression toLvalue(Scope* sc, Expression ex)
6701 {
6702 // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
6703 CondExp e = cast(CondExp)copy();
6704 e.e1 = e1.toLvalue(sc, null).addressOf();
6705 e.e2 = e2.toLvalue(sc, null).addressOf();
6706 e.type = type.pointerTo();
6707 return new PtrExp(loc, e, type);
6708 }
6709
modifiableLvalue(Scope * sc,Expression e)6710 override Expression modifiableLvalue(Scope* sc, Expression e)
6711 {
6712 if (!e1.isLvalue() && !e2.isLvalue())
6713 {
6714 error("conditional expression `%s` is not a modifiable lvalue", toChars());
6715 return ErrorExp.get();
6716 }
6717 e1 = e1.modifiableLvalue(sc, e1);
6718 e2 = e2.modifiableLvalue(sc, e2);
6719 return toLvalue(sc, this);
6720 }
6721
hookDtors(Scope * sc)6722 void hookDtors(Scope* sc)
6723 {
6724 extern (C++) final class DtorVisitor : StoppableVisitor
6725 {
6726 alias visit = typeof(super).visit;
6727 public:
6728 Scope* sc;
6729 CondExp ce;
6730 VarDeclaration vcond;
6731 bool isThen;
6732
6733 extern (D) this(Scope* sc, CondExp ce)
6734 {
6735 this.sc = sc;
6736 this.ce = ce;
6737 }
6738
6739 override void visit(Expression e)
6740 {
6741 //printf("(e = %s)\n", e.toChars());
6742 }
6743
6744 override void visit(DeclarationExp e)
6745 {
6746 auto v = e.declaration.isVarDeclaration();
6747 if (v && !v.isDataseg())
6748 {
6749 if (v._init)
6750 {
6751 if (auto ei = v._init.isExpInitializer())
6752 walkPostorder(ei.exp, this);
6753 }
6754
6755 if (v.edtor)
6756 walkPostorder(v.edtor, this);
6757
6758 if (v.needsScopeDtor())
6759 {
6760 if (!vcond)
6761 {
6762 vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd);
6763 vcond.dsymbolSemantic(sc);
6764
6765 Expression de = new DeclarationExp(ce.econd.loc, vcond);
6766 de = de.expressionSemantic(sc);
6767
6768 Expression ve = new VarExp(ce.econd.loc, vcond);
6769 ce.econd = Expression.combine(de, ve);
6770 }
6771
6772 //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
6773 Expression ve = new VarExp(vcond.loc, vcond);
6774 if (isThen)
6775 v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor);
6776 else
6777 v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor);
6778 v.edtor = v.edtor.expressionSemantic(sc);
6779 //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars());
6780 }
6781 }
6782 }
6783 }
6784
6785 scope DtorVisitor v = new DtorVisitor(sc, this);
6786 //printf("+%s\n", toChars());
6787 v.isThen = true;
6788 walkPostorder(e1, v);
6789 v.isThen = false;
6790 walkPostorder(e2, v);
6791 //printf("-%s\n", toChars());
6792 }
6793
accept(Visitor v)6794 override void accept(Visitor v)
6795 {
6796 v.visit(this);
6797 }
6798 }
6799
6800 /// Returns: if this token is the `op` for a derived `DefaultInitExp` class.
isDefaultInitOp(EXP op)6801 bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc
6802 {
6803 return op == EXP.prettyFunction || op == EXP.functionString ||
6804 op == EXP.line || op == EXP.moduleString ||
6805 op == EXP.file || op == EXP.fileFullPath ;
6806 }
6807
6808 /***********************************************************
6809 * A special keyword when used as a function's default argument
6810 *
6811 * When possible, special keywords are resolved in the parser, but when
6812 * appearing as a default argument, they result in an expression deriving
6813 * from this base class that is resolved for each function call.
6814 *
6815 * ---
6816 * const x = __LINE__; // resolved in the parser
6817 * void foo(string file = __FILE__, int line = __LINE__); // DefaultInitExp
6818 * ---
6819 *
6820 * https://dlang.org/spec/expression.html#specialkeywords
6821 */
6822 extern (C++) class DefaultInitExp : Expression
6823 {
this(const ref Loc loc,EXP op,int size)6824 extern (D) this(const ref Loc loc, EXP op, int size)
6825 {
6826 super(loc, op, size);
6827 }
6828
accept(Visitor v)6829 override void accept(Visitor v)
6830 {
6831 v.visit(this);
6832 }
6833 }
6834
6835 /***********************************************************
6836 * The `__FILE__` token as a default argument
6837 */
6838 extern (C++) final class FileInitExp : DefaultInitExp
6839 {
this(const ref Loc loc,EXP tok)6840 extern (D) this(const ref Loc loc, EXP tok)
6841 {
6842 super(loc, tok, __traits(classInstanceSize, FileInitExp));
6843 }
6844
resolveLoc(const ref Loc loc,Scope * sc)6845 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6846 {
6847 //printf("FileInitExp::resolve() %s\n", toChars());
6848 const(char)* s;
6849 if (op == EXP.fileFullPath)
6850 s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars());
6851 else
6852 s = loc.isValid() ? loc.filename : sc._module.ident.toChars();
6853
6854 Expression e = new StringExp(loc, s.toDString());
6855 e = e.expressionSemantic(sc);
6856 e = e.castTo(sc, type);
6857 return e;
6858 }
6859
accept(Visitor v)6860 override void accept(Visitor v)
6861 {
6862 v.visit(this);
6863 }
6864 }
6865
6866 /***********************************************************
6867 * The `__LINE__` token as a default argument
6868 */
6869 extern (C++) final class LineInitExp : DefaultInitExp
6870 {
this(const ref Loc loc)6871 extern (D) this(const ref Loc loc)
6872 {
6873 super(loc, EXP.line, __traits(classInstanceSize, LineInitExp));
6874 }
6875
resolveLoc(const ref Loc loc,Scope * sc)6876 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6877 {
6878 Expression e = new IntegerExp(loc, loc.linnum, Type.tint32);
6879 e = e.castTo(sc, type);
6880 return e;
6881 }
6882
accept(Visitor v)6883 override void accept(Visitor v)
6884 {
6885 v.visit(this);
6886 }
6887 }
6888
6889 /***********************************************************
6890 * The `__MODULE__` token as a default argument
6891 */
6892 extern (C++) final class ModuleInitExp : DefaultInitExp
6893 {
this(const ref Loc loc)6894 extern (D) this(const ref Loc loc)
6895 {
6896 super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp));
6897 }
6898
resolveLoc(const ref Loc loc,Scope * sc)6899 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6900 {
6901 const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString();
6902 Expression e = new StringExp(loc, s);
6903 e = e.expressionSemantic(sc);
6904 e = e.castTo(sc, type);
6905 return e;
6906 }
6907
accept(Visitor v)6908 override void accept(Visitor v)
6909 {
6910 v.visit(this);
6911 }
6912 }
6913
6914 /***********************************************************
6915 * The `__FUNCTION__` token as a default argument
6916 */
6917 extern (C++) final class FuncInitExp : DefaultInitExp
6918 {
this(const ref Loc loc)6919 extern (D) this(const ref Loc loc)
6920 {
6921 super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp));
6922 }
6923
resolveLoc(const ref Loc loc,Scope * sc)6924 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6925 {
6926 const(char)* s;
6927 if (sc.callsc && sc.callsc.func)
6928 s = sc.callsc.func.Dsymbol.toPrettyChars();
6929 else if (sc.func)
6930 s = sc.func.Dsymbol.toPrettyChars();
6931 else
6932 s = "";
6933 Expression e = new StringExp(loc, s.toDString());
6934 e = e.expressionSemantic(sc);
6935 e.type = Type.tstring;
6936 return e;
6937 }
6938
accept(Visitor v)6939 override void accept(Visitor v)
6940 {
6941 v.visit(this);
6942 }
6943 }
6944
6945 /***********************************************************
6946 * The `__PRETTY_FUNCTION__` token as a default argument
6947 */
6948 extern (C++) final class PrettyFuncInitExp : DefaultInitExp
6949 {
this(const ref Loc loc)6950 extern (D) this(const ref Loc loc)
6951 {
6952 super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp));
6953 }
6954
resolveLoc(const ref Loc loc,Scope * sc)6955 override Expression resolveLoc(const ref Loc loc, Scope* sc)
6956 {
6957 FuncDeclaration fd = (sc.callsc && sc.callsc.func)
6958 ? sc.callsc.func
6959 : sc.func;
6960
6961 const(char)* s;
6962 if (fd)
6963 {
6964 const funcStr = fd.Dsymbol.toPrettyChars();
6965 OutBuffer buf;
6966 functionToBufferWithIdent(fd.type.isTypeFunction(), &buf, funcStr, fd.isStatic);
6967 s = buf.extractChars();
6968 }
6969 else
6970 {
6971 s = "";
6972 }
6973
6974 Expression e = new StringExp(loc, s.toDString());
6975 e = e.expressionSemantic(sc);
6976 e.type = Type.tstring;
6977 return e;
6978 }
6979
accept(Visitor v)6980 override void accept(Visitor v)
6981 {
6982 v.visit(this);
6983 }
6984 }
6985
6986 /**
6987 * Objective-C class reference expression.
6988 *
6989 * Used to get the metaclass of an Objective-C class, `NSObject.Class`.
6990 */
6991 extern (C++) final class ObjcClassReferenceExp : Expression
6992 {
6993 ClassDeclaration classDeclaration;
6994
this(const ref Loc loc,ClassDeclaration classDeclaration)6995 extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration)
6996 {
6997 super(loc, EXP.objcClassReference,
6998 __traits(classInstanceSize, ObjcClassReferenceExp));
6999 this.classDeclaration = classDeclaration;
7000 type = objc.getRuntimeMetaclass(classDeclaration).getType();
7001 }
7002
accept(Visitor v)7003 override void accept(Visitor v)
7004 {
7005 v.visit(this);
7006 }
7007 }
7008
7009 /*******************
7010 * C11 6.5.1.1 Generic Selection
7011 * For ImportC
7012 */
7013 extern (C++) final class GenericExp : Expression
7014 {
7015 Expression cntlExp; /// controlling expression of a generic selection (not evaluated)
7016 Types* types; /// type-names for generic associations (null entry for `default`)
7017 Expressions* exps; /// 1:1 mapping of typeNames to exps
7018
this(const ref Loc loc,Expression cntlExp,Types * types,Expressions * exps)7019 extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps)
7020 {
7021 super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp));
7022 this.cntlExp = cntlExp;
7023 this.types = types;
7024 this.exps = exps;
7025 assert(types.length == exps.length); // must be the same and >=1
7026 }
7027
syntaxCopy()7028 override GenericExp syntaxCopy()
7029 {
7030 return new GenericExp(loc, cntlExp.syntaxCopy(), Type.arraySyntaxCopy(types), Expression.arraySyntaxCopy(exps));
7031 }
7032
accept(Visitor v)7033 override void accept(Visitor v)
7034 {
7035 v.visit(this);
7036 }
7037 }
7038
7039 /***************************************
7040 * Parameters:
7041 * sc: scope
7042 * flag: 1: do not issue error message for invalid modification
7043 2: the exp is a DotVarExp and a subfield of the leftmost
7044 variable is modified
7045 * Returns:
7046 * Whether the type is modifiable
7047 */
7048 extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none)
7049 {
7050 switch(exp.op)
7051 {
7052 case EXP.variable:
7053 auto varExp = cast(VarExp)exp;
7054
7055 //printf("VarExp::checkModifiable %s", varExp.toChars());
7056 assert(varExp.type);
7057 return varExp.var.checkModify(varExp.loc, sc, null, flag);
7058
7059 case EXP.dotVariable:
7060 auto dotVarExp = cast(DotVarExp)exp;
7061
7062 //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars());
7063 if (dotVarExp.e1.op == EXP.this_)
7064 return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag);
7065
7066 /* https://issues.dlang.org/show_bug.cgi?id=12764
7067 * If inside a constructor and an expression of type `this.field.var`
7068 * is encountered, where `field` is a struct declaration with
7069 * default construction disabled, we must make sure that
7070 * assigning to `var` does not imply that `field` was initialized
7071 */
7072 if (sc.func && sc.func.isCtorDeclaration())
7073 {
7074 // if inside a constructor scope and e1 of this DotVarExp
7075 // is another DotVarExp, then check if the leftmost expression is a `this` identifier
7076 if (auto dve = dotVarExp.e1.isDotVarExp())
7077 {
7078 // Iterate the chain of DotVarExp to find `this`
7079 // Keep track whether access to fields was limited to union members
7080 // s.t. one can initialize an entire struct inside nested unions
7081 // (but not its members)
7082 bool onlyUnion = true;
7083 while (true)
7084 {
7085 auto v = dve.var.isVarDeclaration();
7086 assert(v);
7087
7088 // Accessing union member?
7089 auto t = v.type.isTypeStruct();
7090 if (!t || !t.sym.isUnionDeclaration())
7091 onlyUnion = false;
7092
7093 // Another DotVarExp left?
7094 if (!dve.e1 || dve.e1.op != EXP.dotVariable)
7095 break;
7096
7097 dve = cast(DotVarExp) dve.e1;
7098 }
7099
7100 if (dve.e1.op == EXP.this_)
7101 {
7102 scope v = dve.var.isVarDeclaration();
7103 /* if v is a struct member field with no initializer, no default construction
7104 * and v wasn't intialized before
7105 */
7106 if (v && v.isField() && !v._init && !v.ctorinit)
7107 {
7108 if (auto ts = v.type.isTypeStruct())
7109 {
7110 if (ts.sym.noDefaultCtor)
7111 {
7112 /* checkModify will consider that this is an initialization
7113 * of v while it is actually an assignment of a field of v
7114 */
7115 scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag);
7116 if (modifyLevel == Modifiable.initialization)
7117 {
7118 // https://issues.dlang.org/show_bug.cgi?id=22118
7119 // v is a union type field that was assigned
7120 // a variable, therefore it counts as initialization
7121 if (v.ctorinit)
7122 return Modifiable.initialization;
7123
7124 return Modifiable.yes;
7125 }
7126 return modifyLevel;
7127 }
7128 }
7129 }
7130 }
7131 }
7132 }
7133
7134 //printf("\te1 = %s\n", e1.toChars());
7135 return dotVarExp.e1.checkModifiable(sc, flag);
7136
7137 case EXP.star:
7138 auto ptrExp = cast(PtrExp)exp;
7139 if (auto se = ptrExp.e1.isSymOffExp())
7140 {
7141 return se.var.checkModify(ptrExp.loc, sc, null, flag);
7142 }
7143 else if (auto ae = ptrExp.e1.isAddrExp())
7144 {
7145 return ae.e1.checkModifiable(sc, flag);
7146 }
7147 return Modifiable.yes;
7148
7149 case EXP.slice:
7150 auto sliceExp = cast(SliceExp)exp;
7151
7152 //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars());
7153 auto e1 = sliceExp.e1;
7154 if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice)
7155 {
7156 return e1.checkModifiable(sc, flag);
7157 }
7158 return Modifiable.yes;
7159
7160 case EXP.comma:
7161 return (cast(CommaExp)exp).e2.checkModifiable(sc, flag);
7162
7163 case EXP.index:
7164 auto indexExp = cast(IndexExp)exp;
7165 auto e1 = indexExp.e1;
7166 if (e1.type.ty == Tsarray ||
7167 e1.type.ty == Taarray ||
7168 (e1.op == EXP.index && e1.type.ty != Tarray) ||
7169 e1.op == EXP.slice)
7170 {
7171 return e1.checkModifiable(sc, flag);
7172 }
7173 return Modifiable.yes;
7174
7175 case EXP.question:
7176 auto condExp = cast(CondExp)exp;
7177 if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no
7178 && condExp.e2.checkModifiable(sc, flag) != Modifiable.no)
7179 return Modifiable.yes;
7180 return Modifiable.no;
7181
7182 default:
7183 return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable
7184 }
7185 }
7186
7187 /******************************
7188 * Provide efficient way to implement isUnaExp(), isBinExp(), isBinAssignExp()
7189 */
7190 private immutable ubyte[EXP.max + 1] exptab =
7191 () {
7192 ubyte[EXP.max + 1] tab;
with(EXPFLAGS)7193 with (EXPFLAGS)
7194 {
7195 foreach (i; Eunary) { tab[i] |= unary; }
7196 foreach (i; Ebinary) { tab[i] |= unary | binary; }
7197 foreach (i; EbinaryAssign) { tab[i] |= unary | binary | binaryAssign; }
7198 }
7199 return tab;
7200 } ();
7201
7202 private enum EXPFLAGS : ubyte
7203 {
7204 unary = 1,
7205 binary = 2,
7206 binaryAssign = 4,
7207 }
7208
7209 private enum Eunary =
7210 [
7211 EXP.import_, EXP.assert_, EXP.throw_, EXP.dotIdentifier, EXP.dotTemplateDeclaration,
7212 EXP.dotVariable, EXP.dotTemplateInstance, EXP.delegate_, EXP.dotType, EXP.call,
7213 EXP.address, EXP.star, EXP.negate, EXP.uadd, EXP.tilde, EXP.not, EXP.delete_, EXP.cast_,
7214 EXP.vector, EXP.vectorArray, EXP.slice, EXP.arrayLength, EXP.array, EXP.delegatePointer,
7215 EXP.delegateFunctionPointer, EXP.preMinusMinus, EXP.prePlusPlus,
7216 ];
7217
7218 private enum Ebinary =
7219 [
7220 EXP.dot, EXP.comma, EXP.index, EXP.minusMinus, EXP.plusPlus, EXP.assign,
7221 EXP.add, EXP.min, EXP.concatenate, EXP.mul, EXP.div, EXP.mod, EXP.pow, EXP.leftShift,
7222 EXP.rightShift, EXP.unsignedRightShift, EXP.and, EXP.or, EXP.xor, EXP.andAnd, EXP.orOr,
7223 EXP.lessThan, EXP.lessOrEqual, EXP.greaterThan, EXP.greaterOrEqual,
7224 EXP.in_, EXP.remove, EXP.equal, EXP.notEqual, EXP.identity, EXP.notIdentity,
7225 EXP.question,
7226 EXP.construct, EXP.blit,
7227 ];
7228
7229 private enum EbinaryAssign =
7230 [
7231 EXP.addAssign, EXP.minAssign, EXP.mulAssign, EXP.divAssign, EXP.modAssign,
7232 EXP.andAssign, EXP.orAssign, EXP.xorAssign, EXP.powAssign,
7233 EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign,
7234 EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign,
7235 ];
7236