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 |
* CTFE for expressions involving pointers, slices, array concatenation etc.
|
|
3 |
*
|
|
4 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
5 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
6 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
7 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
|
|
8 |
* Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html
|
|
9 |
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
|
|
10 |
*/
|
|
11 |
|
|
12 |
module dmd.ctfeexpr; |
|
13 |
|
|
14 |
import core.stdc.stdio; |
|
15 |
import core.stdc.stdlib; |
|
16 |
import core.stdc.string; |
|
17 |
import dmd.arraytypes; |
|
18 |
import dmd.complex; |
|
19 |
import dmd.constfold; |
|
20 |
import dmd.compiler; |
|
21 |
import dmd.dclass; |
|
22 |
import dmd.declaration; |
|
23 |
import dmd.dinterpret; |
|
24 |
import dmd.dstruct; |
|
25 |
import dmd.dtemplate; |
|
26 |
import dmd.errors; |
|
27 |
import dmd.expression; |
|
28 |
import dmd.func; |
|
29 |
import dmd.globals; |
|
30 |
import dmd.mtype; |
|
31 |
import dmd.root.ctfloat; |
|
32 |
import dmd.root.port; |
|
33 |
import dmd.root.rmem; |
|
34 |
import dmd.tokens; |
|
35 |
import dmd.visitor; |
|
36 |
|
|
37 |
|
|
38 |
/***********************************************************
|
|
39 |
* A reference to a class, or an interface. We need this when we
|
|
40 |
* point to a base class (we must record what the type is).
|
|
41 |
*/
|
|
42 |
extern (C++) final class ClassReferenceExp : Expression |
|
43 |
{
|
|
44 |
StructLiteralExp value; |
|
45 |
|
|
46 | 1 |
extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) |
47 |
{
|
|
48 | 1 |
super(loc, TOK.classReference, __traits(classInstanceSize, ClassReferenceExp)); |
49 | 1 |
assert(lit && lit.sd && lit.sd.isClassDeclaration()); |
50 | 1 |
this.value = lit; |
51 | 1 |
this.type = type; |
52 |
}
|
|
53 |
|
|
54 |
ClassDeclaration originalClass() |
|
55 |
{
|
|
56 | 1 |
return value.sd.isClassDeclaration(); |
57 |
}
|
|
58 |
|
|
59 |
// Return index of the field, or -1 if not found
|
|
60 |
private int getFieldIndex(Type fieldtype, uint fieldoffset) |
|
61 |
{
|
|
62 |
ClassDeclaration cd = originalClass(); |
|
63 |
uint fieldsSoFar = 0; |
|
64 |
for (size_t j = 0; j < value.elements.dim; j++) |
|
65 |
{
|
|
66 |
while (j - fieldsSoFar >= cd.fields.dim) |
|
67 |
{
|
|
68 |
fieldsSoFar += cd.fields.dim; |
|
69 |
cd = cd.baseClass; |
|
70 |
}
|
|
71 |
VarDeclaration v2 = cd.fields[j - fieldsSoFar]; |
|
72 |
if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size()) |
|
73 |
{
|
|
74 |
return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar)); |
|
75 |
}
|
|
76 |
}
|
|
77 |
return -1; |
|
78 |
}
|
|
79 |
|
|
80 |
// Return index of the field, or -1 if not found
|
|
81 |
// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
|
|
82 |
int findFieldIndexByName(VarDeclaration v) |
|
83 |
{
|
|
84 | 1 |
ClassDeclaration cd = originalClass(); |
85 | 1 |
size_t fieldsSoFar = 0; |
86 | 1 |
for (size_t j = 0; j < value.elements.dim; j++) |
87 |
{
|
|
88 | 1 |
while (j - fieldsSoFar >= cd.fields.dim) |
89 |
{
|
|
90 | 1 |
fieldsSoFar += cd.fields.dim; |
91 | 1 |
cd = cd.baseClass; |
92 |
}
|
|
93 | 1 |
VarDeclaration v2 = cd.fields[j - fieldsSoFar]; |
94 | 1 |
if (v == v2) |
95 |
{
|
|
96 | 1 |
return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar)); |
97 |
}
|
|
98 |
}
|
|
99 |
return -1; |
|
100 |
}
|
|
101 |
|
|
102 |
override void accept(Visitor v) |
|
103 |
{
|
|
104 | 1 |
v.visit(this); |
105 |
}
|
|
106 |
}
|
|
107 |
|
|
108 |
/*************************
|
|
109 |
* Same as getFieldIndex, but checks for a direct match with the VarDeclaration
|
|
110 |
* Returns:
|
|
111 |
* index of the field, or -1 if not found
|
|
112 |
*/
|
|
113 |
int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure |
|
114 |
{
|
|
115 | 1 |
foreach (i, field; sd.fields) |
116 |
{
|
|
117 | 1 |
if (field == v) |
118 | 1 |
return cast(int)i; |
119 |
}
|
|
120 |
return -1; |
|
121 |
}
|
|
122 |
|
|
123 |
/***********************************************************
|
|
124 |
* Fake class which holds the thrown exception.
|
|
125 |
* Used for implementing exception handling.
|
|
126 |
*/
|
|
127 |
extern (C++) final class ThrownExceptionExp : Expression |
|
128 |
{
|
|
129 |
ClassReferenceExp thrown; // the thing being tossed |
|
130 |
|
|
131 | 1 |
extern (D) this(const ref Loc loc, ClassReferenceExp victim) |
132 |
{
|
|
133 | 1 |
super(loc, TOK.thrownException, __traits(classInstanceSize, ThrownExceptionExp)); |
134 | 1 |
this.thrown = victim; |
135 | 1 |
this.type = victim.type; |
136 |
}
|
|
137 |
|
|
138 |
override const(char)* toChars() const |
|
139 |
{
|
|
140 |
return "CTFE ThrownException"; |
|
141 |
}
|
|
142 |
|
|
143 |
// Generate an error message when this exception is not caught
|
|
144 |
extern (D) void generateUncaughtError() |
|
145 |
{
|
|
146 | 1 |
UnionExp ue = void; |
147 | 1 |
Expression e = resolveSlice((*thrown.value.elements)[0], &ue); |
148 | 1 |
StringExp se = e.toStringExp(); |
149 | 1 |
thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars()); |
150 |
/* Also give the line where the throw statement was. We won't have it
|
|
151 |
* in the case where the ThrowStatement is generated internally
|
|
152 |
* (eg, in ScopeStatement)
|
|
153 |
*/
|
|
154 | 1 |
if (loc.isValid() && !loc.equals(thrown.loc)) |
155 |
.errorSupplemental(loc, "thrown from here"); |
|
156 |
}
|
|
157 |
|
|
158 |
override void accept(Visitor v) |
|
159 |
{
|
|
160 |
v.visit(this); |
|
161 |
}
|
|
162 |
}
|
|
163 |
|
|
164 |
/***********************************************************
|
|
165 |
* This type is only used by the interpreter.
|
|
166 |
*/
|
|
167 |
extern (C++) final class CTFEExp : Expression |
|
168 |
{
|
|
169 | 1 |
extern (D) this(TOK tok) |
170 |
{
|
|
171 | 1 |
super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp)); |
172 | 1 |
type = Type.tvoid; |
173 |
}
|
|
174 |
|
|
175 |
override const(char)* toChars() const |
|
176 |
{
|
|
177 | 1 |
switch (op) |
178 |
{
|
|
179 |
case TOK.cantExpression: |
|
180 |
return "<cant>"; |
|
181 |
case TOK.voidExpression: |
|
182 |
return "<void>"; |
|
183 | 1 |
case TOK.showCtfeContext: |
184 | 1 |
return "<error>"; |
185 |
case TOK.break_: |
|
186 |
return "<break>"; |
|
187 |
case TOK.continue_: |
|
188 |
return "<continue>"; |
|
189 |
case TOK.goto_: |
|
190 |
return "<goto>"; |
|
191 |
default: |
|
192 |
assert(0); |
|
193 |
}
|
|
194 |
}
|
|
195 |
|
|
196 |
extern (D) __gshared CTFEExp cantexp; |
|
197 |
extern (D) __gshared CTFEExp voidexp; |
|
198 |
extern (D) __gshared CTFEExp breakexp; |
|
199 |
extern (D) __gshared CTFEExp continueexp; |
|
200 |
extern (D) __gshared CTFEExp gotoexp; |
|
201 |
/* Used when additional information is needed regarding
|
|
202 |
* a ctfe error.
|
|
203 |
*/
|
|
204 |
extern (D) __gshared CTFEExp showcontext; |
|
205 |
|
|
206 |
extern (D) static bool isCantExp(const Expression e) |
|
207 |
{
|
|
208 | 1 |
return e && e.op == TOK.cantExpression; |
209 |
}
|
|
210 |
|
|
211 |
extern (D) static bool isGotoExp(const Expression e) |
|
212 |
{
|
|
213 | 1 |
return e && e.op == TOK.goto_; |
214 |
}
|
|
215 |
}
|
|
216 |
|
|
217 |
// True if 'e' is CTFEExp::cantexp, or an exception
|
|
218 |
bool exceptionOrCantInterpret(const Expression e) |
|
219 |
{
|
|
220 | 1 |
return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext); |
221 |
}
|
|
222 |
|
|
223 |
/************** Aggregate literals (AA/string/array/struct) ******************/
|
|
224 |
// Given expr, which evaluates to an array/AA/string literal,
|
|
225 |
// return true if it needs to be copied
|
|
226 |
bool needToCopyLiteral(const Expression expr) |
|
227 |
{
|
|
228 | 1 |
Expression e = cast()expr; |
229 |
for (;;) |
|
230 |
{
|
|
231 | 1 |
switch (e.op) |
232 |
{
|
|
233 | 1 |
case TOK.arrayLiteral: |
234 | 1 |
return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; |
235 | 1 |
case TOK.assocArrayLiteral: |
236 | 1 |
return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; |
237 | 1 |
case TOK.structLiteral: |
238 | 1 |
return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code; |
239 | 1 |
case TOK.string_: |
240 | 1 |
case TOK.this_: |
241 | 1 |
case TOK.variable: |
242 | 1 |
return false; |
243 |
case TOK.assign: |
|
244 |
return false; |
|
245 |
case TOK.index: |
|
246 |
case TOK.dotVariable: |
|
247 | 1 |
case TOK.slice: |
248 | 1 |
case TOK.cast_: |
249 | 1 |
e = (cast(UnaExp)e).e1; |
250 | 1 |
continue; |
251 |
case TOK.concatenate: |
|
252 |
return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2); |
|
253 |
case TOK.concatenateAssign: |
|
254 |
case TOK.concatenateElemAssign: |
|
255 |
case TOK.concatenateDcharAssign: |
|
256 |
e = (cast(BinExp)e).e2; |
|
257 |
continue; |
|
258 | 1 |
default: |
259 | 1 |
return false; |
260 |
}
|
|
261 |
}
|
|
262 |
}
|
|
263 |
|
|
264 |
private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null) |
|
265 |
{
|
|
266 | 1 |
if (!oldelems) |
267 |
return oldelems; |
|
268 | 1 |
incArrayAllocs(); |
269 | 1 |
auto newelems = new Expressions(oldelems.dim); |
270 | 1 |
foreach (i, el; *oldelems) |
271 |
{
|
|
272 | 1 |
(*newelems)[i] = copyLiteral(el ? el : basis).copy(); |
273 |
}
|
|
274 | 1 |
return newelems; |
275 |
}
|
|
276 |
|
|
277 |
// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
|
|
278 |
// This value will be used for in-place modification.
|
|
279 |
UnionExp copyLiteral(Expression e) |
|
280 |
{
|
|
281 | 1 |
UnionExp ue = void; |
282 | 1 |
if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp! |
283 |
{
|
|
284 | 1 |
char* s = cast(char*)mem.xcalloc(se.len + 1, se.sz); |
285 | 1 |
const slice = se.peekData(); |
286 | 1 |
memcpy(s, slice.ptr, slice.length); |
287 | 1 |
emplaceExp!(StringExp)(&ue, se.loc, s[0 .. se.len * se.sz], se.len, se.sz); |
288 | 1 |
StringExp se2 = cast(StringExp)ue.exp(); |
289 | 1 |
se2.committed = se.committed; |
290 | 1 |
se2.postfix = se.postfix; |
291 | 1 |
se2.type = se.type; |
292 | 1 |
se2.ownedByCtfe = OwnedBy.ctfe; |
293 | 1 |
return ue; |
294 |
}
|
|
295 | 1 |
if (auto ale = e.isArrayLiteralExp()) |
296 |
{
|
|
297 | 1 |
auto elements = copyLiteralArray(ale.elements, ale.basis); |
298 |
|
|
299 | 1 |
emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements); |
300 |
|
|
301 | 1 |
ArrayLiteralExp r = cast(ArrayLiteralExp)ue.exp(); |
302 | 1 |
r.ownedByCtfe = OwnedBy.ctfe; |
303 | 1 |
return ue; |
304 |
}
|
|
305 | 1 |
if (auto aae = e.isAssocArrayLiteralExp()) |
306 |
{
|
|
307 | 1 |
emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values)); |
308 | 1 |
AssocArrayLiteralExp r = cast(AssocArrayLiteralExp)ue.exp(); |
309 | 1 |
r.type = e.type; |
310 | 1 |
r.ownedByCtfe = OwnedBy.ctfe; |
311 | 1 |
return ue; |
312 |
}
|
|
313 | 1 |
if (auto sle = e.isStructLiteralExp()) |
314 |
{
|
|
315 |
/* syntaxCopy doesn't work for struct literals, because of a nasty special
|
|
316 |
* case: block assignment is permitted inside struct literals, eg,
|
|
317 |
* an int[4] array can be initialized with a single int.
|
|
318 |
*/
|
|
319 | 1 |
auto oldelems = sle.elements; |
320 | 1 |
auto newelems = new Expressions(oldelems.dim); |
321 | 1 |
foreach (i, ref el; *newelems) |
322 |
{
|
|
323 |
// We need the struct definition to detect block assignment
|
|
324 | 1 |
auto v = sle.sd.fields[i]; |
325 | 1 |
auto m = (*oldelems)[i]; |
326 |
|
|
327 |
// If it is a void assignment, use the default initializer
|
|
328 | 1 |
if (!m) |
329 |
m = voidInitLiteral(v.type, v).copy(); |
|
330 |
|
|
331 | 1 |
if (v.type.ty == Tarray || v.type.ty == Taarray) |
332 |
{
|
|
333 |
// Don't have to copy array references
|
|
334 |
}
|
|
335 |
else
|
|
336 |
{
|
|
337 |
// Buzilla 15681: Copy the source element always.
|
|
338 | 1 |
m = copyLiteral(m).copy(); |
339 |
|
|
340 |
// Block assignment from inside struct literals
|
|
341 | 1 |
if (v.type.ty != m.type.ty && v.type.ty == Tsarray) |
342 |
{
|
|
343 |
auto tsa = v.type.isTypeSArray(); |
|
344 |
auto len = cast(size_t)tsa.dim.toInteger(); |
|
345 |
UnionExp uex = void; |
|
346 |
m = createBlockDuplicatedArrayLiteral(&uex, e.loc, v.type, m, len); |
|
347 |
if (m == uex.exp()) |
|
348 |
m = uex.copy(); |
|
349 |
}
|
|
350 |
}
|
|
351 | 1 |
el = m; |
352 |
}
|
|
353 | 1 |
emplaceExp!(StructLiteralExp)(&ue, e.loc, sle.sd, newelems, sle.stype); |
354 | 1 |
auto r = ue.exp().isStructLiteralExp(); |
355 | 1 |
r.type = e.type; |
356 | 1 |
r.ownedByCtfe = OwnedBy.ctfe; |
357 | 1 |
r.origin = sle.origin; |
358 | 1 |
return ue; |
359 |
}
|
|
360 | 1 |
if (e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.symbolOffset || e.op == TOK.null_ || e.op == TOK.variable || e.op == TOK.dotVariable || e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.char_ || e.op == TOK.complex80 || e.op == TOK.void_ || e.op == TOK.vector || e.op == TOK.typeid_) |
361 |
{
|
|
362 |
// Simple value types
|
|
363 |
// Keep e1 for DelegateExp and DotVarExp
|
|
364 | 1 |
emplaceExp!(UnionExp)(&ue, e); |
365 | 1 |
Expression r = ue.exp(); |
366 | 1 |
r.type = e.type; |
367 | 1 |
return ue; |
368 |
}
|
|
369 | 1 |
if (auto se = e.isSliceExp()) |
370 |
{
|
|
371 | 1 |
if (se.type.toBasetype().ty == Tsarray) |
372 |
{
|
|
373 |
// same with resolveSlice()
|
|
374 | 1 |
if (se.e1.op == TOK.null_) |
375 |
{
|
|
376 |
emplaceExp!(NullExp)(&ue, se.loc, se.type); |
|
377 |
return ue; |
|
378 |
}
|
|
379 | 1 |
ue = Slice(se.type, se.e1, se.lwr, se.upr); |
380 | 1 |
auto r = ue.exp().isArrayLiteralExp(); |
381 | 1 |
r.elements = copyLiteralArray(r.elements); |
382 | 1 |
r.ownedByCtfe = OwnedBy.ctfe; |
383 | 1 |
return ue; |
384 |
}
|
|
385 |
else
|
|
386 |
{
|
|
387 |
// Array slices only do a shallow copy
|
|
388 | 1 |
emplaceExp!(SliceExp)(&ue, e.loc, se.e1, se.lwr, se.upr); |
389 | 1 |
Expression r = ue.exp(); |
390 | 1 |
r.type = e.type; |
391 | 1 |
return ue; |
392 |
}
|
|
393 |
}
|
|
394 | 1 |
if (isPointer(e.type)) |
395 |
{
|
|
396 |
// For pointers, we only do a shallow copy.
|
|
397 | 1 |
if (auto ae = e.isAddrExp()) |
398 | 1 |
emplaceExp!(AddrExp)(&ue, e.loc, ae.e1); |
399 |
else if (auto ie = e.isIndexExp()) |
|
400 |
emplaceExp!(IndexExp)(&ue, e.loc, ie.e1, ie.e2); |
|
401 |
else if (auto dve = e.isDotVarExp()) |
|
402 |
{
|
|
403 |
emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads); |
|
404 |
}
|
|
405 |
else
|
|
406 |
assert(0); |
|
407 |
|
|
408 | 1 |
Expression r = ue.exp(); |
409 | 1 |
r.type = e.type; |
410 | 1 |
return ue; |
411 |
}
|
|
412 | 1 |
if (auto cre = e.isClassReferenceExp()) |
413 |
{
|
|
414 | 1 |
emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type); |
415 | 1 |
return ue; |
416 |
}
|
|
417 | 1 |
if (e.op == TOK.error) |
418 |
{
|
|
419 | 1 |
emplaceExp!(UnionExp)(&ue, e); |
420 | 1 |
return ue; |
421 |
}
|
|
422 |
e.error("CTFE internal error: literal `%s`", e.toChars()); |
|
423 |
assert(0); |
|
424 |
}
|
|
425 |
|
|
426 |
/* Deal with type painting.
|
|
427 |
* Type painting is a major nuisance: we can't just set
|
|
428 |
* e.type = type, because that would change the original literal.
|
|
429 |
* But, we can't simply copy the literal either, because that would change
|
|
430 |
* the values of any pointers.
|
|
431 |
*/
|
|
432 |
Expression paintTypeOntoLiteral(Type type, Expression lit) |
|
433 |
{
|
|
434 | 1 |
if (lit.type.equals(type)) |
435 | 1 |
return lit; |
436 | 1 |
return paintTypeOntoLiteralCopy(type, lit).copy(); |
437 |
}
|
|
438 |
|
|
439 |
Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit) |
|
440 |
{
|
|
441 | 1 |
if (lit.type.equals(type)) |
442 | 1 |
return lit; |
443 | 1 |
*pue = paintTypeOntoLiteralCopy(type, lit); |
444 | 1 |
return pue.exp(); |
445 |
}
|
|
446 |
|
|
447 |
private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit) |
|
448 |
{
|
|
449 | 1 |
UnionExp ue; |
450 | 1 |
if (lit.type.equals(type)) |
451 |
{
|
|
452 | 1 |
emplaceExp!(UnionExp)(&ue, lit); |
453 | 1 |
return ue; |
454 |
}
|
|
455 |
// If it is a cast to inout, retain the original type of the referenced part.
|
|
456 | 1 |
if (type.hasWild() && type.hasPointers()) |
457 |
{
|
|
458 | 1 |
emplaceExp!(UnionExp)(&ue, lit); |
459 | 1 |
ue.exp().type = type; |
460 | 1 |
return ue; |
461 |
}
|
|
462 | 1 |
if (auto se = lit.isSliceExp()) |
463 |
{
|
|
464 | 1 |
emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr); |
465 |
}
|
|
466 | 1 |
else if (auto ie = lit.isIndexExp()) |
467 |
{
|
|
468 | 1 |
emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2); |
469 |
}
|
|
470 | 1 |
else if (lit.op == TOK.arrayLiteral) |
471 |
{
|
|
472 | 1 |
emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy()); |
473 |
}
|
|
474 | 1 |
else if (lit.op == TOK.string_) |
475 |
{
|
|
476 |
// For strings, we need to introduce another level of indirection
|
|
477 | 1 |
emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy()); |
478 |
}
|
|
479 | 1 |
else if (auto aae = lit.isAssocArrayLiteralExp()) |
480 |
{
|
|
481 |
// TODO: we should be creating a reference to this AAExp, not
|
|
482 |
// just a ref to the keys and values.
|
|
483 |
OwnedBy wasOwned = aae.ownedByCtfe; |
|
484 |
emplaceExp!(AssocArrayLiteralExp)(&ue, lit.loc, aae.keys, aae.values); |
|
485 |
aae = cast(AssocArrayLiteralExp)ue.exp(); |
|
486 |
aae.ownedByCtfe = wasOwned; |
|
487 |
}
|
|
488 |
else
|
|
489 |
{
|
|
490 |
// Can't type paint from struct to struct*; this needs another
|
|
491 |
// level of indirection
|
|
492 | 1 |
if (lit.op == TOK.structLiteral && isPointer(type)) |
493 |
lit.error("CTFE internal error: painting `%s`", type.toChars()); |
|
494 | 1 |
ue = copyLiteral(lit); |
495 |
}
|
|
496 | 1 |
ue.exp().type = type; |
497 | 1 |
return ue; |
498 |
}
|
|
499 |
|
|
500 |
/*************************************
|
|
501 |
* If e is a SliceExp, constant fold it.
|
|
502 |
* Params:
|
|
503 |
* e = expression to resolve
|
|
504 |
* pue = if not null, store resulting expression here
|
|
505 |
* Returns:
|
|
506 |
* resulting expression
|
|
507 |
*/
|
|
508 |
Expression resolveSlice(Expression e, UnionExp* pue = null) |
|
509 |
{
|
|
510 | 1 |
SliceExp se = e.isSliceExp(); |
511 | 1 |
if (!se) |
512 | 1 |
return e; |
513 | 1 |
if (se.e1.op == TOK.null_) |
514 |
return se.e1; |
|
515 | 1 |
if (pue) |
516 |
{
|
|
517 | 1 |
*pue = Slice(e.type, se.e1, se.lwr, se.upr); |
518 | 1 |
return pue.exp(); |
519 |
}
|
|
520 |
else
|
|
521 | 1 |
return Slice(e.type, se.e1, se.lwr, se.upr).copy(); |
522 |
}
|
|
523 |
|
|
524 |
/* Determine the array length, without interpreting it.
|
|
525 |
* e must be an array literal, or a slice
|
|
526 |
* It's very wasteful to resolve the slice when we only
|
|
527 |
* need the length.
|
|
528 |
*/
|
|
529 |
uinteger_t resolveArrayLength(const Expression e) |
|
530 |
{
|
|
531 | 1 |
switch (e.op) |
532 |
{
|
|
533 |
case TOK.vector: |
|
534 |
return e.isVectorExp().dim; |
|
535 |
|
|
536 | 1 |
case TOK.null_: |
537 | 1 |
return 0; |
538 |
|
|
539 | 1 |
case TOK.slice: |
540 |
{
|
|
541 | 1 |
auto se = cast(SliceExp)e; |
542 | 1 |
const ilo = se.lwr.toInteger(); |
543 | 1 |
const iup = se.upr.toInteger(); |
544 | 1 |
return iup - ilo; |
545 |
}
|
|
546 |
|
|
547 | 1 |
case TOK.string_: |
548 | 1 |
return e.isStringExp().len; |
549 |
|
|
550 | 1 |
case TOK.arrayLiteral: |
551 |
{
|
|
552 | 1 |
const ale = e.isArrayLiteralExp(); |
553 | 1 |
return ale.elements ? ale.elements.dim : 0; |
554 |
}
|
|
555 |
|
|
556 |
case TOK.assocArrayLiteral: |
|
557 |
{
|
|
558 |
return e.isAssocArrayLiteralExp().keys.dim; |
|
559 |
}
|
|
560 |
|
|
561 |
default: |
|
562 |
assert(0); |
|
563 |
}
|
|
564 |
}
|
|
565 |
|
|
566 |
/******************************
|
|
567 |
* Helper for NewExp
|
|
568 |
* Create an array literal consisting of 'elem' duplicated 'dim' times.
|
|
569 |
* Params:
|
|
570 |
* pue = where to store result
|
|
571 |
* loc = source location where the interpretation occurs
|
|
572 |
* type = target type of the result
|
|
573 |
* elem = the source of array element, it will be owned by the result
|
|
574 |
* dim = element number of the result
|
|
575 |
* Returns:
|
|
576 |
* Constructed ArrayLiteralExp
|
|
577 |
*/
|
|
578 |
ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim) |
|
579 |
{
|
|
580 | 1 |
if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray) |
581 |
{
|
|
582 |
// If it is a multidimensional array literal, do it recursively
|
|
583 | 1 |
auto tsa = type.nextOf().isTypeSArray(); |
584 | 1 |
const len = cast(size_t)tsa.dim.toInteger(); |
585 | 1 |
UnionExp ue = void; |
586 | 1 |
elem = createBlockDuplicatedArrayLiteral(&ue, loc, type.nextOf(), elem, len); |
587 | 1 |
if (elem == ue.exp()) |
588 | 1 |
elem = ue.copy(); |
589 |
}
|
|
590 |
|
|
591 |
// Buzilla 15681
|
|
592 | 1 |
const tb = elem.type.toBasetype(); |
593 | 1 |
const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray; |
594 |
|
|
595 | 1 |
auto elements = new Expressions(dim); |
596 | 1 |
foreach (i, ref el; *elements) |
597 |
{
|
|
598 | 1 |
el = mustCopy && i ? copyLiteral(elem).copy() : elem; |
599 |
}
|
|
600 | 1 |
emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements); |
601 | 1 |
auto ale = pue.exp().isArrayLiteralExp(); |
602 | 1 |
ale.ownedByCtfe = OwnedBy.ctfe; |
603 | 1 |
return ale; |
604 |
}
|
|
605 |
|
|
606 |
/******************************
|
|
607 |
* Helper for NewExp
|
|
608 |
* Create a string literal consisting of 'value' duplicated 'dim' times.
|
|
609 |
*/
|
|
610 |
StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz) |
|
611 |
{
|
|
612 | 1 |
auto s = cast(char*)mem.xcalloc(dim, sz); |
613 | 1 |
foreach (elemi; 0 .. dim) |
614 |
{
|
|
615 | 1 |
switch (sz) |
616 |
{
|
|
617 | 1 |
case 1: |
618 | 1 |
s[elemi] = cast(char)value; |
619 | 1 |
break; |
620 |
case 2: |
|
621 |
(cast(wchar*)s)[elemi] = cast(wchar)value; |
|
622 |
break; |
|
623 |
case 4: |
|
624 |
(cast(dchar*)s)[elemi] = value; |
|
625 |
break; |
|
626 |
default: |
|
627 |
assert(0); |
|
628 |
}
|
|
629 |
}
|
|
630 | 1 |
emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz); |
631 | 1 |
auto se = pue.exp().isStringExp(); |
632 | 1 |
se.type = type; |
633 | 1 |
se.committed = true; |
634 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
635 | 1 |
return se; |
636 |
}
|
|
637 |
|
|
638 |
// Return true if t is an AA
|
|
639 |
bool isAssocArray(Type t) |
|
640 |
{
|
|
641 | 1 |
return t.toBasetype().isTypeAArray() !is null; |
642 |
}
|
|
643 |
|
|
644 |
// Given a template AA type, extract the corresponding built-in AA type
|
|
645 |
TypeAArray toBuiltinAAType(Type t) |
|
646 |
{
|
|
647 | 1 |
return t.toBasetype().isTypeAArray(); |
648 |
}
|
|
649 |
|
|
650 |
/************** TypeInfo operations ************************************/
|
|
651 |
// Return true if type is TypeInfo_Class
|
|
652 |
bool isTypeInfo_Class(const Type type) |
|
653 |
{
|
|
654 | 1 |
auto tc = cast()type.isTypeClass(); |
655 | 1 |
return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null)); |
656 |
}
|
|
657 |
|
|
658 |
/************** Pointer operations ************************************/
|
|
659 |
// Return true if t is a pointer (not a function pointer)
|
|
660 |
bool isPointer(Type t) |
|
661 |
{
|
|
662 | 1 |
Type tb = t.toBasetype(); |
663 | 1 |
return tb.ty == Tpointer && tb.nextOf().ty != Tfunction; |
664 |
}
|
|
665 |
|
|
666 |
// For CTFE only. Returns true if 'e' is true or a non-null pointer.
|
|
667 |
bool isTrueBool(Expression e) |
|
668 |
{
|
|
669 | 1 |
return e.isBool(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != TOK.null_); |
670 |
}
|
|
671 |
|
|
672 |
/* Is it safe to convert from srcPointee* to destPointee* ?
|
|
673 |
* srcPointee is the genuine type (never void).
|
|
674 |
* destPointee may be void.
|
|
675 |
*/
|
|
676 |
bool isSafePointerCast(Type srcPointee, Type destPointee) |
|
677 |
{
|
|
678 |
// It's safe to cast S** to D** if it's OK to cast S* to D*
|
|
679 | 1 |
while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer) |
680 |
{
|
|
681 | 1 |
srcPointee = srcPointee.nextOf(); |
682 | 1 |
destPointee = destPointee.nextOf(); |
683 |
}
|
|
684 |
// It's OK if both are the same (modulo const)
|
|
685 | 1 |
if (srcPointee.constConv(destPointee)) |
686 | 1 |
return true; |
687 |
// It's OK if function pointers differ only in safe/pure/nothrow
|
|
688 | 1 |
if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction) |
689 |
return srcPointee.covariant(destPointee) == 1; |
|
690 |
// it's OK to cast to void*
|
|
691 | 1 |
if (destPointee.ty == Tvoid) |
692 | 1 |
return true; |
693 |
// It's OK to cast from V[K] to void*
|
|
694 | 1 |
if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr) |
695 |
return true; |
|
696 |
// It's OK if they are the same size (static array of) integers, eg:
|
|
697 |
// int* --> uint*
|
|
698 |
// int[5][] --> uint[5][]
|
|
699 | 1 |
if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray) |
700 |
{
|
|
701 | 1 |
if (srcPointee.size() != destPointee.size()) |
702 |
return false; |
|
703 | 1 |
srcPointee = srcPointee.baseElemOf(); |
704 | 1 |
destPointee = destPointee.baseElemOf(); |
705 |
}
|
|
706 | 1 |
return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size(); |
707 |
}
|
|
708 |
|
|
709 |
Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) |
|
710 |
{
|
|
711 | 1 |
*ofs = 0; |
712 | 1 |
if (auto ae = e.isAddrExp()) |
713 | 1 |
e = ae.e1; |
714 | 1 |
if (auto soe = e.isSymOffExp()) |
715 | 1 |
*ofs = soe.offset; |
716 | 1 |
if (auto dve = e.isDotVarExp()) |
717 |
{
|
|
718 |
const ex = dve.e1; |
|
719 |
const v = dve.var.isVarDeclaration(); |
|
720 |
assert(v); |
|
721 |
StructLiteralExp se = (ex.op == TOK.classReference) |
|
722 |
? (cast(ClassReferenceExp)ex).value |
|
723 |
: cast(StructLiteralExp)ex; |
|
724 |
|
|
725 |
// We can't use getField, because it makes a copy
|
|
726 |
const i = (ex.op == TOK.classReference) |
|
727 |
? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset) |
|
728 |
: se.getFieldIndex(e.type, v.offset); |
|
729 |
e = (*se.elements)[i]; |
|
730 |
}
|
|
731 | 1 |
if (auto ie = e.isIndexExp()) |
732 |
{
|
|
733 |
// Note that each AA element is part of its own memory block
|
|
734 | 1 |
if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == TOK.string_ || ie.e1.op == TOK.arrayLiteral) && ie.e2.op == TOK.int64) |
735 |
{
|
|
736 | 1 |
*ofs = ie.e2.toInteger(); |
737 | 1 |
return ie.e1; |
738 |
}
|
|
739 |
}
|
|
740 | 1 |
if (auto se = e.isSliceExp()) |
741 |
{
|
|
742 | 1 |
if (se && e.type.toBasetype().ty == Tsarray && |
743 | 1 |
(se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral) && se.lwr.op == TOK.int64) |
744 |
{
|
|
745 | 1 |
*ofs = se.lwr.toInteger(); |
746 | 1 |
return se.e1; |
747 |
}
|
|
748 |
}
|
|
749 | 1 |
return e; |
750 |
}
|
|
751 |
|
|
752 |
/** Return true if agg1 and agg2 are pointers to the same memory block
|
|
753 |
*/
|
|
754 |
bool pointToSameMemoryBlock(Expression agg1, Expression agg2) |
|
755 |
{
|
|
756 | 1 |
if (agg1 == agg2) |
757 | 1 |
return true; |
758 |
// For integers cast to pointers, we regard them as non-comparable
|
|
759 |
// unless they are identical. (This may be overly strict).
|
|
760 | 1 |
if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger()) |
761 |
{
|
|
762 |
return true; |
|
763 |
}
|
|
764 |
// Note that type painting can occur with VarExp, so we
|
|
765 |
// must compare the variables being pointed to.
|
|
766 | 1 |
if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var) |
767 |
{
|
|
768 | 1 |
return true; |
769 |
}
|
|
770 | 1 |
if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) |
771 |
{
|
|
772 | 1 |
return true; |
773 |
}
|
|
774 | 1 |
return false; |
775 |
}
|
|
776 |
|
|
777 |
// return e1 - e2 as an integer, or error if not possible
|
|
778 |
UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2) |
|
779 |
{
|
|
780 | 1 |
UnionExp ue = void; |
781 | 1 |
dinteger_t ofs1, ofs2; |
782 | 1 |
Expression agg1 = getAggregateFromPointer(e1, &ofs1); |
783 | 1 |
Expression agg2 = getAggregateFromPointer(e2, &ofs2); |
784 | 1 |
if (agg1 == agg2) |
785 |
{
|
|
786 | 1 |
Type pointee = (cast(TypePointer)agg1.type).next; |
787 | 1 |
const sz = pointee.size(); |
788 | 1 |
emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); |
789 |
}
|
|
790 | 1 |
else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ && |
791 |
(cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr) |
|
792 |
{
|
|
793 |
Type pointee = (cast(TypePointer)agg1.type).next; |
|
794 |
const sz = pointee.size(); |
|
795 |
emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); |
|
796 |
}
|
|
797 | 1 |
else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && |
798 | 1 |
(cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) |
799 |
{
|
|
800 | 1 |
emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type); |
801 |
}
|
|
802 |
else
|
|
803 |
{
|
|
804 |
error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars()); |
|
805 |
emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); |
|
806 |
}
|
|
807 | 1 |
return ue; |
808 |
}
|
|
809 |
|
|
810 |
// Return eptr op e2, where eptr is a pointer, e2 is an integer,
|
|
811 |
// and op is TOK.add or TOK.min
|
|
812 |
UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2) |
|
813 |
{
|
|
814 | 1 |
UnionExp ue; |
815 | 1 |
if (eptr.type.nextOf().ty == Tvoid) |
816 |
{
|
|
817 | 1 |
error(loc, "cannot perform arithmetic on `void*` pointers at compile time"); |
818 |
Lcant: |
|
819 | 1 |
emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); |
820 | 1 |
return ue; |
821 |
}
|
|
822 | 1 |
if (eptr.op == TOK.address) |
823 | 1 |
eptr = (cast(AddrExp)eptr).e1; |
824 | 1 |
dinteger_t ofs1; |
825 | 1 |
Expression agg1 = getAggregateFromPointer(eptr, &ofs1); |
826 | 1 |
if (agg1.op == TOK.symbolOffset) |
827 |
{
|
|
828 | 1 |
if ((cast(SymOffExp)agg1).var.type.ty != Tsarray) |
829 |
{
|
|
830 |
error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time"); |
|
831 |
goto Lcant; |
|
832 |
}
|
|
833 |
}
|
|
834 | 1 |
else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral) |
835 |
{
|
|
836 |
error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); |
|
837 |
goto Lcant; |
|
838 |
}
|
|
839 | 1 |
dinteger_t ofs2 = e2.toInteger(); |
840 | 1 |
Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next; |
841 | 1 |
dinteger_t sz = pointee.size(); |
842 | 1 |
sinteger_t indx; |
843 | 1 |
dinteger_t len; |
844 | 1 |
if (agg1.op == TOK.symbolOffset) |
845 |
{
|
|
846 | 1 |
indx = ofs1 / sz; |
847 | 1 |
len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger(); |
848 |
}
|
|
849 |
else
|
|
850 |
{
|
|
851 | 1 |
Expression dollar = ArrayLength(Type.tsize_t, agg1).copy(); |
852 | 1 |
assert(!CTFEExp.isCantExp(dollar)); |
853 | 1 |
indx = ofs1; |
854 | 1 |
len = dollar.toInteger(); |
855 |
}
|
|
856 | 1 |
if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus) |
857 | 1 |
indx += ofs2 / sz; |
858 | 1 |
else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus) |
859 | 1 |
indx -= ofs2 / sz; |
860 |
else
|
|
861 |
{
|
|
862 |
error(loc, "CTFE internal error: bad pointer operation"); |
|
863 |
goto Lcant; |
|
864 |
}
|
|
865 | 1 |
if (indx < 0 || len < indx) |
866 |
{
|
|
867 | 1 |
error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len); |
868 | 1 |
goto Lcant; |
869 |
}
|
|
870 | 1 |
if (agg1.op == TOK.symbolOffset) |
871 |
{
|
|
872 | 1 |
emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz); |
873 | 1 |
SymOffExp se = cast(SymOffExp)ue.exp(); |
874 | 1 |
se.type = type; |
875 | 1 |
return ue; |
876 |
}
|
|
877 | 1 |
if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_) |
878 |
{
|
|
879 |
error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars()); |
|
880 |
goto Lcant; |
|
881 |
}
|
|
882 | 1 |
if (eptr.type.toBasetype().ty == Tsarray) |
883 |
{
|
|
884 | 1 |
dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger(); |
885 |
// Create a CTFE pointer &agg1[indx .. indx+dim]
|
|
886 | 1 |
auto se = ctfeEmplaceExp!SliceExp(loc, agg1, |
887 |
ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t), |
|
888 |
ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t)); |
|
889 | 1 |
se.type = type.toBasetype().nextOf(); |
890 | 1 |
emplaceExp!(AddrExp)(&ue, loc, se); |
891 | 1 |
ue.exp().type = type; |
892 | 1 |
return ue; |
893 |
}
|
|
894 |
// Create a CTFE pointer &agg1[indx]
|
|
895 | 1 |
auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t); |
896 | 1 |
Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs); |
897 | 1 |
ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992 |
898 | 1 |
emplaceExp!(AddrExp)(&ue, loc, ie); |
899 | 1 |
ue.exp().type = type; |
900 | 1 |
return ue; |
901 |
}
|
|
902 |
|
|
903 |
// Return 1 if true, 0 if false
|
|
904 |
// -1 if comparison is illegal because they point to non-comparable memory blocks
|
|
905 |
int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2) |
|
906 |
{
|
|
907 | 1 |
if (pointToSameMemoryBlock(agg1, agg2)) |
908 |
{
|
|
909 | 1 |
int n; |
910 | 1 |
switch (op) |
911 |
{
|
|
912 | 1 |
case TOK.lessThan: |
913 | 1 |
n = (ofs1 < ofs2); |
914 | 1 |
break; |
915 | 1 |
case TOK.lessOrEqual: |
916 | 1 |
n = (ofs1 <= ofs2); |
917 | 1 |
break; |
918 | 1 |
case TOK.greaterThan: |
919 | 1 |
n = (ofs1 > ofs2); |
920 | 1 |
break; |
921 | 1 |
case TOK.greaterOrEqual: |
922 | 1 |
n = (ofs1 >= ofs2); |
923 | 1 |
break; |
924 | 1 |
case TOK.identity: |
925 | 1 |
case TOK.equal: |
926 | 1 |
n = (ofs1 == ofs2); |
927 | 1 |
break; |
928 | 1 |
case TOK.notIdentity: |
929 | 1 |
case TOK.notEqual: |
930 | 1 |
n = (ofs1 != ofs2); |
931 | 1 |
break; |
932 |
default: |
|
933 |
assert(0); |
|
934 |
}
|
|
935 | 1 |
return n; |
936 |
}
|
|
937 | 1 |
const null1 = (agg1.op == TOK.null_); |
938 | 1 |
const null2 = (agg2.op == TOK.null_); |
939 | 1 |
int cmp; |
940 | 1 |
if (null1 || null2) |
941 |
{
|
|
942 | 1 |
switch (op) |
943 |
{
|
|
944 |
case TOK.lessThan: |
|
945 |
cmp = null1 && !null2; |
|
946 |
break; |
|
947 | 1 |
case TOK.greaterThan: |
948 | 1 |
cmp = !null1 && null2; |
949 | 1 |
break; |
950 | 1 |
case TOK.lessOrEqual: |
951 | 1 |
cmp = null1; |
952 | 1 |
break; |
953 | 1 |
case TOK.greaterOrEqual: |
954 | 1 |
cmp = null2; |
955 | 1 |
break; |
956 | 1 |
case TOK.identity: |
957 | 1 |
case TOK.equal: |
958 | 1 |
case TOK.notIdentity: // 'cmp' gets inverted below |
959 | 1 |
case TOK.notEqual: |
960 | 1 |
cmp = (null1 == null2); |
961 | 1 |
break; |
962 |
default: |
|
963 |
assert(0); |
|
964 |
}
|
|
965 |
}
|
|
966 |
else
|
|
967 |
{
|
|
968 | 1 |
switch (op) |
969 |
{
|
|
970 | 1 |
case TOK.identity: |
971 | 1 |
case TOK.equal: |
972 | 1 |
case TOK.notIdentity: // 'cmp' gets inverted below |
973 | 1 |
case TOK.notEqual: |
974 | 1 |
cmp = 0; |
975 | 1 |
break; |
976 | 1 |
default: |
977 | 1 |
return -1; // memory blocks are different |
978 |
}
|
|
979 |
}
|
|
980 | 1 |
if (op == TOK.notIdentity || op == TOK.notEqual) |
981 | 1 |
cmp ^= 1; |
982 | 1 |
return cmp; |
983 |
}
|
|
984 |
|
|
985 |
// True if conversion from type 'from' to 'to' involves a reinterpret_cast
|
|
986 |
// floating point -> integer or integer -> floating point
|
|
987 |
bool isFloatIntPaint(Type to, Type from) |
|
988 |
{
|
|
989 | 1 |
return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral()); |
990 |
}
|
|
991 |
|
|
992 |
// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
|
|
993 |
Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to) |
|
994 |
{
|
|
995 | 1 |
if (exceptionOrCantInterpret(fromVal)) |
996 |
return fromVal; |
|
997 | 1 |
assert(to.size() == 4 || to.size() == 8); |
998 | 1 |
return Compiler.paintAsType(pue, fromVal, to); |
999 |
}
|
|
1000 |
|
|
1001 |
/******** Constant folding, with support for CTFE ***************************/
|
|
1002 |
/// Return true if non-pointer expression e can be compared
|
|
1003 |
/// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
|
|
1004 |
bool isCtfeComparable(Expression e) |
|
1005 |
{
|
|
1006 | 1 |
if (e.op == TOK.slice) |
1007 | 1 |
e = (cast(SliceExp)e).e1; |
1008 | 1 |
if (e.isConst() != 1) |
1009 |
{
|
|
1010 | 1 |
if (e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.arrayLiteral || e.op == TOK.structLiteral || e.op == TOK.assocArrayLiteral || e.op == TOK.classReference) |
1011 |
{
|
|
1012 | 1 |
return true; |
1013 |
}
|
|
1014 |
// https://issues.dlang.org/show_bug.cgi?id=14123
|
|
1015 |
// TypeInfo object is comparable in CTFE
|
|
1016 | 1 |
if (e.op == TOK.typeid_) |
1017 | 1 |
return true; |
1018 |
return false; |
|
1019 |
}
|
|
1020 | 1 |
return true; |
1021 |
}
|
|
1022 |
|
|
1023 |
/// Map TOK comparison ops
|
|
1024 |
private bool numCmp(N)(TOK op, N n1, N n2) |
|
1025 |
{
|
|
1026 | 1 |
switch (op) |
1027 |
{
|
|
1028 | 1 |
case TOK.lessThan: |
1029 | 1 |
return n1 < n2; |
1030 | 1 |
case TOK.lessOrEqual: |
1031 | 1 |
return n1 <= n2; |
1032 | 1 |
case TOK.greaterThan: |
1033 | 1 |
return n1 > n2; |
1034 | 1 |
case TOK.greaterOrEqual: |
1035 | 1 |
return n1 >= n2; |
1036 |
|
|
1037 |
default: |
|
1038 |
assert(0); |
|
1039 |
}
|
|
1040 |
}
|
|
1041 |
|
|
1042 |
/// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
1043 |
bool specificCmp(TOK op, int rawCmp) |
|
1044 |
{
|
|
1045 |
return numCmp!int(op, rawCmp, 0); |
|
1046 |
}
|
|
1047 |
|
|
1048 |
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
1049 |
bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2) |
|
1050 |
{
|
|
1051 | 1 |
return numCmp!dinteger_t(op, n1, n2); |
1052 |
}
|
|
1053 |
|
|
1054 |
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
1055 |
bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2) |
|
1056 |
{
|
|
1057 | 1 |
return numCmp!sinteger_t(op, n1, n2); |
1058 |
}
|
|
1059 |
|
|
1060 |
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
|
|
1061 |
bool realCmp(TOK op, real_t r1, real_t r2) |
|
1062 |
{
|
|
1063 |
// Don't rely on compiler, handle NAN arguments separately
|
|
1064 | 1 |
if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered |
1065 |
{
|
|
1066 | 1 |
switch (op) |
1067 |
{
|
|
1068 | 1 |
case TOK.lessThan: |
1069 | 1 |
case TOK.lessOrEqual: |
1070 | 1 |
case TOK.greaterThan: |
1071 | 1 |
case TOK.greaterOrEqual: |
1072 | 1 |
return false; |
1073 |
|
|
1074 |
default: |
|
1075 |
assert(0); |
|
1076 |
}
|
|
1077 |
}
|
|
1078 |
else
|
|
1079 |
{
|
|
1080 | 1 |
return numCmp!real_t(op, r1, r2); |
1081 |
}
|
|
1082 |
}
|
|
1083 |
|
|
1084 |
/* Conceptually the same as memcmp(e1, e2).
|
|
1085 |
* e1 and e2 may be strings, arrayliterals, or slices.
|
|
1086 |
* For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
|
|
1087 |
* For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
|
|
1088 |
* Returns:
|
|
1089 |
* -1,0,1
|
|
1090 |
*/
|
|
1091 |
private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len) |
|
1092 |
{
|
|
1093 |
// Resolve slices, if necessary
|
|
1094 | 1 |
uinteger_t lo1 = 0; |
1095 | 1 |
uinteger_t lo2 = 0; |
1096 |
|
|
1097 | 1 |
Expression x1 = e1; |
1098 | 1 |
if (auto sle1 = x1.isSliceExp()) |
1099 |
{
|
|
1100 | 1 |
lo1 = sle1.lwr.toInteger(); |
1101 | 1 |
x1 = sle1.e1; |
1102 |
}
|
|
1103 | 1 |
auto se1 = x1.isStringExp(); |
1104 | 1 |
auto ae1 = x1.isArrayLiteralExp(); |
1105 |
|
|
1106 | 1 |
Expression x2 = e2; |
1107 | 1 |
if (auto sle2 = x2.isSliceExp()) |
1108 |
{
|
|
1109 | 1 |
lo2 = sle2.lwr.toInteger(); |
1110 | 1 |
x2 = sle2.e1; |
1111 |
}
|
|
1112 | 1 |
auto se2 = x2.isStringExp(); |
1113 | 1 |
auto ae2 = x2.isArrayLiteralExp(); |
1114 |
|
|
1115 |
// Now both must be either TOK.arrayLiteral or TOK.string_
|
|
1116 | 1 |
if (se1 && se2) |
1117 | 1 |
return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); |
1118 | 1 |
if (se1 && ae2) |
1119 |
return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); |
|
1120 | 1 |
if (se2 && ae1) |
1121 |
return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len); |
|
1122 | 1 |
assert(ae1 && ae2); |
1123 |
// Comparing two array literals. This case is potentially recursive.
|
|
1124 |
// If they aren't strings, we just need an equality check rather than
|
|
1125 |
// a full cmp.
|
|
1126 | 1 |
const bool needCmp = ae1.type.nextOf().isintegral(); |
1127 | 1 |
foreach (size_t i; 0 .. cast(size_t)len) |
1128 |
{
|
|
1129 | 1 |
Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; |
1130 | 1 |
Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)]; |
1131 | 1 |
if (needCmp) |
1132 |
{
|
|
1133 | 1 |
const sinteger_t c = ee1.toInteger() - ee2.toInteger(); |
1134 | 1 |
if (c > 0) |
1135 | 1 |
return 1; |
1136 | 1 |
if (c < 0) |
1137 | 1 |
return -1; |
1138 |
}
|
|
1139 |
else
|
|
1140 |
{
|
|
1141 | 1 |
if (ctfeRawCmp(loc, ee1, ee2)) |
1142 | 1 |
return 1; |
1143 |
}
|
|
1144 |
}
|
|
1145 | 1 |
return 0; |
1146 |
}
|
|
1147 |
|
|
1148 |
/* Given a delegate expression e, return .funcptr.
|
|
1149 |
* If e is NullExp, return NULL.
|
|
1150 |
*/
|
|
1151 |
private FuncDeclaration funcptrOf(Expression e) |
|
1152 |
{
|
|
1153 | 1 |
assert(e.type.ty == Tdelegate); |
1154 | 1 |
if (auto de = e.isDelegateExp()) |
1155 | 1 |
return de.func; |
1156 | 1 |
if (auto fe = e.isFuncExp()) |
1157 | 1 |
return fe.fd; |
1158 |
assert(e.op == TOK.null_); |
|
1159 |
return null; |
|
1160 |
}
|
|
1161 |
|
|
1162 |
private bool isArray(const Expression e) |
|
1163 |
{
|
|
1164 | 1 |
return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_; |
1165 |
}
|
|
1166 |
|
|
1167 |
/*****
|
|
1168 |
* Params:
|
|
1169 |
* loc = source file location
|
|
1170 |
* e1 = left operand
|
|
1171 |
* e2 = right operand
|
|
1172 |
* identity = true for `is` identity comparisons
|
|
1173 |
* Returns:
|
|
1174 |
* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
|
|
1175 |
* For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
|
|
1176 |
*/
|
|
1177 |
private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false) |
|
1178 |
{
|
|
1179 | 1 |
if (e1.op == TOK.classReference || e2.op == TOK.classReference) |
1180 |
{
|
|
1181 | 1 |
if (e1.op == TOK.classReference && e2.op == TOK.classReference && |
1182 | 1 |
(cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value) |
1183 | 1 |
return 0; |
1184 | 1 |
return 1; |
1185 |
}
|
|
1186 | 1 |
if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_) |
1187 |
{
|
|
1188 |
// printf("e1: %s\n", e1.toChars());
|
|
1189 |
// printf("e2: %s\n", e2.toChars());
|
|
1190 | 1 |
Type t1 = isType((cast(TypeidExp)e1).obj); |
1191 | 1 |
Type t2 = isType((cast(TypeidExp)e2).obj); |
1192 | 1 |
assert(t1); |
1193 | 1 |
assert(t2); |
1194 | 1 |
return t1 != t2; |
1195 |
}
|
|
1196 |
// null == null, regardless of type
|
|
1197 | 1 |
if (e1.op == TOK.null_ && e2.op == TOK.null_) |
1198 | 1 |
return 0; |
1199 | 1 |
if (e1.type.ty == Tpointer && e2.type.ty == Tpointer) |
1200 |
{
|
|
1201 |
// Can only be an equality test.
|
|
1202 | 1 |
dinteger_t ofs1, ofs2; |
1203 | 1 |
Expression agg1 = getAggregateFromPointer(e1, &ofs1); |
1204 | 1 |
Expression agg2 = getAggregateFromPointer(e2, &ofs2); |
1205 | 1 |
if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) |
1206 |
{
|
|
1207 | 1 |
if (ofs1 == ofs2) |
1208 | 1 |
return 0; |
1209 |
}
|
|
1210 |
return 1; |
|
1211 |
}
|
|
1212 | 1 |
if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate) |
1213 |
{
|
|
1214 |
// If .funcptr isn't the same, they are not equal
|
|
1215 | 1 |
if (funcptrOf(e1) != funcptrOf(e2)) |
1216 | 1 |
return 1; |
1217 |
// If both are delegate literals, assume they have the
|
|
1218 |
// same closure pointer. TODO: We don't support closures yet!
|
|
1219 | 1 |
if (e1.op == TOK.function_ && e2.op == TOK.function_) |
1220 | 1 |
return 0; |
1221 | 1 |
assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_); |
1222 |
// Same .funcptr. Do they have the same .ptr?
|
|
1223 | 1 |
Expression ptr1 = (cast(DelegateExp)e1).e1; |
1224 | 1 |
Expression ptr2 = (cast(DelegateExp)e2).e1; |
1225 | 1 |
dinteger_t ofs1, ofs2; |
1226 | 1 |
Expression agg1 = getAggregateFromPointer(ptr1, &ofs1); |
1227 | 1 |
Expression agg2 = getAggregateFromPointer(ptr2, &ofs2); |
1228 |
// If they are TOK.variable, it means they are FuncDeclarations
|
|
1229 | 1 |
if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) |
1230 |
{
|
|
1231 | 1 |
return 0; |
1232 |
}
|
|
1233 | 1 |
return 1; |
1234 |
}
|
|
1235 | 1 |
if (isArray(e1) && isArray(e2)) |
1236 |
{
|
|
1237 | 1 |
const uinteger_t len1 = resolveArrayLength(e1); |
1238 | 1 |
const uinteger_t len2 = resolveArrayLength(e2); |
1239 |
// workaround for dmc optimizer bug calculating wrong len for
|
|
1240 |
// uinteger_t len = (len1 < len2 ? len1 : len2);
|
|
1241 |
// if (len == 0) ...
|
|
1242 | 1 |
if (len1 > 0 && len2 > 0) |
1243 |
{
|
|
1244 | 1 |
const uinteger_t len = (len1 < len2 ? len1 : len2); |
1245 | 1 |
const int res = ctfeCmpArrays(loc, e1, e2, len); |
1246 | 1 |
if (res != 0) |
1247 | 1 |
return res; |
1248 |
}
|
|
1249 | 1 |
return cast(int)(len1 - len2); |
1250 |
}
|
|
1251 | 1 |
if (e1.type.isintegral()) |
1252 |
{
|
|
1253 | 1 |
return e1.toInteger() != e2.toInteger(); |
1254 |
}
|
|
1255 | 1 |
if (e1.type.isreal() || e1.type.isimaginary()) |
1256 |
{
|
|
1257 | 1 |
real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); |
1258 | 1 |
real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); |
1259 | 1 |
if (identity) |
1260 | 1 |
return !RealIdentical(r1, r2); |
1261 | 1 |
if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered |
1262 |
{
|
|
1263 | 1 |
return 1; // they are not equal |
1264 |
}
|
|
1265 |
else
|
|
1266 |
{
|
|
1267 | 1 |
return (r1 != r2); |
1268 |
}
|
|
1269 |
}
|
|
1270 | 1 |
else if (e1.type.iscomplex()) |
1271 |
{
|
|
1272 | 1 |
auto c1 = e1.toComplex(); |
1273 | 1 |
auto c2 = e2.toComplex(); |
1274 | 1 |
if (identity) |
1275 |
{
|
|
1276 | 1 |
return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im); |
1277 |
}
|
|
1278 | 1 |
return c1 != c2; |
1279 |
}
|
|
1280 | 1 |
if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral) |
1281 |
{
|
|
1282 | 1 |
StructLiteralExp es1 = cast(StructLiteralExp)e1; |
1283 | 1 |
StructLiteralExp es2 = cast(StructLiteralExp)e2; |
1284 |
// For structs, we only need to return 0 or 1 (< and > aren't legal).
|
|
1285 | 1 |
if (es1.sd != es2.sd) |
1286 |
return 1; |
|
1287 | 1 |
else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim)) |
1288 | 1 |
return 0; // both arrays are empty |
1289 | 1 |
else if (!es1.elements || !es2.elements) |
1290 |
return 1; |
|
1291 | 1 |
else if (es1.elements.dim != es2.elements.dim) |
1292 |
return 1; |
|
1293 |
else
|
|
1294 |
{
|
|
1295 | 1 |
foreach (size_t i; 0 .. es1.elements.dim) |
1296 |
{
|
|
1297 | 1 |
Expression ee1 = (*es1.elements)[i]; |
1298 | 1 |
Expression ee2 = (*es2.elements)[i]; |
1299 |
|
|
1300 |
// https://issues.dlang.org/show_bug.cgi?id=16284
|
|
1301 | 1 |
if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp |
1302 |
continue; |
|
1303 |
|
|
1304 | 1 |
if (ee1 == ee2) |
1305 | 1 |
continue; |
1306 | 1 |
if (!ee1 || !ee2) |
1307 |
return 1; |
|
1308 | 1 |
const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); |
1309 | 1 |
if (cmp) |
1310 | 1 |
return 1; |
1311 |
}
|
|
1312 | 1 |
return 0; // All elements are equal |
1313 |
}
|
|
1314 |
}
|
|
1315 | 1 |
if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral) |
1316 |
{
|
|
1317 | 1 |
AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1; |
1318 | 1 |
AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2; |
1319 | 1 |
size_t dim = es1.keys.dim; |
1320 | 1 |
if (es2.keys.dim != dim) |
1321 | 1 |
return 1; |
1322 | 1 |
bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim); |
1323 | 1 |
memset(used, 0, bool.sizeof * dim); |
1324 | 1 |
foreach (size_t i; 0 .. dim) |
1325 |
{
|
|
1326 | 1 |
Expression k1 = (*es1.keys)[i]; |
1327 | 1 |
Expression v1 = (*es1.values)[i]; |
1328 | 1 |
Expression v2 = null; |
1329 | 1 |
foreach (size_t j; 0 .. dim) |
1330 |
{
|
|
1331 | 1 |
if (used[j]) |
1332 | 1 |
continue; |
1333 | 1 |
Expression k2 = (*es2.keys)[j]; |
1334 | 1 |
if (ctfeRawCmp(loc, k1, k2, identity)) |
1335 | 1 |
continue; |
1336 | 1 |
used[j] = true; |
1337 | 1 |
v2 = (*es2.values)[j]; |
1338 | 1 |
break; |
1339 |
}
|
|
1340 | 1 |
if (!v2 || ctfeRawCmp(loc, v1, v2, identity)) |
1341 |
{
|
|
1342 | 1 |
mem.xfree(used); |
1343 | 1 |
return 1; |
1344 |
}
|
|
1345 |
}
|
|
1346 | 1 |
mem.xfree(used); |
1347 | 1 |
return 0; |
1348 |
}
|
|
1349 |
error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars()); |
|
1350 |
assert(0); |
|
1351 |
}
|
|
1352 |
|
|
1353 |
/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
|
|
1354 |
bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2) |
|
1355 |
{
|
|
1356 | 1 |
return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual); |
1357 |
}
|
|
1358 |
|
|
1359 |
/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
|
|
1360 |
bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2) |
|
1361 |
{
|
|
1362 |
//printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
|
|
1363 |
//printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op),
|
|
1364 |
// Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars());
|
|
1365 | 1 |
bool cmp; |
1366 | 1 |
if (e1.op == TOK.null_) |
1367 |
{
|
|
1368 | 1 |
cmp = (e2.op == TOK.null_); |
1369 |
}
|
|
1370 | 1 |
else if (e2.op == TOK.null_) |
1371 |
{
|
|
1372 | 1 |
cmp = false; |
1373 |
}
|
|
1374 | 1 |
else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset) |
1375 |
{
|
|
1376 |
SymOffExp es1 = cast(SymOffExp)e1; |
|
1377 |
SymOffExp es2 = cast(SymOffExp)e2; |
|
1378 |
cmp = (es1.var == es2.var && es1.offset == es2.offset); |
|
1379 |
}
|
|
1380 | 1 |
else if (e1.type.isreal()) |
1381 |
cmp = RealIdentical(e1.toReal(), e2.toReal()); |
|
1382 | 1 |
else if (e1.type.isimaginary()) |
1383 |
cmp = RealIdentical(e1.toImaginary(), e2.toImaginary()); |
|
1384 | 1 |
else if (e1.type.iscomplex()) |
1385 |
{
|
|
1386 |
complex_t v1 = e1.toComplex(); |
|
1387 |
complex_t v2 = e2.toComplex(); |
|
1388 |
cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1)); |
|
1389 |
}
|
|
1390 |
else
|
|
1391 |
{
|
|
1392 | 1 |
cmp = !ctfeRawCmp(loc, e1, e2, true); |
1393 |
}
|
|
1394 | 1 |
if (op == TOK.notIdentity || op == TOK.notEqual) |
1395 | 1 |
cmp ^= true; |
1396 | 1 |
return cmp; |
1397 |
}
|
|
1398 |
|
|
1399 |
/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
|
|
1400 |
bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2) |
|
1401 |
{
|
|
1402 | 1 |
Type t1 = e1.type.toBasetype(); |
1403 | 1 |
Type t2 = e2.type.toBasetype(); |
1404 |
|
|
1405 | 1 |
if (t1.isString() && t2.isString()) |
1406 |
return specificCmp(op, ctfeRawCmp(loc, e1, e2)); |
|
1407 | 1 |
else if (t1.isreal()) |
1408 | 1 |
return realCmp(op, e1.toReal(), e2.toReal()); |
1409 | 1 |
else if (t1.isimaginary()) |
1410 |
return realCmp(op, e1.toImaginary(), e2.toImaginary()); |
|
1411 | 1 |
else if (t1.isunsigned() || t2.isunsigned()) |
1412 | 1 |
return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); |
1413 |
else
|
|
1414 | 1 |
return intSignedCmp(op, e1.toInteger(), e2.toInteger()); |
1415 |
}
|
|
1416 |
|
|
1417 |
UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) |
|
1418 |
{
|
|
1419 | 1 |
Type t1 = e1.type.toBasetype(); |
1420 | 1 |
Type t2 = e2.type.toBasetype(); |
1421 | 1 |
UnionExp ue; |
1422 | 1 |
if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral()) |
1423 |
{
|
|
1424 |
// [chars] ~ string => string (only valid for CTFE)
|
|
1425 | 1 |
StringExp es1 = cast(StringExp)e2; |
1426 | 1 |
ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1; |
1427 | 1 |
const len = es1.len + es2.elements.dim; |
1428 | 1 |
const sz = es1.sz; |
1429 | 1 |
void* s = mem.xmalloc((len + 1) * sz); |
1430 | 1 |
const data1 = es1.peekData(); |
1431 | 1 |
memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length); |
1432 | 1 |
foreach (size_t i; 0 .. es2.elements.dim) |
1433 |
{
|
|
1434 | 1 |
Expression es2e = (*es2.elements)[i]; |
1435 | 1 |
if (es2e.op != TOK.int64) |
1436 |
{
|
|
1437 |
emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); |
|
1438 |
return ue; |
|
1439 |
}
|
|
1440 | 1 |
dinteger_t v = es2e.toInteger(); |
1441 | 1 |
Port.valcpy(cast(char*)s + i * sz, v, sz); |
1442 |
}
|
|
1443 |
// Add terminating 0
|
|
1444 | 1 |
memset(cast(char*)s + len * sz, 0, sz); |
1445 | 1 |
emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
1446 | 1 |
StringExp es = cast(StringExp)ue.exp(); |
1447 | 1 |
es.committed = 0; |
1448 | 1 |
es.type = type; |
1449 | 1 |
return ue; |
1450 |
}
|
|
1451 | 1 |
if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral()) |
1452 |
{
|
|
1453 |
// string ~ [chars] => string (only valid for CTFE)
|
|
1454 |
// Concatenate the strings
|
|
1455 | 1 |
StringExp es1 = cast(StringExp)e1; |
1456 | 1 |
ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; |
1457 | 1 |
const len = es1.len + es2.elements.dim; |
1458 | 1 |
const sz = es1.sz; |
1459 | 1 |
void* s = mem.xmalloc((len + 1) * sz); |
1460 | 1 |
auto slice = es1.peekData(); |
1461 | 1 |
memcpy(s, slice.ptr, slice.length); |
1462 | 1 |
foreach (size_t i; 0 .. es2.elements.dim) |
1463 |
{
|
|
1464 | 1 |
Expression es2e = (*es2.elements)[i]; |
1465 | 1 |
if (es2e.op != TOK.int64) |
1466 |
{
|
|
1467 |
emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); |
|
1468 |
return ue; |
|
1469 |
}
|
|
1470 | 1 |
const v = es2e.toInteger(); |
1471 | 1 |
Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz); |
1472 |
}
|
|
1473 |
// Add terminating 0
|
|
1474 | 1 |
memset(cast(char*)s + len * sz, 0, sz); |
1475 | 1 |
emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); |
1476 | 1 |
StringExp es = cast(StringExp)ue.exp(); |
1477 | 1 |
es.sz = sz; |
1478 | 1 |
es.committed = 0; //es1.committed; |
1479 | 1 |
es.type = type; |
1480 | 1 |
return ue; |
1481 |
}
|
|
1482 | 1 |
if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) |
1483 |
{
|
|
1484 |
// [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
|
|
1485 | 1 |
ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; |
1486 | 1 |
ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; |
1487 | 1 |
emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements)); |
1488 | 1 |
es1 = cast(ArrayLiteralExp)ue.exp(); |
1489 | 1 |
es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements)); |
1490 | 1 |
return ue; |
1491 |
}
|
|
1492 | 1 |
if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf())) |
1493 |
{
|
|
1494 |
// [ e1 ] ~ null ----> [ e1 ].dup
|
|
1495 | 1 |
ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy()); |
1496 | 1 |
return ue; |
1497 |
}
|
|
1498 | 1 |
if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) |
1499 |
{
|
|
1500 |
// null ~ [ e2 ] ----> [ e2 ].dup
|
|
1501 | 1 |
ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy()); |
1502 | 1 |
return ue; |
1503 |
}
|
|
1504 | 1 |
ue = Cat(type, e1, e2); |
1505 | 1 |
return ue; |
1506 |
}
|
|
1507 |
|
|
1508 |
/* Given an AA literal 'ae', and a key 'e2':
|
|
1509 |
* Return ae[e2] if present, or NULL if not found.
|
|
1510 |
*/
|
|
1511 |
Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2) |
|
1512 |
{
|
|
1513 |
/* Search the keys backwards, in case there are duplicate keys
|
|
1514 |
*/
|
|
1515 | 1 |
for (size_t i = ae.keys.dim; i;) |
1516 |
{
|
|
1517 | 1 |
--i; |
1518 | 1 |
Expression ekey = (*ae.keys)[i]; |
1519 | 1 |
const int eq = ctfeEqual(loc, TOK.equal, ekey, e2); |
1520 | 1 |
if (eq) |
1521 |
{
|
|
1522 | 1 |
return (*ae.values)[i]; |
1523 |
}
|
|
1524 |
}
|
|
1525 | 1 |
return null; |
1526 |
}
|
|
1527 |
|
|
1528 |
/* Same as for constfold.Index, except that it only works for static arrays,
|
|
1529 |
* dynamic arrays, and strings. We know that e1 is an
|
|
1530 |
* interpreted CTFE expression, so it cannot have side-effects.
|
|
1531 |
*/
|
|
1532 |
Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx) |
|
1533 |
{
|
|
1534 |
//printf("ctfeIndex(e1 = %s)\n", e1.toChars());
|
|
1535 | 1 |
assert(e1.type); |
1536 | 1 |
if (auto es1 = e1.isStringExp()) |
1537 |
{
|
|
1538 | 1 |
if (indx >= es1.len) |
1539 |
{
|
|
1540 |
error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len); |
|
1541 |
return CTFEExp.cantexp; |
|
1542 |
}
|
|
1543 | 1 |
emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type); |
1544 | 1 |
return pue.exp(); |
1545 |
}
|
|
1546 |
|
|
1547 | 1 |
if (auto ale = e1.isArrayLiteralExp()) |
1548 |
{
|
|
1549 | 1 |
if (indx >= ale.elements.dim) |
1550 |
{
|
|
1551 |
error(loc, "array index %llu is out of bounds `%s[0 .. %llu]`", indx, e1.toChars(), cast(ulong)ale.elements.dim); |
|
1552 |
return CTFEExp.cantexp; |
|
1553 |
}
|
|
1554 | 1 |
Expression e = (*ale.elements)[cast(size_t)indx]; |
1555 | 1 |
return paintTypeOntoLiteral(pue, type, e); |
1556 |
}
|
|
1557 |
|
|
1558 |
assert(0); |
|
1559 |
}
|
|
1560 |
|
|
1561 |
Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e) |
|
1562 |
{
|
|
1563 |
Expression paint() |
|
1564 |
{
|
|
1565 | 1 |
return paintTypeOntoLiteral(pue, to, e); |
1566 |
}
|
|
1567 |
|
|
1568 | 1 |
if (e.op == TOK.null_) |
1569 | 1 |
return paint(); |
1570 |
|
|
1571 | 1 |
if (e.op == TOK.classReference) |
1572 |
{
|
|
1573 |
// Disallow reinterpreting class casts. Do this by ensuring that
|
|
1574 |
// the original class can implicitly convert to the target class
|
|
1575 | 1 |
ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass(); |
1576 | 1 |
if (originalClass.type.implicitConvTo(to.mutableOf())) |
1577 | 1 |
return paint(); |
1578 |
else
|
|
1579 |
{
|
|
1580 | 1 |
emplaceExp!(NullExp)(pue, loc, to); |
1581 | 1 |
return pue.exp(); |
1582 |
}
|
|
1583 |
}
|
|
1584 |
|
|
1585 |
// Allow TypeInfo type painting
|
|
1586 | 1 |
if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to)) |
1587 | 1 |
return paint(); |
1588 |
|
|
1589 |
// Allow casting away const for struct literals
|
|
1590 | 1 |
if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0)) |
1591 | 1 |
return paint(); |
1592 |
|
|
1593 | 1 |
Expression r; |
1594 | 1 |
if (e.type.equals(type) && type.equals(to)) |
1595 |
{
|
|
1596 |
// necessary not to change e's address for pointer comparisons
|
|
1597 | 1 |
r = e; |
1598 |
}
|
|
1599 | 1 |
else if (to.toBasetype().ty == Tarray && |
1600 | 1 |
type.toBasetype().ty == Tarray && |
1601 | 1 |
to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size()) |
1602 |
{
|
|
1603 |
// https://issues.dlang.org/show_bug.cgi?id=12495
|
|
1604 |
// Array reinterpret casts: eg. string to immutable(ubyte)[]
|
|
1605 | 1 |
return paint(); |
1606 |
}
|
|
1607 |
else
|
|
1608 |
{
|
|
1609 | 1 |
*pue = Cast(loc, type, to, e); |
1610 | 1 |
r = pue.exp(); |
1611 |
}
|
|
1612 |
|
|
1613 | 1 |
if (CTFEExp.isCantExp(r)) |
1614 | 1 |
error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars()); |
1615 |
|
|
1616 | 1 |
if (auto ae = e.isArrayLiteralExp()) |
1617 | 1 |
ae.ownedByCtfe = OwnedBy.ctfe; |
1618 |
|
|
1619 | 1 |
if (auto se = e.isStringExp()) |
1620 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
1621 |
|
|
1622 | 1 |
return r; |
1623 |
}
|
|
1624 |
|
|
1625 |
/******** Assignment helper functions ***************************/
|
|
1626 |
/* Set dest = src, where both dest and src are container value literals
|
|
1627 |
* (ie, struct literals, or static arrays (can be an array literal or a string))
|
|
1628 |
* Assignment is recursively in-place.
|
|
1629 |
* Purpose: any reference to a member of 'dest' will remain valid after the
|
|
1630 |
* assignment.
|
|
1631 |
*/
|
|
1632 |
void assignInPlace(Expression dest, Expression src) |
|
1633 |
{
|
|
1634 | 1 |
if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_)) |
1635 |
{
|
|
1636 |
printf("invalid op %d %d\n", src.op, dest.op); |
|
1637 |
assert(0); |
|
1638 |
}
|
|
1639 | 1 |
Expressions* oldelems; |
1640 | 1 |
Expressions* newelems; |
1641 | 1 |
if (dest.op == TOK.structLiteral) |
1642 |
{
|
|
1643 | 1 |
assert(dest.op == src.op); |
1644 | 1 |
oldelems = (cast(StructLiteralExp)dest).elements; |
1645 | 1 |
newelems = (cast(StructLiteralExp)src).elements; |
1646 | 1 |
auto sd = (cast(StructLiteralExp)dest).sd; |
1647 | 1 |
const nfields = sd.nonHiddenFields(); |
1648 | 1 |
const nvthis = sd.fields.dim - nfields; |
1649 | 1 |
if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim) |
1650 |
foreach (_; 0 .. newelems.dim - oldelems.dim) |
|
1651 |
oldelems.push(null); |
|
1652 |
}
|
|
1653 | 1 |
else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral) |
1654 |
{
|
|
1655 | 1 |
oldelems = (cast(ArrayLiteralExp)dest).elements; |
1656 | 1 |
newelems = (cast(ArrayLiteralExp)src).elements; |
1657 |
}
|
|
1658 | 1 |
else if (dest.op == TOK.string_ && src.op == TOK.string_) |
1659 |
{
|
|
1660 | 1 |
sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0); |
1661 | 1 |
return; |
1662 |
}
|
|
1663 | 1 |
else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_) |
1664 |
{
|
|
1665 |
sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0); |
|
1666 |
return; |
|
1667 |
}
|
|
1668 | 1 |
else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_) |
1669 |
{
|
|
1670 | 1 |
sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0); |
1671 | 1 |
return; |
1672 |
}
|
|
1673 |
else
|
|
1674 |
{
|
|
1675 |
printf("invalid op %d %d\n", src.op, dest.op); |
|
1676 |
assert(0); |
|
1677 |
}
|
|
1678 | 1 |
assert(oldelems.dim == newelems.dim); |
1679 | 1 |
foreach (size_t i; 0 .. oldelems.dim) |
1680 |
{
|
|
1681 | 1 |
Expression e = (*newelems)[i]; |
1682 | 1 |
Expression o = (*oldelems)[i]; |
1683 | 1 |
if (e.op == TOK.structLiteral) |
1684 |
{
|
|
1685 | 1 |
assert(o.op == e.op); |
1686 | 1 |
assignInPlace(o, e); |
1687 |
}
|
|
1688 | 1 |
else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray) |
1689 |
{
|
|
1690 | 1 |
assignInPlace(o, e); |
1691 |
}
|
|
1692 |
else
|
|
1693 |
{
|
|
1694 | 1 |
(*oldelems)[i] = (*newelems)[i]; |
1695 |
}
|
|
1696 |
}
|
|
1697 |
}
|
|
1698 |
|
|
1699 |
// Given an AA literal aae, set aae[index] = newval and return newval.
|
|
1700 |
Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) |
|
1701 |
{
|
|
1702 |
/* Create new associative array literal reflecting updated key/value
|
|
1703 |
*/
|
|
1704 | 1 |
Expressions* keysx = aae.keys; |
1705 | 1 |
Expressions* valuesx = aae.values; |
1706 | 1 |
int updated = 0; |
1707 | 1 |
for (size_t j = valuesx.dim; j;) |
1708 |
{
|
|
1709 | 1 |
j--; |
1710 | 1 |
Expression ekey = (*aae.keys)[j]; |
1711 | 1 |
int eq = ctfeEqual(loc, TOK.equal, ekey, index); |
1712 | 1 |
if (eq) |
1713 |
{
|
|
1714 | 1 |
(*valuesx)[j] = newval; |
1715 | 1 |
updated = 1; |
1716 |
}
|
|
1717 |
}
|
|
1718 | 1 |
if (!updated) |
1719 |
{
|
|
1720 |
// Append index/newval to keysx[]/valuesx[]
|
|
1721 | 1 |
valuesx.push(newval); |
1722 | 1 |
keysx.push(index); |
1723 |
}
|
|
1724 | 1 |
return newval; |
1725 |
}
|
|
1726 |
|
|
1727 |
/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
|
|
1728 |
/// oldlen, change its length to newlen. If the newlen is longer than oldlen,
|
|
1729 |
/// all new elements will be set to the default initializer for the element type.
|
|
1730 |
UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) |
|
1731 |
{
|
|
1732 | 1 |
UnionExp ue; |
1733 | 1 |
Type elemType = arrayType.next; |
1734 | 1 |
assert(elemType); |
1735 | 1 |
Expression defaultElem = elemType.defaultInitLiteral(loc); |
1736 | 1 |
auto elements = new Expressions(newlen); |
1737 |
// Resolve slices
|
|
1738 | 1 |
size_t indxlo = 0; |
1739 | 1 |
if (oldval.op == TOK.slice) |
1740 |
{
|
|
1741 |
indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger(); |
|
1742 |
oldval = (cast(SliceExp)oldval).e1; |
|
1743 |
}
|
|
1744 | 1 |
size_t copylen = oldlen < newlen ? oldlen : newlen; |
1745 | 1 |
if (oldval.op == TOK.string_) |
1746 |
{
|
|
1747 | 1 |
StringExp oldse = cast(StringExp)oldval; |
1748 | 1 |
void* s = mem.xcalloc(newlen + 1, oldse.sz); |
1749 | 1 |
const data = oldse.peekData(); |
1750 | 1 |
memcpy(s, data.ptr, copylen * oldse.sz); |
1751 | 1 |
const defaultValue = cast(uint)defaultElem.toInteger(); |
1752 | 1 |
foreach (size_t elemi; copylen .. newlen) |
1753 |
{
|
|
1754 | 1 |
switch (oldse.sz) |
1755 |
{
|
|
1756 | 1 |
case 1: |
1757 | 1 |
(cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue; |
1758 | 1 |
break; |
1759 |
case 2: |
|
1760 |
(cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue; |
|
1761 |
break; |
|
1762 |
case 4: |
|
1763 |
(cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue; |
|
1764 |
break; |
|
1765 |
default: |
|
1766 |
assert(0); |
|
1767 |
}
|
|
1768 |
}
|
|
1769 | 1 |
emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz); |
1770 | 1 |
StringExp se = cast(StringExp)ue.exp(); |
1771 | 1 |
se.type = arrayType; |
1772 | 1 |
se.sz = oldse.sz; |
1773 | 1 |
se.committed = oldse.committed; |
1774 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
1775 |
}
|
|
1776 |
else
|
|
1777 |
{
|
|
1778 | 1 |
if (oldlen != 0) |
1779 |
{
|
|
1780 | 1 |
assert(oldval.op == TOK.arrayLiteral); |
1781 | 1 |
ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval; |
1782 | 1 |
foreach (size_t i; 0 .. copylen) |
1783 | 1 |
(*elements)[i] = (*ae.elements)[indxlo + i]; |
1784 |
}
|
|
1785 | 1 |
if (elemType.ty == Tstruct || elemType.ty == Tsarray) |
1786 |
{
|
|
1787 |
/* If it is an aggregate literal representing a value type,
|
|
1788 |
* we need to create a unique copy for each element
|
|
1789 |
*/
|
|
1790 | 1 |
foreach (size_t i; copylen .. newlen) |
1791 | 1 |
(*elements)[i] = copyLiteral(defaultElem).copy(); |
1792 |
}
|
|
1793 |
else
|
|
1794 |
{
|
|
1795 | 1 |
foreach (size_t i; copylen .. newlen) |
1796 | 1 |
(*elements)[i] = defaultElem; |
1797 |
}
|
|
1798 | 1 |
emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements); |
1799 | 1 |
ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp(); |
1800 | 1 |
aae.ownedByCtfe = OwnedBy.ctfe; |
1801 |
}
|
|
1802 | 1 |
return ue; |
1803 |
}
|
|
1804 |
|
|
1805 |
/*************************** CTFE Sanity Checks ***************************/
|
|
1806 |
|
|
1807 |
bool isCtfeValueValid(Expression newval) |
|
1808 |
{
|
|
1809 | 1 |
Type tb = newval.type.toBasetype(); |
1810 | 1 |
switch (newval.op) |
1811 |
{
|
|
1812 | 1 |
case TOK.int64: |
1813 | 1 |
case TOK.float64: |
1814 | 1 |
case TOK.char_: |
1815 | 1 |
case TOK.complex80: |
1816 | 1 |
return tb.isscalar(); |
1817 |
|
|
1818 | 1 |
case TOK.null_: |
1819 | 1 |
return tb.ty == Tnull || |
1820 | 1 |
tb.ty == Tpointer || |
1821 | 1 |
tb.ty == Tarray || |
1822 | 1 |
tb.ty == Taarray || |
1823 | 1 |
tb.ty == Tclass || |
1824 | 1 |
tb.ty == Tdelegate; |
1825 |
|
|
1826 | 1 |
case TOK.string_: |
1827 | 1 |
return true; // CTFE would directly use the StringExp in AST. |
1828 |
|
|
1829 | 1 |
case TOK.arrayLiteral: |
1830 | 1 |
return true; //((ArrayLiteralExp *)newval)->ownedByCtfe; |
1831 |
|
|
1832 | 1 |
case TOK.assocArrayLiteral: |
1833 | 1 |
return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe; |
1834 |
|
|
1835 | 1 |
case TOK.structLiteral: |
1836 | 1 |
return true; //((StructLiteralExp *)newval)->ownedByCtfe; |
1837 |
|
|
1838 | 1 |
case TOK.classReference: |
1839 | 1 |
return true; |
1840 |
|
|
1841 | 1 |
case TOK.type: |
1842 | 1 |
return true; |
1843 |
|
|
1844 | 1 |
case TOK.vector: |
1845 | 1 |
return true; // vector literal |
1846 |
|
|
1847 | 1 |
case TOK.function_: |
1848 | 1 |
return true; // function literal or delegate literal |
1849 |
|
|
1850 | 1 |
case TOK.delegate_: |
1851 |
{
|
|
1852 |
// &struct.func or &clasinst.func
|
|
1853 |
// &nestedfunc
|
|
1854 | 1 |
Expression ethis = (cast(DelegateExp)newval).e1; |
1855 | 1 |
return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func); |
1856 |
}
|
|
1857 |
|
|
1858 | 1 |
case TOK.symbolOffset: |
1859 |
{
|
|
1860 |
// function pointer, or pointer to static variable
|
|
1861 | 1 |
Declaration d = (cast(SymOffExp)newval).var; |
1862 | 1 |
return d.isFuncDeclaration() || d.isDataseg(); |
1863 |
}
|
|
1864 |
|
|
1865 | 1 |
case TOK.typeid_: |
1866 |
{
|
|
1867 |
// always valid
|
|
1868 | 1 |
return true; |
1869 |
}
|
|
1870 |
|
|
1871 | 1 |
case TOK.address: |
1872 |
{
|
|
1873 |
// e1 should be a CTFE reference
|
|
1874 | 1 |
Expression e1 = (cast(AddrExp)newval).e1; |
1875 | 1 |
return tb.ty == Tpointer && |
1876 |
(
|
|
1877 | 1 |
(e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) || |
1878 | 1 |
e1.op == TOK.variable || |
1879 | 1 |
e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) || |
1880 | 1 |
e1.op == TOK.index && isCtfeReferenceValid(e1) || |
1881 | 1 |
e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray |
1882 |
);
|
|
1883 |
}
|
|
1884 |
|
|
1885 | 1 |
case TOK.slice: |
1886 |
{
|
|
1887 |
// e1 should be an array aggregate
|
|
1888 | 1 |
const SliceExp se = cast(SliceExp)newval; |
1889 | 1 |
assert(se.lwr && se.lwr.op == TOK.int64); |
1890 | 1 |
assert(se.upr && se.upr.op == TOK.int64); |
1891 | 1 |
return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral); |
1892 |
}
|
|
1893 |
|
|
1894 | 1 |
case TOK.void_: |
1895 | 1 |
return true; // uninitialized value |
1896 |
|
|
1897 |
default: |
|
1898 |
newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars()); |
|
1899 |
return false; |
|
1900 |
}
|
|
1901 |
}
|
|
1902 |
|
|
1903 |
bool isCtfeReferenceValid(Expression newval) |
|
1904 |
{
|
|
1905 | 1 |
switch (newval.op) |
1906 |
{
|
|
1907 |
case TOK.this_: |
|
1908 |
return true; |
|
1909 |
|
|
1910 | 1 |
case TOK.variable: |
1911 |
{
|
|
1912 | 1 |
const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration(); |
1913 | 1 |
assert(v); |
1914 |
// Must not be a reference to a reference
|
|
1915 | 1 |
return true; |
1916 |
}
|
|
1917 |
|
|
1918 | 1 |
case TOK.index: |
1919 |
{
|
|
1920 | 1 |
const Expression eagg = (cast(IndexExp)newval).e1; |
1921 | 1 |
return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral; |
1922 |
}
|
|
1923 |
|
|
1924 | 1 |
case TOK.dotVariable: |
1925 |
{
|
|
1926 | 1 |
Expression eagg = (cast(DotVarExp)newval).e1; |
1927 | 1 |
return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg); |
1928 |
}
|
|
1929 |
|
|
1930 | 1 |
default: |
1931 |
// Internally a ref variable may directly point a stack memory.
|
|
1932 |
// e.g. ref int v = 1;
|
|
1933 | 1 |
return isCtfeValueValid(newval); |
1934 |
}
|
|
1935 |
}
|
|
1936 |
|
|
1937 |
// Used for debugging only
|
|
1938 |
void showCtfeExpr(Expression e, int level = 0) |
|
1939 |
{
|
|
1940 |
for (int i = level; i > 0; --i) |
|
1941 |
printf(" "); |
|
1942 |
Expressions* elements = null; |
|
1943 |
// We need the struct definition to detect block assignment
|
|
1944 |
StructDeclaration sd = null; |
|
1945 |
ClassDeclaration cd = null; |
|
1946 |
if (e.op == TOK.structLiteral) |
|
1947 |
{
|
|
1948 |
elements = (cast(StructLiteralExp)e).elements; |
|
1949 |
sd = (cast(StructLiteralExp)e).sd; |
|
1950 |
printf("STRUCT type = %s %p:\n", e.type.toChars(), e); |
|
1951 |
}
|
|
1952 |
else if (e.op == TOK.classReference) |
|
1953 |
{
|
|
1954 |
elements = (cast(ClassReferenceExp)e).value.elements; |
|
1955 |
cd = (cast(ClassReferenceExp)e).originalClass(); |
|
1956 |
printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value); |
|
1957 |
}
|
|
1958 |
else if (e.op == TOK.arrayLiteral) |
|
1959 |
{
|
|
1960 |
elements = (cast(ArrayLiteralExp)e).elements; |
|
1961 |
printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e); |
|
1962 |
}
|
|
1963 |
else if (e.op == TOK.assocArrayLiteral) |
|
1964 |
{
|
|
1965 |
printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e); |
|
1966 |
}
|
|
1967 |
else if (e.op == TOK.string_) |
|
1968 |
{
|
|
1969 |
printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr); |
|
1970 |
}
|
|
1971 |
else if (e.op == TOK.slice) |
|
1972 |
{
|
|
1973 |
printf("SLICE %p: %s\n", e, e.toChars()); |
|
1974 |
showCtfeExpr((cast(SliceExp)e).e1, level + 1); |
|
1975 |
}
|
|
1976 |
else if (e.op == TOK.variable) |
|
1977 |
{
|
|
1978 |
printf("VAR %p %s\n", e, e.toChars()); |
|
1979 |
VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); |
|
1980 |
if (v && getValue(v)) |
|
1981 |
showCtfeExpr(getValue(v), level + 1); |
|
1982 |
}
|
|
1983 |
else if (e.op == TOK.address) |
|
1984 |
{
|
|
1985 |
// This is potentially recursive. We mustn't try to print the thing we're pointing to.
|
|
1986 |
printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars()); |
|
1987 |
}
|
|
1988 |
else
|
|
1989 |
printf("VALUE %p: %s\n", e, e.toChars()); |
|
1990 |
if (elements) |
|
1991 |
{
|
|
1992 |
size_t fieldsSoFar = 0; |
|
1993 |
for (size_t i = 0; i < elements.dim; i++) |
|
1994 |
{
|
|
1995 |
Expression z = null; |
|
1996 |
VarDeclaration v = null; |
|
1997 |
if (i > 15) |
|
1998 |
{
|
|
1999 |
printf("...(total %d elements)\n", cast(int)elements.dim); |
|
2000 |
return; |
|
2001 |
}
|
|
2002 |
if (sd) |
|
2003 |
{
|
|
2004 |
v = sd.fields[i]; |
|
2005 |
z = (*elements)[i]; |
|
2006 |
}
|
|
2007 |
else if (cd) |
|
2008 |
{
|
|
2009 |
while (i - fieldsSoFar >= cd.fields.dim) |
|
2010 |
{
|
|
2011 |
fieldsSoFar += cd.fields.dim; |
|
2012 |
cd = cd.baseClass; |
|
2013 |
for (int j = level; j > 0; --j) |
|
2014 |
printf(" "); |
|
2015 |
printf(" BASE CLASS: %s\n", cd.toChars()); |
|
2016 |
}
|
|
2017 |
v = cd.fields[i - fieldsSoFar]; |
|
2018 |
assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim)); |
|
2019 |
size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i; |
|
2020 |
assert(indx < elements.dim); |
|
2021 |
z = (*elements)[indx]; |
|
2022 |
}
|
|
2023 |
if (!z) |
|
2024 |
{
|
|
2025 |
for (int j = level; j > 0; --j) |
|
2026 |
printf(" "); |
|
2027 |
printf(" void\n"); |
|
2028 |
continue; |
|
2029 |
}
|
|
2030 |
if (v) |
|
2031 |
{
|
|
2032 |
// If it is a void assignment, use the default initializer
|
|
2033 |
if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray) |
|
2034 |
{
|
|
2035 |
for (int j = level; --j;) |
|
2036 |
printf(" "); |
|
2037 |
printf(" field: block initialized static array\n"); |
|
2038 |
continue; |
|
2039 |
}
|
|
2040 |
}
|
|
2041 |
showCtfeExpr(z, level + 1); |
|
2042 |
}
|
|
2043 |
}
|
|
2044 |
}
|
|
2045 |
|
|
2046 |
/*************************** Void initialization ***************************/
|
|
2047 |
UnionExp voidInitLiteral(Type t, VarDeclaration var) |
|
2048 |
{
|
|
2049 | 1 |
UnionExp ue; |
2050 | 1 |
if (t.ty == Tsarray) |
2051 |
{
|
|
2052 | 1 |
TypeSArray tsa = cast(TypeSArray)t; |
2053 | 1 |
Expression elem = voidInitLiteral(tsa.next, var).copy(); |
2054 |
// For aggregate value types (structs, static arrays) we must
|
|
2055 |
// create an a separate copy for each element.
|
|
2056 | 1 |
const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); |
2057 | 1 |
const d = cast(size_t)tsa.dim.toInteger(); |
2058 | 1 |
auto elements = new Expressions(d); |
2059 | 1 |
foreach (i; 0 .. d) |
2060 |
{
|
|
2061 | 1 |
if (mustCopy && i > 0) |
2062 | 1 |
elem = copyLiteral(elem).copy(); |
2063 | 1 |
(*elements)[i] = elem; |
2064 |
}
|
|
2065 | 1 |
emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements); |
2066 | 1 |
ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp(); |
2067 | 1 |
ae.ownedByCtfe = OwnedBy.ctfe; |
2068 |
}
|
|
2069 | 1 |
else if (t.ty == Tstruct) |
2070 |
{
|
|
2071 | 1 |
TypeStruct ts = cast(TypeStruct)t; |
2072 | 1 |
auto exps = new Expressions(ts.sym.fields.dim); |
2073 | 1 |
foreach (size_t i; 0 .. ts.sym.fields.dim) |
2074 |
{
|
|
2075 | 1 |
(*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy(); |
2076 |
}
|
|
2077 | 1 |
emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps); |
2078 | 1 |
StructLiteralExp se = cast(StructLiteralExp)ue.exp(); |
2079 | 1 |
se.type = ts; |
2080 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
2081 |
}
|
|
2082 |
else
|
|
2083 | 1 |
emplaceExp!(VoidInitExp)(&ue, var); |
2084 | 1 |
return ue; |
2085 |
}
|
Read our documentation on viewing source code .