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