Internally these would treat the cast same as a normal conversion from int[7] to int[], which allows code at CTFE to erroneously succeed where it would raise a SEGV at run-time.
1 |
/**
|
|
2 |
* Defines the bulk of the classes which represent the AST at the expression level.
|
|
3 |
*
|
|
4 |
* Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions)
|
|
5 |
*
|
|
6 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
7 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
8 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
9 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expression.d, _expression.d)
|
|
10 |
* Documentation: https://dlang.org/phobos/dmd_expression.html
|
|
11 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expression.d
|
|
12 |
*/
|
|
13 |
|
|
14 |
module dmd.expression; |
|
15 |
|
|
16 |
import core.stdc.stdarg; |
|
17 |
import core.stdc.stdio; |
|
18 |
import core.stdc.string; |
|
19 |
|
|
20 |
import dmd.aggregate; |
|
21 |
import dmd.aliasthis; |
|
22 |
import dmd.apply; |
|
23 |
import dmd.arrayop; |
|
24 |
import dmd.arraytypes; |
|
25 |
import dmd.ast_node; |
|
26 |
import dmd.gluelayer; |
|
27 |
import dmd.canthrow; |
|
28 |
import dmd.complex; |
|
29 |
import dmd.constfold; |
|
30 |
import dmd.ctfeexpr; |
|
31 |
import dmd.ctorflow; |
|
32 |
import dmd.dcast; |
|
33 |
import dmd.dclass; |
|
34 |
import dmd.declaration; |
|
35 |
import dmd.delegatize; |
|
36 |
import dmd.dimport; |
|
37 |
import dmd.dinterpret; |
|
38 |
import dmd.dmodule; |
|
39 |
import dmd.dscope; |
|
40 |
import dmd.dstruct; |
|
41 |
import dmd.dsymbol; |
|
42 |
import dmd.dsymbolsem; |
|
43 |
import dmd.dtemplate; |
|
44 |
import dmd.errors; |
|
45 |
import dmd.escape; |
|
46 |
import dmd.expressionsem; |
|
47 |
import dmd.func; |
|
48 |
import dmd.globals; |
|
49 |
import dmd.hdrgen; |
|
50 |
import dmd.id; |
|
51 |
import dmd.identifier; |
|
52 |
import dmd.inline; |
|
53 |
import dmd.mtype; |
|
54 |
import dmd.nspace; |
|
55 |
import dmd.objc; |
|
56 |
import dmd.opover; |
|
57 |
import dmd.optimize; |
|
58 |
import dmd.root.ctfloat; |
|
59 |
import dmd.root.filename; |
|
60 |
import dmd.root.outbuffer; |
|
61 |
import dmd.root.rmem; |
|
62 |
import dmd.root.rootobject; |
|
63 |
import dmd.root.string; |
|
64 |
import dmd.safe; |
|
65 |
import dmd.sideeffect; |
|
66 |
import dmd.target; |
|
67 |
import dmd.tokens; |
|
68 |
import dmd.typesem; |
|
69 |
import dmd.utf; |
|
70 |
import dmd.visitor; |
|
71 |
|
|
72 |
enum LOGSEMANTIC = false; |
|
73 |
void emplaceExp(T : Expression, Args...)(void* p, Args args) |
|
74 |
{
|
|
75 | 1 |
scope tmp = new T(args); |
76 | 1 |
memcpy(p, cast(void*)tmp, __traits(classInstanceSize, T)); |
77 |
}
|
|
78 |
|
|
79 |
void emplaceExp(T : UnionExp)(T* p, Expression e) |
|
80 |
{
|
|
81 | 1 |
memcpy(p, cast(void*)e, e.size); |
82 |
}
|
|
83 |
|
|
84 |
// Return value for `checkModifiable`
|
|
85 |
enum Modifiable |
|
86 |
{
|
|
87 |
/// Not modifiable
|
|
88 |
no, |
|
89 |
/// Modifiable (the type is mutable)
|
|
90 |
yes, |
|
91 |
/// Modifiable because it is initialization
|
|
92 |
initialization, |
|
93 |
}
|
|
94 |
|
|
95 |
/****************************************
|
|
96 |
* Find the first non-comma expression.
|
|
97 |
* Params:
|
|
98 |
* e = Expressions connected by commas
|
|
99 |
* Returns:
|
|
100 |
* left-most non-comma expression
|
|
101 |
*/
|
|
102 |
inout(Expression) firstComma(inout Expression e) |
|
103 |
{
|
|
104 | 1 |
Expression ex = cast()e; |
105 | 1 |
while (ex.op == TOK.comma) |
106 | 1 |
ex = (cast(CommaExp)ex).e1; |
107 | 1 |
return cast(inout)ex; |
108 |
|
|
109 |
}
|
|
110 |
|
|
111 |
/****************************************
|
|
112 |
* Find the last non-comma expression.
|
|
113 |
* Params:
|
|
114 |
* e = Expressions connected by commas
|
|
115 |
* Returns:
|
|
116 |
* right-most non-comma expression
|
|
117 |
*/
|
|
118 |
|
|
119 |
inout(Expression) lastComma(inout Expression e) |
|
120 |
{
|
|
121 | 1 |
Expression ex = cast()e; |
122 | 1 |
while (ex.op == TOK.comma) |
123 | 1 |
ex = (cast(CommaExp)ex).e2; |
124 | 1 |
return cast(inout)ex; |
125 |
|
|
126 |
}
|
|
127 |
|
|
128 |
/*****************************************
|
|
129 |
* Determine if `this` is available by walking up the enclosing
|
|
130 |
* scopes until a function is found.
|
|
131 |
*
|
|
132 |
* Params:
|
|
133 |
* sc = where to start looking for the enclosing function
|
|
134 |
* Returns:
|
|
135 |
* Found function if it satisfies `isThis()`, otherwise `null`
|
|
136 |
*/
|
|
137 |
FuncDeclaration hasThis(Scope* sc) |
|
138 |
{
|
|
139 |
//printf("hasThis()\n");
|
|
140 | 1 |
Dsymbol p = sc.parent; |
141 | 1 |
while (p && p.isTemplateMixin()) |
142 | 1 |
p = p.parent; |
143 | 1 |
FuncDeclaration fdthis = p ? p.isFuncDeclaration() : null; |
144 |
//printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis.toChars() : "");
|
|
145 |
|
|
146 |
// Go upwards until we find the enclosing member function
|
|
147 | 1 |
FuncDeclaration fd = fdthis; |
148 | 1 |
while (1) |
149 |
{
|
|
150 | 1 |
if (!fd) |
151 |
{
|
|
152 | 1 |
return null; |
153 |
}
|
|
154 | 1 |
if (!fd.isNested() || fd.isThis() || (fd.isThis2 && fd.isMember2())) |
155 | 1 |
break; |
156 |
|
|
157 | 1 |
Dsymbol parent = fd.parent; |
158 | 1 |
while (1) |
159 |
{
|
|
160 | 1 |
if (!parent) |
161 |
return null; |
|
162 | 1 |
TemplateInstance ti = parent.isTemplateInstance(); |
163 | 1 |
if (ti) |
164 | 1 |
parent = ti.parent; |
165 |
else
|
|
166 | 1 |
break; |
167 |
}
|
|
168 | 1 |
fd = parent.isFuncDeclaration(); |
169 |
}
|
|
170 |
|
|
171 | 1 |
if (!fd.isThis() && !(fd.isThis2 && fd.isMember2())) |
172 |
{
|
|
173 | 1 |
return null; |
174 |
}
|
|
175 |
|
|
176 | 1 |
assert(fd.vthis); |
177 | 1 |
return fd; |
178 |
|
|
179 |
}
|
|
180 |
|
|
181 |
/***********************************
|
|
182 |
* Determine if a `this` is needed to access `d`.
|
|
183 |
* Params:
|
|
184 |
* sc = context
|
|
185 |
* d = declaration to check
|
|
186 |
* Returns:
|
|
187 |
* true means a `this` is needed
|
|
188 |
*/
|
|
189 |
bool isNeedThisScope(Scope* sc, Declaration d) |
|
190 |
{
|
|
191 | 1 |
if (sc.intypeof == 1) |
192 | 1 |
return false; |
193 |
|
|
194 | 1 |
AggregateDeclaration ad = d.isThis(); |
195 | 1 |
if (!ad) |
196 | 1 |
return false; |
197 |
//printf("d = %s, ad = %s\n", d.toChars(), ad.toChars());
|
|
198 |
|
|
199 | 1 |
for (Dsymbol s = sc.parent; s; s = s.toParentLocal()) |
200 |
{
|
|
201 |
//printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2());
|
|
202 | 1 |
if (AggregateDeclaration ad2 = s.isAggregateDeclaration()) |
203 |
{
|
|
204 | 1 |
if (ad2 == ad) |
205 | 1 |
return false; |
206 | 1 |
else if (ad2.isNested()) |
207 |
continue; |
|
208 |
else
|
|
209 | 1 |
return true; |
210 |
}
|
|
211 | 1 |
if (FuncDeclaration f = s.isFuncDeclaration()) |
212 |
{
|
|
213 | 1 |
if (f.isMemberLocal()) |
214 | 1 |
break; |
215 |
}
|
|
216 |
}
|
|
217 | 1 |
return true; |
218 |
}
|
|
219 |
|
|
220 |
/******************************
|
|
221 |
* check e is exp.opDispatch!(tiargs) or not
|
|
222 |
* It's used to switch to UFCS the semantic analysis path
|
|
223 |
*/
|
|
224 |
bool isDotOpDispatch(Expression e) |
|
225 |
{
|
|
226 | 1 |
if (auto dtie = e.isDotTemplateInstanceExp()) |
227 | 1 |
return dtie.ti.name == Id.opDispatch; |
228 | 1 |
return false; |
229 |
}
|
|
230 |
|
|
231 |
/****************************************
|
|
232 |
* Expand tuples.
|
|
233 |
* Input:
|
|
234 |
* exps aray of Expressions
|
|
235 |
* Output:
|
|
236 |
* exps rewritten in place
|
|
237 |
*/
|
|
238 |
extern (C++) void expandTuples(Expressions* exps) |
|
239 |
{
|
|
240 |
//printf("expandTuples()\n");
|
|
241 | 1 |
if (exps is null) |
242 |
return; |
|
243 |
|
|
244 | 1 |
for (size_t i = 0; i < exps.dim; i++) |
245 |
{
|
|
246 | 1 |
Expression arg = (*exps)[i]; |
247 | 1 |
if (!arg) |
248 | 1 |
continue; |
249 |
|
|
250 |
// Look for tuple with 0 members
|
|
251 | 1 |
if (auto e = arg.isTypeExp()) |
252 |
{
|
|
253 | 1 |
if (auto tt = e.type.toBasetype().isTypeTuple()) |
254 |
{
|
|
255 | 1 |
if (!tt.arguments || tt.arguments.dim == 0) |
256 |
{
|
|
257 | 1 |
exps.remove(i); |
258 | 1 |
if (i == exps.dim) |
259 | 1 |
return; |
260 |
}
|
|
261 |
else // Expand a TypeTuple |
|
262 |
{
|
|
263 | 1 |
exps.remove(i); |
264 | 1 |
auto texps = new Expressions(tt.arguments.length); |
265 | 1 |
foreach (j, a; *tt.arguments) |
266 | 1 |
(*texps)[j] = new TypeExp(e.loc, a.type); |
267 | 1 |
exps.insert(i, texps); |
268 |
}
|
|
269 | 1 |
i--; |
270 | 1 |
continue; |
271 |
}
|
|
272 |
}
|
|
273 |
|
|
274 |
// Inline expand all the tuples
|
|
275 | 1 |
while (arg.op == TOK.tuple) |
276 |
{
|
|
277 | 1 |
TupleExp te = cast(TupleExp)arg; |
278 | 1 |
exps.remove(i); // remove arg |
279 | 1 |
exps.insert(i, te.exps); // replace with tuple contents |
280 | 1 |
if (i == exps.dim) |
281 | 1 |
return; // empty tuple, no more arguments |
282 | 1 |
(*exps)[i] = Expression.combine(te.e0, (*exps)[i]); |
283 | 1 |
arg = (*exps)[i]; |
284 |
}
|
|
285 |
}
|
|
286 |
}
|
|
287 |
|
|
288 |
/****************************************
|
|
289 |
* Expand alias this tuples.
|
|
290 |
*/
|
|
291 |
TupleDeclaration isAliasThisTuple(Expression e) |
|
292 |
{
|
|
293 | 1 |
if (!e.type) |
294 |
return null; |
|
295 |
|
|
296 | 1 |
Type t = e.type.toBasetype(); |
297 | 1 |
while (true) |
298 |
{
|
|
299 | 1 |
if (Dsymbol s = t.toDsymbol(null)) |
300 |
{
|
|
301 | 1 |
if (auto ad = s.isAggregateDeclaration()) |
302 |
{
|
|
303 | 1 |
s = ad.aliasthis ? ad.aliasthis.sym : null; |
304 | 1 |
if (s && s.isVarDeclaration()) |
305 |
{
|
|
306 | 1 |
TupleDeclaration td = s.isVarDeclaration().toAlias().isTupleDeclaration(); |
307 | 1 |
if (td && td.isexp) |
308 | 1 |
return td; |
309 |
}
|
|
310 | 1 |
if (Type att = t.aliasthisOf()) |
311 |
{
|
|
312 | 1 |
t = att; |
313 | 1 |
continue; |
314 |
}
|
|
315 |
}
|
|
316 |
}
|
|
317 | 1 |
return null; |
318 |
}
|
|
319 |
}
|
|
320 |
|
|
321 |
int expandAliasThisTuples(Expressions* exps, size_t starti = 0) |
|
322 |
{
|
|
323 | 1 |
if (!exps || exps.dim == 0) |
324 |
return -1; |
|
325 |
|
|
326 | 1 |
for (size_t u = starti; u < exps.dim; u++) |
327 |
{
|
|
328 | 1 |
Expression exp = (*exps)[u]; |
329 | 1 |
if (TupleDeclaration td = exp.isAliasThisTuple) |
330 |
{
|
|
331 | 1 |
exps.remove(u); |
332 | 1 |
foreach (i, o; *td.objects) |
333 |
{
|
|
334 | 1 |
auto d = o.isExpression().isDsymbolExp().s.isDeclaration(); |
335 | 1 |
auto e = new DotVarExp(exp.loc, exp, d); |
336 | 1 |
assert(d.type); |
337 | 1 |
e.type = d.type; |
338 | 1 |
exps.insert(u + i, e); |
339 |
}
|
|
340 |
version (none) |
|
341 |
{
|
|
342 |
printf("expansion ->\n"); |
|
343 |
foreach (e; exps) |
|
344 |
{
|
|
345 |
printf("\texps[%d] e = %s %s\n", i, Token.tochars[e.op], e.toChars()); |
|
346 |
}
|
|
347 |
}
|
|
348 | 1 |
return cast(int)u; |
349 |
}
|
|
350 |
}
|
|
351 | 1 |
return -1; |
352 |
}
|
|
353 |
|
|
354 |
/****************************************
|
|
355 |
* If `s` is a function template, i.e. the only member of a template
|
|
356 |
* and that member is a function, return that template.
|
|
357 |
* Params:
|
|
358 |
* s = symbol that might be a function template
|
|
359 |
* Returns:
|
|
360 |
* template for that function, otherwise null
|
|
361 |
*/
|
|
362 |
TemplateDeclaration getFuncTemplateDecl(Dsymbol s) |
|
363 |
{
|
|
364 | 1 |
FuncDeclaration f = s.isFuncDeclaration(); |
365 | 1 |
if (f && f.parent) |
366 |
{
|
|
367 | 1 |
if (auto ti = f.parent.isTemplateInstance()) |
368 |
{
|
|
369 | 1 |
if (!ti.isTemplateMixin() && ti.tempdecl) |
370 |
{
|
|
371 | 1 |
auto td = ti.tempdecl.isTemplateDeclaration(); |
372 | 1 |
if (td.onemember && td.ident == f.ident) |
373 |
{
|
|
374 | 1 |
return td; |
375 |
}
|
|
376 |
}
|
|
377 |
}
|
|
378 |
}
|
|
379 | 1 |
return null; |
380 |
}
|
|
381 |
|
|
382 |
/************************************************
|
|
383 |
* If we want the value of this expression, but do not want to call
|
|
384 |
* the destructor on it.
|
|
385 |
*/
|
|
386 |
Expression valueNoDtor(Expression e) |
|
387 |
{
|
|
388 | 1 |
auto ex = lastComma(e); |
389 |
|
|
390 | 1 |
if (auto ce = ex.isCallExp()) |
391 |
{
|
|
392 |
/* The struct value returned from the function is transferred
|
|
393 |
* so do not call the destructor on it.
|
|
394 |
* Recognize:
|
|
395 |
* ((S _ctmp = S.init), _ctmp).this(...)
|
|
396 |
* and make sure the destructor is not called on _ctmp
|
|
397 |
* BUG: if ex is a CommaExp, we should go down the right side.
|
|
398 |
*/
|
|
399 | 1 |
if (auto dve = ce.e1.isDotVarExp()) |
400 |
{
|
|
401 | 1 |
if (dve.var.isCtorDeclaration()) |
402 |
{
|
|
403 |
// It's a constructor call
|
|
404 | 1 |
if (auto comma = dve.e1.isCommaExp()) |
405 |
{
|
|
406 | 1 |
if (auto ve = comma.e2.isVarExp()) |
407 |
{
|
|
408 | 1 |
VarDeclaration ctmp = ve.var.isVarDeclaration(); |
409 | 1 |
if (ctmp) |
410 |
{
|
|
411 | 1 |
ctmp.storage_class |= STC.nodtor; |
412 | 1 |
assert(!ce.isLvalue()); |
413 |
}
|
|
414 |
}
|
|
415 |
}
|
|
416 |
}
|
|
417 |
}
|
|
418 |
}
|
|
419 | 1 |
else if (auto ve = ex.isVarExp()) |
420 |
{
|
|
421 | 1 |
auto vtmp = ve.var.isVarDeclaration(); |
422 | 1 |
if (vtmp && (vtmp.storage_class & STC.rvalue)) |
423 |
{
|
|
424 | 1 |
vtmp.storage_class |= STC.nodtor; |
425 |
}
|
|
426 |
}
|
|
427 | 1 |
return e; |
428 |
}
|
|
429 |
|
|
430 |
/*********************************************
|
|
431 |
* If e is an instance of a struct, and that struct has a copy constructor,
|
|
432 |
* rewrite e as:
|
|
433 |
* (tmp = e),tmp
|
|
434 |
* Input:
|
|
435 |
* sc = just used to specify the scope of created temporary variable
|
|
436 |
* destinationType = the type of the object on which the copy constructor is called;
|
|
437 |
* may be null if the struct defines a postblit
|
|
438 |
*/
|
|
439 |
private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) |
|
440 |
{
|
|
441 | 1 |
if (auto ts = e.type.baseElemOf().isTypeStruct()) |
442 |
{
|
|
443 | 1 |
StructDeclaration sd = ts.sym; |
444 | 1 |
if (sd.postblit || sd.hasCopyCtor) |
445 |
{
|
|
446 |
/* Create a variable tmp, and replace the argument e with:
|
|
447 |
* (tmp = e),tmp
|
|
448 |
* and let AssignExp() handle the construction.
|
|
449 |
* This is not the most efficient, ideally tmp would be constructed
|
|
450 |
* directly onto the stack.
|
|
451 |
*/
|
|
452 | 1 |
auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); |
453 | 1 |
if (sd.hasCopyCtor && destinationType) |
454 | 1 |
tmp.type = destinationType; |
455 | 1 |
tmp.storage_class |= STC.nodtor; |
456 | 1 |
tmp.dsymbolSemantic(sc); |
457 | 1 |
Expression de = new DeclarationExp(e.loc, tmp); |
458 | 1 |
Expression ve = new VarExp(e.loc, tmp); |
459 | 1 |
de.type = Type.tvoid; |
460 | 1 |
ve.type = e.type; |
461 | 1 |
return Expression.combine(de, ve); |
462 |
}
|
|
463 |
}
|
|
464 | 1 |
return e; |
465 |
}
|
|
466 |
|
|
467 |
/************************************************
|
|
468 |
* Handle the postblit call on lvalue, or the move of rvalue.
|
|
469 |
*
|
|
470 |
* Params:
|
|
471 |
* sc = the scope where the expression is encountered
|
|
472 |
* e = the expression the needs to be moved or copied (source)
|
|
473 |
* t = if the struct defines a copy constructor, the type of the destination
|
|
474 |
*
|
|
475 |
* Returns:
|
|
476 |
* The expression that copy constructs or moves the value.
|
|
477 |
*/
|
|
478 |
extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) |
|
479 |
{
|
|
480 | 1 |
if (auto ce = e.isCondExp()) |
481 |
{
|
|
482 | 1 |
ce.e1 = doCopyOrMove(sc, ce.e1); |
483 | 1 |
ce.e2 = doCopyOrMove(sc, ce.e2); |
484 |
}
|
|
485 |
else
|
|
486 |
{
|
|
487 | 1 |
e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e); |
488 |
}
|
|
489 | 1 |
return e; |
490 |
}
|
|
491 |
|
|
492 |
/****************************************************************/
|
|
493 |
/* A type meant as a union of all the Expression types,
|
|
494 |
* to serve essentially as a Variant that will sit on the stack
|
|
495 |
* during CTFE to reduce memory consumption.
|
|
496 |
*/
|
|
497 |
extern (C++) struct UnionExp |
|
498 |
{
|
|
499 |
// yes, default constructor does nothing
|
|
500 |
extern (D) this(Expression e) |
|
501 |
{
|
|
502 |
memcpy(&this, cast(void*)e, e.size); |
|
503 |
}
|
|
504 |
|
|
505 |
/* Extract pointer to Expression
|
|
506 |
*/
|
|
507 |
extern (C++) Expression exp() return |
|
508 |
{
|
|
509 | 1 |
return cast(Expression)&u; |
510 |
}
|
|
511 |
|
|
512 |
/* Convert to an allocated Expression
|
|
513 |
*/
|
|
514 |
extern (C++) Expression copy() |
|
515 |
{
|
|
516 | 1 |
Expression e = exp(); |
517 |
//if (e.size > sizeof(u)) printf("%s\n", Token::toChars(e.op));
|
|
518 | 1 |
assert(e.size <= u.sizeof); |
519 | 1 |
switch (e.op) |
520 |
{
|
|
521 | 1 |
case TOK.cantExpression: return CTFEExp.cantexp; |
522 |
case TOK.voidExpression: return CTFEExp.voidexp; |
|
523 |
case TOK.break_: return CTFEExp.breakexp; |
|
524 |
case TOK.continue_: return CTFEExp.continueexp; |
|
525 |
case TOK.goto_: return CTFEExp.gotoexp; |
|
526 | 1 |
default: return e.copy(); |
527 |
}
|
|
528 |
}
|
|
529 |
|
|
530 |
private: |
|
531 |
// Ensure that the union is suitably aligned.
|
|
532 |
align(8) union __AnonStruct__u |
|
533 |
{
|
|
534 |
char[__traits(classInstanceSize, Expression)] exp; |
|
535 |
char[__traits(classInstanceSize, IntegerExp)] integerexp; |
|
536 |
char[__traits(classInstanceSize, ErrorExp)] errorexp; |
|
537 |
char[__traits(classInstanceSize, RealExp)] realexp; |
|
538 |
char[__traits(classInstanceSize, ComplexExp)] complexexp; |
|
539 |
char[__traits(classInstanceSize, SymOffExp)] symoffexp; |
|
540 |
char[__traits(classInstanceSize, StringExp)] stringexp; |
|
541 |
char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp; |
|
542 |
char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp; |
|
543 |
char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp; |
|
544 |
char[__traits(classInstanceSize, NullExp)] nullexp; |
|
545 |
char[__traits(classInstanceSize, DotVarExp)] dotvarexp; |
|
546 |
char[__traits(classInstanceSize, AddrExp)] addrexp; |
|
547 |
char[__traits(classInstanceSize, IndexExp)] indexexp; |
|
548 |
char[__traits(classInstanceSize, SliceExp)] sliceexp; |
|
549 |
char[__traits(classInstanceSize, VectorExp)] vectorexp; |
|
550 |
}
|
|
551 |
|
|
552 |
__AnonStruct__u u; |
|
553 |
}
|
|
554 |
|
|
555 |
/********************************
|
|
556 |
* Test to see if two reals are the same.
|
|
557 |
* Regard NaN's as equivalent.
|
|
558 |
* Regard +0 and -0 as different.
|
|
559 |
* Params:
|
|
560 |
* x1 = first operand
|
|
561 |
* x2 = second operand
|
|
562 |
* Returns:
|
|
563 |
* true if x1 is x2
|
|
564 |
* else false
|
|
565 |
*/
|
|
566 |
bool RealIdentical(real_t x1, real_t x2) |
|
567 |
{
|
|
568 | 1 |
return (CTFloat.isNaN(x1) && CTFloat.isNaN(x2)) || CTFloat.isIdentical(x1, x2); |
569 |
}
|
|
570 |
|
|
571 |
/************************ TypeDotIdExp ************************************/
|
|
572 |
/* Things like:
|
|
573 |
* int.size
|
|
574 |
* foo.size
|
|
575 |
* (foo).size
|
|
576 |
* cast(foo).size
|
|
577 |
*/
|
|
578 |
DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident) |
|
579 |
{
|
|
580 | 1 |
return new DotIdExp(loc, new TypeExp(loc, type), ident); |
581 |
}
|
|
582 |
|
|
583 |
/***************************************************
|
|
584 |
* Given an Expression, find the variable it really is.
|
|
585 |
*
|
|
586 |
* For example, `a[index]` is really `a`, and `s.f` is really `s`.
|
|
587 |
* Params:
|
|
588 |
* e = Expression to look at
|
|
589 |
* Returns:
|
|
590 |
* variable if there is one, null if not
|
|
591 |
*/
|
|
592 |
VarDeclaration expToVariable(Expression e) |
|
593 |
{
|
|
594 | 1 |
while (1) |
595 |
{
|
|
596 | 1 |
switch (e.op) |
597 |
{
|
|
598 | 1 |
case TOK.variable: |
599 | 1 |
return (cast(VarExp)e).var.isVarDeclaration(); |
600 |
|
|
601 | 1 |
case TOK.dotVariable: |
602 | 1 |
e = (cast(DotVarExp)e).e1; |
603 | 1 |
continue; |
604 |
|
|
605 | 1 |
case TOK.index: |
606 |
{
|
|
607 | 1 |
IndexExp ei = cast(IndexExp)e; |
608 | 1 |
e = ei.e1; |
609 | 1 |
Type ti = e.type.toBasetype(); |
610 | 1 |
if (ti.ty == Tsarray) |
611 | 1 |
continue; |
612 | 1 |
return null; |
613 |
}
|
|
614 |
|
|
615 |
case TOK.slice: |
|
616 |
{
|
|
617 |
SliceExp ei = cast(SliceExp)e; |
|
618 |
e = ei.e1; |
|
619 |
Type ti = e.type.toBasetype(); |
|
620 |
if (ti.ty == Tsarray) |
|
621 |
continue; |
|
622 |
return null; |
|
623 |
}
|
|
624 |
|
|
625 | 1 |
case TOK.this_: |
626 | 1 |
case TOK.super_: |
627 | 1 |
return (cast(ThisExp)e).var.isVarDeclaration(); |
628 |
|
|
629 | 1 |
default: |
630 | 1 |
return null; |
631 |
}
|
|
632 |
}
|
|
633 |
}
|
|
634 |
|
|
635 |
enum OwnedBy : ubyte |
|
636 |
{
|
|
637 |
code, // normal code expression in AST |
|
638 |
ctfe, // value expression for CTFE |
|
639 |
cache, // constant value cached for CTFE |
|
640 |
}
|
|
641 |
|
|
642 |
enum WANTvalue = 0; // default |
|
643 |
enum WANTexpand = 1; // expand const/immutable variables if possible |
|
644 |
|
|
645 |
/***********************************************************
|
|
646 |
* http://dlang.org/spec/expression.html#expression
|
|
647 |
*/
|
|
648 |
extern (C++) abstract class Expression : ASTNode |
|
649 |
{
|
|
650 |
const TOK op; // to minimize use of dynamic_cast |
|
651 |
ubyte size; // # of bytes in Expression so we can copy() it |
|
652 |
ubyte parens; // if this is a parenthesized expression |
|
653 |
Type type; // !=null means that semantic() has been run |
|
654 |
Loc loc; // file location |
|
655 |
|
|
656 | 1 |
extern (D) this(const ref Loc loc, TOK op, int size) |
657 |
{
|
|
658 |
//printf("Expression::Expression(op = %d) this = %p\n", op, this);
|
|
659 | 1 |
this.loc = loc; |
660 | 1 |
this.op = op; |
661 | 1 |
this.size = cast(ubyte)size; |
662 |
}
|
|
663 |
|
|
664 |
static void _init() |
|
665 |
{
|
|
666 | 1 |
CTFEExp.cantexp = new CTFEExp(TOK.cantExpression); |
667 | 1 |
CTFEExp.voidexp = new CTFEExp(TOK.voidExpression); |
668 | 1 |
CTFEExp.breakexp = new CTFEExp(TOK.break_); |
669 | 1 |
CTFEExp.continueexp = new CTFEExp(TOK.continue_); |
670 | 1 |
CTFEExp.gotoexp = new CTFEExp(TOK.goto_); |
671 | 1 |
CTFEExp.showcontext = new CTFEExp(TOK.showCtfeContext); |
672 |
}
|
|
673 |
|
|
674 |
/**
|
|
675 |
* Deinitializes the global state of the compiler.
|
|
676 |
*
|
|
677 |
* This can be used to restore the state set by `_init` to its original
|
|
678 |
* state.
|
|
679 |
*/
|
|
680 |
static void deinitialize() |
|
681 |
{
|
|
682 |
CTFEExp.cantexp = CTFEExp.cantexp.init; |
|
683 |
CTFEExp.voidexp = CTFEExp.voidexp.init; |
|
684 |
CTFEExp.breakexp = CTFEExp.breakexp.init; |
|
685 |
CTFEExp.continueexp = CTFEExp.continueexp.init; |
|
686 |
CTFEExp.gotoexp = CTFEExp.gotoexp.init; |
|
687 |
CTFEExp.showcontext = CTFEExp.showcontext.init; |
|
688 |
}
|
|
689 |
|
|
690 |
/*********************************
|
|
691 |
* Does *not* do a deep copy.
|
|
692 |
*/
|
|
693 |
final Expression copy() |
|
694 |
{
|
|
695 | 1 |
Expression e; |
696 | 1 |
if (!size) |
697 |
{
|
|
698 |
debug
|
|
699 |
{
|
|
700 |
fprintf(stderr, "No expression copy for: %s\n", toChars()); |
|
701 |
printf("op = %d\n", op); |
|
702 |
}
|
|
703 |
assert(0); |
|
704 |
}
|
|
705 |
|
|
706 |
// memory never freed, so can use the faster bump-pointer-allocation
|
|
707 | 1 |
e = cast(Expression)allocmemory(size); |
708 |
//printf("Expression::copy(op = %d) e = %p\n", op, e);
|
|
709 | 1 |
return cast(Expression)memcpy(cast(void*)e, cast(void*)this, size); |
710 |
}
|
|
711 |
|
|
712 |
Expression syntaxCopy() |
|
713 |
{
|
|
714 |
//printf("Expression::syntaxCopy()\n");
|
|
715 |
//print();
|
|
716 | 1 |
return copy(); |
717 |
}
|
|
718 |
|
|
719 |
// kludge for template.isExpression()
|
|
720 |
override final DYNCAST dyncast() const |
|
721 |
{
|
|
722 | 1 |
return DYNCAST.expression; |
723 |
}
|
|
724 |
|
|
725 |
override const(char)* toChars() const |
|
726 |
{
|
|
727 | 1 |
OutBuffer buf; |
728 | 1 |
HdrGenState hgs; |
729 | 1 |
toCBuffer(this, &buf, &hgs); |
730 | 1 |
return buf.extractChars(); |
731 |
}
|
|
732 |
|
|
733 |
final void error(const(char)* format, ...) const |
|
734 |
{
|
|
735 | 1 |
if (type != Type.terror) |
736 |
{
|
|
737 | 1 |
va_list ap; |
738 | 1 |
va_start(ap, format); |
739 | 1 |
.verror(loc, format, ap); |
740 | 1 |
va_end(ap); |
741 |
}
|
|
742 |
}
|
|
743 |
|
|
744 |
final void errorSupplemental(const(char)* format, ...) |
|
745 |
{
|
|
746 | 1 |
if (type == Type.terror) |
747 |
return; |
|
748 |
|
|
749 | 1 |
va_list ap; |
750 | 1 |
va_start(ap, format); |
751 | 1 |
.verrorSupplemental(loc, format, ap); |
752 | 1 |
va_end(ap); |
753 |
}
|
|
754 |
|
|
755 |
final void warning(const(char)* format, ...) const |
|
756 |
{
|
|
757 | 1 |
if (type != Type.terror) |
758 |
{
|
|
759 | 1 |
va_list ap; |
760 | 1 |
va_start(ap, format); |
761 | 1 |
.vwarning(loc, format, ap); |
762 | 1 |
va_end(ap); |
763 |
}
|
|
764 |
}
|
|
765 |
|
|
766 |
final void deprecation(const(char)* format, ...) const |
|
767 |
{
|
|
768 | 1 |
if (type != Type.terror) |
769 |
{
|
|
770 | 1 |
va_list ap; |
771 | 1 |
va_start(ap, format); |
772 | 1 |
.vdeprecation(loc, format, ap); |
773 | 1 |
va_end(ap); |
774 |
}
|
|
775 |
}
|
|
776 |
|
|
777 |
/**********************************
|
|
778 |
* Combine e1 and e2 by CommaExp if both are not NULL.
|
|
779 |
*/
|
|
780 |
extern (D) static Expression combine(Expression e1, Expression e2) |
|
781 |
{
|
|
782 | 1 |
if (e1) |
783 |
{
|
|
784 | 1 |
if (e2) |
785 |
{
|
|
786 | 1 |
e1 = new CommaExp(e1.loc, e1, e2); |
787 | 1 |
e1.type = e2.type; |
788 |
}
|
|
789 |
}
|
|
790 |
else
|
|
791 | 1 |
e1 = e2; |
792 | 1 |
return e1; |
793 |
}
|
|
794 |
|
|
795 |
extern (D) static Expression combine(Expression e1, Expression e2, Expression e3) |
|
796 |
{
|
|
797 | 1 |
return combine(combine(e1, e2), e3); |
798 |
}
|
|
799 |
|
|
800 |
extern (D) static Expression combine(Expression e1, Expression e2, Expression e3, Expression e4) |
|
801 |
{
|
|
802 | 1 |
return combine(combine(e1, e2), combine(e3, e4)); |
803 |
}
|
|
804 |
|
|
805 |
/**********************************
|
|
806 |
* If 'e' is a tree of commas, returns the rightmost expression
|
|
807 |
* by stripping off it from the tree. The remained part of the tree
|
|
808 |
* is returned via e0.
|
|
809 |
* Otherwise 'e' is directly returned and e0 is set to NULL.
|
|
810 |
*/
|
|
811 |
extern (D) static Expression extractLast(Expression e, out Expression e0) |
|
812 |
{
|
|
813 | 1 |
if (e.op != TOK.comma) |
814 |
{
|
|
815 | 1 |
return e; |
816 |
}
|
|
817 |
|
|
818 | 1 |
CommaExp ce = cast(CommaExp)e; |
819 | 1 |
if (ce.e2.op != TOK.comma) |
820 |
{
|
|
821 | 1 |
e0 = ce.e1; |
822 | 1 |
return ce.e2; |
823 |
}
|
|
824 |
else
|
|
825 |
{
|
|
826 | 1 |
e0 = e; |
827 |
|
|
828 | 1 |
Expression* pce = &ce.e2; |
829 | 1 |
while ((cast(CommaExp)(*pce)).e2.op == TOK.comma) |
830 |
{
|
|
831 |
pce = &(cast(CommaExp)(*pce)).e2; |
|
832 |
}
|
|
833 | 1 |
assert((*pce).op == TOK.comma); |
834 | 1 |
ce = cast(CommaExp)(*pce); |
835 | 1 |
*pce = ce.e1; |
836 |
|
|
837 | 1 |
return ce.e2; |
838 |
}
|
|
839 |
}
|
|
840 |
|
|
841 |
extern (D) static Expressions* arraySyntaxCopy(Expressions* exps) |
|
842 |
{
|
|
843 | 1 |
Expressions* a = null; |
844 | 1 |
if (exps) |
845 |
{
|
|
846 | 1 |
a = new Expressions(exps.dim); |
847 | 1 |
foreach (i, e; *exps) |
848 |
{
|
|
849 | 1 |
(*a)[i] = e ? e.syntaxCopy() : null; |
850 |
}
|
|
851 |
}
|
|
852 | 1 |
return a; |
853 |
}
|
|
854 |
|
|
855 |
dinteger_t toInteger() |
|
856 |
{
|
|
857 |
//printf("Expression %s\n", Token::toChars(op));
|
|
858 | 1 |
error("integer constant expression expected instead of `%s`", toChars()); |
859 | 1 |
return 0; |
860 |
}
|
|
861 |
|
|
862 |
uinteger_t toUInteger() |
|
863 |
{
|
|
864 |
//printf("Expression %s\n", Token::toChars(op));
|
|
865 | 1 |
return cast(uinteger_t)toInteger(); |
866 |
}
|
|
867 |
|
|
868 |
real_t toReal() |
|
869 |
{
|
|
870 |
error("floating point constant expression expected instead of `%s`", toChars()); |
|
871 |
return CTFloat.zero; |
|
872 |
}
|
|
873 |
|
|
874 |
real_t toImaginary() |
|
875 |
{
|
|
876 |
error("floating point constant expression expected instead of `%s`", toChars()); |
|
877 |
return CTFloat.zero; |
|
878 |
}
|
|
879 |
|
|
880 |
complex_t toComplex() |
|
881 |
{
|
|
882 |
error("floating point constant expression expected instead of `%s`", toChars()); |
|
883 |
return complex_t(CTFloat.zero); |
|
884 |
}
|
|
885 |
|
|
886 |
StringExp toStringExp() |
|
887 |
{
|
|
888 | 1 |
return null; |
889 |
}
|
|
890 |
|
|
891 |
TupleExp toTupleExp() |
|
892 |
{
|
|
893 |
return null; |
|
894 |
}
|
|
895 |
|
|
896 |
/***************************************
|
|
897 |
* Return !=0 if expression is an lvalue.
|
|
898 |
*/
|
|
899 |
bool isLvalue() |
|
900 |
{
|
|
901 | 1 |
return false; |
902 |
}
|
|
903 |
|
|
904 |
/*******************************
|
|
905 |
* Give error if we're not an lvalue.
|
|
906 |
* If we can, convert expression to be an lvalue.
|
|
907 |
*/
|
|
908 |
Expression toLvalue(Scope* sc, Expression e) |
|
909 |
{
|
|
910 | 1 |
if (!e) |
911 | 1 |
e = this; |
912 | 1 |
else if (!loc.isValid()) |
913 |
loc = e.loc; |
|
914 |
|
|
915 | 1 |
if (e.op == TOK.type) |
916 | 1 |
error("`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind()); |
917 |
else
|
|
918 | 1 |
error("`%s` is not an lvalue and cannot be modified", e.toChars()); |
919 |
|
|
920 | 1 |
return ErrorExp.get(); |
921 |
}
|
|
922 |
|
|
923 |
Expression modifiableLvalue(Scope* sc, Expression e) |
|
924 |
{
|
|
925 |
//printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars());
|
|
926 |
// See if this expression is a modifiable lvalue (i.e. not const)
|
|
927 | 1 |
if (checkModifiable(sc) == Modifiable.yes) |
928 |
{
|
|
929 | 1 |
assert(type); |
930 | 1 |
if (!type.isMutable()) |
931 |
{
|
|
932 | 1 |
if (auto dve = this.isDotVarExp()) |
933 |
{
|
|
934 | 1 |
if (isNeedThisScope(sc, dve.var)) |
935 | 1 |
for (Dsymbol s = sc.func; s; s = s.toParentLocal()) |
936 |
{
|
|
937 | 1 |
FuncDeclaration ff = s.isFuncDeclaration(); |
938 | 1 |
if (!ff) |
939 | 1 |
break; |
940 | 1 |
if (!ff.type.isMutable) |
941 |
{
|
|
942 | 1 |
error("cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod)); |
943 | 1 |
return ErrorExp.get(); |
944 |
}
|
|
945 |
}
|
|
946 |
}
|
|
947 | 1 |
error("cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars()); |
948 | 1 |
return ErrorExp.get(); |
949 |
}
|
|
950 | 1 |
else if (!type.isAssignable()) |
951 |
{
|
|
952 | 1 |
error("cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members", |
953 |
toChars(), type.toChars()); |
|
954 | 1 |
return ErrorExp.get(); |
955 |
}
|
|
956 |
}
|
|
957 | 1 |
return toLvalue(sc, e); |
958 |
}
|
|
959 |
|
|
960 |
final Expression implicitCastTo(Scope* sc, Type t) |
|
961 |
{
|
|
962 | 1 |
return .implicitCastTo(this, sc, t); |
963 |
}
|
|
964 |
|
|
965 |
final MATCH implicitConvTo(Type t) |
|
966 |
{
|
|
967 | 1 |
return .implicitConvTo(this, t); |
968 |
}
|
|
969 |
|
|
970 |
final Expression castTo(Scope* sc, Type t) |
|
971 |
{
|
|
972 | 1 |
return .castTo(this, sc, t); |
973 |
}
|
|
974 |
|
|
975 |
/****************************************
|
|
976 |
* Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc.
|
|
977 |
*/
|
|
978 |
Expression resolveLoc(const ref Loc loc, Scope* sc) |
|
979 |
{
|
|
980 | 1 |
this.loc = loc; |
981 | 1 |
return this; |
982 |
}
|
|
983 |
|
|
984 |
/****************************************
|
|
985 |
* Check that the expression has a valid type.
|
|
986 |
* If not, generates an error "... has no type".
|
|
987 |
* Returns:
|
|
988 |
* true if the expression is not valid.
|
|
989 |
* Note:
|
|
990 |
* When this function returns true, `checkValue()` should also return true.
|
|
991 |
*/
|
|
992 |
bool checkType() |
|
993 |
{
|
|
994 | 1 |
return false; |
995 |
}
|
|
996 |
|
|
997 |
/****************************************
|
|
998 |
* Check that the expression has a valid value.
|
|
999 |
* If not, generates an error "... has no value".
|
|
1000 |
* Returns:
|
|
1001 |
* true if the expression is not valid or has void type.
|
|
1002 |
*/
|
|
1003 |
bool checkValue() |
|
1004 |
{
|
|
1005 | 1 |
if (type && type.toBasetype().ty == Tvoid) |
1006 |
{
|
|
1007 | 1 |
error("expression `%s` is `void` and has no value", toChars()); |
1008 |
//print(); assert(0);
|
|
1009 | 1 |
if (!global.gag) |
1010 | 1 |
type = Type.terror; |
1011 | 1 |
return true; |
1012 |
}
|
|
1013 | 1 |
return false; |
1014 |
}
|
|
1015 |
|
|
1016 |
extern (D) final bool checkScalar() |
|
1017 |
{
|
|
1018 | 1 |
if (op == TOK.error) |
1019 | 1 |
return true; |
1020 | 1 |
if (type.toBasetype().ty == Terror) |
1021 |
return true; |
|
1022 | 1 |
if (!type.isscalar()) |
1023 |
{
|
|
1024 | 1 |
error("`%s` is not a scalar, it is a `%s`", toChars(), type.toChars()); |
1025 | 1 |
return true; |
1026 |
}
|
|
1027 | 1 |
return checkValue(); |
1028 |
}
|
|
1029 |
|
|
1030 |
extern (D) final bool checkNoBool() |
|
1031 |
{
|
|
1032 | 1 |
if (op == TOK.error) |
1033 |
return true; |
|
1034 | 1 |
if (type.toBasetype().ty == Terror) |
1035 |
return true; |
|
1036 | 1 |
if (type.toBasetype().ty == Tbool) |
1037 |
{
|
|
1038 |
error("operation not allowed on `bool` `%s`", toChars()); |
|
1039 |
return true; |
|
1040 |
}
|
|
1041 | 1 |
return false; |
1042 |
}
|
|
1043 |
|
|
1044 |
extern (D) final bool checkIntegral() |
|
1045 |
{
|
|
1046 | 1 |
if (op == TOK.error) |
1047 |
return true; |
|
1048 | 1 |
if (type.toBasetype().ty == Terror) |
1049 |
return true; |
|
1050 | 1 |
if (!type.isintegral()) |
1051 |
{
|
|
1052 | 1 |
error("`%s` is not of integral type, it is a `%s`", toChars(), type.toChars()); |
1053 | 1 |
return true; |
1054 |
}
|
|
1055 | 1 |
return checkValue(); |
1056 |
}
|
|
1057 |
|
|
1058 |
extern (D) final bool checkArithmetic() |
|
1059 |
{
|
|
1060 | 1 |
if (op == TOK.error) |
1061 |
return true; |
|
1062 | 1 |
if (type.toBasetype().ty == Terror) |
1063 |
return true; |
|
1064 | 1 |
if (!type.isintegral() && !type.isfloating()) |
1065 |
{
|
|
1066 | 1 |
error("`%s` is not of arithmetic type, it is a `%s`", toChars(), type.toChars()); |
1067 | 1 |
return true; |
1068 |
}
|
|
1069 | 1 |
return checkValue(); |
1070 |
}
|
|
1071 |
|
|
1072 |
final bool checkDeprecated(Scope* sc, Dsymbol s) |
|
1073 |
{
|
|
1074 | 1 |
return s.checkDeprecated(loc, sc); |
1075 |
}
|
|
1076 |
|
|
1077 |
extern (D) final bool checkDisabled(Scope* sc, Dsymbol s) |
|
1078 |
{
|
|
1079 | 1 |
if (auto d = s.isDeclaration()) |
1080 |
{
|
|
1081 | 1 |
return d.checkDisabled(loc, sc); |
1082 |
}
|
|
1083 |
|
|
1084 | 1 |
return false; |
1085 |
}
|
|
1086 |
|
|
1087 |
/*********************************************
|
|
1088 |
* Calling function f.
|
|
1089 |
* Check the purity, i.e. if we're in a pure function
|
|
1090 |
* we can only call other pure functions.
|
|
1091 |
* Returns true if error occurs.
|
|
1092 |
*/
|
|
1093 |
extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f) |
|
1094 |
{
|
|
1095 | 1 |
if (!sc.func) |
1096 | 1 |
return false; |
1097 | 1 |
if (sc.func == f) |
1098 | 1 |
return false; |
1099 | 1 |
if (sc.intypeof == 1) |
1100 | 1 |
return false; |
1101 | 1 |
if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) |
1102 | 1 |
return false; |
1103 |
|
|
1104 |
// If the call has a pure parent, then the called func must be pure.
|
|
1105 | 1 |
if (!f.isPure() && checkImpure(sc)) |
1106 |
{
|
|
1107 | 1 |
error("`pure` %s `%s` cannot call impure %s `%s`", |
1108 |
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), |
|
1109 |
f.toPrettyChars()); |
|
1110 | 1 |
return true; |
1111 |
}
|
|
1112 | 1 |
return false; |
1113 |
}
|
|
1114 |
|
|
1115 |
/*******************************************
|
|
1116 |
* Accessing variable v.
|
|
1117 |
* Check for purity and safety violations.
|
|
1118 |
* Returns true if error occurs.
|
|
1119 |
*/
|
|
1120 |
extern (D) final bool checkPurity(Scope* sc, VarDeclaration v) |
|
1121 |
{
|
|
1122 |
//printf("v = %s %s\n", v.type.toChars(), v.toChars());
|
|
1123 |
/* Look for purity and safety violations when accessing variable v
|
|
1124 |
* from current function.
|
|
1125 |
*/
|
|
1126 | 1 |
if (!sc.func) |
1127 | 1 |
return false; |
1128 | 1 |
if (sc.intypeof == 1) |
1129 | 1 |
return false; // allow violations inside typeof(expression) |
1130 | 1 |
if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) |
1131 | 1 |
return false; // allow violations inside compile-time evaluated expressions and debug conditionals |
1132 | 1 |
if (v.ident == Id.ctfe) |
1133 | 1 |
return false; // magic variable never violates pure and safe |
1134 | 1 |
if (v.isImmutable()) |
1135 | 1 |
return false; // always safe and pure to access immutables... |
1136 | 1 |
if (v.isConst() && !v.isRef() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf())) |
1137 | 1 |
return false; // or const global/parameter values which have no mutable indirections |
1138 | 1 |
if (v.storage_class & STC.manifest) |
1139 | 1 |
return false; // ...or manifest constants |
1140 |
|
|
1141 | 1 |
if (v.type.ty == Tstruct) |
1142 |
{
|
|
1143 | 1 |
StructDeclaration sd = (cast(TypeStruct)v.type).sym; |
1144 | 1 |
if (sd.hasNoFields) |
1145 | 1 |
return false; |
1146 |
}
|
|
1147 |
|
|
1148 | 1 |
bool err = false; |
1149 | 1 |
if (v.isDataseg()) |
1150 |
{
|
|
1151 |
// https://issues.dlang.org/show_bug.cgi?id=7533
|
|
1152 |
// Accessing implicit generated __gate is pure.
|
|
1153 | 1 |
if (v.ident == Id.gate) |
1154 | 1 |
return false; |
1155 |
|
|
1156 | 1 |
if (checkImpure(sc)) |
1157 |
{
|
|
1158 | 1 |
error("`pure` %s `%s` cannot access mutable static data `%s`", |
1159 |
sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); |
|
1160 | 1 |
err = true; |
1161 |
}
|
|
1162 |
}
|
|
1163 |
else
|
|
1164 |
{
|
|
1165 |
/* Given:
|
|
1166 |
* void f() {
|
|
1167 |
* int fx;
|
|
1168 |
* pure void g() {
|
|
1169 |
* int gx;
|
|
1170 |
* /+pure+/ void h() {
|
|
1171 |
* int hx;
|
|
1172 |
* /+pure+/ void i() { }
|
|
1173 |
* }
|
|
1174 |
* }
|
|
1175 |
* }
|
|
1176 |
* i() can modify hx and gx but not fx
|
|
1177 |
*/
|
|
1178 |
|
|
1179 | 1 |
Dsymbol vparent = v.toParent2(); |
1180 | 1 |
for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent)) |
1181 |
{
|
|
1182 | 1 |
if (s == vparent) |
1183 | 1 |
break; |
1184 |
|
|
1185 | 1 |
if (AggregateDeclaration ad = s.isAggregateDeclaration()) |
1186 |
{
|
|
1187 | 1 |
if (ad.isNested()) |
1188 | 1 |
continue; |
1189 | 1 |
break; |
1190 |
}
|
|
1191 | 1 |
FuncDeclaration ff = s.isFuncDeclaration(); |
1192 | 1 |
if (!ff) |
1193 | 1 |
break; |
1194 | 1 |
if (ff.isNested() || ff.isThis()) |
1195 |
{
|
|
1196 | 1 |
if (ff.type.isImmutable() || |
1197 | 1 |
ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) |
1198 |
{
|
|
1199 | 1 |
OutBuffer ffbuf; |
1200 | 1 |
OutBuffer vbuf; |
1201 | 1 |
MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); |
1202 | 1 |
MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); |
1203 | 1 |
error("%s%s `%s` cannot access %sdata `%s`", |
1204 |
ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); |
|
1205 | 1 |
err = true; |
1206 | 1 |
break; |
1207 |
}
|
|
1208 | 1 |
continue; |
1209 |
}
|
|
1210 | 1 |
break; |
1211 |
}
|
|
1212 |
}
|
|
1213 |
|
|
1214 |
/* Do not allow safe functions to access __gshared data
|
|
1215 |
*/
|
|
1216 | 1 |
if (v.storage_class & STC.gshared) |
1217 |
{
|
|
1218 | 1 |
if (sc.func.setUnsafe()) |
1219 |
{
|
|
1220 | 1 |
error("`@safe` %s `%s` cannot access `__gshared` data `%s`", |
1221 |
sc.func.kind(), sc.func.toChars(), v.toChars()); |
|
1222 | 1 |
err = true; |
1223 |
}
|
|
1224 |
}
|
|
1225 |
|
|
1226 | 1 |
return err; |
1227 |
}
|
|
1228 |
|
|
1229 |
/*
|
|
1230 |
Check if sc.func is impure or can be made impure.
|
|
1231 |
Returns true on error, i.e. if sc.func is pure and cannot be made impure.
|
|
1232 |
*/
|
|
1233 |
private static bool checkImpure(Scope* sc) |
|
1234 |
{
|
|
1235 | 1 |
return sc.func && (sc.flags & SCOPE.compile |
1236 | 1 |
? sc.func.isPureBypassingInference() >= PURE.weak |
1237 | 1 |
: sc.func.setImpure()); |
1238 |
}
|
|
1239 |
|
|
1240 |
/*********************************************
|
|
1241 |
* Calling function f.
|
|
1242 |
* Check the safety, i.e. if we're in a @safe function
|
|
1243 |
* we can only call @safe or @trusted functions.
|
|
1244 |
* Returns true if error occurs.
|
|
1245 |
*/
|
|
1246 |
extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f) |
|
1247 |
{
|
|
1248 | 1 |
if (!sc.func) |
1249 | 1 |
return false; |
1250 | 1 |
if (sc.func == f) |
1251 | 1 |
return false; |
1252 | 1 |
if (sc.intypeof == 1) |
1253 | 1 |
return false; |
1254 | 1 |
if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) |
1255 | 1 |
return false; |
1256 |
|
|
1257 | 1 |
if (!f.isSafe() && !f.isTrusted()) |
1258 |
{
|
|
1259 | 1 |
if (sc.flags & SCOPE.compile ? sc.func.isSafeBypassingInference() : sc.func.setUnsafe()) |
1260 |
{
|
|
1261 | 1 |
if (!loc.isValid()) // e.g. implicitly generated dtor |
1262 | 1 |
loc = sc.func.loc; |
1263 |
|
|
1264 | 1 |
const prettyChars = f.toPrettyChars(); |
1265 | 1 |
error("`@safe` %s `%s` cannot call `@system` %s `%s`", |
1266 |
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), |
|
1267 |
prettyChars); |
|
1268 | 1 |
.errorSupplemental(f.loc, "`%s` is declared here", prettyChars); |
1269 | 1 |
return true; |
1270 |
}
|
|
1271 |
}
|
|
1272 | 1 |
return false; |
1273 |
}
|
|
1274 |
|
|
1275 |
/*********************************************
|
|
1276 |
* Calling function f.
|
|
1277 |
* Check the @nogc-ness, i.e. if we're in a @nogc function
|
|
1278 |
* we can only call other @nogc functions.
|
|
1279 |
* Returns true if error occurs.
|
|
1280 |
*/
|
|
1281 |
extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f) |
|
1282 |
{
|
|
1283 | 1 |
if (!sc.func) |
1284 | 1 |
return false; |
1285 | 1 |
if (sc.func == f) |
1286 | 1 |
return false; |
1287 | 1 |
if (sc.intypeof == 1) |
1288 | 1 |
return false; |
1289 | 1 |
if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) |
1290 | 1 |
return false; |
1291 |
|
|
1292 | 1 |
if (!f.isNogc()) |
1293 |
{
|
|
1294 | 1 |
if (sc.flags & SCOPE.compile ? sc.func.isNogcBypassingInference() : sc.func.setGC()) |
1295 |
{
|
|
1296 | 1 |
if (loc.linnum == 0) // e.g. implicitly generated dtor |
1297 | 1 |
loc = sc.func.loc; |
1298 |
|
|
1299 |
// Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
|
|
1300 |
// so don't print anything to avoid double error messages.
|
|
1301 | 1 |
if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT)) |
1302 | 1 |
error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", |
1303 |
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); |
|
1304 | 1 |
return true; |
1305 |
}
|
|
1306 |
}
|
|
1307 | 1 |
return false; |
1308 |
}
|
|
1309 |
|
|
1310 |
/********************************************
|
|
1311 |
* Check that the postblit is callable if t is an array of structs.
|
|
1312 |
* Returns true if error happens.
|
|
1313 |
*/
|
|
1314 |
extern (D) final bool checkPostblit(Scope* sc, Type t) |
|
1315 |
{
|
|
1316 | 1 |
if (auto ts = t.baseElemOf().isTypeStruct()) |
1317 |
{
|
|
1318 | 1 |
if (global.params.useTypeInfo) |
1319 |
{
|
|
1320 |
// https://issues.dlang.org/show_bug.cgi?id=11395
|
|
1321 |
// Require TypeInfo generation for array concatenation
|
|
1322 | 1 |
semanticTypeInfo(sc, t); |
1323 |
}
|
|
1324 |
|
|
1325 | 1 |
StructDeclaration sd = ts.sym; |
1326 | 1 |
if (sd.postblit) |
1327 |
{
|
|
1328 | 1 |
if (sd.postblit.checkDisabled(loc, sc)) |
1329 | 1 |
return true; |
1330 |
|
|
1331 |
//checkDeprecated(sc, sd.postblit); // necessary?
|
|
1332 | 1 |
checkPurity(sc, sd.postblit); |
1333 | 1 |
checkSafety(sc, sd.postblit); |
1334 | 1 |
checkNogc(sc, sd.postblit); |
1335 |
//checkAccess(sd, loc, sc, sd.postblit); // necessary?
|
|
1336 | 1 |
return false; |
1337 |
}
|
|
1338 |
}
|
|
1339 | 1 |
return false; |
1340 |
}
|
|
1341 |
|
|
1342 |
extern (D) final bool checkRightThis(Scope* sc) |
|
1343 |
{
|
|
1344 | 1 |
if (op == TOK.error) |
1345 | 1 |
return true; |
1346 | 1 |
if (op == TOK.variable && type.ty != Terror) |
1347 |
{
|
|
1348 | 1 |
VarExp ve = cast(VarExp)this; |
1349 | 1 |
if (isNeedThisScope(sc, ve.var)) |
1350 |
{
|
|
1351 |
//printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
|
|
1352 |
// sc.intypeof, sc.getStructClassScope(), func, fdthis);
|
|
1353 | 1 |
error("need `this` for `%s` of type `%s`", ve.var.toChars(), ve.var.type.toChars()); |
1354 | 1 |
return true; |
1355 |
}
|
|
1356 |
}
|
|
1357 | 1 |
return false; |
1358 |
}
|
|
1359 |
|
|
1360 |
/*******************************
|
|
1361 |
* Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
|
|
1362 |
* ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
|
|
1363 |
* Returns true if error occurs.
|
|
1364 |
*/
|
|
1365 |
extern (D) final bool checkReadModifyWrite(TOK rmwOp, Expression ex = null) |
|
1366 |
{
|
|
1367 |
//printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : "");
|
|
1368 | 1 |
if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass()) |
1369 | 1 |
return false; |
1370 |
|
|
1371 |
// atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
|
|
1372 | 1 |
switch (rmwOp) |
1373 |
{
|
|
1374 | 1 |
case TOK.plusPlus: |
1375 | 1 |
case TOK.prePlusPlus: |
1376 | 1 |
rmwOp = TOK.addAssign; |
1377 | 1 |
break; |
1378 | 1 |
case TOK.minusMinus: |
1379 | 1 |
case TOK.preMinusMinus: |
1380 | 1 |
rmwOp = TOK.minAssign; |
1381 | 1 |
break; |
1382 | 1 |
default: |
1383 | 1 |
break; |
1384 |
}
|
|
1385 |
|
|
1386 | 1 |
error("read-modify-write operations are not allowed for `shared` variables"); |
1387 | 1 |
errorSupplemental("Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", |
1388 | 1 |
Token.toChars(rmwOp), toChars(), ex ? ex.toChars() : "1"); |
1389 | 1 |
return true; |
1390 |
}
|
|
1391 |
|
|
1392 |
/***************************************
|
|
1393 |
* Parameters:
|
|
1394 |
* sc: scope
|
|
1395 |
* flag: 1: do not issue error message for invalid modification
|
|
1396 |
* Returns:
|
|
1397 |
* Whether the type is modifiable
|
|
1398 |
*/
|
|
1399 |
Modifiable checkModifiable(Scope* sc, int flag = 0) |
|
1400 |
{
|
|
1401 | 1 |
return type ? Modifiable.yes : Modifiable.no; // default modifiable |
1402 |
}
|
|
1403 |
|
|
1404 |
/*****************************
|
|
1405 |
* If expression can be tested for true or false,
|
|
1406 |
* returns the modified expression.
|
|
1407 |
* Otherwise returns ErrorExp.
|
|
1408 |
*/
|
|
1409 |
Expression toBoolean(Scope* sc) |
|
1410 |
{
|
|
1411 |
// Default is 'yes' - do nothing
|
|
1412 | 1 |
Expression e = this; |
1413 | 1 |
Type t = type; |
1414 | 1 |
Type tb = type.toBasetype(); |
1415 | 1 |
Type att = null; |
1416 |
|
|
1417 | 1 |
while (1) |
1418 |
{
|
|
1419 |
// Structs can be converted to bool using opCast(bool)()
|
|
1420 | 1 |
if (auto ts = tb.isTypeStruct()) |
1421 |
{
|
|
1422 | 1 |
AggregateDeclaration ad = ts.sym; |
1423 |
/* Don't really need to check for opCast first, but by doing so we
|
|
1424 |
* get better error messages if it isn't there.
|
|
1425 |
*/
|
|
1426 | 1 |
if (Dsymbol fd = search_function(ad, Id._cast)) |
1427 |
{
|
|
1428 | 1 |
e = new CastExp(loc, e, Type.tbool); |
1429 | 1 |
e = e.expressionSemantic(sc); |
1430 | 1 |
return e; |
1431 |
}
|
|
1432 |
|
|
1433 |
// Forward to aliasthis.
|
|
1434 | 1 |
if (ad.aliasthis && tb != att) |
1435 |
{
|
|
1436 | 1 |
if (!att && tb.checkAliasThisRec()) |
1437 | 1 |
att = tb; |
1438 | 1 |
e = resolveAliasThis(sc, e); |
1439 | 1 |
t = e.type; |
1440 | 1 |
tb = e.type.toBasetype(); |
1441 | 1 |
continue; |
1442 |
}
|
|
1443 |
}
|
|
1444 | 1 |
break; |
1445 |
}
|
|
1446 |
|
|
1447 | 1 |
if (!t.isBoolean()) |
1448 |
{
|
|
1449 | 1 |
if (tb != Type.terror) |
1450 | 1 |
error("expression `%s` of type `%s` does not have a boolean value", toChars(), t.toChars()); |
1451 | 1 |
return ErrorExp.get(); |
1452 |
}
|
|
1453 | 1 |
return e; |
1454 |
}
|
|
1455 |
|
|
1456 |
/************************************************
|
|
1457 |
* Destructors are attached to VarDeclarations.
|
|
1458 |
* Hence, if expression returns a temp that needs a destructor,
|
|
1459 |
* make sure and create a VarDeclaration for that temp.
|
|
1460 |
*/
|
|
1461 |
Expression addDtorHook(Scope* sc) |
|
1462 |
{
|
|
1463 | 1 |
return this; |
1464 |
}
|
|
1465 |
|
|
1466 |
/******************************
|
|
1467 |
* Take address of expression.
|
|
1468 |
*/
|
|
1469 |
final Expression addressOf() |
|
1470 |
{
|
|
1471 |
//printf("Expression::addressOf()\n");
|
|
1472 |
debug
|
|
1473 |
{
|
|
1474 | 1 |
assert(op == TOK.error || isLvalue()); |
1475 |
}
|
|
1476 | 1 |
Expression e = new AddrExp(loc, this, type.pointerTo()); |
1477 | 1 |
return e; |
1478 |
}
|
|
1479 |
|
|
1480 |
/******************************
|
|
1481 |
* If this is a reference, dereference it.
|
|
1482 |
*/
|
|
1483 |
final Expression deref() |
|
1484 |
{
|
|
1485 |
//printf("Expression::deref()\n");
|
|
1486 |
// type could be null if forward referencing an 'auto' variable
|
|
1487 | 1 |
if (type) |
1488 | 1 |
if (auto tr = type.isTypeReference()) |
1489 |
{
|
|
1490 |
Expression e = new PtrExp(loc, this, tr.next); |
|
1491 |
return e; |
|
1492 |
}
|
|
1493 | 1 |
return this; |
1494 |
}
|
|
1495 |
|
|
1496 |
final Expression optimize(int result, bool keepLvalue = false) |
|
1497 |
{
|
|
1498 | 1 |
return Expression_optimize(this, result, keepLvalue); |
1499 |
}
|
|
1500 |
|
|
1501 |
// Entry point for CTFE.
|
|
1502 |
// A compile-time result is required. Give an error if not possible
|
|
1503 |
final Expression ctfeInterpret() |
|
1504 |
{
|
|
1505 | 1 |
return .ctfeInterpret(this); |
1506 |
}
|
|
1507 |
|
|
1508 |
final int isConst() |
|
1509 |
{
|
|
1510 | 1 |
return .isConst(this); |
1511 |
}
|
|
1512 |
|
|
1513 |
/********************************
|
|
1514 |
* Does this expression statically evaluate to a boolean 'result' (true or false)?
|
|
1515 |
*/
|
|
1516 |
bool isBool(bool result) |
|
1517 |
{
|
|
1518 | 1 |
return false; |
1519 |
}
|
|
1520 |
|
|
1521 |
bool hasCode() |
|
1522 |
{
|
|
1523 | 1 |
return true; |
1524 |
}
|
|
1525 |
|
|
1526 |
final pure inout nothrow @nogc |
|
1527 |
{
|
|
1528 | 1 |
inout(IntegerExp) isIntegerExp() { return op == TOK.int64 ? cast(typeof(return))this : null; } |
1529 |
inout(ErrorExp) isErrorExp() { return op == TOK.error ? cast(typeof(return))this : null; } |
|
1530 | 1 |
inout(VoidInitExp) isVoidInitExp() { return op == TOK.void_ ? cast(typeof(return))this : null; } |
1531 | 1 |
inout(RealExp) isRealExp() { return op == TOK.float64 ? cast(typeof(return))this : null; } |
1532 | 1 |
inout(ComplexExp) isComplexExp() { return op == TOK.complex80 ? cast(typeof(return))this : null; } |
1533 |
inout(IdentifierExp) isIdentifierExp() { return op == TOK.identifier ? cast(typeof(return))this : null; } |
|
1534 |
inout(DollarExp) isDollarExp() { return op == TOK.dollar ? cast(typeof(return))this : null; } |
|
1535 | 1 |
inout(DsymbolExp) isDsymbolExp() { return op == TOK.dSymbol ? cast(typeof(return))this : null; } |
1536 | 1 |
inout(ThisExp) isThisExp() { return op == TOK.this_ ? cast(typeof(return))this : null; } |
1537 |
inout(SuperExp) isSuperExp() { return op == TOK.super_ ? cast(typeof(return))this : null; } |
|
1538 | 1 |
inout(NullExp) isNullExp() { return op == TOK.null_ ? cast(typeof(return))this : null; } |
1539 | 1 |
inout(StringExp) isStringExp() { return op == TOK.string_ ? cast(typeof(return))this : null; } |
1540 | 1 |
inout(TupleExp) isTupleExp() { return op == TOK.tuple ? cast(typeof(return))this : null; } |
1541 | 1 |
inout(ArrayLiteralExp) isArrayLiteralExp() { return op == TOK.arrayLiteral ? cast(typeof(return))this : null; } |
1542 | 1 |
inout(AssocArrayLiteralExp) isAssocArrayLiteralExp() { return op == TOK.assocArrayLiteral ? cast(typeof(return))this : null; } |
1543 | 1 |
inout(StructLiteralExp) isStructLiteralExp() { return op == TOK.structLiteral ? cast(typeof(return))this : null; } |
1544 | 1 |
inout(TypeExp) isTypeExp() { return op == TOK.type ? cast(typeof(return))this : null; } |
1545 | 1 |
inout(ScopeExp) isScopeExp() { return op == TOK.scope_ ? cast(typeof(return))this : null; } |
1546 | 1 |
inout(TemplateExp) isTemplateExp() { return op == TOK.template_ ? cast(typeof(return))this : null; } |
1547 | 1 |
inout(NewExp) isNewExp() { return op == TOK.new_ ? cast(typeof(return))this : null; } |
1548 |
inout(NewAnonClassExp) isNewAnonClassExp() { return op == TOK.newAnonymousClass ? cast(typeof(return))this : null; } |
|
1549 | 1 |
inout(SymOffExp) isSymOffExp() { return op == TOK.symbolOffset ? cast(typeof(return))this : null; } |
1550 | 1 |
inout(VarExp) isVarExp() { return op == TOK.variable ? cast(typeof(return))this : null; } |
1551 | 1 |
inout(OverExp) isOverExp() { return op == TOK.overloadSet ? cast(typeof(return))this : null; } |
1552 | 1 |
inout(FuncExp) isFuncExp() { return op == TOK.function_ ? cast(typeof(return))this : null; } |
1553 | 1 |
inout(DeclarationExp) isDeclarationExp() { return op == TOK.declaration ? cast(typeof(return))this : null; } |
1554 | 1 |
inout(TypeidExp) isTypeidExp() { return op == TOK.typeid_ ? cast(typeof(return))this : null; } |
1555 |
inout(TraitsExp) isTraitsExp() { return op == TOK.traits ? cast(typeof(return))this : null; } |
|
1556 |
inout(HaltExp) isHaltExp() { return op == TOK.halt ? cast(typeof(return))this : null; } |
|
1557 |
inout(IsExp) isExp() { return op == TOK.is_ ? cast(typeof(return))this : null; } |
|
1558 | 1 |
inout(CompileExp) isCompileExp() { return op == TOK.mixin_ ? cast(typeof(return))this : null; } |
1559 |
inout(ImportExp) isImportExp() { return op == TOK.import_ ? cast(typeof(return))this : null; } |
|
1560 | 1 |
inout(AssertExp) isAssertExp() { return op == TOK.assert_ ? cast(typeof(return))this : null; } |
1561 | 1 |
inout(DotIdExp) isDotIdExp() { return op == TOK.dotIdentifier ? cast(typeof(return))this : null; } |
1562 | 1 |
inout(DotTemplateExp) isDotTemplateExp() { return op == TOK.dotTemplateDeclaration ? cast(typeof(return))this : null; } |
1563 | 1 |
inout(DotVarExp) isDotVarExp() { return op == TOK.dotVariable ? cast(typeof(return))this : null; } |
1564 | 1 |
inout(DotTemplateInstanceExp) isDotTemplateInstanceExp() { return op == TOK.dotTemplateInstance ? cast(typeof(return))this : null; } |
1565 | 1 |
inout(DelegateExp) isDelegateExp() { return op == TOK.delegate_ ? cast(typeof(return))this : null; } |
1566 | 1 |
inout(DotTypeExp) isDotTypeExp() { return op == TOK.dotType ? cast(typeof(return))this : null; } |
1567 | 1 |
inout(CallExp) isCallExp() { return op == TOK.call ? cast(typeof(return))this : null; } |
1568 | 1 |
inout(AddrExp) isAddrExp() { return op == TOK.address ? cast(typeof(return))this : null; } |
1569 | 1 |
inout(PtrExp) isPtrExp() { return op == TOK.star ? cast(typeof(return))this : null; } |
1570 |
inout(NegExp) isNegExp() { return op == TOK.negate ? cast(typeof(return))this : null; } |
|
1571 |
inout(UAddExp) isUAddExp() { return op == TOK.uadd ? cast(typeof(return))this : null; } |
|
1572 |
inout(ComExp) isComExp() { return op == TOK.tilde ? cast(typeof(return))this : null; } |
|
1573 | 1 |
inout(NotExp) isNotExp() { return op == TOK.not ? cast(typeof(return))this : null; } |
1574 |
inout(DeleteExp) isDeleteExp() { return op == TOK.delete_ ? cast(typeof(return))this : null; } |
|
1575 | 1 |
inout(CastExp) isCastExp() { return op == TOK.cast_ ? cast(typeof(return))this : null; } |
1576 | 1 |
inout(VectorExp) isVectorExp() { return op == TOK.vector ? cast(typeof(return))this : null; } |
1577 |
inout(VectorArrayExp) isVectorArrayExp() { return op == TOK.vectorArray ? cast(typeof(return))this : null; } |
|
1578 | 1 |
inout(SliceExp) isSliceExp() { return op == TOK.slice ? cast(typeof(return))this : null; } |
1579 | 1 |
inout(ArrayLengthExp) isArrayLengthExp() { return op == TOK.arrayLength ? cast(typeof(return))this : null; } |
1580 | 1 |
inout(ArrayExp) isArrayExp() { return op == TOK.array ? cast(typeof(return))this : null; } |
1581 | 1 |
inout(DotExp) isDotExp() { return op == TOK.dot ? cast(typeof(return))this : null; } |
1582 | 1 |
inout(CommaExp) isCommaExp() { return op == TOK.comma ? cast(typeof(return))this : null; } |
1583 |
inout(IntervalExp) isIntervalExp() { return op == TOK.interval ? cast(typeof(return))this : null; } |
|
1584 |
inout(DelegatePtrExp) isDelegatePtrExp() { return op == TOK.delegatePointer ? cast(typeof(return))this : null; } |
|
1585 |
inout(DelegateFuncptrExp) isDelegateFuncptrExp() { return op == TOK.delegateFunctionPointer ? cast(typeof(return))this : null; } |
|
1586 | 1 |
inout(IndexExp) isIndexExp() { return op == TOK.index ? cast(typeof(return))this : null; } |
1587 |
inout(PostExp) isPostExp() { return (op == TOK.plusPlus || op == TOK.minusMinus) ? cast(typeof(return))this : null; } |
|
1588 |
inout(PreExp) isPreExp() { return (op == TOK.prePlusPlus || op == TOK.preMinusMinus) ? cast(typeof(return))this : null; } |
|
1589 |
inout(AssignExp) isAssignExp() { return op == TOK.assign ? cast(typeof(return))this : null; } |
|
1590 | 1 |
inout(ConstructExp) isConstructExp() { return op == TOK.construct ? cast(typeof(return))this : null; } |
1591 |
inout(BlitExp) isBlitExp() { return op == TOK.blit ? cast(typeof(return))this : null; } |
|
1592 | 1 |
inout(AddAssignExp) isAddAssignExp() { return op == TOK.addAssign ? cast(typeof(return))this : null; } |
1593 |
inout(MinAssignExp) isMinAssignExp() { return op == TOK.minAssign ? cast(typeof(return))this : null; } |
|
1594 |
inout(MulAssignExp) isMulAssignExp() { return op == TOK.mulAssign ? cast(typeof(return))this : null; } |
|
1595 |
|
|
1596 |
inout(DivAssignExp) isDivAssignExp() { return op == TOK.divAssign ? cast(typeof(return))this : null; } |
|
1597 |
inout(ModAssignExp) isModAssignExp() { return op == TOK.modAssign ? cast(typeof(return))this : null; } |
|
1598 |
inout(AndAssignExp) isAndAssignExp() { return op == TOK.andAssign ? cast(typeof(return))this : null; } |
|
1599 |
inout(OrAssignExp) isOrAssignExp() { return op == TOK.orAssign ? cast(typeof(return))this : null; } |
|
1600 |
inout(XorAssignExp) isXorAssignExp() { return op == TOK.xorAssign ? cast(typeof(return))this : null; } |
|
1601 |
inout(PowAssignExp) isPowAssignExp() { return op == TOK.powAssign ? cast(typeof(return))this : null; } |
|
1602 |
|
|
1603 |
inout(ShlAssignExp) isShlAssignExp() { return op == TOK.leftShiftAssign ? cast(typeof(return))this : null; } |
|
1604 |
inout(ShrAssignExp) isShrAssignExp() { return op == TOK.rightShiftAssign ? cast(typeof(return))this : null; } |
|
1605 |
inout(UshrAssignExp) isUshrAssignExp() { return op == TOK.unsignedRightShiftAssign ? cast(typeof(return))this : null; } |
|
1606 |
|
|
1607 |
inout(CatAssignExp) isCatAssignExp() { return op == TOK.concatenateAssign |
|
1608 |
? cast(typeof(return))this |
|
1609 |
: null; } |
|
1610 |
|
|
1611 |
inout(CatElemAssignExp) isCatElemAssignExp() { return op == TOK.concatenateElemAssign |
|
1612 |
? cast(typeof(return))this |
|
1613 |
: null; } |
|
1614 |
|
|
1615 |
inout(CatDcharAssignExp) isCatDcharAssignExp() { return op == TOK.concatenateDcharAssign |
|
1616 |
? cast(typeof(return))this |
|
1617 |
: null; } |
|
1618 |
|
|
1619 | 1 |
inout(AddExp) isAddExp() { return op == TOK.add ? cast(typeof(return))this : null; } |
1620 |
inout(MinExp) isMinExp() { return op == TOK.min ? cast(typeof(return))this : null; } |
|
1621 |
inout(CatExp) isCatExp() { return op == TOK.concatenate ? cast(typeof(return))this : null; } |
|
1622 |
inout(MulExp) isMulExp() { return op == TOK.mul ? cast(typeof(return))this : null; } |
|
1623 |
inout(DivExp) isDivExp() { return op == TOK.div ? cast(typeof(return))this : null; } |
|
1624 |
inout(ModExp) isModExp() { return op == TOK.mod ? cast(typeof(return))this : null; } |
|
1625 |
inout(PowExp) isPowExp() { return op == TOK.pow ? cast(typeof(return))this : null; } |
|
1626 |
inout(ShlExp) isShlExp() { return op == TOK.leftShift ? cast(typeof(return))this : null; } |
|
1627 |
inout(ShrExp) isShrExp() { return op == TOK.rightShift ? cast(typeof(return))this : null; } |
|
1628 |
inout(UshrExp) isUshrExp() { return op == TOK.unsignedRightShift ? cast(typeof(return))this : null; } |
|
1629 |
inout(AndExp) isAndExp() { return op == TOK.and ? cast(typeof(return))this : null; } |
|
1630 |
inout(OrExp) isOrExp() { return op == TOK.or ? cast(typeof(return))this : null; } |
|
1631 |
inout(XorExp) isXorExp() { return op == TOK.xor ? cast(typeof(return))this : null; } |
|
1632 |
inout(LogicalExp) isLogicalExp() { return (op == TOK.andAnd || op == TOK.orOr) ? cast(typeof(return))this : null; } |
|
1633 |
//inout(CmpExp) isCmpExp() { return op == TOK. ? cast(typeof(return))this : null; }
|
|
1634 |
inout(InExp) isInExp() { return op == TOK.in_ ? cast(typeof(return))this : null; } |
|
1635 |
inout(RemoveExp) isRemoveExp() { return op == TOK.remove ? cast(typeof(return))this : null; } |
|
1636 |
inout(EqualExp) isEqualExp() { return (op == TOK.equal || op == TOK.notEqual) ? cast(typeof(return))this : null; } |
|
1637 |
inout(IdentityExp) isIdentityExp() { return (op == TOK.identity || op == TOK.notIdentity) ? cast(typeof(return))this : null; } |
|
1638 | 1 |
inout(CondExp) isCondExp() { return op == TOK.question ? cast(typeof(return))this : null; } |
1639 |
|
|
1640 |
inout(DefaultInitExp) isDefaultInitExp() { return op == TOK.default_ ? cast(typeof(return))this : null; } |
|
1641 |
inout(FileInitExp) isFileInitExp() { return (op == TOK.file || op == TOK.fileFullPath) ? cast(typeof(return))this : null; } |
|
1642 |
inout(LineInitExp) isLineInitExp() { return op == TOK.line ? cast(typeof(return))this : null; } |
|
1643 |
inout(ModuleInitExp) isModuleInitExp() { return op == TOK.moduleString ? cast(typeof(return))this : null; } |
|
1644 |
inout(FuncInitExp) isFuncInitExp() { return op == TOK.functionString ? cast(typeof(return))this : null; } |
|
1645 |
inout(PrettyFuncInitExp) isPrettyFuncInitExp() { return op == TOK.prettyFunction ? cast(typeof(return))this : null; } |
|
1646 | 1 |
inout(ClassReferenceExp) isClassReferenceExp() { return op == TOK.classReference ? cast(typeof(return))this : null; } |
1647 |
}
|
|
1648 |
|
|
1649 |
override void accept(Visitor v) |
|
1650 |
{
|
|
1651 | 1 |
v.visit(this); |
1652 |
}
|
|
1653 |
}
|
|
1654 |
|
|
1655 |
/***********************************************************
|
|
1656 |
*/
|
|
1657 |
extern (C++) final class IntegerExp : Expression |
|
1658 |
{
|
|
1659 |
private dinteger_t value; |
|
1660 |
|
|
1661 | 1 |
extern (D) this(const ref Loc loc, dinteger_t value, Type type) |
1662 |
{
|
|
1663 | 1 |
super(loc, TOK.int64, __traits(classInstanceSize, IntegerExp)); |
1664 |
//printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : "");
|
|
1665 | 1 |
assert(type); |
1666 | 1 |
if (!type.isscalar()) |
1667 |
{
|
|
1668 |
//printf("%s, loc = %d\n", toChars(), loc.linnum);
|
|
1669 | 1 |
if (type.ty != Terror) |
1670 | 1 |
error("integral constant must be scalar type, not `%s`", type.toChars()); |
1671 | 1 |
type = Type.terror; |
1672 |
}
|
|
1673 | 1 |
this.type = type; |
1674 | 1 |
this.value = normalize(type.toBasetype().ty, value); |
1675 |
}
|
|
1676 |
|
|
1677 | 1 |
extern (D) this(dinteger_t value) |
1678 |
{
|
|
1679 | 1 |
super(Loc.initial, TOK.int64, __traits(classInstanceSize, IntegerExp)); |
1680 | 1 |
this.type = Type.tint32; |
1681 | 1 |
this.value = cast(d_int32)value; |
1682 |
}
|
|
1683 |
|
|
1684 |
static IntegerExp create(Loc loc, dinteger_t value, Type type) |
|
1685 |
{
|
|
1686 |
return new IntegerExp(loc, value, type); |
|
1687 |
}
|
|
1688 |
|
|
1689 |
// Same as create, but doesn't allocate memory.
|
|
1690 |
static void emplace(UnionExp* pue, Loc loc, dinteger_t value, Type type) |
|
1691 |
{
|
|
1692 |
emplaceExp!(IntegerExp)(pue, loc, value, type); |
|
1693 |
}
|
|
1694 |
|
|
1695 |
override bool equals(const RootObject o) const |
|
1696 |
{
|
|
1697 | 1 |
if (this == o) |
1698 | 1 |
return true; |
1699 | 1 |
if (auto ne = (cast(Expression)o).isIntegerExp()) |
1700 |
{
|
|
1701 | 1 |
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && value == ne.value) |
1702 |
{
|
|
1703 | 1 |
return true; |
1704 |
}
|
|
1705 |
}
|
|
1706 | 1 |
return false; |
1707 |
}
|
|
1708 |
|
|
1709 |
override dinteger_t toInteger() |
|
1710 |
{
|
|
1711 |
// normalize() is necessary until we fix all the paints of 'type'
|
|
1712 | 1 |
return value = normalize(type.toBasetype().ty, value); |
1713 |
}
|
|
1714 |
|
|
1715 |
override real_t toReal() |
|
1716 |
{
|
|
1717 |
// normalize() is necessary until we fix all the paints of 'type'
|
|
1718 | 1 |
const ty = type.toBasetype().ty; |
1719 | 1 |
const val = normalize(ty, value); |
1720 | 1 |
value = val; |
1721 | 1 |
return (ty == Tuns64) |
1722 | 1 |
? real_t(cast(d_uns64)val) |
1723 | 1 |
: real_t(cast(d_int64)val); |
1724 |
}
|
|
1725 |
|
|
1726 |
override real_t toImaginary() |
|
1727 |
{
|
|
1728 | 1 |
return CTFloat.zero; |
1729 |
}
|
|
1730 |
|
|
1731 |
override complex_t toComplex() |
|
1732 |
{
|
|
1733 | 1 |
return complex_t(toReal()); |
1734 |
}
|
|
1735 |
|
|
1736 |
override bool isBool(bool result) |
|
1737 |
{
|
|
1738 | 1 |
bool r = toInteger() != 0; |
1739 | 1 |
return result ? r : !r; |
1740 |
}
|
|
1741 |
|
|
1742 |
override Expression toLvalue(Scope* sc, Expression e) |
|
1743 |
{
|
|
1744 | 1 |
if (!e) |
1745 |
e = this; |
|
1746 | 1 |
else if (!loc.isValid()) |
1747 |
loc = e.loc; |
|
1748 | 1 |
e.error("cannot modify constant `%s`", e.toChars()); |
1749 | 1 |
return ErrorExp.get(); |
1750 |
}
|
|
1751 |
|
|
1752 |
override void accept(Visitor v) |
|
1753 |
{
|
|
1754 | 1 |
v.visit(this); |
1755 |
}
|
|
1756 |
|
|
1757 |
dinteger_t getInteger() |
|
1758 |
{
|
|
1759 | 1 |
return value; |
1760 |
}
|
|
1761 |
|
|
1762 |
void setInteger(dinteger_t value) |
|
1763 |
{
|
|
1764 | 1 |
this.value = normalize(type.toBasetype().ty, value); |
1765 |
}
|
|
1766 |
|
|
1767 |
extern (D) static dinteger_t normalize(TY ty, dinteger_t value) |
|
1768 |
{
|
|
1769 |
/* 'Normalize' the value of the integer to be in range of the type
|
|
1770 |
*/
|
|
1771 | 1 |
dinteger_t result; |
1772 | 1 |
switch (ty) |
1773 |
{
|
|
1774 | 1 |
case Tbool: |
1775 | 1 |
result = (value != 0); |
1776 | 1 |
break; |
1777 |
|
|
1778 | 1 |
case Tint8: |
1779 | 1 |
result = cast(d_int8)value; |
1780 | 1 |
break; |
1781 |
|
|
1782 | 1 |
case Tchar: |
1783 | 1 |
case Tuns8: |
1784 | 1 |
result = cast(d_uns8)value; |
1785 | 1 |
break; |
1786 |
|
|
1787 | 1 |
case Tint16: |
1788 | 1 |
result = cast(d_int16)value; |
1789 | 1 |
break; |
1790 |
|
|
1791 | 1 |
case Twchar: |
1792 | 1 |
case Tuns16: |
1793 | 1 |
result = cast(d_uns16)value; |
1794 | 1 |
break; |
1795 |
|
|
1796 | 1 |
case Tint32: |
1797 | 1 |
result = cast(d_int32)value; |
1798 | 1 |
break; |
1799 |
|
|
1800 | 1 |
case Tdchar: |
1801 | 1 |
case Tuns32: |
1802 | 1 |
result = cast(d_uns32)value; |
1803 | 1 |
break; |
1804 |
|
|
1805 | 1 |
case Tint64: |
1806 | 1 |
result = cast(d_int64)value; |
1807 | 1 |
break; |
1808 |
|
|
1809 | 1 |
case Tuns64: |
1810 | 1 |
result = cast(d_uns64)value; |
1811 | 1 |
break; |
1812 |
|
|
1813 | 1 |
case Tpointer: |
1814 | 1 |
if (target.ptrsize == 8) |
1815 | 1 |
goto case Tuns64; |
1816 | 1 |
if (target.ptrsize == 4) |
1817 | 1 |
goto case Tuns32; |
1818 |
if (target.ptrsize == 2) |
|
1819 |
goto case Tuns16; |
|
1820 |
assert(0); |
|
1821 |
|
|
1822 | 1 |
default: |
1823 | 1 |
break; |
1824 |
}
|
|
1825 | 1 |
return result; |
1826 |
}
|
|
1827 |
|
|
1828 |
override Expression syntaxCopy() |
|
1829 |
{
|
|
1830 | 1 |
return this; |
1831 |
}
|
|
1832 |
|
|
1833 |
/**
|
|
1834 |
* Use this instead of creating new instances for commonly used literals
|
|
1835 |
* such as 0 or 1.
|
|
1836 |
*
|
|
1837 |
* Parameters:
|
|
1838 |
* v = The value of the expression
|
|
1839 |
* Returns:
|
|
1840 |
* A static instance of the expression, typed as `Tint32`.
|
|
1841 |
*/
|
|
1842 |
static IntegerExp literal(int v)() |
|
1843 |
{
|
|
1844 | 1 |
__gshared IntegerExp theConstant; |
1845 | 1 |
if (!theConstant) |
1846 | 1 |
theConstant = new IntegerExp(v); |
1847 | 1 |
return theConstant; |
1848 |
}
|
|
1849 |
|
|
1850 |
/**
|
|
1851 |
* Use this instead of creating new instances for commonly used bools.
|
|
1852 |
*
|
|
1853 |
* Parameters:
|
|
1854 |
* b = The value of the expression
|
|
1855 |
* Returns:
|
|
1856 |
* A static instance of the expression, typed as `Type.tbool`.
|
|
1857 |
*/
|
|
1858 |
static IntegerExp createBool(bool b) |
|
1859 |
{
|
|
1860 | 1 |
__gshared IntegerExp trueExp, falseExp; |
1861 | 1 |
if (!trueExp) |
1862 |
{
|
|
1863 | 1 |
trueExp = new IntegerExp(Loc.initial, 1, Type.tbool); |
1864 | 1 |
falseExp = new IntegerExp(Loc.initial, 0, Type.tbool); |
1865 |
}
|
|
1866 | 1 |
return b ? trueExp : falseExp; |
1867 |
}
|
|
1868 |
}
|
|
1869 |
|
|
1870 |
/***********************************************************
|
|
1871 |
* Use this expression for error recovery.
|
|
1872 |
* It should behave as a 'sink' to prevent further cascaded error messages.
|
|
1873 |
*/
|
|
1874 |
extern (C++) final class ErrorExp : Expression |
|
1875 |
{
|
|
1876 | 1 |
private extern (D) this() |
1877 |
{
|
|
1878 | 1 |
super(Loc.initial, TOK.error, __traits(classInstanceSize, ErrorExp)); |
1879 | 1 |
type = Type.terror; |
1880 |
}
|
|
1881 |
|
|
1882 |
static ErrorExp get () |
|
1883 |
{
|
|
1884 | 1 |
if (errorexp is null) |
1885 | 1 |
errorexp = new ErrorExp(); |
1886 |
|
|
1887 | 1 |
if (global.errors == 0 && global.gaggedErrors == 0) |
1888 |
{
|
|
1889 |
/* Unfortunately, errors can still leak out of gagged errors,
|
|
1890 |
* and we need to set the error count to prevent bogus code
|
|
1891 |
* generation. At least give a message.
|
|
1892 |
*/
|
|
1893 | 1 |
.error(Loc.initial, "unknown, please file report on issues.dlang.org"); |
1894 |
}
|
|
1895 |
|
|
1896 | 1 |
return errorexp; |
1897 |
}
|
|
1898 |
|
|
1899 |
override Expression toLvalue(Scope* sc, Expression e) |
|
1900 |
{
|
|
1901 | 1 |
return this; |
1902 |
}
|
|
1903 |
|
|
1904 |
override void accept(Visitor v) |
|
1905 |
{
|
|
1906 | 1 |
v.visit(this); |
1907 |
}
|
|
1908 |
|
|
1909 |
extern (C++) __gshared ErrorExp errorexp; // handy shared value |
|
1910 |
}
|
|
1911 |
|
|
1912 |
|
|
1913 |
/***********************************************************
|
|
1914 |
* An uninitialized value,
|
|
1915 |
* generated from void initializers.
|
|
1916 |
*/
|
|
1917 |
extern (C++) final class VoidInitExp : Expression |
|
1918 |
{
|
|
1919 |
VarDeclaration var; /// the variable from where the void value came from, null if not known |
|
1920 |
/// Useful for error messages
|
|
1921 |
|
|
1922 | 1 |
extern (D) this(VarDeclaration var) |
1923 |
{
|
|
1924 | 1 |
super(var.loc, TOK.void_, __traits(classInstanceSize, VoidInitExp)); |
1925 | 1 |
this.var = var; |
1926 | 1 |
this.type = var.type; |
1927 |
}
|
|
1928 |
|
|
1929 |
override const(char)* toChars() const |
|
1930 |
{
|
|
1931 |
return "void"; |
|
1932 |
}
|
|
1933 |
|
|
1934 |
override void accept(Visitor v) |
|
1935 |
{
|
|
1936 | 1 |
v.visit(this); |
1937 |
}
|
|
1938 |
}
|
|
1939 |
|
|
1940 |
|
|
1941 |
/***********************************************************
|
|
1942 |
*/
|
|
1943 |
extern (C++) final class RealExp : Expression |
|
1944 |
{
|
|
1945 |
real_t value; |
|
1946 |
|
|
1947 | 1 |
extern (D) this(const ref Loc loc, real_t value, Type type) |
1948 |
{
|
|
1949 | 1 |
super(loc, TOK.float64, __traits(classInstanceSize, RealExp)); |
1950 |
//printf("RealExp::RealExp(%Lg)\n", value);
|
|
1951 | 1 |
this.value = value; |
1952 | 1 |
this.type = type; |
1953 |
}
|
|
1954 |
|
|
1955 |
static RealExp create(Loc loc, real_t value, Type type) |
|
1956 |
{
|
|
1957 |
return new RealExp(loc, value, type); |
|
1958 |
}
|
|
1959 |
|
|
1960 |
// Same as create, but doesn't allocate memory.
|
|
1961 |
static void emplace(UnionExp* pue, Loc loc, real_t value, Type type) |
|
1962 |
{
|
|
1963 |
emplaceExp!(RealExp)(pue, loc, value, type); |
|
1964 |
}
|
|
1965 |
|
|
1966 |
override bool equals(const RootObject o) const |
|
1967 |
{
|
|
1968 | 1 |
if (this == o) |
1969 |
return true; |
|
1970 | 1 |
if (auto ne = (cast(Expression)o).isRealExp()) |
1971 |
{
|
|
1972 | 1 |
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(value, ne.value)) |
1973 |
{
|
|
1974 | 1 |
return true; |
1975 |
}
|
|
1976 |
}
|
|
1977 | 1 |
return false; |
1978 |
}
|
|
1979 |
|
|
1980 |
override dinteger_t toInteger() |
|
1981 |
{
|
|
1982 | 1 |
return cast(sinteger_t)toReal(); |
1983 |
}
|
|
1984 |
|
|
1985 |
override uinteger_t toUInteger() |
|
1986 |
{
|
|
1987 |
return cast(uinteger_t)toReal(); |
|
1988 |
}
|
|
1989 |
|
|
1990 |
override real_t toReal() |
|
1991 |
{
|
|
1992 | 1 |
return type.isreal() ? value : CTFloat.zero; |
1993 |
}
|
|
1994 |
|
|
1995 |
override real_t toImaginary() |
|
1996 |
{
|
|
1997 | 1 |
return type.isreal() ? CTFloat.zero : value; |
1998 |
}
|
|
1999 |
|
|
2000 |
override complex_t toComplex() |
|
2001 |
{
|
|
2002 | 1 |
return complex_t(toReal(), toImaginary()); |
2003 |
}
|
|
2004 |
|
|
2005 |
override bool isBool(bool result) |
|
2006 |
{
|
|
2007 | 1 |
return result ? cast(bool)value : !cast(bool)value; |
2008 |
}
|
|
2009 |
|
|
2010 |
override void accept(Visitor v) |
|
2011 |
{
|
|
2012 | 1 |
v.visit(this); |
2013 |
}
|
|
2014 |
}
|
|
2015 |
|
|
2016 |
/***********************************************************
|
|
2017 |
*/
|
|
2018 |
extern (C++) final class ComplexExp : Expression |
|
2019 |
{
|
|
2020 |
complex_t value; |
|
2021 |
|
|
2022 | 1 |
extern (D) this(const ref Loc loc, complex_t value, Type type) |
2023 |
{
|
|
2024 | 1 |
super(loc, TOK.complex80, __traits(classInstanceSize, ComplexExp)); |
2025 | 1 |
this.value = value; |
2026 | 1 |
this.type = type; |
2027 |
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
|
|
2028 |
}
|
|
2029 |
|
|
2030 |
static ComplexExp create(Loc loc, complex_t value, Type type) |
|
2031 |
{
|
|
2032 |
return new ComplexExp(loc, value, type); |
|
2033 |
}
|
|
2034 |
|
|
2035 |
// Same as create, but doesn't allocate memory.
|
|
2036 |
static void emplace(UnionExp* pue, Loc loc, complex_t value, Type type) |
|
2037 |
{
|
|
2038 |
emplaceExp!(ComplexExp)(pue, loc, value, type); |
|
2039 |
}
|
|
2040 |
|
|
2041 |
override bool equals(const RootObject o) const |
|
2042 |
{
|
|
2043 |
if (this == o) |
|
2044 |
return true; |
|
2045 |
if (auto ne = (cast(Expression)o).isComplexExp()) |
|
2046 |
{
|
|
2047 |
if (type.toHeadMutable().equals(ne.type.toHeadMutable()) && RealIdentical(creall(value), creall(ne.value)) && RealIdentical(cimagl(value), cimagl(ne.value))) |
|
2048 |
{
|
|
2049 |
return true; |
|
2050 |
}
|
|
2051 |
}
|
|
2052 |
return false; |
|
2053 |
}
|
|
2054 |
|
|
2055 |
override dinteger_t toInteger() |
|
2056 |
{
|
|
2057 |
return cast(sinteger_t)toReal(); |
|
2058 |
}
|
|
2059 |
|
|
2060 |
override uinteger_t toUInteger() |
|
2061 |
{
|
|
2062 |
return cast(uinteger_t)toReal(); |
|
2063 |
}
|
|
2064 |
|
|
2065 |
override real_t toReal() |
|
2066 |
{
|
|
2067 | 1 |
return creall(value); |
2068 |
}
|
|
2069 |
|
|
2070 |
override real_t toImaginary() |
|
2071 |
{
|
|
2072 | 1 |
return cimagl(value); |
2073 |
}
|
|
2074 |
|
|
2075 |
override complex_t toComplex() |
|
2076 |
{
|
|
2077 | 1 |
return value; |
2078 |
}
|
|
2079 |
|
|
2080 |
override bool isBool(bool result) |
|
2081 |
{
|
|
2082 | 1 |
if (result) |
2083 |
return cast(bool)value; |
|
2084 |
else
|
|
2085 | 1 |
return !value; |
2086 |
}
|
|
2087 |
|
|
2088 |
override void accept(Visitor v) |
|
2089 |
{
|
|
2090 | 1 |
v.visit(this); |
2091 |
}
|
|
2092 |
}
|
|
2093 |
|
|
2094 |
/***********************************************************
|
|
2095 |
*/
|
|
2096 |
extern (C++) class IdentifierExp : Expression |
|
2097 |
{
|
|
2098 |
Identifier ident; |
|
2099 |
|
|
2100 | 1 |
extern (D) this(const ref Loc loc, Identifier ident) |
2101 |
{
|
|
2102 | 1 |
super(loc, TOK.identifier, __traits(classInstanceSize, IdentifierExp)); |
2103 | 1 |
this.ident = ident; |
2104 |
}
|
|
2105 |
|
|
2106 |
static IdentifierExp create(Loc loc, Identifier ident) |
|
2107 |
{
|
|
2108 | 1 |
return new IdentifierExp(loc, ident); |
2109 |
}
|
|
2110 |
|
|
2111 |
override final bool isLvalue() |
|
2112 |
{
|
|
2113 | 1 |
return true; |
2114 |
}
|
|
2115 |
|
|
2116 |
override final Expression toLvalue(Scope* sc, Expression e) |
|
2117 |
{
|
|
2118 |
return this; |
|
2119 |
}
|
|
2120 |
|
|
2121 |
override void accept(Visitor v) |
|
2122 |
{
|
|
2123 | 1 |
v.visit(this); |
2124 |
}
|
|
2125 |
}
|
|
2126 |
|
|
2127 |
/***********************************************************
|
|
2128 |
*/
|
|
2129 |
extern (C++) final class DollarExp : IdentifierExp |
|
2130 |
{
|
|
2131 | 1 |
extern (D) this(const ref Loc loc) |
2132 |
{
|
|
2133 | 1 |
super(loc, Id.dollar); |
2134 |
}
|
|
2135 |
|
|
2136 |
override void accept(Visitor v) |
|
2137 |
{
|
|
2138 | 1 |
v.visit(this); |
2139 |
}
|
|
2140 |
}
|
|
2141 |
|
|
2142 |
/***********************************************************
|
|
2143 |
* Won't be generated by parser.
|
|
2144 |
*/
|
|
2145 |
extern (C++) final class DsymbolExp : Expression |
|
2146 |
{
|
|
2147 |
Dsymbol s; |
|
2148 |
bool hasOverloads; |
|
2149 |
|
|
2150 | 1 |
extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) |
2151 |
{
|
|
2152 | 1 |
super(loc, TOK.dSymbol, __traits(classInstanceSize, DsymbolExp)); |
2153 | 1 |
this.s = s; |
2154 | 1 |
this.hasOverloads = hasOverloads; |
2155 |
}
|
|
2156 |
|
|
2157 |
override bool isLvalue() |
|
2158 |
{
|
|
2159 |
return true; |
|
2160 |
}
|
|
2161 |
|
|
2162 |
override Expression toLvalue(Scope* sc, Expression e) |
|
2163 |
{
|
|
2164 |
return this; |
|
2165 |
}
|
|
2166 |
|
|
2167 |
override void accept(Visitor v) |
|
2168 |
{
|
|
2169 | 1 |
v.visit(this); |
2170 |
}
|
|
2171 |
}
|
|
2172 |
|
|
2173 |
/***********************************************************
|
|
2174 |
* http://dlang.org/spec/expression.html#this
|
|
2175 |
*/
|
|
2176 |
extern (C++) class ThisExp : Expression |
|
2177 |
{
|
|
2178 |
VarDeclaration var; |
|
2179 |
|
|
2180 | 1 |
extern (D) this(const ref Loc loc) |
2181 |
{
|
|
2182 | 1 |
super(loc, TOK.this_, __traits(classInstanceSize, ThisExp)); |
2183 |
//printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
|
|
2184 |
}
|
|
2185 |
|
|
2186 | 1 |
this(const ref Loc loc, const TOK tok) |
2187 |
{
|
|
2188 | 1 |
super(loc, tok, __traits(classInstanceSize, ThisExp)); |
2189 |
//printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
|
|
2190 |
}
|
|
2191 |
|
|
2192 |
override Expression syntaxCopy() |
|
2193 |
{
|
|
2194 | 1 |
auto r = cast(ThisExp) super.syntaxCopy(); |
2195 |
// require new semantic (possibly new `var` etc.)
|
|
2196 | 1 |
r.type = null; |
2197 | 1 |
r.var = null; |
2198 | 1 |
return r; |
2199 |
}
|
|
2200 |
|
|
2201 |
override final bool isBool(bool result) |
|
2202 |
{
|
|
2203 | 1 |
return result; |
2204 |
}
|
|
2205 |
|
|
2206 |
override final bool isLvalue() |
|
2207 |
{
|
|
2208 |
// Class `this` should be an rvalue; struct `this` should be an lvalue.
|
|
2209 | 1 |
return type.toBasetype().ty != Tclass; |
2210 |
}
|
|
2211 |
|
|
2212 |
override final Expression toLvalue(Scope* sc, Expression e) |
|
2213 |
{
|
|
2214 | 1 |
if (type.toBasetype().ty == Tclass) |
2215 |
{
|
|
2216 |
// Class `this` is an rvalue; struct `this` is an lvalue.
|
|
2217 | 1 |
return Expression.toLvalue(sc, e); |
2218 |
}
|
|
2219 | 1 |
return this; |
2220 |
}
|
|
2221 |
|
|
2222 |
override void accept(Visitor v) |
|
2223 |
{
|
|
2224 | 1 |
v.visit(this); |
2225 |
}
|
|
2226 |
}
|
|
2227 |
|
|
2228 |
/***********************************************************
|
|
2229 |
* http://dlang.org/spec/expression.html#super
|
|
2230 |
*/
|
|
2231 |
extern (C++) final class SuperExp : ThisExp |
|
2232 |
{
|
|
2233 | 1 |
extern (D) this(const ref Loc loc) |
2234 |
{
|
|
2235 | 1 |
super(loc, TOK.super_); |
2236 |
}
|
|
2237 |
|
|
2238 |
override void accept(Visitor v) |
|
2239 |
{
|
|
2240 | 1 |
v.visit(this); |
2241 |
}
|
|
2242 |
}
|
|
2243 |
|
|
2244 |
/***********************************************************
|
|
2245 |
* http://dlang.org/spec/expression.html#null
|
|
2246 |
*/
|
|
2247 |
extern (C++) final class NullExp : Expression |
|
2248 |
{
|
|
2249 | 1 |
extern (D) this(const ref Loc loc, Type type = null) |
2250 |
{
|
|
2251 | 1 |
super(loc, TOK.null_, __traits(classInstanceSize, NullExp)); |
2252 | 1 |
this.type = type; |
2253 |
}
|
|
2254 |
|
|
2255 |
override bool equals(const RootObject o) const |
|
2256 |
{
|
|
2257 | 1 |
if (auto e = o.isExpression()) |
2258 |
{
|
|
2259 | 1 |
if (e.op == TOK.null_ && type.equals(e.type)) |
2260 |
{
|
|
2261 | 1 |
return true; |
2262 |
}
|
|
2263 |
}
|
|
2264 | 1 |
return false; |
2265 |
}
|
|
2266 |
|
|
2267 |
override bool isBool(bool result) |
|
2268 |
{
|
|
2269 | 1 |
return result ? false : true; |
2270 |
}
|
|
2271 |
|
|
2272 |
override StringExp toStringExp() |
|
2273 |
{
|
|
2274 | 1 |
if (implicitConvTo(Type.tstring)) |
2275 |
{
|
|
2276 | 1 |
auto se = new StringExp(loc, (cast(char*)mem.xcalloc(1, 1))[0 .. 0]); |
2277 | 1 |
se.type = Type.tstring; |
2278 | 1 |
return se; |
2279 |
}
|
|
2280 | 1 |
return null; |
2281 |
}
|
|
2282 |
|
|
2283 |
override void accept(Visitor v) |
|
2284 |
{
|
|
2285 | 1 |
v.visit(this); |
2286 |
}
|
|
2287 |
}
|
|
2288 |
|
|
2289 |
/***********************************************************
|
|
2290 |
* http://dlang.org/spec/expression.html#string_literals
|
|
2291 |
*/
|
|
2292 |
extern (C++) final class StringExp : Expression |
|
2293 |
{
|
|
2294 |
private union |
|
2295 |
{
|
|
2296 |
char* string; // if sz == 1 |
|
2297 |
wchar* wstring; // if sz == 2 |
|
2298 |
dchar* dstring; // if sz == 4 |
|
2299 |
} // (const if ownedByCtfe == OwnedBy.code) |
|
2300 |
size_t len; // number of code units |
|
2301 |
ubyte sz = 1; // 1: char, 2: wchar, 4: dchar |
|
2302 |
ubyte committed; // !=0 if type is committed |
|
2303 |
enum char NoPostfix = 0; |
|
2304 |
char postfix = NoPostfix; // 'c', 'w', 'd' |
|
2305 |
OwnedBy ownedByCtfe = OwnedBy.code; |
|
2306 |
|
|
2307 | 1 |
extern (D) this(const ref Loc loc, const(void)[] string) |
2308 |
{
|
|
2309 | 1 |
super(loc, TOK.string_, __traits(classInstanceSize, StringExp)); |
2310 | 1 |
this.string = cast(char*)string.ptr; // note that this.string should be const |
2311 | 1 |
this.len = string.length; |
2312 | 1 |
this.sz = 1; // work around LDC bug #1286 |
2313 |
}
|
|
2314 |
|
|
2315 | 1 |
extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) |
2316 |
{
|
|
2317 | 1 |
super(loc, TOK.string_, __traits(classInstanceSize, StringExp)); |
2318 | 1 |
this.string = cast(char*)string.ptr; // note that this.string should be const |
2319 | 1 |
this.len = len; |
2320 | 1 |
this.sz = sz; |
2321 | 1 |
this.postfix = postfix; |
2322 |
}
|
|
2323 |
|
|
2324 |
static StringExp create(Loc loc, char* s) |
|
2325 |
{
|
|
2326 | 1 |
return new StringExp(loc, s.toDString()); |
2327 |
}
|
|
2328 |
|
|
2329 |
static StringExp create(Loc loc, void* string, size_t len) |
|
2330 |
{
|
|
2331 |
return new StringExp(loc, string[0 .. len]); |
|
2332 |
}
|
|
2333 |
|
|
2334 |
// Same as create, but doesn't allocate memory.
|
|
2335 |
static void emplace(UnionExp* pue, Loc loc, char* s) |
|
2336 |
{
|
|
2337 |
emplaceExp!(StringExp)(pue, loc, s.toDString()); |
|
2338 |
}
|
|
2339 |
|
|
2340 |
extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string) |
|
2341 |
{
|
|
2342 |
emplaceExp!(StringExp)(pue, loc, string); |
|
2343 |
}
|
|
2344 |
|
|
2345 |
extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix) |
|
2346 |
{
|
|
2347 |
emplaceExp!(StringExp)(pue, loc, string, len, sz, postfix); |
|
2348 |
}
|
|
2349 |
|
|
2350 |
override bool equals(const RootObject o) const |
|
2351 |
{
|
|
2352 |
//printf("StringExp::equals('%s') %s\n", o.toChars(), toChars());
|
|
2353 | 1 |
if (auto e = o.isExpression()) |
2354 |
{
|
|
2355 | 1 |
if (auto se = e.isStringExp()) |
2356 |
{
|
|
2357 | 1 |
return compare(se) == 0; |
2358 |
}
|
|
2359 |
}
|
|
2360 |
return false; |
|
2361 |
}
|
|
2362 |
|
|
2363 |
/**********************************
|
|
2364 |
* Return the number of code units the string would be if it were re-encoded
|
|
2365 |
* as tynto.
|
|
2366 |
* Params:
|
|
2367 |
* tynto = code unit type of the target encoding
|
|
2368 |
* Returns:
|
|
2369 |
* number of code units
|
|
2370 |
*/
|
|
2371 |
size_t numberOfCodeUnits(int tynto = 0) const |
|
2372 |
{
|
|
2373 | 1 |
int encSize; |
2374 | 1 |
switch (tynto) |
2375 |
{
|
|
2376 | 1 |
case 0: return len; |
2377 | 1 |
case Tchar: encSize = 1; break; |
2378 | 1 |
case Twchar: encSize = 2; break; |
2379 | 1 |
case Tdchar: encSize = 4; break; |
2380 |
default: |
|
2381 |
assert(0); |
|
2382 |
}
|
|
2383 | 1 |
if (sz == encSize) |
2384 | 1 |
return len; |
2385 |
|
|
2386 | 1 |
size_t result = 0; |
2387 | 1 |
dchar c; |
2388 |
|
|
2389 | 1 |
switch (sz) |
2390 |
{
|
|
2391 | 1 |
case 1: |
2392 | 1 |
for (size_t u = 0; u < len;) |
2393 |
{
|
|
2394 | 1 |
if (const s = utf_decodeChar(string[0 .. len], u, c)) |
2395 |
{
|
|
2396 |
error("%.*s", cast(int)s.length, s.ptr); |
|
2397 |
return 0; |
|
2398 |
}
|
|
2399 | 1 |
result += utf_codeLength(encSize, c); |
2400 |
}
|
|
2401 | 1 |
break; |
2402 |
|
|
2403 |
case 2: |
|
2404 |
for (size_t u = 0; u < len;) |
|
2405 |
{
|
|
2406 |
if (const s = utf_decodeWchar(wstring[0 .. len], u, c)) |
|
2407 |
{
|
|
2408 |
error("%.*s", cast(int)s.length, s.ptr); |
|
2409 |
return 0; |
|
2410 |
}
|
|
2411 |
result += utf_codeLength(encSize, c); |
|
2412 |
}
|
|
2413 |
break; |
|
2414 |
|
|
2415 |
case 4: |
|
2416 |
foreach (u; 0 .. len) |
|
2417 |
{
|
|
2418 |
result += utf_codeLength(encSize, dstring[u]); |
|
2419 |
}
|
|
2420 |
break; |
|
2421 |
|
|
2422 |
default: |
|
2423 |
assert(0); |
|
2424 |
}
|
|
2425 | 1 |
return result; |
2426 |
}
|
|
2427 |
|
|
2428 |
/**********************************************
|
|
2429 |
* Write the contents of the string to dest.
|
|
2430 |
* Use numberOfCodeUnits() to determine size of result.
|
|
2431 |
* Params:
|
|
2432 |
* dest = destination
|
|
2433 |
* tyto = encoding type of the result
|
|
2434 |
* zero = add terminating 0
|
|
2435 |
*/
|
|
2436 |
void writeTo(void* dest, bool zero, int tyto = 0) const |
|
2437 |
{
|
|
2438 | 1 |
int encSize; |
2439 | 1 |
switch (tyto) |
2440 |
{
|
|
2441 | 1 |
case 0: encSize = sz; break; |
2442 |
case Tchar: encSize = 1; break; |
|
2443 |
case Twchar: encSize = 2; break; |
|
2444 |
case Tdchar: encSize = 4; break; |
|
2445 |
default: |
|
2446 |
assert(0); |
|
2447 |
}
|
|
2448 | 1 |
if (sz == encSize) |
2449 |
{
|
|
2450 | 1 |
memcpy(dest, string, len * sz); |
2451 | 1 |
if (zero) |
2452 | 1 |
memset(dest + len * sz, 0, sz); |
2453 |
}
|
|
2454 |
else
|
|
2455 |
assert(0); |
|
2456 |
}
|
|
2457 |
|
|
2458 |
/*********************************************
|
|
2459 |
* Get the code unit at index i
|
|
2460 |
* Params:
|
|
2461 |
* i = index
|
|
2462 |
* Returns:
|
|
2463 |
* code unit at index i
|
|
2464 |
*/
|
|
2465 |
dchar getCodeUnit(size_t i) const pure |
|
2466 |
{
|
|
2467 | 1 |
assert(i < len); |
2468 | 1 |
final switch (sz) |
2469 |
{
|
|
2470 | 1 |
case 1: |
2471 | 1 |
return string[i]; |
2472 | 1 |
case 2: |
2473 | 1 |
return wstring[i]; |
2474 | 1 |
case 4: |
2475 | 1 |
return dstring[i]; |
2476 |
}
|
|
2477 |
}
|
|
2478 |
|
|
2479 |
/*********************************************
|
|
2480 |
* Set the code unit at index i to c
|
|
2481 |
* Params:
|
|
2482 |
* i = index
|
|
2483 |
* c = code unit to set it to
|
|
2484 |
*/
|
|
2485 |
void setCodeUnit(size_t i, dchar c) |
|
2486 |
{
|
|
2487 | 1 |
assert(i < len); |
2488 | 1 |
final switch (sz) |
2489 |
{
|
|
2490 | 1 |
case 1: |
2491 | 1 |
string[i] = cast(char)c; |
2492 | 1 |
break; |
2493 |
case 2: |
|
2494 |
wstring[i] = cast(wchar)c; |
|
2495 |
break; |
|
2496 |
case 4: |
|
2497 |
dstring[i] = c; |
|
2498 |
break; |
|
2499 |
}
|
|
2500 |
}
|
|
2501 |
|
|
2502 |
override StringExp toStringExp() |
|
2503 |
{
|
|
2504 | 1 |
return this; |
2505 |
}
|
|
2506 |
|
|
2507 |
/****************************************
|
|
2508 |
* Convert string to char[].
|
|
2509 |
*/
|
|
2510 |
StringExp toUTF8(Scope* sc) |
|
2511 |
{
|
|
2512 | 1 |
if (sz != 1) |
2513 |
{
|
|
2514 |
// Convert to UTF-8 string
|
|
2515 | 1 |
committed = 0; |
2516 | 1 |
Expression e = castTo(sc, Type.tchar.arrayOf()); |
2517 | 1 |
e = e.optimize(WANTvalue); |
2518 | 1 |
auto se = e.isStringExp(); |
2519 | 1 |
assert(se.sz == 1); |
2520 | 1 |
return se; |
2521 |
}
|
|
2522 | 1 |
return this; |
2523 |
}
|
|
2524 |
|
|
2525 |
/**
|
|
2526 |
* Compare two `StringExp` by length, then value
|
|
2527 |
*
|
|
2528 |
* The comparison is not the usual C-style comparison as seen with
|
|
2529 |
* `strcmp` or `memcmp`, but instead first compare based on the length.
|
|
2530 |
* This allows both faster lookup and sorting when comparing sparse data.
|
|
2531 |
*
|
|
2532 |
* This ordering scheme is relied on by the string-switching feature.
|
|
2533 |
* Code in Druntime's `core.internal.switch_` relies on this ordering
|
|
2534 |
* when doing a binary search among case statements.
|
|
2535 |
*
|
|
2536 |
* Both `StringExp` should be of the same encoding.
|
|
2537 |
*
|
|
2538 |
* Params:
|
|
2539 |
* se2 = String expression to compare `this` to
|
|
2540 |
*
|
|
2541 |
* Returns:
|
|
2542 |
* `0` when `this` is equal to se2, a value greater than `0` if
|
|
2543 |
* `this` should be considered greater than `se2`,
|
|
2544 |
* and a value less than `0` if `this` is lesser than `se2`.
|
|
2545 |
*/
|
|
2546 |
int compare(const StringExp se2) const nothrow pure @nogc |
|
2547 |
{
|
|
2548 |
//printf("StringExp::compare()\n");
|
|
2549 | 1 |
const len1 = len; |
2550 | 1 |
const len2 = se2.len; |
2551 |
|
|
2552 | 1 |
assert(this.sz == se2.sz, "Comparing string expressions of different sizes"); |
2553 |
//printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
|
|
2554 | 1 |
if (len1 == len2) |
2555 |
{
|
|
2556 | 1 |
switch (sz) |
2557 |
{
|
|
2558 | 1 |
case 1: |
2559 | 1 |
return memcmp(string, se2.string, len1); |
2560 |
|
|
2561 |
case 2: |
|
2562 |
{
|
|
2563 |
wchar* s1 = cast(wchar*)string; |
|
2564 |
wchar* s2 = cast(wchar*)se2.string; |
|
2565 |
foreach (u; 0 .. len) |
|
2566 |
{
|
|
2567 |
if (s1[u] != s2[u]) |
|
2568 |
return s1[u] - s2[u]; |
|
2569 |
}
|
|
2570 |
}
|
|
2571 |
break; |
|
2572 | 1 |
case 4: |
2573 |
{
|
|
2574 | 1 |
dchar* s1 = cast(dchar*)string; |
2575 | 1 |
dchar* s2 = cast(dchar*)se2.string; |
2576 | 1 |
foreach (u; 0 .. len) |
2577 |
{
|
|
2578 | 1 |
if (s1[u] != s2[u]) |
2579 | 1 |
return s1[u] - s2[u]; |
2580 |
}
|
|
2581 |
}
|
|
2582 |
break; |
|
2583 |
default: |
|
2584 |
assert(0); |
|
2585 |
}
|
|
2586 |
}
|
|
2587 | 1 |
return cast(int)(len1 - len2); |
2588 |
}
|
|
2589 |
|
|
2590 |
override bool isBool(bool result) |
|
2591 |
{
|
|
2592 | 1 |
return result; |
2593 |
}
|
|
2594 |
|
|
2595 |
override bool isLvalue() |
|
2596 |
{
|
|
2597 |
/* string literal is rvalue in default, but
|
|
2598 |
* conversion to reference of static array is only allowed.
|
|
2599 |
*/
|
|
2600 | 1 |
return (type && type.toBasetype().ty == Tsarray); |
2601 |
}
|
|
2602 |
|
|
2603 |
override Expression toLvalue(Scope* sc, Expression e) |
|
2604 |
{
|
|
2605 |
//printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL);
|
|
2606 | 1 |
return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e); |
2607 |
}
|
|
2608 |
|
|
2609 |
override Expression modifiableLvalue(Scope* sc, Expression e) |
|
2610 |
{
|
|
2611 | 1 |
error("cannot modify string literal `%s`", toChars()); |
2612 | 1 |
return ErrorExp.get(); |
2613 |
}
|
|
2614 |
|
|
2615 |
uint charAt(uinteger_t i) const |
|
2616 |
{
|
|
2617 | 1 |
uint value; |
2618 | 1 |
switch (sz) |
2619 |
{
|
|
2620 | 1 |
case 1: |
2621 | 1 |
value = (cast(char*)string)[cast(size_t)i]; |
2622 | 1 |
break; |
2623 |
|
|
2624 | 1 |
case 2: |
2625 | 1 |
value = (cast(ushort*)string)[cast(size_t)i]; |
2626 | 1 |
break; |
2627 |
|
|
2628 | 1 |
case 4: |
2629 | 1 |
value = (cast(uint*)string)[cast(size_t)i]; |
2630 | 1 |
break; |
2631 |
|
|
2632 |
default: |
|
2633 |
assert(0); |
|
2634 |
}
|
|
2635 | 1 |
return value; |
2636 |
}
|
|
2637 |
|
|
2638 |
/********************************
|
|
2639 |
* Convert string contents to a 0 terminated string,
|
|
2640 |
* allocated by mem.xmalloc().
|
|
2641 |
*/
|
|
2642 |
extern (D) const(char)[] toStringz() const |
|
2643 |
{
|
|
2644 | 1 |
auto nbytes = len * sz; |
2645 | 1 |
char* s = cast(char*)mem.xmalloc(nbytes + sz); |
2646 | 1 |
writeTo(s, true); |
2647 | 1 |
return s[0 .. nbytes]; |
2648 |
}
|
|
2649 |
|
|
2650 |
extern (D) const(char)[] peekString() const |
|
2651 |
{
|
|
2652 | 1 |
assert(sz == 1); |
2653 | 1 |
return this.string[0 .. len]; |
2654 |
}
|
|
2655 |
|
|
2656 |
extern (D) const(wchar)[] peekWstring() const |
|
2657 |
{
|
|
2658 | 1 |
assert(sz == 2); |
2659 | 1 |
return this.wstring[0 .. len]; |
2660 |
}
|
|
2661 |
|
|
2662 |
extern (D) const(dchar)[] peekDstring() const |
|
2663 |
{
|
|
2664 | 1 |
assert(sz == 4); |
2665 | 1 |
return this.dstring[0 .. len]; |
2666 |
}
|
|
2667 |
|
|
2668 |
/*******************
|
|
2669 |
* Get a slice of the data.
|
|
2670 |
*/
|
|
2671 |
extern (D) const(ubyte)[] peekData() const |
|
2672 |
{
|
|
2673 | 1 |
return cast(const(ubyte)[])this.string[0 .. len * sz]; |
2674 |
}
|
|
2675 |
|
|
2676 |
/*******************
|
|
2677 |
* Borrow a slice of the data, so the caller can modify
|
|
2678 |
* it in-place (!)
|
|
2679 |
*/
|
|
2680 |
extern (D) ubyte[] borrowData() |
|
2681 |
{
|
|
2682 | 1 |
return cast(ubyte[])this.string[0 .. len * sz]; |
2683 |
}
|
|
2684 |
|
|
2685 |
/***********************
|
|
2686 |
* Set new string data.
|
|
2687 |
* `this` becomes the new owner of the data.
|
|
2688 |
*/
|
|
2689 |
extern (D) void setData(void* s, size_t len, ubyte sz) |
|
2690 |
{
|
|
2691 | 1 |
this.string = cast(char*)s; |
2692 | 1 |
this.len = len; |
2693 | 1 |
this.sz = sz; |
2694 |
}
|
|
2695 |
|
|
2696 |
override void accept(Visitor v) |
|
2697 |
{
|
|
2698 | 1 |
v.visit(this); |
2699 |
}
|
|
2700 |
}
|
|
2701 |
|
|
2702 |
/***********************************************************
|
|
2703 |
*/
|
|
2704 |
extern (C++) final class TupleExp : Expression |
|
2705 |
{
|
|
2706 |
/* Tuple-field access may need to take out its side effect part.
|
|
2707 |
* For example:
|
|
2708 |
* foo().tupleof
|
|
2709 |
* is rewritten as:
|
|
2710 |
* (ref __tup = foo(); tuple(__tup.field0, __tup.field1, ...))
|
|
2711 |
* The declaration of temporary variable __tup will be stored in TupleExp.e0.
|
|
2712 |
*/
|
|
2713 |
Expression e0; |
|
2714 |
|
|
2715 |
Expressions* exps; |
|
2716 |
|
|
2717 | 1 |
extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) |
2718 |
{
|
|
2719 | 1 |
super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp)); |
2720 |
//printf("TupleExp(this = %p)\n", this);
|
|
2721 | 1 |
this.e0 = e0; |
2722 | 1 |
this.exps = exps; |
2723 |
}
|
|
2724 |
|
|
2725 | 1 |
extern (D) this(const ref Loc loc, Expressions* exps) |
2726 |
{
|
|
2727 | 1 |
super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp)); |
2728 |
//printf("TupleExp(this = %p)\n", this);
|
|
2729 | 1 |
this.exps = exps; |
2730 |
}
|
|
2731 |
|
|
2732 | 1 |
extern (D) this(const ref Loc loc, TupleDeclaration tup) |
2733 |
{
|
|
2734 | 1 |
super(loc, TOK.tuple, __traits(classInstanceSize, TupleExp)); |
2735 | 1 |
this.exps = new Expressions(); |
2736 |
|
|
2737 | 1 |
this.exps.reserve(tup.objects.dim); |
2738 | 1 |
foreach (o; *tup.objects) |
2739 |
{
|
|
2740 | 1 |
if (Dsymbol s = getDsymbol(o)) |
2741 |
{
|
|
2742 |
/* If tuple element represents a symbol, translate to DsymbolExp
|
|
2743 |
* to supply implicit 'this' if needed later.
|
|
2744 |
*/
|
|
2745 | 1 |
Expression e = new DsymbolExp(loc, s); |
2746 | 1 |
this.exps.push(e); |
2747 |
}
|
|
2748 | 1 |
else if (auto eo = o.isExpression()) |
2749 |
{
|
|
2750 | 1 |
auto e = eo.copy(); |
2751 | 1 |
e.loc = loc; // https://issues.dlang.org/show_bug.cgi?id=15669 |
2752 | 1 |
this.exps.push(e); |
2753 |
}
|
|
2754 | 1 |
else if (auto t = o.isType()) |
2755 |
{
|
|
2756 | 1 |
Expression e = new TypeExp(loc, t); |
2757 | 1 |
this.exps.push(e); |
2758 |
}
|
|
2759 |
else
|
|
2760 |
{
|
|
2761 |
error("`%s` is not an expression", o.toChars()); |
|
2762 |
}
|
|
2763 |
}
|
|
2764 |
}
|
|
2765 |
|
|
2766 |
static TupleExp create(Loc loc, Expressions* exps) |
|
2767 |
{
|
|
2768 |
return new TupleExp(loc, exps); |
|
2769 |
}
|
|
2770 |
|
|
2771 |
override TupleExp toTupleExp() |
|
2772 |
{
|
|
2773 | 1 |
return this; |
2774 |
}
|
|
2775 |
|
|
2776 |
override Expression syntaxCopy() |
|
2777 |
{
|
|
2778 | 1 |
return new TupleExp(loc, e0 ? e0.syntaxCopy() : null, arraySyntaxCopy(exps)); |
2779 |
}
|
|
2780 |
|
|
2781 |
override bool equals(const RootObject o) const |
|
2782 |
{
|
|
2783 |
if (this == o) |
|
2784 |
return true; |
|
2785 |
if (auto e = o.isExpression()) |
|
2786 |
if (auto te = e.isTupleExp()) |
|
2787 |
{
|
|
2788 |
if (exps.dim != te.exps.dim) |
|
2789 |
return false; |
|
2790 |
if (e0 && !e0.equals(te.e0) || !e0 && te.e0) |
|
2791 |
return false; |
|
2792 |
foreach (i, e1; *exps) |
|
2793 |
{
|
|
2794 |
auto e2 = (*te.exps)[i]; |
|
2795 |
if (!e1.equals(e2)) |
|
2796 |
return false; |
|
2797 |
}
|
|
2798 |
return true; |
|
2799 |
}
|
|
2800 |
return false; |
|
2801 |
}
|
|
2802 |
|
|
2803 |
override void accept(Visitor v) |
|
2804 |
{
|
|
2805 | 1 |
v.visit(this); |
2806 |
}
|
|
2807 |
}
|
|
2808 |
|
|
2809 |
/***********************************************************
|
|
2810 |
* [ e1, e2, e3, ... ]
|
|
2811 |
*
|
|
2812 |
* http://dlang.org/spec/expression.html#array_literals
|
|
2813 |
*/
|
|
2814 |
extern (C++) final class ArrayLiteralExp : Expression |
|
2815 |
{
|
|
2816 |
/** If !is null, elements[] can be sparse and basis is used for the
|
|
2817 |
* "default" element value. In other words, non-null elements[i] overrides
|
|
2818 |
* this 'basis' value.
|
|
2819 |
*/
|
|
2820 |
Expression basis; |
|
2821 |
|
|
2822 |
Expressions* elements; |
|
2823 |
OwnedBy ownedByCtfe = OwnedBy.code; |
|
2824 |
|
|
2825 |
|
|
2826 | 1 |
extern (D) this(const ref Loc loc, Type type, Expressions* elements) |
2827 |
{
|
|
2828 | 1 |
super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); |
2829 | 1 |
this.type = type; |
2830 | 1 |
this.elements = elements; |
2831 |
}
|
|
2832 |
|
|
2833 | 1 |
extern (D) this(const ref Loc loc, Type type, Expression e) |
2834 |
{
|
|
2835 | 1 |
super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); |
2836 | 1 |
this.type = type; |
2837 | 1 |
elements = new Expressions(); |
2838 | 1 |
elements.push(e); |
2839 |
}
|
|
2840 |
|
|
2841 | 1 |
extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) |
2842 |
{
|
|
2843 | 1 |
super(loc, TOK.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); |
2844 | 1 |
this.type = type; |
2845 | 1 |
this.basis = basis; |
2846 | 1 |
this.elements = elements; |
2847 |
}
|
|
2848 |
|
|
2849 |
static ArrayLiteralExp create(Loc loc, Expressions* elements) |
|
2850 |
{
|
|
2851 |
return new ArrayLiteralExp(loc, null, elements); |
|
2852 |
}
|
|
2853 |
|
|
2854 |
// Same as create, but doesn't allocate memory.
|
|
2855 |
static void emplace(UnionExp* pue, Loc loc, Expressions* elements) |
|
2856 |
{
|
|
2857 |
emplaceExp!(ArrayLiteralExp)(pue, loc, null, elements); |
|
2858 |
}
|
|
2859 |
|
|
2860 |
override Expression syntaxCopy() |
|
2861 |
{
|
|
2862 | 1 |
return new ArrayLiteralExp(loc, |
2863 |
null, |
|
2864 | 1 |
basis ? basis.syntaxCopy() : null, |
2865 |
arraySyntaxCopy(elements)); |
|
2866 |
}
|
|
2867 |
|
|
2868 |
override bool equals(const RootObject o) const |
|
2869 |
{
|
|
2870 | 1 |
if (this == o) |
2871 |
return true; |
|
2872 | 1 |
auto e = o.isExpression(); |
2873 | 1 |
if (!e) |
2874 |
return false; |
|
2875 | 1 |
if (auto ae = e.isArrayLiteralExp()) |
2876 |
{
|
|
2877 | 1 |
if (elements.dim != ae.elements.dim) |
2878 |
return false; |
|
2879 | 1 |
if (elements.dim == 0 && !type.equals(ae.type)) |
2880 |
{
|
|
2881 |
return false; |
|
2882 |
}
|
|
2883 |
|
|
2884 | 1 |
foreach (i, e1; *elements) |
2885 |
{
|
|
2886 | 1 |
auto e2 = (*ae.elements)[i]; |
2887 | 1 |
auto e1x = e1 ? e1 : basis; |
2888 | 1 |
auto e2x = e2 ? e2 : ae.basis; |
2889 |
|
|
2890 | 1 |
if (e1x != e2x && (!e1x || !e2x || !e1x.equals(e2x))) |
2891 |
return false; |
|
2892 |
}
|
|
2893 | 1 |
return true; |
2894 |
}
|
|
2895 |
return false; |
|
2896 |
}
|
|
2897 |
|
|
2898 |
Expression getElement(size_t i) |
|
2899 |
{
|
|
2900 |
return this[i]; |
|
2901 |
}
|
|
2902 |
|
|
2903 |
Expression opIndex(size_t i) |
|
2904 |
{
|
|
2905 | 1 |
auto el = (*elements)[i]; |
2906 | 1 |
return el ? el : basis; |
2907 |
}
|
|
2908 |
|
|
2909 |
override bool isBool(bool result) |
|
2910 |
{
|
|
2911 | 1 |
size_t dim = elements ? elements.dim : 0; |
2912 | 1 |
return result ? (dim != 0) : (dim == 0); |
2913 |
}
|
|
2914 |
|
|
2915 |
override StringExp toStringExp() |
|
2916 |
{
|
|
2917 | 1 |
TY telem = type.nextOf().toBasetype().ty; |
2918 | 1 |
if (telem.isSomeChar || (telem == Tvoid && (!elements || elements.dim == 0))) |
2919 |
{
|
|
2920 | 1 |
ubyte sz = 1; |
2921 | 1 |
if (telem == Twchar) |
2922 | 1 |
sz = 2; |
2923 | 1 |
else if (telem == Tdchar) |
2924 | 1 |
sz = 4; |
2925 |
|
|
2926 | 1 |
OutBuffer buf; |
2927 | 1 |
if (elements) |
2928 |
{
|
|
2929 | 1 |
foreach (i; 0 .. elements.dim) |
2930 |
{
|
|
2931 | 1 |
auto ch = this[i]; |
2932 | 1 |
if (ch.op != TOK.int64) |
2933 |
return null; |
|
2934 | 1 |
if (sz == 1) |
2935 | 1 |
buf.writeByte(cast(uint)ch.toInteger()); |
2936 | 1 |
else if (sz == 2) |
2937 | 1 |
buf.writeword(cast(uint)ch.toInteger()); |
2938 |
else
|
|
2939 | 1 |
buf.write4(cast(uint)ch.toInteger()); |
2940 |
}
|
|
2941 |
}
|
|
2942 | 1 |
char prefix; |
2943 | 1 |
if (sz == 1) |
2944 |
{
|
|
2945 | 1 |
prefix = 'c'; |
2946 | 1 |
buf.writeByte(0); |
2947 |
}
|
|
2948 | 1 |
else if (sz == 2) |
2949 |
{
|
|
2950 | 1 |
prefix = 'w'; |
2951 | 1 |
buf.writeword(0); |
2952 |
}
|
|
2953 |
else
|
|
2954 |
{
|
|
2955 | 1 |
prefix = 'd'; |
2956 | 1 |
buf.write4(0); |
2957 |
}
|
|
2958 |
|
|