]>
Commit | Line | Data |
---|---|---|
5fee5ec3 IB |
1 | /** |
2 | * The entry point for CTFE. | |
3 | * | |
4 | * Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE)) | |
5 | * | |
f99303eb | 6 | * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved |
c43b5909 IB |
7 | * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
8 | * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
5fee5ec3 IB |
9 | * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d, _dinterpret.d) |
10 | * Documentation: https://dlang.org/phobos/dmd_dinterpret.html | |
11 | * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinterpret.d | |
12 | */ | |
13 | ||
14 | module dmd.dinterpret; | |
15 | ||
16 | import core.stdc.stdio; | |
17 | import core.stdc.stdlib; | |
18 | import core.stdc.string; | |
5fee5ec3 IB |
19 | import dmd.arraytypes; |
20 | import dmd.astenums; | |
21 | import dmd.attrib; | |
22 | import dmd.builtin; | |
23 | import dmd.constfold; | |
24 | import dmd.ctfeexpr; | |
25 | import dmd.dclass; | |
26 | import dmd.declaration; | |
27 | import dmd.dstruct; | |
28 | import dmd.dsymbol; | |
29 | import dmd.dsymbolsem; | |
30 | import dmd.dtemplate; | |
31 | import dmd.errors; | |
32 | import dmd.expression; | |
33 | import dmd.expressionsem; | |
34 | import dmd.func; | |
35 | import dmd.globals; | |
9c7d5e88 | 36 | import dmd.hdrgen; |
5fee5ec3 IB |
37 | import dmd.id; |
38 | import dmd.identifier; | |
39 | import dmd.init; | |
40 | import dmd.initsem; | |
f99303eb | 41 | import dmd.location; |
5fee5ec3 IB |
42 | import dmd.mtype; |
43 | import dmd.printast; | |
44 | import dmd.root.rmem; | |
45 | import dmd.root.array; | |
0fb57034 | 46 | import dmd.root.ctfloat; |
5fee5ec3 IB |
47 | import dmd.root.region; |
48 | import dmd.root.rootobject; | |
c43b5909 | 49 | import dmd.root.utf; |
5fee5ec3 IB |
50 | import dmd.statement; |
51 | import dmd.tokens; | |
5fee5ec3 IB |
52 | import dmd.visitor; |
53 | ||
54 | /************************************* | |
55 | * Entry point for CTFE. | |
56 | * A compile-time result is required. Give an error if not possible. | |
57 | * | |
58 | * `e` must be semantically valid expression. In other words, it should not | |
59 | * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over | |
60 | * functions and may invoke a function that contains `ErrorStatement` in its body. | |
61 | * If that, the "CTFE failed because of previous errors" error is raised. | |
62 | */ | |
63 | public Expression ctfeInterpret(Expression e) | |
64 | { | |
65 | switch (e.op) | |
66 | { | |
9c7d5e88 IB |
67 | case EXP.int64: |
68 | case EXP.float64: | |
69 | case EXP.complex80: | |
70 | case EXP.null_: | |
71 | case EXP.void_: | |
72 | case EXP.string_: | |
73 | case EXP.this_: | |
74 | case EXP.super_: | |
75 | case EXP.type: | |
76 | case EXP.typeid_: | |
77 | case EXP.template_: // non-eponymous template/instance | |
78 | case EXP.scope_: // ditto | |
79 | case EXP.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here | |
80 | case EXP.dotTemplateInstance: // ditto | |
81 | case EXP.dot: // ditto | |
5fee5ec3 IB |
82 | if (e.type.ty == Terror) |
83 | return ErrorExp.get(); | |
9c7d5e88 | 84 | goto case EXP.error; |
5fee5ec3 | 85 | |
9c7d5e88 | 86 | case EXP.error: |
5fee5ec3 IB |
87 | return e; |
88 | ||
89 | default: | |
90 | break; | |
91 | } | |
92 | ||
93 | assert(e.type); // https://issues.dlang.org/show_bug.cgi?id=14642 | |
94 | //assert(e.type.ty != Terror); // FIXME | |
95 | if (e.type.ty == Terror) | |
96 | return ErrorExp.get(); | |
97 | ||
98 | auto rgnpos = ctfeGlobals.region.savePos(); | |
99 | ||
100 | Expression result = interpret(e, null); | |
101 | ||
d7569187 IB |
102 | // Report an error if the expression contained a `ThrowException` and |
103 | // hence generated an uncaught exception | |
104 | if (auto tee = result.isThrownExceptionExp()) | |
105 | { | |
106 | tee.generateUncaughtError(); | |
107 | result = CTFEExp.cantexp; | |
108 | } | |
109 | else | |
110 | result = copyRegionExp(result); | |
5fee5ec3 IB |
111 | |
112 | if (!CTFEExp.isCantExp(result)) | |
113 | result = scrubReturnValue(e.loc, result); | |
114 | if (CTFEExp.isCantExp(result)) | |
115 | result = ErrorExp.get(); | |
116 | ||
117 | ctfeGlobals.region.release(rgnpos); | |
118 | ||
119 | return result; | |
120 | } | |
121 | ||
122 | /* Run CTFE on the expression, but allow the expression to be a TypeExp | |
123 | * or a tuple containing a TypeExp. (This is required by pragma(msg)). | |
124 | */ | |
125 | public Expression ctfeInterpretForPragmaMsg(Expression e) | |
126 | { | |
9c7d5e88 | 127 | if (e.op == EXP.error || e.op == EXP.type) |
5fee5ec3 IB |
128 | return e; |
129 | ||
130 | // It's also OK for it to be a function declaration (happens only with | |
131 | // __traits(getOverloads)) | |
132 | if (auto ve = e.isVarExp()) | |
133 | if (ve.var.isFuncDeclaration()) | |
134 | { | |
135 | return e; | |
136 | } | |
137 | ||
138 | auto tup = e.isTupleExp(); | |
139 | if (!tup) | |
140 | return e.ctfeInterpret(); | |
141 | ||
142 | // Tuples need to be treated separately, since they are | |
143 | // allowed to contain a TypeExp in this case. | |
144 | ||
145 | Expressions* expsx = null; | |
146 | foreach (i, g; *tup.exps) | |
147 | { | |
148 | auto h = ctfeInterpretForPragmaMsg(g); | |
149 | if (h != g) | |
150 | { | |
151 | if (!expsx) | |
152 | { | |
153 | expsx = tup.exps.copy(); | |
154 | } | |
155 | (*expsx)[i] = h; | |
156 | } | |
157 | } | |
158 | if (expsx) | |
159 | { | |
160 | auto te = new TupleExp(e.loc, expsx); | |
161 | expandTuples(te.exps); | |
162 | te.type = new TypeTuple(te.exps); | |
163 | return te; | |
164 | } | |
165 | return e; | |
166 | } | |
167 | ||
f99303eb | 168 | public Expression getValue(VarDeclaration vd) |
5fee5ec3 IB |
169 | { |
170 | return ctfeGlobals.stack.getValue(vd); | |
171 | } | |
172 | ||
173 | /************************************************* | |
174 | * Allocate an Expression in the ctfe region. | |
175 | * Params: | |
176 | * T = type of Expression to allocate | |
177 | * args = arguments to Expression's constructor | |
178 | * Returns: | |
179 | * allocated Expression | |
180 | */ | |
181 | T ctfeEmplaceExp(T : Expression, Args...)(Args args) | |
182 | { | |
183 | if (mem.isGCEnabled) | |
184 | return new T(args); | |
185 | auto p = ctfeGlobals.region.malloc(__traits(classInstanceSize, T)); | |
186 | emplaceExp!T(p, args); | |
187 | return cast(T)p; | |
188 | } | |
189 | ||
190 | // CTFE diagnostic information | |
191 | public extern (C++) void printCtfePerformanceStats() | |
192 | { | |
193 | debug (SHOWPERFORMANCE) | |
194 | { | |
195 | printf(" ---- CTFE Performance ----\n"); | |
196 | printf("max call depth = %d\tmax stack = %d\n", ctfeGlobals.maxCallDepth, ctfeGlobals.stack.maxStackUsage()); | |
197 | printf("array allocs = %d\tassignments = %d\n\n", ctfeGlobals.numArrayAllocs, ctfeGlobals.numAssignments); | |
198 | } | |
199 | } | |
200 | ||
201 | /************************** | |
202 | */ | |
203 | ||
204 | void incArrayAllocs() | |
205 | { | |
206 | ++ctfeGlobals.numArrayAllocs; | |
207 | } | |
208 | ||
209 | /* ================================================ Implementation ======================================= */ | |
210 | ||
211 | private: | |
212 | ||
213 | /*************** | |
214 | * Collect together globals used by CTFE | |
215 | */ | |
216 | struct CtfeGlobals | |
217 | { | |
218 | Region region; | |
219 | ||
220 | CtfeStack stack; | |
221 | ||
222 | int callDepth = 0; // current number of recursive calls | |
223 | ||
224 | // When printing a stack trace, suppress this number of calls | |
225 | int stackTraceCallsToSuppress = 0; | |
226 | ||
227 | int maxCallDepth = 0; // highest number of recursive calls | |
228 | int numArrayAllocs = 0; // Number of allocated arrays | |
229 | int numAssignments = 0; // total number of assignments executed | |
230 | } | |
231 | ||
232 | __gshared CtfeGlobals ctfeGlobals; | |
233 | ||
234 | enum CTFEGoal : int | |
235 | { | |
236 | RValue, /// Must return an Rvalue (== CTFE value) | |
237 | LValue, /// Must return an Lvalue (== CTFE reference) | |
238 | Nothing, /// The return value is not required | |
239 | } | |
240 | ||
241 | //debug = LOG; | |
242 | //debug = LOGASSIGN; | |
243 | //debug = LOGCOMPILE; | |
244 | //debug = SHOWPERFORMANCE; | |
245 | ||
246 | // Maximum allowable recursive function calls in CTFE | |
247 | enum CTFE_RECURSION_LIMIT = 1000; | |
248 | ||
249 | /** | |
250 | The values of all CTFE variables | |
251 | */ | |
252 | struct CtfeStack | |
253 | { | |
254 | private: | |
255 | /* The stack. Every declaration we encounter is pushed here, | |
256 | * together with the VarDeclaration, and the previous | |
257 | * stack address of that variable, so that we can restore it | |
258 | * when we leave the stack frame. | |
259 | * Note that when a function is forward referenced, the interpreter must | |
260 | * run semantic3, and that may start CTFE again with a NULL istate. Thus | |
261 | * the stack might not be empty when CTFE begins. | |
262 | * | |
263 | * Ctfe Stack addresses are just 0-based integers, but we save | |
264 | * them as 'void *' because Array can only do pointers. | |
265 | */ | |
266 | Expressions values; // values on the stack | |
267 | VarDeclarations vars; // corresponding variables | |
268 | Array!(void*) savedId; // id of the previous state of that var | |
269 | ||
270 | Array!(void*) frames; // all previous frame pointers | |
271 | Expressions savedThis; // all previous values of localThis | |
272 | ||
273 | /* Global constants get saved here after evaluation, so we never | |
274 | * have to redo them. This saves a lot of time and memory. | |
275 | */ | |
276 | Expressions globalValues; // values of global constants | |
277 | ||
278 | size_t framepointer; // current frame pointer | |
279 | size_t maxStackPointer; // most stack we've ever used | |
280 | Expression localThis; // value of 'this', or NULL if none | |
281 | ||
282 | public: | |
d6679fa2 | 283 | size_t stackPointer() @safe |
5fee5ec3 | 284 | { |
6d799f0a | 285 | return values.length; |
5fee5ec3 IB |
286 | } |
287 | ||
288 | // The current value of 'this', or NULL if none | |
d6679fa2 | 289 | Expression getThis() @safe |
5fee5ec3 IB |
290 | { |
291 | return localThis; | |
292 | } | |
293 | ||
294 | // Largest number of stack positions we've used | |
d6679fa2 | 295 | size_t maxStackUsage() @safe |
5fee5ec3 IB |
296 | { |
297 | return maxStackPointer; | |
298 | } | |
299 | ||
300 | // Start a new stack frame, using the provided 'this'. | |
f99303eb | 301 | void startFrame(Expression thisexp) |
5fee5ec3 IB |
302 | { |
303 | frames.push(cast(void*)cast(size_t)framepointer); | |
304 | savedThis.push(localThis); | |
305 | framepointer = stackPointer(); | |
306 | localThis = thisexp; | |
307 | } | |
308 | ||
f99303eb | 309 | void endFrame() |
5fee5ec3 | 310 | { |
6d799f0a IB |
311 | size_t oldframe = cast(size_t)frames[frames.length - 1]; |
312 | localThis = savedThis[savedThis.length - 1]; | |
5fee5ec3 IB |
313 | popAll(framepointer); |
314 | framepointer = oldframe; | |
6d799f0a IB |
315 | frames.setDim(frames.length - 1); |
316 | savedThis.setDim(savedThis.length - 1); | |
5fee5ec3 IB |
317 | } |
318 | ||
f99303eb | 319 | bool isInCurrentFrame(VarDeclaration v) |
5fee5ec3 IB |
320 | { |
321 | if (v.isDataseg() && !v.isCTFE()) | |
322 | return false; // It's a global | |
323 | return v.ctfeAdrOnStack >= framepointer; | |
324 | } | |
325 | ||
f99303eb | 326 | Expression getValue(VarDeclaration v) |
5fee5ec3 IB |
327 | { |
328 | //printf("getValue() %s\n", v.toChars()); | |
329 | if ((v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE()) | |
330 | { | |
6d799f0a | 331 | assert(v.ctfeAdrOnStack < globalValues.length); |
5fee5ec3 IB |
332 | return globalValues[v.ctfeAdrOnStack]; |
333 | } | |
334 | assert(v.ctfeAdrOnStack < stackPointer()); | |
335 | return values[v.ctfeAdrOnStack]; | |
336 | } | |
337 | ||
f99303eb | 338 | void setValue(VarDeclaration v, Expression e) |
5fee5ec3 IB |
339 | { |
340 | //printf("setValue() %s : %s\n", v.toChars(), e.toChars()); | |
341 | assert(!v.isDataseg() || v.isCTFE()); | |
342 | assert(v.ctfeAdrOnStack < stackPointer()); | |
343 | values[v.ctfeAdrOnStack] = e; | |
344 | } | |
345 | ||
f99303eb | 346 | void push(VarDeclaration v) |
5fee5ec3 IB |
347 | { |
348 | //printf("push() %s\n", v.toChars()); | |
349 | assert(!v.isDataseg() || v.isCTFE()); | |
350 | if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone && v.ctfeAdrOnStack >= framepointer) | |
351 | { | |
352 | // Already exists in this frame, reuse it. | |
353 | values[v.ctfeAdrOnStack] = null; | |
354 | return; | |
355 | } | |
356 | savedId.push(cast(void*)cast(size_t)v.ctfeAdrOnStack); | |
6d799f0a | 357 | v.ctfeAdrOnStack = cast(uint)values.length; |
5fee5ec3 IB |
358 | vars.push(v); |
359 | values.push(null); | |
360 | } | |
361 | ||
f99303eb | 362 | void pop(VarDeclaration v) |
5fee5ec3 IB |
363 | { |
364 | assert(!v.isDataseg() || v.isCTFE()); | |
365 | assert(!v.isReference()); | |
366 | const oldid = v.ctfeAdrOnStack; | |
367 | v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[oldid]; | |
6d799f0a | 368 | if (v.ctfeAdrOnStack == values.length - 1) |
5fee5ec3 IB |
369 | { |
370 | values.pop(); | |
371 | vars.pop(); | |
372 | savedId.pop(); | |
373 | } | |
374 | } | |
375 | ||
f99303eb | 376 | void popAll(size_t stackpointer) |
5fee5ec3 IB |
377 | { |
378 | if (stackPointer() > maxStackPointer) | |
379 | maxStackPointer = stackPointer(); | |
6d799f0a IB |
380 | assert(values.length >= stackpointer); |
381 | for (size_t i = stackpointer; i < values.length; ++i) | |
5fee5ec3 IB |
382 | { |
383 | VarDeclaration v = vars[i]; | |
384 | v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[i]; | |
385 | } | |
386 | values.setDim(stackpointer); | |
387 | vars.setDim(stackpointer); | |
388 | savedId.setDim(stackpointer); | |
389 | } | |
390 | ||
f99303eb | 391 | void saveGlobalConstant(VarDeclaration v, Expression e) |
5fee5ec3 IB |
392 | { |
393 | assert(v._init && (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !v.isCTFE()); | |
6d799f0a | 394 | v.ctfeAdrOnStack = cast(uint)globalValues.length; |
5fee5ec3 IB |
395 | globalValues.push(copyRegionExp(e)); |
396 | } | |
397 | } | |
398 | ||
399 | private struct InterState | |
400 | { | |
401 | InterState* caller; // calling function's InterState | |
402 | FuncDeclaration fd; // function being interpreted | |
403 | Statement start; // if !=NULL, start execution at this statement | |
404 | ||
405 | /* target of CTFEExp result; also | |
406 | * target of labelled CTFEExp or | |
407 | * CTFEExp. (null if no label). | |
408 | */ | |
409 | Statement gotoTarget; | |
410 | } | |
411 | ||
412 | /************************************* | |
413 | * Attempt to interpret a function given the arguments. | |
414 | * Params: | |
415 | * pue = storage for result | |
416 | * fd = function being called | |
417 | * istate = state for calling function (NULL if none) | |
418 | * arguments = function arguments | |
419 | * thisarg = 'this', if a needThis() function, NULL if not. | |
420 | * | |
421 | * Returns: | |
9c7d5e88 | 422 | * result expression if successful, EXP.cantExpression if not, |
5fee5ec3 IB |
423 | * or CTFEExp if function returned void. |
424 | */ | |
425 | private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg) | |
426 | { | |
427 | debug (LOG) | |
428 | { | |
429 | printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars()); | |
430 | } | |
431 | assert(pue); | |
432 | if (fd.semanticRun == PASS.semantic3) | |
433 | { | |
434 | fd.error("circular dependency. Functions cannot be interpreted while being compiled"); | |
435 | return CTFEExp.cantexp; | |
436 | } | |
437 | if (!fd.functionSemantic3()) | |
438 | return CTFEExp.cantexp; | |
439 | if (fd.semanticRun < PASS.semantic3done) | |
440 | { | |
441 | fd.error("circular dependency. Functions cannot be interpreted while being compiled"); | |
442 | return CTFEExp.cantexp; | |
443 | } | |
444 | ||
445 | auto tf = fd.type.toBasetype().isTypeFunction(); | |
446 | if (tf.parameterList.varargs != VarArg.none && arguments && | |
6d799f0a | 447 | ((fd.parameters && arguments.length != fd.parameters.length) || (!fd.parameters && arguments.length))) |
5fee5ec3 IB |
448 | { |
449 | fd.error("C-style variadic functions are not yet implemented in CTFE"); | |
450 | return CTFEExp.cantexp; | |
451 | } | |
452 | ||
453 | // Nested functions always inherit the 'this' pointer from the parent, | |
454 | // except for delegates. (Note that the 'this' pointer may be null). | |
455 | // Func literals report isNested() even if they are in global scope, | |
456 | // so we need to check that the parent is a function. | |
457 | if (fd.isNested() && fd.toParentLocal().isFuncDeclaration() && !thisarg && istate) | |
458 | thisarg = ctfeGlobals.stack.getThis(); | |
459 | ||
460 | if (fd.needThis() && !thisarg) | |
461 | { | |
462 | // error, no this. Prevent segfault. | |
463 | // Here should be unreachable by the strict 'this' check in front-end. | |
464 | fd.error("need `this` to access member `%s`", fd.toChars()); | |
465 | return CTFEExp.cantexp; | |
466 | } | |
467 | ||
468 | // Place to hold all the arguments to the function while | |
469 | // we are evaluating them. | |
6d799f0a IB |
470 | size_t dim = arguments ? arguments.length : 0; |
471 | assert((fd.parameters ? fd.parameters.length : 0) == dim); | |
5fee5ec3 IB |
472 | |
473 | /* Evaluate all the arguments to the function, | |
474 | * store the results in eargs[] | |
475 | */ | |
476 | Expressions eargs = Expressions(dim); | |
477 | for (size_t i = 0; i < dim; i++) | |
478 | { | |
479 | Expression earg = (*arguments)[i]; | |
480 | Parameter fparam = tf.parameterList[i]; | |
481 | ||
482 | if (fparam.isReference()) | |
483 | { | |
484 | if (!istate && (fparam.storageClass & STC.out_)) | |
485 | { | |
486 | // initializing an out parameter involves writing to it. | |
487 | earg.error("global `%s` cannot be passed as an `out` parameter at compile time", earg.toChars()); | |
488 | return CTFEExp.cantexp; | |
489 | } | |
490 | // Convert all reference arguments into lvalue references | |
491 | earg = interpretRegion(earg, istate, CTFEGoal.LValue); | |
492 | if (CTFEExp.isCantExp(earg)) | |
493 | return earg; | |
494 | } | |
ec486b73 | 495 | else if (fparam.isLazy()) |
5fee5ec3 IB |
496 | { |
497 | } | |
498 | else | |
499 | { | |
500 | /* Value parameters | |
501 | */ | |
502 | Type ta = fparam.type.toBasetype(); | |
503 | if (ta.ty == Tsarray) | |
504 | if (auto eaddr = earg.isAddrExp()) | |
505 | { | |
506 | /* Static arrays are passed by a simple pointer. | |
507 | * Skip past this to get at the actual arg. | |
508 | */ | |
509 | earg = eaddr.e1; | |
510 | } | |
511 | ||
512 | earg = interpretRegion(earg, istate); | |
513 | if (CTFEExp.isCantExp(earg)) | |
514 | return earg; | |
515 | ||
516 | /* Struct literals are passed by value, but we don't need to | |
517 | * copy them if they are passed as const | |
518 | */ | |
9c7d5e88 | 519 | if (earg.op == EXP.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_))) |
5fee5ec3 IB |
520 | earg = copyLiteral(earg).copy(); |
521 | } | |
522 | if (auto tee = earg.isThrownExceptionExp()) | |
523 | { | |
524 | if (istate) | |
525 | return tee; | |
526 | tee.generateUncaughtError(); | |
527 | return CTFEExp.cantexp; | |
528 | } | |
529 | eargs[i] = earg; | |
530 | } | |
531 | ||
532 | // Now that we've evaluated all the arguments, we can start the frame | |
533 | // (this is the moment when the 'call' actually takes place). | |
534 | InterState istatex; | |
535 | istatex.caller = istate; | |
536 | istatex.fd = fd; | |
537 | ||
235d5a96 | 538 | if (fd.hasDualContext()) |
5fee5ec3 IB |
539 | { |
540 | Expression arg0 = thisarg; | |
541 | if (arg0 && arg0.type.ty == Tstruct) | |
542 | { | |
543 | Type t = arg0.type.pointerTo(); | |
544 | arg0 = ctfeEmplaceExp!AddrExp(arg0.loc, arg0); | |
545 | arg0.type = t; | |
546 | } | |
547 | auto elements = new Expressions(2); | |
548 | (*elements)[0] = arg0; | |
549 | (*elements)[1] = ctfeGlobals.stack.getThis(); | |
550 | Type t2 = Type.tvoidptr.sarrayOf(2); | |
551 | const loc = thisarg ? thisarg.loc : fd.loc; | |
552 | thisarg = ctfeEmplaceExp!ArrayLiteralExp(loc, t2, elements); | |
553 | thisarg = ctfeEmplaceExp!AddrExp(loc, thisarg); | |
554 | thisarg.type = t2.pointerTo(); | |
555 | } | |
556 | ||
557 | ctfeGlobals.stack.startFrame(thisarg); | |
558 | if (fd.vthis && thisarg) | |
559 | { | |
560 | ctfeGlobals.stack.push(fd.vthis); | |
561 | setValue(fd.vthis, thisarg); | |
562 | } | |
563 | ||
564 | for (size_t i = 0; i < dim; i++) | |
565 | { | |
566 | Expression earg = eargs[i]; | |
567 | Parameter fparam = tf.parameterList[i]; | |
568 | VarDeclaration v = (*fd.parameters)[i]; | |
569 | debug (LOG) | |
570 | { | |
571 | printf("arg[%zu] = %s\n", i, earg.toChars()); | |
572 | } | |
573 | ctfeGlobals.stack.push(v); | |
574 | ||
9c7d5e88 | 575 | if (fparam.isReference() && earg.op == EXP.variable && |
5fee5ec3 IB |
576 | earg.isVarExp().var.toParent2() == fd) |
577 | { | |
578 | VarDeclaration vx = earg.isVarExp().var.isVarDeclaration(); | |
579 | if (!vx) | |
580 | { | |
581 | fd.error("cannot interpret `%s` as a `ref` parameter", earg.toChars()); | |
582 | return CTFEExp.cantexp; | |
583 | } | |
584 | ||
585 | /* vx is a variable that is declared in fd. | |
586 | * It means that fd is recursively called. e.g. | |
587 | * | |
588 | * void fd(int n, ref int v = dummy) { | |
589 | * int vx; | |
590 | * if (n == 1) fd(2, vx); | |
591 | * } | |
592 | * fd(1); | |
593 | * | |
594 | * The old value of vx on the stack in fd(1) | |
595 | * should be saved at the start of fd(2, vx) call. | |
596 | */ | |
597 | const oldadr = vx.ctfeAdrOnStack; | |
598 | ||
599 | ctfeGlobals.stack.push(vx); | |
600 | assert(!hasValue(vx)); // vx is made uninitialized | |
601 | ||
602 | // https://issues.dlang.org/show_bug.cgi?id=14299 | |
603 | // v.ctfeAdrOnStack should be saved already | |
604 | // in the stack before the overwrite. | |
605 | v.ctfeAdrOnStack = oldadr; | |
606 | assert(hasValue(v)); // ref parameter v should refer existing value. | |
607 | } | |
608 | else | |
609 | { | |
610 | // Value parameters and non-trivial references | |
611 | setValueWithoutChecking(v, earg); | |
612 | } | |
613 | debug (LOG) | |
614 | { | |
615 | printf("interpreted arg[%zu] = %s\n", i, earg.toChars()); | |
616 | showCtfeExpr(earg); | |
617 | } | |
618 | debug (LOGASSIGN) | |
619 | { | |
620 | printf("interpreted arg[%zu] = %s\n", i, earg.toChars()); | |
621 | showCtfeExpr(earg); | |
622 | } | |
623 | } | |
624 | ||
625 | if (fd.vresult) | |
626 | ctfeGlobals.stack.push(fd.vresult); | |
627 | ||
628 | // Enter the function | |
629 | ++ctfeGlobals.callDepth; | |
630 | if (ctfeGlobals.callDepth > ctfeGlobals.maxCallDepth) | |
631 | ctfeGlobals.maxCallDepth = ctfeGlobals.callDepth; | |
632 | ||
633 | Expression e = null; | |
634 | while (1) | |
635 | { | |
636 | if (ctfeGlobals.callDepth > CTFE_RECURSION_LIMIT) | |
637 | { | |
638 | // This is a compiler error. It must not be suppressed. | |
639 | global.gag = 0; | |
640 | fd.error("CTFE recursion limit exceeded"); | |
641 | e = CTFEExp.cantexp; | |
642 | break; | |
643 | } | |
3b007164 | 644 | e = interpretStatement(pue, fd.fbody, &istatex); |
5fee5ec3 IB |
645 | if (CTFEExp.isCantExp(e)) |
646 | { | |
647 | debug (LOG) | |
648 | { | |
649 | printf("function body failed to interpret\n"); | |
650 | } | |
651 | } | |
652 | ||
653 | if (istatex.start) | |
654 | { | |
655 | fd.error("CTFE internal error: failed to resume at statement `%s`", istatex.start.toChars()); | |
656 | return CTFEExp.cantexp; | |
657 | } | |
658 | ||
659 | /* This is how we deal with a recursive statement AST | |
660 | * that has arbitrary goto statements in it. | |
661 | * Bubble up a 'result' which is the target of the goto | |
662 | * statement, then go recursively down the AST looking | |
663 | * for that statement, then execute starting there. | |
664 | */ | |
665 | if (CTFEExp.isGotoExp(e)) | |
666 | { | |
667 | istatex.start = istatex.gotoTarget; // set starting statement | |
668 | istatex.gotoTarget = null; | |
669 | } | |
670 | else | |
671 | { | |
9c7d5e88 | 672 | assert(!e || (e.op != EXP.continue_ && e.op != EXP.break_)); |
5fee5ec3 IB |
673 | break; |
674 | } | |
675 | } | |
676 | // If fell off the end of a void function, return void | |
5eb9927a IB |
677 | if (!e) |
678 | { | |
679 | if (tf.next.ty == Tvoid) | |
680 | e = CTFEExp.voidexp; | |
681 | else | |
682 | { | |
683 | /* missing a return statement can happen with C functions | |
684 | * https://issues.dlang.org/show_bug.cgi?id=23056 | |
685 | */ | |
686 | fd.error("no return value from function"); | |
687 | e = CTFEExp.cantexp; | |
688 | } | |
689 | } | |
690 | ||
9c7d5e88 | 691 | if (tf.isref && e.op == EXP.variable && e.isVarExp().var == fd.vthis) |
5fee5ec3 | 692 | e = thisarg; |
235d5a96 | 693 | if (tf.isref && fd.hasDualContext() && e.op == EXP.index) |
5fee5ec3 IB |
694 | { |
695 | auto ie = e.isIndexExp(); | |
696 | auto pe = ie.e1.isPtrExp(); | |
697 | auto ve = !pe ? null : pe.e1.isVarExp(); | |
698 | if (ve && ve.var == fd.vthis) | |
699 | { | |
700 | auto ne = ie.e2.isIntegerExp(); | |
701 | assert(ne); | |
702 | auto ale = thisarg.isAddrExp().e1.isArrayLiteralExp(); | |
703 | e = (*ale.elements)[cast(size_t)ne.getInteger()]; | |
704 | if (auto ae = e.isAddrExp()) | |
705 | { | |
706 | e = ae.e1; | |
707 | } | |
708 | } | |
709 | } | |
5fee5ec3 IB |
710 | |
711 | // Leave the function | |
712 | --ctfeGlobals.callDepth; | |
713 | ||
714 | ctfeGlobals.stack.endFrame(); | |
715 | ||
716 | // If it generated an uncaught exception, report error. | |
717 | if (!istate && e.isThrownExceptionExp()) | |
718 | { | |
719 | if (e == pue.exp()) | |
720 | e = pue.copy(); | |
721 | e.isThrownExceptionExp().generateUncaughtError(); | |
722 | e = CTFEExp.cantexp; | |
723 | } | |
724 | ||
725 | return e; | |
726 | } | |
727 | ||
728 | /// used to collect coverage information in ctfe | |
729 | void incUsageCtfe(InterState* istate, const ref Loc loc) | |
730 | { | |
731 | if (global.params.ctfe_cov && istate) | |
732 | { | |
733 | auto line = loc.linnum; | |
734 | auto mod = istate.fd.getModule(); | |
735 | ||
736 | ++mod.ctfe_cov[line]; | |
737 | } | |
738 | } | |
739 | ||
3b007164 IB |
740 | /*********************************** |
741 | * Interpret the statement. | |
742 | * Params: | |
743 | * s = Statement to interpret | |
744 | * istate = context | |
745 | * Returns: | |
746 | * NULL continue to next statement | |
747 | * EXP.cantExpression cannot interpret statement at compile time | |
748 | * !NULL expression from return statement, or thrown exception | |
749 | */ | |
750 | ||
751 | Expression interpretStatement(Statement s, InterState* istate) | |
5fee5ec3 | 752 | { |
3b007164 IB |
753 | UnionExp ue = void; |
754 | auto result = interpretStatement(&ue, s, istate); | |
755 | if (result == ue.exp()) | |
756 | result = ue.copy(); | |
757 | return result; | |
758 | } | |
5fee5ec3 | 759 | |
3b007164 IB |
760 | /// |
761 | Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) | |
762 | { | |
763 | Expression result; | |
5fee5ec3 | 764 | |
9c7d5e88 | 765 | // If e is EXP.throw_exception or EXP.cantExpression, |
5fee5ec3 IB |
766 | // set it to 'result' and returns true. |
767 | bool exceptionOrCant(Expression e) | |
768 | { | |
769 | if (exceptionOrCantInterpret(e)) | |
770 | { | |
771 | // Make sure e is not pointing to a stack temporary | |
9c7d5e88 | 772 | result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; |
5fee5ec3 IB |
773 | return true; |
774 | } | |
775 | return false; | |
776 | } | |
777 | ||
5fee5ec3 IB |
778 | /******************************** Statement ***************************/ |
779 | ||
3b007164 | 780 | void visitDefaultCase(Statement s) |
5fee5ec3 IB |
781 | { |
782 | debug (LOG) | |
783 | { | |
3b007164 | 784 | printf("%s Statement::interpret() %s\n", s.loc.toChars(), s.toChars()); |
5fee5ec3 IB |
785 | } |
786 | if (istate.start) | |
787 | { | |
788 | if (istate.start != s) | |
789 | return; | |
790 | istate.start = null; | |
791 | } | |
792 | ||
793 | s.error("statement `%s` cannot be interpreted at compile time", s.toChars()); | |
794 | result = CTFEExp.cantexp; | |
795 | } | |
796 | ||
3b007164 | 797 | void visitExp(ExpStatement s) |
5fee5ec3 IB |
798 | { |
799 | debug (LOG) | |
800 | { | |
801 | printf("%s ExpStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : ""); | |
802 | } | |
803 | if (istate.start) | |
804 | { | |
805 | if (istate.start != s) | |
806 | return; | |
807 | istate.start = null; | |
808 | } | |
809 | if (s.exp && s.exp.hasCode) | |
810 | incUsageCtfe(istate, s.loc); | |
811 | ||
812 | Expression e = interpret(pue, s.exp, istate, CTFEGoal.Nothing); | |
813 | if (exceptionOrCant(e)) | |
814 | return; | |
815 | } | |
816 | ||
3b007164 IB |
817 | void visitDtorExp(DtorExpStatement s) |
818 | { | |
819 | visitExp(s); | |
820 | } | |
821 | ||
822 | void visitCompound(CompoundStatement s) | |
5fee5ec3 IB |
823 | { |
824 | debug (LOG) | |
825 | { | |
826 | printf("%s CompoundStatement::interpret()\n", s.loc.toChars()); | |
827 | } | |
828 | if (istate.start == s) | |
829 | istate.start = null; | |
830 | ||
6d799f0a | 831 | const dim = s.statements ? s.statements.length : 0; |
5fee5ec3 IB |
832 | foreach (i; 0 .. dim) |
833 | { | |
834 | Statement sx = (*s.statements)[i]; | |
3b007164 | 835 | result = interpretStatement(pue, sx, istate); |
5fee5ec3 IB |
836 | if (result) |
837 | break; | |
838 | } | |
839 | debug (LOG) | |
840 | { | |
841 | printf("%s -CompoundStatement::interpret() %p\n", s.loc.toChars(), result); | |
842 | } | |
843 | } | |
844 | ||
3b007164 IB |
845 | void visitCompoundAsm(CompoundAsmStatement s) |
846 | { | |
847 | visitCompound(s); | |
848 | } | |
849 | ||
850 | void visitUnrolledLoop(UnrolledLoopStatement s) | |
5fee5ec3 IB |
851 | { |
852 | debug (LOG) | |
853 | { | |
854 | printf("%s UnrolledLoopStatement::interpret()\n", s.loc.toChars()); | |
855 | } | |
856 | if (istate.start == s) | |
857 | istate.start = null; | |
858 | ||
6d799f0a | 859 | const dim = s.statements ? s.statements.length : 0; |
5fee5ec3 IB |
860 | foreach (i; 0 .. dim) |
861 | { | |
862 | Statement sx = (*s.statements)[i]; | |
3b007164 | 863 | Expression e = interpretStatement(pue, sx, istate); |
5fee5ec3 IB |
864 | if (!e) // succeeds to interpret, or goto target was not found |
865 | continue; | |
866 | if (exceptionOrCant(e)) | |
867 | return; | |
9c7d5e88 | 868 | if (e.op == EXP.break_) |
5fee5ec3 IB |
869 | { |
870 | if (istate.gotoTarget && istate.gotoTarget != s) | |
871 | { | |
872 | result = e; // break at a higher level | |
873 | return; | |
874 | } | |
875 | istate.gotoTarget = null; | |
876 | result = null; | |
877 | return; | |
878 | } | |
9c7d5e88 | 879 | if (e.op == EXP.continue_) |
5fee5ec3 IB |
880 | { |
881 | if (istate.gotoTarget && istate.gotoTarget != s) | |
882 | { | |
883 | result = e; // continue at a higher level | |
884 | return; | |
885 | } | |
886 | istate.gotoTarget = null; | |
887 | continue; | |
888 | } | |
889 | ||
890 | // expression from return statement, or thrown exception | |
891 | result = e; | |
892 | break; | |
893 | } | |
894 | } | |
895 | ||
3b007164 | 896 | void visitIf(IfStatement s) |
5fee5ec3 IB |
897 | { |
898 | debug (LOG) | |
899 | { | |
900 | printf("%s IfStatement::interpret(%s)\n", s.loc.toChars(), s.condition.toChars()); | |
901 | } | |
902 | incUsageCtfe(istate, s.loc); | |
903 | if (istate.start == s) | |
904 | istate.start = null; | |
905 | if (istate.start) | |
906 | { | |
907 | Expression e = null; | |
3b007164 | 908 | e = interpretStatement(s.ifbody, istate); |
5fee5ec3 | 909 | if (!e && istate.start) |
3b007164 | 910 | e = interpretStatement(s.elsebody, istate); |
5fee5ec3 IB |
911 | result = e; |
912 | return; | |
913 | } | |
914 | ||
915 | UnionExp ue = void; | |
916 | Expression e = interpret(&ue, s.condition, istate); | |
917 | assert(e); | |
918 | if (exceptionOrCant(e)) | |
919 | return; | |
920 | ||
921 | if (isTrueBool(e)) | |
3b007164 | 922 | result = interpretStatement(pue, s.ifbody, istate); |
9c7d5e88 | 923 | else if (e.toBool().hasValue(false)) |
3b007164 | 924 | result = interpretStatement(pue, s.elsebody, istate); |
5fee5ec3 IB |
925 | else |
926 | { | |
927 | // no error, or assert(0)? | |
928 | result = CTFEExp.cantexp; | |
929 | } | |
930 | } | |
931 | ||
3b007164 | 932 | void visitScope(ScopeStatement s) |
5fee5ec3 IB |
933 | { |
934 | debug (LOG) | |
935 | { | |
936 | printf("%s ScopeStatement::interpret()\n", s.loc.toChars()); | |
937 | } | |
938 | if (istate.start == s) | |
939 | istate.start = null; | |
940 | ||
3b007164 | 941 | result = interpretStatement(pue, s.statement, istate); |
5fee5ec3 IB |
942 | } |
943 | ||
3b007164 | 944 | void visitReturn(ReturnStatement s) |
5fee5ec3 IB |
945 | { |
946 | debug (LOG) | |
947 | { | |
948 | printf("%s ReturnStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : ""); | |
949 | } | |
950 | if (istate.start) | |
951 | { | |
952 | if (istate.start != s) | |
953 | return; | |
954 | istate.start = null; | |
955 | } | |
956 | ||
957 | if (!s.exp) | |
958 | { | |
959 | result = CTFEExp.voidexp; | |
960 | return; | |
961 | } | |
962 | ||
963 | incUsageCtfe(istate, s.loc); | |
964 | assert(istate && istate.fd && istate.fd.type && istate.fd.type.ty == Tfunction); | |
965 | TypeFunction tf = cast(TypeFunction)istate.fd.type; | |
966 | ||
967 | /* If the function returns a ref AND it's been called from an assignment, | |
968 | * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. | |
969 | */ | |
970 | if (tf.isref) | |
971 | { | |
972 | result = interpret(pue, s.exp, istate, CTFEGoal.LValue); | |
973 | return; | |
974 | } | |
6d799f0a | 975 | if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.length > 0) |
5fee5ec3 IB |
976 | { |
977 | // To support this, we need to copy all the closure vars | |
978 | // into the delegate literal. | |
979 | s.error("closures are not yet supported in CTFE"); | |
980 | result = CTFEExp.cantexp; | |
981 | return; | |
982 | } | |
983 | ||
9c7d5e88 | 984 | // We need to treat pointers specially, because EXP.symbolOffset can be used to |
5fee5ec3 IB |
985 | // return a value OR a pointer |
986 | Expression e = interpret(pue, s.exp, istate); | |
987 | if (exceptionOrCant(e)) | |
988 | return; | |
989 | ||
5eb9927a IB |
990 | /** |
991 | * Interpret `return a ~= b` (i.e. `return _d_arrayappendT{,Trace}(a, b)`) as: | |
992 | * a ~= b; | |
993 | * return a; | |
994 | * This is needed because `a ~= b` has to be interpreted as an lvalue, in order to avoid | |
995 | * assigning a larger array into a smaller one, such as: | |
996 | * `a = [1, 2], a ~= [3]` => `[1, 2] ~= [3]` => `[1, 2] = [1, 2, 3]` | |
997 | */ | |
998 | if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace)) | |
999 | { | |
1000 | auto rs = new ReturnStatement(s.loc, e); | |
3b007164 | 1001 | visitReturn(rs); |
5eb9927a IB |
1002 | return; |
1003 | } | |
1004 | ||
5fee5ec3 IB |
1005 | // Disallow returning pointers to stack-allocated variables (bug 7876) |
1006 | if (!stopPointersEscaping(s.loc, e)) | |
1007 | { | |
1008 | result = CTFEExp.cantexp; | |
1009 | return; | |
1010 | } | |
1011 | ||
1012 | if (needToCopyLiteral(e)) | |
1013 | e = copyLiteral(e).copy(); | |
1014 | debug (LOGASSIGN) | |
1015 | { | |
1016 | printf("RETURN %s\n", s.loc.toChars()); | |
1017 | showCtfeExpr(e); | |
1018 | } | |
1019 | result = e; | |
1020 | } | |
1021 | ||
3b007164 | 1022 | void visitBreak(BreakStatement s) |
5fee5ec3 IB |
1023 | { |
1024 | debug (LOG) | |
1025 | { | |
1026 | printf("%s BreakStatement::interpret()\n", s.loc.toChars()); | |
1027 | } | |
1028 | incUsageCtfe(istate, s.loc); | |
1029 | if (istate.start) | |
1030 | { | |
1031 | if (istate.start != s) | |
1032 | return; | |
1033 | istate.start = null; | |
1034 | } | |
1035 | ||
1036 | istate.gotoTarget = findGotoTarget(istate, s.ident); | |
1037 | result = CTFEExp.breakexp; | |
1038 | } | |
1039 | ||
3b007164 | 1040 | void visitContinue(ContinueStatement s) |
5fee5ec3 IB |
1041 | { |
1042 | debug (LOG) | |
1043 | { | |
1044 | printf("%s ContinueStatement::interpret()\n", s.loc.toChars()); | |
1045 | } | |
1046 | incUsageCtfe(istate, s.loc); | |
1047 | if (istate.start) | |
1048 | { | |
1049 | if (istate.start != s) | |
1050 | return; | |
1051 | istate.start = null; | |
1052 | } | |
1053 | ||
1054 | istate.gotoTarget = findGotoTarget(istate, s.ident); | |
1055 | result = CTFEExp.continueexp; | |
1056 | } | |
1057 | ||
3b007164 | 1058 | void visitWhile(WhileStatement s) |
5fee5ec3 IB |
1059 | { |
1060 | debug (LOG) | |
1061 | { | |
1062 | printf("WhileStatement::interpret()\n"); | |
1063 | } | |
1064 | assert(0); // rewritten to ForStatement | |
1065 | } | |
1066 | ||
3b007164 | 1067 | void visitDo(DoStatement s) |
5fee5ec3 IB |
1068 | { |
1069 | debug (LOG) | |
1070 | { | |
1071 | printf("%s DoStatement::interpret()\n", s.loc.toChars()); | |
1072 | } | |
1073 | if (istate.start == s) | |
1074 | istate.start = null; | |
1075 | ||
1076 | while (1) | |
1077 | { | |
3b007164 | 1078 | Expression e = interpretStatement(s._body, istate); |
5fee5ec3 IB |
1079 | if (!e && istate.start) // goto target was not found |
1080 | return; | |
1081 | assert(!istate.start); | |
1082 | ||
1083 | if (exceptionOrCant(e)) | |
1084 | return; | |
9c7d5e88 | 1085 | if (e && e.op == EXP.break_) |
5fee5ec3 IB |
1086 | { |
1087 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1088 | { | |
1089 | result = e; // break at a higher level | |
1090 | return; | |
1091 | } | |
1092 | istate.gotoTarget = null; | |
1093 | break; | |
1094 | } | |
9c7d5e88 | 1095 | if (e && e.op == EXP.continue_) |
5fee5ec3 IB |
1096 | { |
1097 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1098 | { | |
1099 | result = e; // continue at a higher level | |
1100 | return; | |
1101 | } | |
1102 | istate.gotoTarget = null; | |
1103 | e = null; | |
1104 | } | |
1105 | if (e) | |
1106 | { | |
1107 | result = e; // bubbled up from ReturnStatement | |
1108 | return; | |
1109 | } | |
1110 | ||
1111 | UnionExp ue = void; | |
1112 | incUsageCtfe(istate, s.condition.loc); | |
1113 | e = interpret(&ue, s.condition, istate); | |
1114 | if (exceptionOrCant(e)) | |
1115 | return; | |
1116 | if (!e.isConst()) | |
1117 | { | |
1118 | result = CTFEExp.cantexp; | |
1119 | return; | |
1120 | } | |
9c7d5e88 | 1121 | if (e.toBool().hasValue(false)) |
5fee5ec3 IB |
1122 | break; |
1123 | assert(isTrueBool(e)); | |
1124 | } | |
1125 | assert(result is null); | |
1126 | } | |
1127 | ||
3b007164 | 1128 | void visitFor(ForStatement s) |
5fee5ec3 IB |
1129 | { |
1130 | debug (LOG) | |
1131 | { | |
1132 | printf("%s ForStatement::interpret()\n", s.loc.toChars()); | |
1133 | } | |
1134 | if (istate.start == s) | |
1135 | istate.start = null; | |
1136 | ||
1137 | UnionExp ueinit = void; | |
3b007164 | 1138 | Expression ei = interpretStatement(&ueinit, s._init, istate); |
5fee5ec3 IB |
1139 | if (exceptionOrCant(ei)) |
1140 | return; | |
1141 | assert(!ei); // s.init never returns from function, or jumps out from it | |
1142 | ||
1143 | while (1) | |
1144 | { | |
1145 | if (s.condition && !istate.start) | |
1146 | { | |
1147 | UnionExp ue = void; | |
1148 | incUsageCtfe(istate, s.condition.loc); | |
1149 | Expression e = interpret(&ue, s.condition, istate); | |
1150 | if (exceptionOrCant(e)) | |
1151 | return; | |
9c7d5e88 | 1152 | if (e.toBool().hasValue(false)) |
5fee5ec3 IB |
1153 | break; |
1154 | assert(isTrueBool(e)); | |
1155 | } | |
1156 | ||
3b007164 | 1157 | Expression e = interpretStatement(pue, s._body, istate); |
5fee5ec3 IB |
1158 | if (!e && istate.start) // goto target was not found |
1159 | return; | |
1160 | assert(!istate.start); | |
1161 | ||
1162 | if (exceptionOrCant(e)) | |
1163 | return; | |
9c7d5e88 | 1164 | if (e && e.op == EXP.break_) |
5fee5ec3 IB |
1165 | { |
1166 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1167 | { | |
1168 | result = e; // break at a higher level | |
1169 | return; | |
1170 | } | |
1171 | istate.gotoTarget = null; | |
1172 | break; | |
1173 | } | |
9c7d5e88 | 1174 | if (e && e.op == EXP.continue_) |
5fee5ec3 IB |
1175 | { |
1176 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1177 | { | |
1178 | result = e; // continue at a higher level | |
1179 | return; | |
1180 | } | |
1181 | istate.gotoTarget = null; | |
1182 | e = null; | |
1183 | } | |
1184 | if (e) | |
1185 | { | |
1186 | result = e; // bubbled up from ReturnStatement | |
1187 | return; | |
1188 | } | |
1189 | ||
1190 | UnionExp uei = void; | |
1191 | if (s.increment) | |
1192 | incUsageCtfe(istate, s.increment.loc); | |
1193 | e = interpret(&uei, s.increment, istate, CTFEGoal.Nothing); | |
1194 | if (exceptionOrCant(e)) | |
1195 | return; | |
1196 | } | |
1197 | assert(result is null); | |
1198 | } | |
1199 | ||
3b007164 | 1200 | void visitForeach(ForeachStatement s) |
5fee5ec3 IB |
1201 | { |
1202 | assert(0); // rewritten to ForStatement | |
1203 | } | |
1204 | ||
3b007164 | 1205 | void visitForeachRange(ForeachRangeStatement s) |
5fee5ec3 IB |
1206 | { |
1207 | assert(0); // rewritten to ForStatement | |
1208 | } | |
1209 | ||
3b007164 | 1210 | void visitSwitch(SwitchStatement s) |
5fee5ec3 IB |
1211 | { |
1212 | debug (LOG) | |
1213 | { | |
1214 | printf("%s SwitchStatement::interpret()\n", s.loc.toChars()); | |
1215 | } | |
1216 | incUsageCtfe(istate, s.loc); | |
1217 | if (istate.start == s) | |
1218 | istate.start = null; | |
1219 | if (istate.start) | |
1220 | { | |
3b007164 | 1221 | Expression e = interpretStatement(s._body, istate); |
5fee5ec3 IB |
1222 | if (istate.start) // goto target was not found |
1223 | return; | |
1224 | if (exceptionOrCant(e)) | |
1225 | return; | |
9c7d5e88 | 1226 | if (e && e.op == EXP.break_) |
5fee5ec3 IB |
1227 | { |
1228 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1229 | { | |
1230 | result = e; // break at a higher level | |
1231 | return; | |
1232 | } | |
1233 | istate.gotoTarget = null; | |
1234 | e = null; | |
1235 | } | |
1236 | result = e; | |
1237 | return; | |
1238 | } | |
1239 | ||
1240 | UnionExp uecond = void; | |
1241 | Expression econdition = interpret(&uecond, s.condition, istate); | |
1242 | if (exceptionOrCant(econdition)) | |
1243 | return; | |
1244 | ||
1245 | Statement scase = null; | |
1246 | if (s.cases) | |
1247 | foreach (cs; *s.cases) | |
1248 | { | |
1249 | UnionExp uecase = void; | |
1250 | Expression ecase = interpret(&uecase, cs.exp, istate); | |
1251 | if (exceptionOrCant(ecase)) | |
1252 | return; | |
9c7d5e88 | 1253 | if (ctfeEqual(cs.exp.loc, EXP.equal, econdition, ecase)) |
5fee5ec3 IB |
1254 | { |
1255 | scase = cs; | |
1256 | break; | |
1257 | } | |
1258 | } | |
1259 | if (!scase) | |
1260 | { | |
1261 | if (s.hasNoDefault) | |
1262 | s.error("no `default` or `case` for `%s` in `switch` statement", econdition.toChars()); | |
1263 | scase = s.sdefault; | |
1264 | } | |
1265 | ||
1266 | assert(scase); | |
1267 | ||
1268 | /* Jump to scase | |
1269 | */ | |
1270 | istate.start = scase; | |
3b007164 | 1271 | Expression e = interpretStatement(pue, s._body, istate); |
5fee5ec3 | 1272 | assert(!istate.start); // jump must not fail |
9c7d5e88 | 1273 | if (e && e.op == EXP.break_) |
5fee5ec3 IB |
1274 | { |
1275 | if (istate.gotoTarget && istate.gotoTarget != s) | |
1276 | { | |
1277 | result = e; // break at a higher level | |
1278 | return; | |
1279 | } | |
1280 | istate.gotoTarget = null; | |
1281 | e = null; | |
1282 | } | |
1283 | result = e; | |
1284 | } | |
1285 | ||
3b007164 | 1286 | void visitCase(CaseStatement s) |
5fee5ec3 IB |
1287 | { |
1288 | debug (LOG) | |
1289 | { | |
1290 | printf("%s CaseStatement::interpret(%s) this = %p\n", s.loc.toChars(), s.exp.toChars(), s); | |
1291 | } | |
1292 | incUsageCtfe(istate, s.loc); | |
1293 | if (istate.start == s) | |
1294 | istate.start = null; | |
1295 | ||
3b007164 | 1296 | result = interpretStatement(pue, s.statement, istate); |
5fee5ec3 IB |
1297 | } |
1298 | ||
3b007164 | 1299 | void visitDefault(DefaultStatement s) |
5fee5ec3 IB |
1300 | { |
1301 | debug (LOG) | |
1302 | { | |
1303 | printf("%s DefaultStatement::interpret()\n", s.loc.toChars()); | |
1304 | } | |
1305 | incUsageCtfe(istate, s.loc); | |
1306 | if (istate.start == s) | |
1307 | istate.start = null; | |
1308 | ||
3b007164 | 1309 | result = interpretStatement(pue, s.statement, istate); |
5fee5ec3 IB |
1310 | } |
1311 | ||
3b007164 | 1312 | void visitGoto(GotoStatement s) |
5fee5ec3 IB |
1313 | { |
1314 | debug (LOG) | |
1315 | { | |
1316 | printf("%s GotoStatement::interpret()\n", s.loc.toChars()); | |
1317 | } | |
1318 | if (istate.start) | |
1319 | { | |
1320 | if (istate.start != s) | |
1321 | return; | |
1322 | istate.start = null; | |
1323 | } | |
1324 | incUsageCtfe(istate, s.loc); | |
1325 | ||
1326 | assert(s.label && s.label.statement); | |
1327 | istate.gotoTarget = s.label.statement; | |
1328 | result = CTFEExp.gotoexp; | |
1329 | } | |
1330 | ||
3b007164 | 1331 | void visitGotoCase(GotoCaseStatement s) |
5fee5ec3 IB |
1332 | { |
1333 | debug (LOG) | |
1334 | { | |
1335 | printf("%s GotoCaseStatement::interpret()\n", s.loc.toChars()); | |
1336 | } | |
1337 | if (istate.start) | |
1338 | { | |
1339 | if (istate.start != s) | |
1340 | return; | |
1341 | istate.start = null; | |
1342 | } | |
1343 | incUsageCtfe(istate, s.loc); | |
1344 | ||
1345 | assert(s.cs); | |
1346 | istate.gotoTarget = s.cs; | |
1347 | result = CTFEExp.gotoexp; | |
1348 | } | |
1349 | ||
3b007164 | 1350 | void visitGotoDefault(GotoDefaultStatement s) |
5fee5ec3 IB |
1351 | { |
1352 | debug (LOG) | |
1353 | { | |
1354 | printf("%s GotoDefaultStatement::interpret()\n", s.loc.toChars()); | |
1355 | } | |
1356 | if (istate.start) | |
1357 | { | |
1358 | if (istate.start != s) | |
1359 | return; | |
1360 | istate.start = null; | |
1361 | } | |
1362 | incUsageCtfe(istate, s.loc); | |
1363 | ||
1364 | assert(s.sw && s.sw.sdefault); | |
1365 | istate.gotoTarget = s.sw.sdefault; | |
1366 | result = CTFEExp.gotoexp; | |
1367 | } | |
1368 | ||
3b007164 | 1369 | void visitLabel(LabelStatement s) |
5fee5ec3 IB |
1370 | { |
1371 | debug (LOG) | |
1372 | { | |
1373 | printf("%s LabelStatement::interpret()\n", s.loc.toChars()); | |
1374 | } | |
1375 | if (istate.start == s) | |
1376 | istate.start = null; | |
1377 | ||
3b007164 | 1378 | result = interpretStatement(pue, s.statement, istate); |
5fee5ec3 IB |
1379 | } |
1380 | ||
3b007164 | 1381 | void visitTryCatch(TryCatchStatement s) |
5fee5ec3 IB |
1382 | { |
1383 | debug (LOG) | |
1384 | { | |
1385 | printf("%s TryCatchStatement::interpret()\n", s.loc.toChars()); | |
1386 | } | |
1387 | if (istate.start == s) | |
1388 | istate.start = null; | |
1389 | if (istate.start) | |
1390 | { | |
1391 | Expression e = null; | |
3b007164 | 1392 | e = interpretStatement(pue, s._body, istate); |
5fee5ec3 IB |
1393 | foreach (ca; *s.catches) |
1394 | { | |
1395 | if (e || !istate.start) // goto target was found | |
1396 | break; | |
3b007164 | 1397 | e = interpretStatement(pue, ca.handler, istate); |
5fee5ec3 IB |
1398 | } |
1399 | result = e; | |
1400 | return; | |
1401 | } | |
1402 | ||
3b007164 | 1403 | Expression e = interpretStatement(s._body, istate); |
5fee5ec3 IB |
1404 | |
1405 | // An exception was thrown | |
1406 | if (e && e.isThrownExceptionExp()) | |
1407 | { | |
1408 | ThrownExceptionExp ex = e.isThrownExceptionExp(); | |
1409 | Type extype = ex.thrown.originalClass().type; | |
1410 | ||
1411 | // Search for an appropriate catch clause. | |
1412 | foreach (ca; *s.catches) | |
1413 | { | |
1414 | Type catype = ca.type; | |
1415 | if (!catype.equals(extype) && !catype.isBaseOf(extype, null)) | |
1416 | continue; | |
1417 | ||
1418 | // Execute the handler | |
1419 | if (ca.var) | |
1420 | { | |
1421 | ctfeGlobals.stack.push(ca.var); | |
1422 | setValue(ca.var, ex.thrown); | |
1423 | } | |
3b007164 | 1424 | e = interpretStatement(ca.handler, istate); |
5fee5ec3 IB |
1425 | if (CTFEExp.isGotoExp(e)) |
1426 | { | |
1427 | /* This is an optimization that relies on the locality of the jump target. | |
1428 | * If the label is in the same catch handler, the following scan | |
1429 | * would find it quickly and can reduce jump cost. | |
1430 | * Otherwise, the catch block may be unnnecessary scanned again | |
1431 | * so it would make CTFE speed slower. | |
1432 | */ | |
1433 | InterState istatex = *istate; | |
1434 | istatex.start = istate.gotoTarget; // set starting statement | |
1435 | istatex.gotoTarget = null; | |
3b007164 | 1436 | Expression eh = interpretStatement(ca.handler, &istatex); |
5fee5ec3 IB |
1437 | if (!istatex.start) |
1438 | { | |
1439 | istate.gotoTarget = null; | |
1440 | e = eh; | |
1441 | } | |
1442 | } | |
1443 | break; | |
1444 | } | |
1445 | } | |
1446 | result = e; | |
1447 | } | |
1448 | ||
3b007164 | 1449 | void visitTryFinally(TryFinallyStatement s) |
5fee5ec3 IB |
1450 | { |
1451 | debug (LOG) | |
1452 | { | |
1453 | printf("%s TryFinallyStatement::interpret()\n", s.loc.toChars()); | |
1454 | } | |
1455 | if (istate.start == s) | |
1456 | istate.start = null; | |
1457 | if (istate.start) | |
1458 | { | |
1459 | Expression e = null; | |
3b007164 | 1460 | e = interpretStatement(pue, s._body, istate); |
5fee5ec3 IB |
1461 | // Jump into/out from finalbody is disabled in semantic analysis. |
1462 | // and jump inside will be handled by the ScopeStatement == finalbody. | |
1463 | result = e; | |
1464 | return; | |
1465 | } | |
1466 | ||
3b007164 | 1467 | Expression ex = interpretStatement(s._body, istate); |
5fee5ec3 IB |
1468 | if (CTFEExp.isCantExp(ex)) |
1469 | { | |
1470 | result = ex; | |
1471 | return; | |
1472 | } | |
1473 | while (CTFEExp.isGotoExp(ex)) | |
1474 | { | |
1475 | // If the goto target is within the body, we must not interpret the finally statement, | |
1476 | // because that will call destructors for objects within the scope, which we should not do. | |
1477 | InterState istatex = *istate; | |
1478 | istatex.start = istate.gotoTarget; // set starting statement | |
1479 | istatex.gotoTarget = null; | |
3b007164 | 1480 | Expression bex = interpretStatement(s._body, &istatex); |
5fee5ec3 IB |
1481 | if (istatex.start) |
1482 | { | |
1483 | // The goto target is outside the current scope. | |
1484 | break; | |
1485 | } | |
1486 | // The goto target was within the body. | |
1487 | if (CTFEExp.isCantExp(bex)) | |
1488 | { | |
1489 | result = bex; | |
1490 | return; | |
1491 | } | |
1492 | *istate = istatex; | |
1493 | ex = bex; | |
1494 | } | |
1495 | ||
3b007164 | 1496 | Expression ey = interpretStatement(s.finalbody, istate); |
5fee5ec3 IB |
1497 | if (CTFEExp.isCantExp(ey)) |
1498 | { | |
1499 | result = ey; | |
1500 | return; | |
1501 | } | |
1502 | if (ey && ey.isThrownExceptionExp()) | |
1503 | { | |
1504 | // Check for collided exceptions | |
1505 | if (ex && ex.isThrownExceptionExp()) | |
1506 | ex = chainExceptions(ex.isThrownExceptionExp(), ey.isThrownExceptionExp()); | |
1507 | else | |
1508 | ex = ey; | |
1509 | } | |
1510 | result = ex; | |
1511 | } | |
1512 | ||
3b007164 | 1513 | void visitThrow(ThrowStatement s) |
5fee5ec3 IB |
1514 | { |
1515 | debug (LOG) | |
1516 | { | |
1517 | printf("%s ThrowStatement::interpret()\n", s.loc.toChars()); | |
1518 | } | |
1519 | if (istate.start) | |
1520 | { | |
1521 | if (istate.start != s) | |
1522 | return; | |
1523 | istate.start = null; | |
1524 | } | |
1525 | ||
3b007164 | 1526 | interpretThrow(result, s.exp, s.loc, istate); |
5fee5ec3 IB |
1527 | } |
1528 | ||
3b007164 | 1529 | void visitScopeGuard(ScopeGuardStatement s) |
5fee5ec3 IB |
1530 | { |
1531 | assert(0); | |
1532 | } | |
1533 | ||
3b007164 | 1534 | void visitWith(WithStatement s) |
5fee5ec3 IB |
1535 | { |
1536 | debug (LOG) | |
1537 | { | |
1538 | printf("%s WithStatement::interpret()\n", s.loc.toChars()); | |
1539 | } | |
1540 | if (istate.start == s) | |
1541 | istate.start = null; | |
1542 | if (istate.start) | |
1543 | { | |
3b007164 | 1544 | result = s._body ? interpretStatement(s._body, istate) : null; |
5fee5ec3 IB |
1545 | return; |
1546 | } | |
1547 | ||
1548 | // If it is with(Enum) {...}, just execute the body. | |
9c7d5e88 | 1549 | if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type) |
5fee5ec3 | 1550 | { |
3b007164 | 1551 | result = interpretStatement(pue, s._body, istate); |
5fee5ec3 IB |
1552 | return; |
1553 | } | |
1554 | ||
1555 | incUsageCtfe(istate, s.loc); | |
1556 | ||
1557 | Expression e = interpret(s.exp, istate); | |
1558 | if (exceptionOrCant(e)) | |
1559 | return; | |
1560 | ||
1561 | if (s.wthis.type.ty == Tpointer && s.exp.type.ty != Tpointer) | |
1562 | { | |
1563 | e = ctfeEmplaceExp!AddrExp(s.loc, e, s.wthis.type); | |
1564 | } | |
1565 | ctfeGlobals.stack.push(s.wthis); | |
1566 | setValue(s.wthis, e); | |
3b007164 | 1567 | e = interpretStatement(s._body, istate); |
5fee5ec3 IB |
1568 | if (CTFEExp.isGotoExp(e)) |
1569 | { | |
1570 | /* This is an optimization that relies on the locality of the jump target. | |
1571 | * If the label is in the same WithStatement, the following scan | |
1572 | * would find it quickly and can reduce jump cost. | |
1573 | * Otherwise, the statement body may be unnnecessary scanned again | |
1574 | * so it would make CTFE speed slower. | |
1575 | */ | |
1576 | InterState istatex = *istate; | |
1577 | istatex.start = istate.gotoTarget; // set starting statement | |
1578 | istatex.gotoTarget = null; | |
3b007164 | 1579 | Expression ex = interpretStatement(s._body, &istatex); |
5fee5ec3 IB |
1580 | if (!istatex.start) |
1581 | { | |
1582 | istate.gotoTarget = null; | |
1583 | e = ex; | |
1584 | } | |
1585 | } | |
1586 | ctfeGlobals.stack.pop(s.wthis); | |
1587 | result = e; | |
1588 | } | |
1589 | ||
3b007164 | 1590 | void visitAsm(AsmStatement s) |
5fee5ec3 IB |
1591 | { |
1592 | debug (LOG) | |
1593 | { | |
1594 | printf("%s AsmStatement::interpret()\n", s.loc.toChars()); | |
1595 | } | |
1596 | if (istate.start) | |
1597 | { | |
1598 | if (istate.start != s) | |
1599 | return; | |
1600 | istate.start = null; | |
1601 | } | |
1602 | s.error("`asm` statements cannot be interpreted at compile time"); | |
1603 | result = CTFEExp.cantexp; | |
1604 | } | |
1605 | ||
3b007164 IB |
1606 | void visitInlineAsm(InlineAsmStatement s) |
1607 | { | |
1608 | visitAsm(s); | |
1609 | } | |
1610 | ||
1611 | void visitGccAsm(GccAsmStatement s) | |
1612 | { | |
1613 | visitAsm(s); | |
1614 | } | |
1615 | ||
1616 | void visitImport(ImportStatement s) | |
5fee5ec3 IB |
1617 | { |
1618 | debug (LOG) | |
1619 | { | |
1620 | printf("ImportStatement::interpret()\n"); | |
1621 | } | |
1622 | if (istate.start) | |
1623 | { | |
1624 | if (istate.start != s) | |
1625 | return; | |
1626 | istate.start = null; | |
1627 | } | |
1628 | } | |
1629 | ||
3b007164 IB |
1630 | if (!s) |
1631 | return null; | |
1632 | ||
1633 | mixin VisitStatement!void visit; | |
1634 | visit.VisitStatement(s); | |
1635 | return result; | |
1636 | } | |
1637 | ||
1638 | /// | |
1639 | ||
1640 | private extern (C++) final class Interpreter : Visitor | |
1641 | { | |
1642 | alias visit = Visitor.visit; | |
1643 | public: | |
1644 | InterState* istate; | |
1645 | CTFEGoal goal; | |
1646 | Expression result; | |
1647 | UnionExp* pue; // storage for `result` | |
1648 | ||
d6679fa2 | 1649 | extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope @safe |
3b007164 IB |
1650 | { |
1651 | this.pue = pue; | |
1652 | this.istate = istate; | |
1653 | this.goal = goal; | |
1654 | } | |
1655 | ||
1656 | // If e is EXP.throw_exception or EXP.cantExpression, | |
1657 | // set it to 'result' and returns true. | |
1658 | bool exceptionOrCant(Expression e) | |
1659 | { | |
1660 | if (exceptionOrCantInterpret(e)) | |
1661 | { | |
1662 | // Make sure e is not pointing to a stack temporary | |
1663 | result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; | |
1664 | return true; | |
1665 | } | |
1666 | return false; | |
1667 | } | |
1668 | ||
5fee5ec3 IB |
1669 | /******************************** Expression ***************************/ |
1670 | ||
1671 | override void visit(Expression e) | |
1672 | { | |
1673 | debug (LOG) | |
1674 | { | |
9c7d5e88 | 1675 | printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), EXPtoString(e.op).ptr, e.toChars()); |
5fee5ec3 IB |
1676 | printf("type = %s\n", e.type.toChars()); |
1677 | showCtfeExpr(e); | |
1678 | } | |
1679 | e.error("cannot interpret `%s` at compile time", e.toChars()); | |
1680 | result = CTFEExp.cantexp; | |
1681 | } | |
1682 | ||
1683 | override void visit(TypeExp e) | |
1684 | { | |
1685 | debug (LOG) | |
1686 | { | |
1687 | printf("%s TypeExp.interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1688 | } | |
1689 | result = e; | |
1690 | } | |
1691 | ||
1692 | override void visit(ThisExp e) | |
1693 | { | |
1694 | debug (LOG) | |
1695 | { | |
1696 | printf("%s ThisExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1697 | } | |
1698 | if (goal == CTFEGoal.LValue) | |
1699 | { | |
1700 | // We might end up here with istate being zero | |
1701 | // https://issues.dlang.org/show_bug.cgi?id=16382 | |
1702 | if (istate && istate.fd.vthis) | |
1703 | { | |
1704 | result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis); | |
235d5a96 | 1705 | if (istate.fd.hasDualContext()) |
5fee5ec3 IB |
1706 | { |
1707 | result = ctfeEmplaceExp!PtrExp(e.loc, result); | |
1708 | result.type = Type.tvoidptr.sarrayOf(2); | |
1709 | result = ctfeEmplaceExp!IndexExp(e.loc, result, IntegerExp.literal!0); | |
1710 | } | |
1711 | result.type = e.type; | |
1712 | } | |
1713 | else | |
1714 | result = e; | |
1715 | return; | |
1716 | } | |
1717 | ||
1718 | result = ctfeGlobals.stack.getThis(); | |
1719 | if (result) | |
1720 | { | |
235d5a96 | 1721 | if (istate && istate.fd.hasDualContext()) |
5fee5ec3 | 1722 | { |
9c7d5e88 | 1723 | assert(result.op == EXP.address); |
235d5a96 | 1724 | result = result.isAddrExp().e1; |
9c7d5e88 | 1725 | assert(result.op == EXP.arrayLiteral); |
235d5a96 | 1726 | result = (*result.isArrayLiteralExp().elements)[0]; |
5fee5ec3 IB |
1727 | if (e.type.ty == Tstruct) |
1728 | { | |
235d5a96 | 1729 | result = result.isAddrExp().e1; |
5fee5ec3 IB |
1730 | } |
1731 | return; | |
1732 | } | |
9c7d5e88 | 1733 | assert(result.op == EXP.structLiteral || result.op == EXP.classReference || result.op == EXP.type); |
5fee5ec3 IB |
1734 | return; |
1735 | } | |
1736 | e.error("value of `this` is not known at compile time"); | |
1737 | result = CTFEExp.cantexp; | |
1738 | } | |
1739 | ||
1740 | override void visit(NullExp e) | |
1741 | { | |
1742 | result = e; | |
1743 | } | |
1744 | ||
1745 | override void visit(IntegerExp e) | |
1746 | { | |
1747 | debug (LOG) | |
1748 | { | |
1749 | printf("%s IntegerExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1750 | } | |
1751 | result = e; | |
1752 | } | |
1753 | ||
1754 | override void visit(RealExp e) | |
1755 | { | |
1756 | debug (LOG) | |
1757 | { | |
1758 | printf("%s RealExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1759 | } | |
1760 | result = e; | |
1761 | } | |
1762 | ||
1763 | override void visit(ComplexExp e) | |
1764 | { | |
1765 | result = e; | |
1766 | } | |
1767 | ||
1768 | override void visit(StringExp e) | |
1769 | { | |
1770 | debug (LOG) | |
1771 | { | |
1772 | printf("%s StringExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1773 | } | |
f99303eb IB |
1774 | if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted the string |
1775 | { | |
1776 | result = e; | |
1777 | return; | |
1778 | } | |
1779 | ||
1780 | if (e.type.ty != Tsarray || | |
1781 | (cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_)) | |
1782 | { | |
1783 | // If it's immutable, we don't need to dup it. Attempts to modify | |
1784 | // string literals are prevented in BinExp::interpretAssignCommon. | |
1785 | result = e; | |
1786 | } | |
1787 | else | |
1788 | { | |
1789 | // https://issues.dlang.org/show_bug.cgi?id=20811 | |
1790 | // Create a copy of mutable string literals, so that any change in | |
1791 | // value via an index or slice will not survive CTFE. | |
1792 | *pue = copyLiteral(e); | |
1793 | result = pue.exp(); | |
1794 | } | |
5fee5ec3 IB |
1795 | } |
1796 | ||
1797 | override void visit(FuncExp e) | |
1798 | { | |
1799 | debug (LOG) | |
1800 | { | |
1801 | printf("%s FuncExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1802 | } | |
1803 | result = e; | |
1804 | } | |
1805 | ||
1806 | override void visit(SymOffExp e) | |
1807 | { | |
1808 | debug (LOG) | |
1809 | { | |
1810 | printf("%s SymOffExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1811 | } | |
1812 | if (e.var.isFuncDeclaration() && e.offset == 0) | |
1813 | { | |
1814 | result = e; | |
1815 | return; | |
1816 | } | |
1817 | if (isTypeInfo_Class(e.type) && e.offset == 0) | |
1818 | { | |
1819 | result = e; | |
1820 | return; | |
1821 | } | |
1822 | if (e.type.ty != Tpointer) | |
1823 | { | |
1824 | // Probably impossible | |
1825 | e.error("cannot interpret `%s` at compile time", e.toChars()); | |
1826 | result = CTFEExp.cantexp; | |
1827 | return; | |
1828 | } | |
1829 | Type pointee = (cast(TypePointer)e.type).next; | |
1830 | if (e.var.isThreadlocal()) | |
1831 | { | |
1832 | e.error("cannot take address of thread-local variable %s at compile time", e.var.toChars()); | |
1833 | result = CTFEExp.cantexp; | |
1834 | return; | |
1835 | } | |
1836 | // Check for taking an address of a shared variable. | |
1837 | // If the shared variable is an array, the offset might not be zero. | |
1838 | Type fromType = null; | |
1839 | if (e.var.type.ty == Tarray || e.var.type.ty == Tsarray) | |
1840 | { | |
1841 | fromType = (cast(TypeArray)e.var.type).next; | |
1842 | } | |
235d5a96 IB |
1843 | if (e.var.isDataseg() && ((e.offset == 0 && isSafePointerCast(e.var.type, pointee)) || |
1844 | (fromType && isSafePointerCast(fromType, pointee)) || | |
1845 | (e.var.isCsymbol() && e.offset + pointee.size() <= e.var.type.size()))) | |
5fee5ec3 IB |
1846 | { |
1847 | result = e; | |
1848 | return; | |
1849 | } | |
1850 | ||
1851 | Expression val = getVarExp(e.loc, istate, e.var, goal); | |
1852 | if (exceptionOrCant(val)) | |
1853 | return; | |
1854 | if (val.type.ty == Tarray || val.type.ty == Tsarray) | |
1855 | { | |
1856 | // Check for unsupported type painting operations | |
1857 | Type elemtype = (cast(TypeArray)val.type).next; | |
fbdaa581 | 1858 | const elemsize = elemtype.size(); |
5fee5ec3 IB |
1859 | |
1860 | // It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*. | |
1861 | if (val.type.ty == Tsarray && pointee.ty == Tsarray && elemsize == pointee.nextOf().size()) | |
1862 | { | |
1863 | size_t d = cast(size_t)(cast(TypeSArray)pointee).dim.toInteger(); | |
1864 | Expression elwr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize, Type.tsize_t); | |
1865 | Expression eupr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize + d, Type.tsize_t); | |
1866 | ||
1867 | // Create a CTFE pointer &val[ofs..ofs+d] | |
1868 | auto se = ctfeEmplaceExp!SliceExp(e.loc, val, elwr, eupr); | |
1869 | se.type = pointee; | |
1870 | emplaceExp!(AddrExp)(pue, e.loc, se, e.type); | |
1871 | result = pue.exp(); | |
1872 | return; | |
1873 | } | |
1874 | ||
1875 | if (!isSafePointerCast(elemtype, pointee)) | |
1876 | { | |
1877 | // It's also OK to cast from &string to string*. | |
1878 | if (e.offset == 0 && isSafePointerCast(e.var.type, pointee)) | |
1879 | { | |
1880 | // Create a CTFE pointer &var | |
1881 | auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var); | |
1882 | ve.type = elemtype; | |
1883 | emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); | |
1884 | result = pue.exp(); | |
1885 | return; | |
1886 | } | |
1887 | e.error("reinterpreting cast from `%s` to `%s` is not supported in CTFE", val.type.toChars(), e.type.toChars()); | |
1888 | result = CTFEExp.cantexp; | |
1889 | return; | |
1890 | } | |
1891 | ||
1892 | const dinteger_t sz = pointee.size(); | |
1893 | dinteger_t indx = e.offset / sz; | |
1894 | assert(sz * indx == e.offset); | |
1895 | Expression aggregate = null; | |
9c7d5e88 | 1896 | if (val.op == EXP.arrayLiteral || val.op == EXP.string_) |
5fee5ec3 IB |
1897 | { |
1898 | aggregate = val; | |
1899 | } | |
1900 | else if (auto se = val.isSliceExp()) | |
1901 | { | |
1902 | aggregate = se.e1; | |
1903 | UnionExp uelwr = void; | |
1904 | Expression lwr = interpret(&uelwr, se.lwr, istate); | |
1905 | indx += lwr.toInteger(); | |
1906 | } | |
1907 | if (aggregate) | |
1908 | { | |
1909 | // Create a CTFE pointer &aggregate[ofs] | |
1910 | auto ofs = ctfeEmplaceExp!IntegerExp(e.loc, indx, Type.tsize_t); | |
1911 | auto ei = ctfeEmplaceExp!IndexExp(e.loc, aggregate, ofs); | |
1912 | ei.type = elemtype; | |
1913 | emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); | |
1914 | result = pue.exp(); | |
1915 | return; | |
1916 | } | |
1917 | } | |
1918 | else if (e.offset == 0 && isSafePointerCast(e.var.type, pointee)) | |
1919 | { | |
1920 | // Create a CTFE pointer &var | |
1921 | auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var); | |
1922 | ve.type = e.var.type; | |
1923 | emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); | |
1924 | result = pue.exp(); | |
1925 | return; | |
1926 | } | |
1927 | ||
1928 | e.error("cannot convert `&%s` to `%s` at compile time", e.var.type.toChars(), e.type.toChars()); | |
1929 | result = CTFEExp.cantexp; | |
1930 | } | |
1931 | ||
1932 | override void visit(AddrExp e) | |
1933 | { | |
1934 | debug (LOG) | |
1935 | { | |
1936 | printf("%s AddrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1937 | } | |
1938 | if (auto ve = e.e1.isVarExp()) | |
1939 | { | |
1940 | Declaration decl = ve.var; | |
1941 | ||
1942 | // We cannot take the address of an imported symbol at compile time | |
1943 | if (decl.isImportedSymbol()) { | |
1944 | e.error("cannot take address of imported symbol `%s` at compile time", decl.toChars()); | |
1945 | result = CTFEExp.cantexp; | |
1946 | return; | |
1947 | } | |
1948 | ||
1949 | if (decl.isDataseg()) { | |
1950 | // Normally this is already done by optimize() | |
1951 | // Do it here in case optimize(WANTvalue) wasn't run before CTFE | |
235d5a96 | 1952 | emplaceExp!(SymOffExp)(pue, e.loc, e.e1.isVarExp().var, 0); |
5fee5ec3 IB |
1953 | result = pue.exp(); |
1954 | result.type = e.type; | |
1955 | return; | |
1956 | } | |
1957 | } | |
1958 | auto er = interpret(e.e1, istate, CTFEGoal.LValue); | |
1959 | if (auto ve = er.isVarExp()) | |
328477f6 | 1960 | if (istate && ve.var == istate.fd.vthis) |
5fee5ec3 IB |
1961 | er = interpret(er, istate); |
1962 | ||
1963 | if (exceptionOrCant(er)) | |
1964 | return; | |
1965 | ||
1966 | // Return a simplified address expression | |
1967 | emplaceExp!(AddrExp)(pue, e.loc, er, e.type); | |
1968 | result = pue.exp(); | |
1969 | } | |
1970 | ||
1971 | override void visit(DelegateExp e) | |
1972 | { | |
1973 | debug (LOG) | |
1974 | { | |
1975 | printf("%s DelegateExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
1976 | } | |
1977 | // TODO: Really we should create a CTFE-only delegate expression | |
1978 | // of a pointer and a funcptr. | |
1979 | ||
1980 | // If it is &nestedfunc, just return it | |
1981 | // TODO: We should save the context pointer | |
1982 | if (auto ve1 = e.e1.isVarExp()) | |
1983 | if (ve1.var == e.func) | |
1984 | { | |
1985 | result = e; | |
1986 | return; | |
1987 | } | |
1988 | ||
1989 | auto er = interpret(pue, e.e1, istate); | |
1990 | if (exceptionOrCant(er)) | |
1991 | return; | |
1992 | if (er == e.e1) | |
1993 | { | |
1994 | // If it has already been CTFE'd, just return it | |
1995 | result = e; | |
1996 | } | |
1997 | else | |
1998 | { | |
1999 | er = (er == pue.exp()) ? pue.copy() : er; | |
2000 | emplaceExp!(DelegateExp)(pue, e.loc, er, e.func, false); | |
2001 | result = pue.exp(); | |
2002 | result.type = e.type; | |
2003 | } | |
2004 | } | |
2005 | ||
2006 | static Expression getVarExp(const ref Loc loc, InterState* istate, Declaration d, CTFEGoal goal) | |
2007 | { | |
2008 | Expression e = CTFEExp.cantexp; | |
2009 | if (VarDeclaration v = d.isVarDeclaration()) | |
2010 | { | |
2011 | /* Magic variable __ctfe always returns true when interpreting | |
2012 | */ | |
2013 | if (v.ident == Id.ctfe) | |
2014 | return IntegerExp.createBool(true); | |
2015 | ||
2016 | if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run | |
2017 | { | |
2018 | v.dsymbolSemantic(null); | |
2019 | if (v.type.ty == Terror) | |
2020 | return CTFEExp.cantexp; | |
2021 | } | |
2022 | ||
2023 | if ((v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !hasValue(v) && v._init && !v.isCTFE()) | |
2024 | { | |
2025 | if (v.inuse) | |
2026 | { | |
2027 | error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); | |
2028 | return CTFEExp.cantexp; | |
2029 | } | |
2030 | if (v._scope) | |
2031 | { | |
2032 | v.inuse++; | |
2033 | v._init = v._init.initializerSemantic(v._scope, v.type, INITinterpret); // might not be run on aggregate members | |
2034 | v.inuse--; | |
2035 | } | |
2036 | e = v._init.initializerToExpression(v.type); | |
2037 | if (!e) | |
2038 | return CTFEExp.cantexp; | |
2039 | assert(e.type); | |
2040 | ||
328477f6 IB |
2041 | // There's a terrible hack in `dmd.dsymbolsem` that special case |
2042 | // a struct with all zeros to an `ExpInitializer(BlitExp(IntegerExp(0)))` | |
2043 | // There's matching code for it in e2ir (toElem's visitAssignExp), | |
2044 | // so we need the same hack here. | |
2045 | // This does not trigger for global as they get a normal initializer. | |
2046 | if (auto ts = e.type.isTypeStruct()) | |
2047 | if (auto ae = e.isBlitExp()) | |
2048 | if (ae.e2.op == EXP.int64) | |
2049 | e = ts.defaultInitLiteral(loc); | |
2050 | ||
9c7d5e88 | 2051 | if (e.op == EXP.construct || e.op == EXP.blit) |
5fee5ec3 IB |
2052 | { |
2053 | AssignExp ae = cast(AssignExp)e; | |
2054 | e = ae.e2; | |
2055 | } | |
2056 | ||
9c7d5e88 | 2057 | if (e.op == EXP.error) |
5fee5ec3 IB |
2058 | { |
2059 | // FIXME: Ultimately all errors should be detected in prior semantic analysis stage. | |
2060 | } | |
2061 | else if (v.isDataseg() || (v.storage_class & STC.manifest)) | |
2062 | { | |
2063 | /* https://issues.dlang.org/show_bug.cgi?id=14304 | |
2064 | * e is a value that is not yet owned by CTFE. | |
2065 | * Mark as "cached", and use it directly during interpretation. | |
2066 | */ | |
2067 | e = scrubCacheValue(e); | |
2068 | ctfeGlobals.stack.saveGlobalConstant(v, e); | |
2069 | } | |
2070 | else | |
2071 | { | |
2072 | v.inuse++; | |
2073 | e = interpret(e, istate); | |
2074 | v.inuse--; | |
2075 | if (CTFEExp.isCantExp(e) && !global.gag && !ctfeGlobals.stackTraceCallsToSuppress) | |
2076 | errorSupplemental(loc, "while evaluating %s.init", v.toChars()); | |
2077 | if (exceptionOrCantInterpret(e)) | |
2078 | return e; | |
2079 | } | |
2080 | } | |
2081 | else if (v.isCTFE() && !hasValue(v)) | |
2082 | { | |
2083 | if (v._init && v.type.size() != 0) | |
2084 | { | |
2085 | if (v._init.isVoidInitializer()) | |
2086 | { | |
2087 | // var should have been initialized when it was created | |
2088 | error(loc, "CTFE internal error: trying to access uninitialized var"); | |
2089 | assert(0); | |
2090 | } | |
2091 | e = v._init.initializerToExpression(); | |
2092 | } | |
2093 | else | |
2094 | // Zero-length arrays don't have an initializer | |
2095 | e = v.type.defaultInitLiteral(e.loc); | |
2096 | ||
2097 | e = interpret(e, istate); | |
2098 | } | |
2099 | else if (!(v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE() && !istate) | |
2100 | { | |
2101 | error(loc, "variable `%s` cannot be read at compile time", v.toChars()); | |
2102 | return CTFEExp.cantexp; | |
2103 | } | |
2104 | else | |
2105 | { | |
2106 | e = hasValue(v) ? getValue(v) : null; | |
2107 | if (!e) | |
2108 | { | |
2109 | // Zero-length arrays don't have an initializer | |
2110 | if (v.type.size() == 0) | |
2111 | e = v.type.defaultInitLiteral(loc); | |
2112 | else if (!v.isCTFE() && v.isDataseg()) | |
2113 | { | |
2114 | error(loc, "static variable `%s` cannot be read at compile time", v.toChars()); | |
2115 | return CTFEExp.cantexp; | |
2116 | } | |
2117 | else | |
2118 | { | |
2119 | assert(!(v._init && v._init.isVoidInitializer())); | |
2120 | // CTFE initiated from inside a function | |
2121 | error(loc, "variable `%s` cannot be read at compile time", v.toChars()); | |
2122 | return CTFEExp.cantexp; | |
2123 | } | |
2124 | } | |
2125 | if (auto vie = e.isVoidInitExp()) | |
2126 | { | |
2127 | error(loc, "cannot read uninitialized variable `%s` in ctfe", v.toPrettyChars()); | |
2128 | errorSupplemental(vie.var.loc, "`%s` was uninitialized and used before set", vie.var.toChars()); | |
2129 | return CTFEExp.cantexp; | |
2130 | } | |
2131 | if (goal != CTFEGoal.LValue && v.isReference()) | |
2132 | e = interpret(e, istate, goal); | |
2133 | } | |
2134 | if (!e) | |
2135 | e = CTFEExp.cantexp; | |
2136 | } | |
2137 | else if (SymbolDeclaration s = d.isSymbolDeclaration()) | |
2138 | { | |
9c7d5e88 IB |
2139 | // exclude void[]-typed `__traits(initSymbol)` |
2140 | if (auto ta = s.type.toBasetype().isTypeDArray()) | |
2141 | { | |
2142 | assert(ta.next.ty == Tvoid); | |
2143 | error(loc, "cannot determine the address of the initializer symbol during CTFE"); | |
2144 | return CTFEExp.cantexp; | |
2145 | } | |
2146 | ||
5fee5ec3 IB |
2147 | // Struct static initializers, for example |
2148 | e = s.dsym.type.defaultInitLiteral(loc); | |
9c7d5e88 | 2149 | if (e.op == EXP.error) |
5fee5ec3 IB |
2150 | error(loc, "CTFE failed because of previous errors in `%s.init`", s.toChars()); |
2151 | e = e.expressionSemantic(null); | |
9c7d5e88 | 2152 | if (e.op == EXP.error) |
5fee5ec3 IB |
2153 | e = CTFEExp.cantexp; |
2154 | else // Convert NULL to CTFEExp | |
2155 | e = interpret(e, istate, goal); | |
2156 | } | |
2157 | else | |
2158 | error(loc, "cannot interpret declaration `%s` at compile time", d.toChars()); | |
2159 | return e; | |
2160 | } | |
2161 | ||
2162 | override void visit(VarExp e) | |
2163 | { | |
2164 | debug (LOG) | |
2165 | { | |
2166 | printf("%s VarExp::interpret() `%s`, goal = %d\n", e.loc.toChars(), e.toChars(), goal); | |
2167 | } | |
2168 | if (e.var.isFuncDeclaration()) | |
2169 | { | |
2170 | result = e; | |
2171 | return; | |
2172 | } | |
2173 | ||
0fb57034 | 2174 | if (goal == CTFEGoal.LValue) |
5fee5ec3 IB |
2175 | { |
2176 | if (auto v = e.var.isVarDeclaration()) | |
2177 | { | |
5fee5ec3 IB |
2178 | if (!hasValue(v)) |
2179 | { | |
0fb57034 IB |
2180 | // Compile-time known non-CTFE variable from an outer context |
2181 | // e.g. global or from a ref argument | |
2182 | if (v.isConst() || v.isImmutable()) | |
2183 | { | |
2184 | result = getVarExp(e.loc, istate, v, goal); | |
2185 | return; | |
2186 | } | |
2187 | ||
5fee5ec3 IB |
2188 | if (!v.isCTFE() && v.isDataseg()) |
2189 | e.error("static variable `%s` cannot be read at compile time", v.toChars()); | |
2190 | else // CTFE initiated from inside a function | |
2191 | e.error("variable `%s` cannot be read at compile time", v.toChars()); | |
2192 | result = CTFEExp.cantexp; | |
2193 | return; | |
2194 | } | |
2195 | ||
2196 | if (v.storage_class & (STC.out_ | STC.ref_)) | |
2197 | { | |
2198 | // Strip off the nest of ref variables | |
2199 | Expression ev = getValue(v); | |
9c7d5e88 IB |
2200 | if (ev.op == EXP.variable || |
2201 | ev.op == EXP.index || | |
2202 | (ev.op == EXP.slice && ev.type.toBasetype().ty == Tsarray) || | |
2203 | ev.op == EXP.dotVariable) | |
5fee5ec3 IB |
2204 | { |
2205 | result = interpret(pue, ev, istate, goal); | |
2206 | return; | |
2207 | } | |
2208 | } | |
2209 | } | |
2210 | result = e; | |
2211 | return; | |
2212 | } | |
2213 | result = getVarExp(e.loc, istate, e.var, goal); | |
2214 | if (exceptionOrCant(result)) | |
2215 | return; | |
2216 | ||
2217 | // Visit the default initializer for noreturn variables | |
2218 | // (Custom initializers would abort the current function call and exit above) | |
2219 | if (result.type.ty == Tnoreturn) | |
2220 | { | |
2221 | result.accept(this); | |
2222 | return; | |
2223 | } | |
2224 | ||
2225 | if ((e.var.storage_class & (STC.ref_ | STC.out_)) == 0 && e.type.baseElemOf().ty != Tstruct) | |
2226 | { | |
2227 | /* Ultimately, STC.ref_|STC.out_ check should be enough to see the | |
2228 | * necessity of type repainting. But currently front-end paints | |
2229 | * non-ref struct variables by the const type. | |
2230 | * | |
2231 | * auto foo(ref const S cs); | |
2232 | * S s; | |
2233 | * foo(s); // VarExp('s') will have const(S) | |
2234 | */ | |
2235 | // A VarExp may include an implicit cast. It must be done explicitly. | |
2236 | result = paintTypeOntoLiteral(pue, e.type, result); | |
2237 | } | |
2238 | } | |
2239 | ||
2240 | override void visit(DeclarationExp e) | |
2241 | { | |
2242 | debug (LOG) | |
2243 | { | |
2244 | printf("%s DeclarationExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2245 | } | |
2246 | Dsymbol s = e.declaration; | |
2247 | while (s.isAttribDeclaration()) | |
2248 | { | |
2249 | auto ad = cast(AttribDeclaration)s; | |
6d799f0a | 2250 | assert(ad.decl && ad.decl.length == 1); // Currently, only one allowed when parsing |
5fee5ec3 IB |
2251 | s = (*ad.decl)[0]; |
2252 | } | |
2253 | if (VarDeclaration v = s.isVarDeclaration()) | |
2254 | { | |
2255 | if (TupleDeclaration td = v.toAlias().isTupleDeclaration()) | |
2256 | { | |
2257 | result = null; | |
2258 | ||
2259 | // Reserve stack space for all tuple members | |
d97f3bca | 2260 | td.foreachVar((s) |
5fee5ec3 | 2261 | { |
d97f3bca | 2262 | VarDeclaration v2 = s.isVarDeclaration(); |
5fee5ec3 IB |
2263 | assert(v2); |
2264 | if (v2.isDataseg() && !v2.isCTFE()) | |
d97f3bca | 2265 | return 0; |
5fee5ec3 IB |
2266 | |
2267 | ctfeGlobals.stack.push(v2); | |
2268 | if (v2._init) | |
2269 | { | |
2270 | Expression einit; | |
2271 | if (ExpInitializer ie = v2._init.isExpInitializer()) | |
2272 | { | |
2273 | einit = interpretRegion(ie.exp, istate, goal); | |
2274 | if (exceptionOrCant(einit)) | |
d97f3bca | 2275 | return 1; |
5fee5ec3 IB |
2276 | } |
2277 | else if (v2._init.isVoidInitializer()) | |
2278 | { | |
2279 | einit = voidInitLiteral(v2.type, v2).copy(); | |
2280 | } | |
2281 | else | |
2282 | { | |
2283 | e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); | |
2284 | result = CTFEExp.cantexp; | |
d97f3bca | 2285 | return 1; |
5fee5ec3 IB |
2286 | } |
2287 | setValue(v2, einit); | |
2288 | } | |
d97f3bca IB |
2289 | return 0; |
2290 | }); | |
5fee5ec3 IB |
2291 | return; |
2292 | } | |
2293 | if (v.isStatic()) | |
2294 | { | |
2295 | // Just ignore static variables which aren't read or written yet | |
2296 | result = null; | |
2297 | return; | |
2298 | } | |
2299 | if (!(v.isDataseg() || v.storage_class & STC.manifest) || v.isCTFE()) | |
2300 | ctfeGlobals.stack.push(v); | |
2301 | if (v._init) | |
2302 | { | |
2303 | if (ExpInitializer ie = v._init.isExpInitializer()) | |
2304 | { | |
2305 | result = interpretRegion(ie.exp, istate, goal); | |
b6df1132 | 2306 | return; |
5fee5ec3 IB |
2307 | } |
2308 | else if (v._init.isVoidInitializer()) | |
2309 | { | |
2310 | result = voidInitLiteral(v.type, v).copy(); | |
2311 | // There is no AssignExp for void initializers, | |
2312 | // so set it here. | |
2313 | setValue(v, result); | |
b6df1132 | 2314 | return; |
5fee5ec3 | 2315 | } |
b6df1132 | 2316 | else if (v._init.isArrayInitializer()) |
5fee5ec3 | 2317 | { |
b6df1132 IB |
2318 | result = v._init.initializerToExpression(v.type); |
2319 | if (result !is null) | |
2320 | return; | |
5fee5ec3 | 2321 | } |
b6df1132 IB |
2322 | e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); |
2323 | result = CTFEExp.cantexp; | |
5fee5ec3 IB |
2324 | } |
2325 | else if (v.type.size() == 0) | |
2326 | { | |
2327 | // Zero-length arrays don't need an initializer | |
2328 | result = v.type.defaultInitLiteral(e.loc); | |
2329 | } | |
2330 | else | |
2331 | { | |
2332 | e.error("variable `%s` cannot be modified at compile time", v.toChars()); | |
2333 | result = CTFEExp.cantexp; | |
2334 | } | |
2335 | return; | |
2336 | } | |
2337 | if (s.isTemplateMixin() || s.isTupleDeclaration()) | |
2338 | { | |
2339 | // These can be made to work, too lazy now | |
2340 | e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); | |
2341 | result = CTFEExp.cantexp; | |
2342 | return; | |
2343 | } | |
2344 | ||
2345 | // Others should not contain executable code, so are trivial to evaluate | |
2346 | result = null; | |
2347 | debug (LOG) | |
2348 | { | |
2349 | printf("-DeclarationExp::interpret(%s): %p\n", e.toChars(), result); | |
2350 | } | |
2351 | } | |
2352 | ||
2353 | override void visit(TypeidExp e) | |
2354 | { | |
2355 | debug (LOG) | |
2356 | { | |
2357 | printf("%s TypeidExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2358 | } | |
2359 | if (Type t = isType(e.obj)) | |
2360 | { | |
2361 | result = e; | |
2362 | return; | |
2363 | } | |
2364 | if (Expression ex = isExpression(e.obj)) | |
2365 | { | |
2366 | result = interpret(pue, ex, istate); | |
2367 | if (exceptionOrCant(ex)) | |
2368 | return; | |
2369 | ||
9c7d5e88 | 2370 | if (result.op == EXP.null_) |
5fee5ec3 IB |
2371 | { |
2372 | e.error("null pointer dereference evaluating typeid. `%s` is `null`", ex.toChars()); | |
2373 | result = CTFEExp.cantexp; | |
2374 | return; | |
2375 | } | |
9c7d5e88 | 2376 | if (result.op != EXP.classReference) |
5fee5ec3 IB |
2377 | { |
2378 | e.error("CTFE internal error: determining classinfo"); | |
2379 | result = CTFEExp.cantexp; | |
2380 | return; | |
2381 | } | |
2382 | ||
235d5a96 | 2383 | ClassDeclaration cd = result.isClassReferenceExp().originalClass(); |
5fee5ec3 IB |
2384 | assert(cd); |
2385 | ||
2386 | emplaceExp!(TypeidExp)(pue, e.loc, cd.type); | |
2387 | result = pue.exp(); | |
2388 | result.type = e.type; | |
2389 | return; | |
2390 | } | |
2391 | visit(cast(Expression)e); | |
2392 | } | |
2393 | ||
2394 | override void visit(TupleExp e) | |
2395 | { | |
2396 | debug (LOG) | |
2397 | { | |
2398 | printf("%s TupleExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2399 | } | |
2400 | if (exceptionOrCant(interpretRegion(e.e0, istate, CTFEGoal.Nothing))) | |
2401 | return; | |
2402 | ||
2403 | auto expsx = e.exps; | |
2404 | foreach (i, exp; *expsx) | |
2405 | { | |
2406 | Expression ex = interpretRegion(exp, istate); | |
2407 | if (exceptionOrCant(ex)) | |
2408 | return; | |
2409 | ||
2410 | // A tuple of assignments can contain void (Bug 5676). | |
2411 | if (goal == CTFEGoal.Nothing) | |
2412 | continue; | |
9c7d5e88 | 2413 | if (ex.op == EXP.voidExpression) |
5fee5ec3 | 2414 | { |
e9251fea | 2415 | e.error("CTFE internal error: void element `%s` in sequence", exp.toChars()); |
5fee5ec3 IB |
2416 | assert(0); |
2417 | } | |
2418 | ||
2419 | /* If any changes, do Copy On Write | |
2420 | */ | |
2421 | if (ex !is exp) | |
2422 | { | |
2423 | expsx = copyArrayOnWrite(expsx, e.exps); | |
2424 | (*expsx)[i] = copyRegionExp(ex); | |
2425 | } | |
2426 | } | |
2427 | ||
2428 | if (expsx !is e.exps) | |
2429 | { | |
2430 | expandTuples(expsx); | |
2431 | emplaceExp!(TupleExp)(pue, e.loc, expsx); | |
2432 | result = pue.exp(); | |
2433 | result.type = new TypeTuple(expsx); | |
2434 | } | |
2435 | else | |
2436 | result = e; | |
2437 | } | |
2438 | ||
2439 | override void visit(ArrayLiteralExp e) | |
2440 | { | |
2441 | debug (LOG) | |
2442 | { | |
3b007164 | 2443 | printf("%s ArrayLiteralExp::interpret() %s, %s\n", e.loc.toChars(), e.type.toChars(), e.toChars()); |
5fee5ec3 IB |
2444 | } |
2445 | if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements | |
2446 | { | |
2447 | result = e; | |
2448 | return; | |
2449 | } | |
2450 | ||
3b007164 IB |
2451 | Type tb = e.type.toBasetype(); |
2452 | Type tn = tb.nextOf().toBasetype(); | |
5fee5ec3 IB |
2453 | bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct); |
2454 | ||
2455 | auto basis = interpretRegion(e.basis, istate); | |
2456 | if (exceptionOrCant(basis)) | |
2457 | return; | |
2458 | ||
2459 | auto expsx = e.elements; | |
6d799f0a | 2460 | size_t dim = expsx ? expsx.length : 0; |
3b007164 | 2461 | |
5fee5ec3 IB |
2462 | for (size_t i = 0; i < dim; i++) |
2463 | { | |
2464 | Expression exp = (*expsx)[i]; | |
2465 | Expression ex; | |
2466 | if (!exp) | |
2467 | { | |
2468 | ex = copyLiteral(basis).copy(); | |
2469 | } | |
2470 | else | |
2471 | { | |
2472 | // segfault bug 6250 | |
235d5a96 | 2473 | assert(exp.op != EXP.index || exp.isIndexExp().e1 != e); |
5fee5ec3 IB |
2474 | |
2475 | ex = interpretRegion(exp, istate); | |
2476 | if (exceptionOrCant(ex)) | |
2477 | return; | |
2478 | ||
2479 | /* Each elements should have distinct CTFE memory. | |
2480 | * int[1] z = 7; | |
2481 | * int[1][] pieces = [z,z]; // here | |
2482 | */ | |
2483 | if (wantCopy) | |
2484 | ex = copyLiteral(ex).copy(); | |
2485 | } | |
2486 | ||
2487 | /* If any changes, do Copy On Write | |
2488 | */ | |
2489 | if (ex !is exp) | |
2490 | { | |
2491 | expsx = copyArrayOnWrite(expsx, e.elements); | |
2492 | (*expsx)[i] = ex; | |
2493 | } | |
2494 | } | |
2495 | ||
2496 | if (expsx !is e.elements) | |
2497 | { | |
2498 | // todo: all tuple expansions should go in semantic phase. | |
2499 | expandTuples(expsx); | |
6d799f0a | 2500 | if (expsx.length != dim) |
5fee5ec3 IB |
2501 | { |
2502 | e.error("CTFE internal error: invalid array literal"); | |
2503 | result = CTFEExp.cantexp; | |
2504 | return; | |
2505 | } | |
2506 | emplaceExp!(ArrayLiteralExp)(pue, e.loc, e.type, basis, expsx); | |
235d5a96 | 2507 | auto ale = pue.exp().isArrayLiteralExp(); |
5fee5ec3 IB |
2508 | ale.ownedByCtfe = OwnedBy.ctfe; |
2509 | result = ale; | |
2510 | } | |
2511 | else if ((cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_)) | |
2512 | { | |
2513 | // If it's immutable, we don't need to dup it | |
2514 | result = e; | |
2515 | } | |
2516 | else | |
2517 | { | |
2518 | *pue = copyLiteral(e); | |
2519 | result = pue.exp(); | |
2520 | } | |
2521 | } | |
2522 | ||
2523 | override void visit(AssocArrayLiteralExp e) | |
2524 | { | |
2525 | debug (LOG) | |
2526 | { | |
2527 | printf("%s AssocArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2528 | } | |
2529 | if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements | |
2530 | { | |
2531 | result = e; | |
2532 | return; | |
2533 | } | |
2534 | ||
2535 | auto keysx = e.keys; | |
2536 | auto valuesx = e.values; | |
2537 | foreach (i, ekey; *keysx) | |
2538 | { | |
2539 | auto evalue = (*valuesx)[i]; | |
2540 | ||
2541 | auto ek = interpretRegion(ekey, istate); | |
2542 | if (exceptionOrCant(ek)) | |
2543 | return; | |
2544 | auto ev = interpretRegion(evalue, istate); | |
2545 | if (exceptionOrCant(ev)) | |
2546 | return; | |
2547 | ||
2548 | /* If any changes, do Copy On Write | |
2549 | */ | |
2550 | if (ek !is ekey || | |
2551 | ev !is evalue) | |
2552 | { | |
2553 | keysx = copyArrayOnWrite(keysx, e.keys); | |
2554 | valuesx = copyArrayOnWrite(valuesx, e.values); | |
2555 | (*keysx)[i] = ek; | |
2556 | (*valuesx)[i] = ev; | |
2557 | } | |
2558 | } | |
2559 | if (keysx !is e.keys) | |
2560 | expandTuples(keysx); | |
2561 | if (valuesx !is e.values) | |
2562 | expandTuples(valuesx); | |
6d799f0a | 2563 | if (keysx.length != valuesx.length) |
5fee5ec3 IB |
2564 | { |
2565 | e.error("CTFE internal error: invalid AA"); | |
2566 | result = CTFEExp.cantexp; | |
2567 | return; | |
2568 | } | |
2569 | ||
2570 | /* Remove duplicate keys | |
2571 | */ | |
6d799f0a | 2572 | for (size_t i = 1; i < keysx.length; i++) |
5fee5ec3 IB |
2573 | { |
2574 | auto ekey = (*keysx)[i - 1]; | |
6d799f0a | 2575 | for (size_t j = i; j < keysx.length; j++) |
5fee5ec3 IB |
2576 | { |
2577 | auto ekey2 = (*keysx)[j]; | |
9c7d5e88 | 2578 | if (!ctfeEqual(e.loc, EXP.equal, ekey, ekey2)) |
5fee5ec3 IB |
2579 | continue; |
2580 | ||
2581 | // Remove ekey | |
2582 | keysx = copyArrayOnWrite(keysx, e.keys); | |
2583 | valuesx = copyArrayOnWrite(valuesx, e.values); | |
2584 | keysx.remove(i - 1); | |
2585 | valuesx.remove(i - 1); | |
2586 | ||
2587 | i -= 1; // redo the i'th iteration | |
2588 | break; | |
2589 | } | |
2590 | } | |
2591 | ||
2592 | if (keysx !is e.keys || | |
2593 | valuesx !is e.values) | |
2594 | { | |
2595 | assert(keysx !is e.keys && | |
2596 | valuesx !is e.values); | |
2597 | auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); | |
2598 | aae.type = e.type; | |
2599 | aae.ownedByCtfe = OwnedBy.ctfe; | |
2600 | result = aae; | |
2601 | } | |
2602 | else | |
2603 | { | |
2604 | *pue = copyLiteral(e); | |
2605 | result = pue.exp(); | |
2606 | } | |
2607 | } | |
2608 | ||
2609 | override void visit(StructLiteralExp e) | |
2610 | { | |
2611 | debug (LOG) | |
2612 | { | |
2613 | printf("%s StructLiteralExp::interpret() %s ownedByCtfe = %d\n", e.loc.toChars(), e.toChars(), e.ownedByCtfe); | |
2614 | } | |
2615 | if (e.ownedByCtfe >= OwnedBy.ctfe) | |
2616 | { | |
2617 | result = e; | |
2618 | return; | |
2619 | } | |
2620 | ||
6d799f0a | 2621 | size_t dim = e.elements ? e.elements.length : 0; |
5fee5ec3 IB |
2622 | auto expsx = e.elements; |
2623 | ||
6d799f0a | 2624 | if (dim != e.sd.fields.length) |
5fee5ec3 IB |
2625 | { |
2626 | // guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral | |
6d799f0a IB |
2627 | const nvthis = e.sd.fields.length - e.sd.nonHiddenFields(); |
2628 | assert(e.sd.fields.length - dim == nvthis); | |
5fee5ec3 IB |
2629 | |
2630 | /* If a nested struct has no initialized hidden pointer, | |
2631 | * set it to null to match the runtime behaviour. | |
2632 | */ | |
2633 | foreach (const i; 0 .. nvthis) | |
2634 | { | |
2635 | auto ne = ctfeEmplaceExp!NullExp(e.loc); | |
2636 | auto vthis = i == 0 ? e.sd.vthis : e.sd.vthis2; | |
2637 | ne.type = vthis.type; | |
2638 | ||
2639 | expsx = copyArrayOnWrite(expsx, e.elements); | |
2640 | expsx.push(ne); | |
2641 | ++dim; | |
2642 | } | |
2643 | } | |
6d799f0a | 2644 | assert(dim == e.sd.fields.length); |
5fee5ec3 IB |
2645 | |
2646 | foreach (i; 0 .. dim) | |
2647 | { | |
2648 | auto v = e.sd.fields[i]; | |
2649 | Expression exp = (*expsx)[i]; | |
2650 | Expression ex; | |
2651 | if (!exp) | |
2652 | { | |
2653 | ex = voidInitLiteral(v.type, v).copy(); | |
2654 | } | |
2655 | else | |
2656 | { | |
2657 | ex = interpretRegion(exp, istate); | |
2658 | if (exceptionOrCant(ex)) | |
2659 | return; | |
2660 | if ((v.type.ty != ex.type.ty) && v.type.ty == Tsarray) | |
2661 | { | |
2662 | // Block assignment from inside struct literals | |
2663 | auto tsa = cast(TypeSArray)v.type; | |
2664 | auto len = cast(size_t)tsa.dim.toInteger(); | |
2665 | UnionExp ue = void; | |
2666 | ex = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len); | |
2667 | if (ex == ue.exp()) | |
2668 | ex = ue.copy(); | |
2669 | } | |
2670 | } | |
2671 | ||
2672 | /* If any changes, do Copy On Write | |
2673 | */ | |
2674 | if (ex !is exp) | |
2675 | { | |
2676 | expsx = copyArrayOnWrite(expsx, e.elements); | |
2677 | (*expsx)[i] = ex; | |
2678 | } | |
2679 | } | |
2680 | ||
2681 | if (expsx !is e.elements) | |
2682 | { | |
2683 | expandTuples(expsx); | |
6d799f0a | 2684 | if (expsx.length != e.sd.fields.length) |
5fee5ec3 IB |
2685 | { |
2686 | e.error("CTFE internal error: invalid struct literal"); | |
2687 | result = CTFEExp.cantexp; | |
2688 | return; | |
2689 | } | |
2690 | emplaceExp!(StructLiteralExp)(pue, e.loc, e.sd, expsx); | |
235d5a96 | 2691 | auto sle = pue.exp().isStructLiteralExp(); |
5fee5ec3 IB |
2692 | sle.type = e.type; |
2693 | sle.ownedByCtfe = OwnedBy.ctfe; | |
2694 | sle.origin = e.origin; | |
2695 | result = sle; | |
2696 | } | |
2697 | else | |
2698 | { | |
2699 | *pue = copyLiteral(e); | |
2700 | result = pue.exp(); | |
2701 | } | |
2702 | } | |
2703 | ||
2704 | // Create an array literal of type 'newtype' with dimensions given by | |
2705 | // 'arguments'[argnum..$] | |
2706 | static Expression recursivelyCreateArrayLiteral(UnionExp* pue, const ref Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) | |
2707 | { | |
2708 | Expression lenExpr = interpret(pue, (*arguments)[argnum], istate); | |
2709 | if (exceptionOrCantInterpret(lenExpr)) | |
2710 | return lenExpr; | |
2711 | size_t len = cast(size_t)lenExpr.toInteger(); | |
2712 | Type elemType = (cast(TypeArray)newtype).next; | |
6d799f0a | 2713 | if (elemType.ty == Tarray && argnum < arguments.length - 1) |
5fee5ec3 IB |
2714 | { |
2715 | Expression elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate, arguments, argnum + 1); | |
2716 | if (exceptionOrCantInterpret(elem)) | |
2717 | return elem; | |
2718 | ||
2719 | auto elements = new Expressions(len); | |
2720 | foreach (ref element; *elements) | |
2721 | element = copyLiteral(elem).copy(); | |
2722 | emplaceExp!(ArrayLiteralExp)(pue, loc, newtype, elements); | |
235d5a96 | 2723 | auto ae = pue.exp().isArrayLiteralExp(); |
5fee5ec3 IB |
2724 | ae.ownedByCtfe = OwnedBy.ctfe; |
2725 | return ae; | |
2726 | } | |
6d799f0a | 2727 | assert(argnum == arguments.length - 1); |
5fee5ec3 IB |
2728 | if (elemType.ty.isSomeChar) |
2729 | { | |
2730 | const ch = cast(dchar)elemType.defaultInitLiteral(loc).toInteger(); | |
2731 | const sz = cast(ubyte)elemType.size(); | |
2732 | return createBlockDuplicatedStringLiteral(pue, loc, newtype, ch, len, sz); | |
2733 | } | |
2734 | else | |
2735 | { | |
2736 | auto el = interpret(elemType.defaultInitLiteral(loc), istate); | |
2737 | return createBlockDuplicatedArrayLiteral(pue, loc, newtype, el, len); | |
2738 | } | |
2739 | } | |
2740 | ||
2741 | override void visit(NewExp e) | |
2742 | { | |
2743 | debug (LOG) | |
2744 | { | |
2745 | printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2746 | } | |
2747 | ||
2748 | Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing); | |
2749 | if (exceptionOrCant(epre)) | |
2750 | return; | |
2751 | ||
2752 | if (e.newtype.ty == Tarray && e.arguments) | |
2753 | { | |
2754 | result = recursivelyCreateArrayLiteral(pue, e.loc, e.newtype, istate, e.arguments, 0); | |
2755 | return; | |
2756 | } | |
2757 | if (auto ts = e.newtype.toBasetype().isTypeStruct()) | |
2758 | { | |
2759 | if (e.member) | |
2760 | { | |
2761 | Expression se = e.newtype.defaultInitLiteral(e.loc); | |
2762 | se = interpret(se, istate); | |
2763 | if (exceptionOrCant(se)) | |
2764 | return; | |
2765 | result = interpretFunction(pue, e.member, istate, e.arguments, se); | |
2766 | ||
2767 | // Repaint as same as CallExp::interpret() does. | |
2768 | result.loc = e.loc; | |
2769 | } | |
2770 | else | |
2771 | { | |
2772 | StructDeclaration sd = ts.sym; | |
2773 | auto exps = new Expressions(); | |
6d799f0a | 2774 | exps.reserve(sd.fields.length); |
5fee5ec3 IB |
2775 | if (e.arguments) |
2776 | { | |
6d799f0a | 2777 | exps.setDim(e.arguments.length); |
5fee5ec3 IB |
2778 | foreach (i, ex; *e.arguments) |
2779 | { | |
2780 | ex = interpretRegion(ex, istate); | |
2781 | if (exceptionOrCant(ex)) | |
2782 | return; | |
2783 | (*exps)[i] = ex; | |
2784 | } | |
2785 | } | |
c8dfa79c | 2786 | sd.fill(e.loc, *exps, false); |
5fee5ec3 IB |
2787 | |
2788 | auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, sd, exps, e.newtype); | |
2789 | se.origin = se; | |
2790 | se.type = e.newtype; | |
2791 | se.ownedByCtfe = OwnedBy.ctfe; | |
2792 | result = interpret(pue, se, istate); | |
2793 | } | |
2794 | if (exceptionOrCant(result)) | |
2795 | return; | |
2796 | Expression ev = (result == pue.exp()) ? pue.copy() : result; | |
2797 | emplaceExp!(AddrExp)(pue, e.loc, ev, e.type); | |
2798 | result = pue.exp(); | |
2799 | return; | |
2800 | } | |
2801 | if (auto tc = e.newtype.toBasetype().isTypeClass()) | |
2802 | { | |
2803 | ClassDeclaration cd = tc.sym; | |
2804 | size_t totalFieldCount = 0; | |
2805 | for (ClassDeclaration c = cd; c; c = c.baseClass) | |
6d799f0a | 2806 | totalFieldCount += c.fields.length; |
5fee5ec3 IB |
2807 | auto elems = new Expressions(totalFieldCount); |
2808 | size_t fieldsSoFar = totalFieldCount; | |
2809 | for (ClassDeclaration c = cd; c; c = c.baseClass) | |
2810 | { | |
6d799f0a | 2811 | fieldsSoFar -= c.fields.length; |
5fee5ec3 IB |
2812 | foreach (i, v; c.fields) |
2813 | { | |
2814 | if (v.inuse) | |
2815 | { | |
2816 | e.error("circular reference to `%s`", v.toPrettyChars()); | |
2817 | result = CTFEExp.cantexp; | |
2818 | return; | |
2819 | } | |
2820 | Expression m; | |
2821 | if (v._init) | |
2822 | { | |
2823 | if (v._init.isVoidInitializer()) | |
2824 | m = voidInitLiteral(v.type, v).copy(); | |
2825 | else | |
2826 | m = v.getConstInitializer(true); | |
2827 | } | |
7e7ebe3e IB |
2828 | else if (v.type.isTypeNoreturn()) |
2829 | { | |
2830 | // Noreturn field with default initializer | |
2831 | (*elems)[fieldsSoFar + i] = null; | |
2832 | continue; | |
2833 | } | |
5fee5ec3 IB |
2834 | else |
2835 | m = v.type.defaultInitLiteral(e.loc); | |
2836 | if (exceptionOrCant(m)) | |
2837 | return; | |
2838 | (*elems)[fieldsSoFar + i] = copyLiteral(m).copy(); | |
2839 | } | |
2840 | } | |
2841 | // Hack: we store a ClassDeclaration instead of a StructDeclaration. | |
2842 | // We probably won't get away with this. | |
2843 | // auto se = new StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype); | |
2844 | auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype); | |
2845 | se.origin = se; | |
2846 | se.ownedByCtfe = OwnedBy.ctfe; | |
0fb57034 | 2847 | Expression eref = ctfeEmplaceExp!ClassReferenceExp(e.loc, se, e.type); |
5fee5ec3 IB |
2848 | if (e.member) |
2849 | { | |
2850 | // Call constructor | |
2851 | if (!e.member.fbody) | |
2852 | { | |
2853 | Expression ctorfail = evaluateIfBuiltin(pue, istate, e.loc, e.member, e.arguments, eref); | |
2854 | if (ctorfail) | |
2855 | { | |
2856 | if (exceptionOrCant(ctorfail)) | |
2857 | return; | |
2858 | result = eref; | |
2859 | return; | |
2860 | } | |
2861 | e.member.error("`%s` cannot be constructed at compile time, because the constructor has no available source code", e.newtype.toChars()); | |
2862 | result = CTFEExp.cantexp; | |
2863 | return; | |
2864 | } | |
2865 | UnionExp ue = void; | |
2866 | Expression ctorfail = interpretFunction(&ue, e.member, istate, e.arguments, eref); | |
2867 | if (exceptionOrCant(ctorfail)) | |
2868 | return; | |
2869 | ||
2870 | /* https://issues.dlang.org/show_bug.cgi?id=14465 | |
2871 | * Repaint the loc, because a super() call | |
2872 | * in the constructor modifies the loc of ClassReferenceExp | |
2873 | * in CallExp::interpret(). | |
2874 | */ | |
2875 | eref.loc = e.loc; | |
2876 | } | |
2877 | result = eref; | |
2878 | return; | |
2879 | } | |
2880 | if (e.newtype.toBasetype().isscalar()) | |
2881 | { | |
2882 | Expression newval; | |
6d799f0a | 2883 | if (e.arguments && e.arguments.length) |
5fee5ec3 IB |
2884 | newval = (*e.arguments)[0]; |
2885 | else | |
2886 | newval = e.newtype.defaultInitLiteral(e.loc); | |
2887 | newval = interpretRegion(newval, istate); | |
2888 | if (exceptionOrCant(newval)) | |
2889 | return; | |
2890 | ||
2891 | // Create a CTFE pointer &[newval][0] | |
2892 | auto elements = new Expressions(1); | |
2893 | (*elements)[0] = newval; | |
2894 | auto ae = ctfeEmplaceExp!ArrayLiteralExp(e.loc, e.newtype.arrayOf(), elements); | |
2895 | ae.ownedByCtfe = OwnedBy.ctfe; | |
2896 | ||
2897 | auto ei = ctfeEmplaceExp!IndexExp(e.loc, ae, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t)); | |
2898 | ei.type = e.newtype; | |
2899 | emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); | |
2900 | result = pue.exp(); | |
2901 | return; | |
2902 | } | |
2903 | e.error("cannot interpret `%s` at compile time", e.toChars()); | |
2904 | result = CTFEExp.cantexp; | |
2905 | } | |
2906 | ||
2907 | override void visit(UnaExp e) | |
2908 | { | |
2909 | debug (LOG) | |
2910 | { | |
2911 | printf("%s UnaExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2912 | } | |
2913 | UnionExp ue = void; | |
2914 | Expression e1 = interpret(&ue, e.e1, istate); | |
2915 | if (exceptionOrCant(e1)) | |
2916 | return; | |
2917 | switch (e.op) | |
2918 | { | |
9c7d5e88 | 2919 | case EXP.negate: |
5fee5ec3 IB |
2920 | *pue = Neg(e.type, e1); |
2921 | break; | |
2922 | ||
9c7d5e88 | 2923 | case EXP.tilde: |
5fee5ec3 IB |
2924 | *pue = Com(e.type, e1); |
2925 | break; | |
2926 | ||
9c7d5e88 | 2927 | case EXP.not: |
5fee5ec3 IB |
2928 | *pue = Not(e.type, e1); |
2929 | break; | |
2930 | ||
2931 | default: | |
2932 | assert(0); | |
2933 | } | |
2934 | result = (*pue).exp(); | |
2935 | } | |
2936 | ||
2937 | override void visit(DotTypeExp e) | |
2938 | { | |
2939 | debug (LOG) | |
2940 | { | |
2941 | printf("%s DotTypeExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
2942 | } | |
2943 | UnionExp ue = void; | |
2944 | Expression e1 = interpret(&ue, e.e1, istate); | |
2945 | if (exceptionOrCant(e1)) | |
2946 | return; | |
2947 | if (e1 == e.e1) | |
2948 | result = e; // optimize: reuse this CTFE reference | |
2949 | else | |
2950 | { | |
235d5a96 | 2951 | auto edt = e.copy().isDotTypeExp(); |
5fee5ec3 IB |
2952 | edt.e1 = (e1 == ue.exp()) ? e1.copy() : e1; // don't return pointer to ue |
2953 | result = edt; | |
2954 | } | |
2955 | } | |
2956 | ||
2957 | extern (D) private void interpretCommon(BinExp e, fp_t fp) | |
2958 | { | |
2959 | debug (LOG) | |
2960 | { | |
2961 | printf("%s BinExp::interpretCommon() %s\n", e.loc.toChars(), e.toChars()); | |
2962 | } | |
9c7d5e88 | 2963 | if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer && e.op == EXP.min) |
5fee5ec3 IB |
2964 | { |
2965 | UnionExp ue1 = void; | |
2966 | Expression e1 = interpret(&ue1, e.e1, istate); | |
2967 | if (exceptionOrCant(e1)) | |
2968 | return; | |
2969 | UnionExp ue2 = void; | |
2970 | Expression e2 = interpret(&ue2, e.e2, istate); | |
2971 | if (exceptionOrCant(e2)) | |
2972 | return; | |
fbdaa581 | 2973 | result = pointerDifference(pue, e.loc, e.type, e1, e2); |
5fee5ec3 IB |
2974 | return; |
2975 | } | |
2976 | if (e.e1.type.ty == Tpointer && e.e2.type.isintegral()) | |
2977 | { | |
2978 | UnionExp ue1 = void; | |
2979 | Expression e1 = interpret(&ue1, e.e1, istate); | |
2980 | if (exceptionOrCant(e1)) | |
2981 | return; | |
2982 | UnionExp ue2 = void; | |
2983 | Expression e2 = interpret(&ue2, e.e2, istate); | |
2984 | if (exceptionOrCant(e2)) | |
2985 | return; | |
fbdaa581 | 2986 | result = pointerArithmetic(pue, e.loc, e.op, e.type, e1, e2); |
5fee5ec3 IB |
2987 | return; |
2988 | } | |
9c7d5e88 | 2989 | if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == EXP.add) |
5fee5ec3 IB |
2990 | { |
2991 | UnionExp ue1 = void; | |
2992 | Expression e1 = interpret(&ue1, e.e1, istate); | |
2993 | if (exceptionOrCant(e1)) | |
2994 | return; | |
2995 | UnionExp ue2 = void; | |
2996 | Expression e2 = interpret(&ue2, e.e2, istate); | |
2997 | if (exceptionOrCant(e2)) | |
2998 | return; | |
fbdaa581 | 2999 | result = pointerArithmetic(pue, e.loc, e.op, e.type, e2, e1); |
5fee5ec3 IB |
3000 | return; |
3001 | } | |
3002 | if (e.e1.type.ty == Tpointer || e.e2.type.ty == Tpointer) | |
3003 | { | |
3004 | e.error("pointer expression `%s` cannot be interpreted at compile time", e.toChars()); | |
3005 | result = CTFEExp.cantexp; | |
3006 | return; | |
3007 | } | |
3008 | ||
3009 | bool evalOperand(UnionExp* pue, Expression ex, out Expression er) | |
3010 | { | |
3011 | er = interpret(pue, ex, istate); | |
3012 | if (exceptionOrCant(er)) | |
3013 | return false; | |
3014 | return true; | |
3015 | } | |
3016 | ||
3017 | UnionExp ue1 = void; | |
3018 | Expression e1; | |
3019 | if (!evalOperand(&ue1, e.e1, e1)) | |
3020 | return; | |
3021 | ||
3022 | UnionExp ue2 = void; | |
3023 | Expression e2; | |
3024 | if (!evalOperand(&ue2, e.e2, e2)) | |
3025 | return; | |
3026 | ||
9c7d5e88 | 3027 | if (e.op == EXP.rightShift || e.op == EXP.leftShift || e.op == EXP.unsignedRightShift) |
5fee5ec3 IB |
3028 | { |
3029 | const sinteger_t i2 = e2.toInteger(); | |
fbdaa581 | 3030 | const uinteger_t sz = e1.type.size() * 8; |
5fee5ec3 IB |
3031 | if (i2 < 0 || i2 >= sz) |
3032 | { | |
3033 | e.error("shift by %lld is outside the range 0..%llu", i2, cast(ulong)sz - 1); | |
3034 | result = CTFEExp.cantexp; | |
3035 | return; | |
3036 | } | |
3037 | } | |
3038 | ||
3039 | /****************************************** | |
3040 | * Perform the operation fp on operands e1 and e2. | |
3041 | */ | |
3042 | UnionExp evaluate(Loc loc, Type type, Expression e1, Expression e2) | |
3043 | { | |
3044 | UnionExp ue = void; | |
3045 | auto ae1 = e1.isArrayLiteralExp(); | |
3046 | auto ae2 = e2.isArrayLiteralExp(); | |
3047 | if (ae1 || ae2) | |
3048 | { | |
3049 | /* Cases: | |
3050 | * 1. T[] op T[] | |
3051 | * 2. T op T[] | |
3052 | * 3. T[] op T | |
3053 | */ | |
3054 | if (ae1 && e2.implicitConvTo(e1.type.toBasetype().nextOf())) // case 3 | |
3055 | ae2 = null; | |
3056 | else if (ae2 && e1.implicitConvTo(e2.type.toBasetype().nextOf())) // case 2 | |
3057 | ae1 = null; | |
3058 | // else case 1 | |
3059 | ||
3060 | auto aex = ae1 ? ae1 : ae2; | |
3061 | if (!aex.elements) | |
3062 | { | |
3063 | emplaceExp!ArrayLiteralExp(&ue, loc, type, cast(Expressions*) null); | |
3064 | return ue; | |
3065 | } | |
3066 | const length = aex.elements.length; | |
3067 | Expressions* elements = new Expressions(length); | |
3068 | ||
3069 | emplaceExp!ArrayLiteralExp(&ue, loc, type, elements); | |
3070 | foreach (i; 0 .. length) | |
3071 | { | |
3072 | Expression e1x = ae1 ? ae1[i] : e1; | |
3073 | Expression e2x = ae2 ? ae2[i] : e2; | |
3074 | UnionExp uex = evaluate(loc, e1x.type, e1x, e2x); | |
3075 | // This can be made more efficient by making use of ue.basis | |
3076 | (*elements)[i] = uex.copy(); | |
3077 | } | |
3078 | return ue; | |
3079 | } | |
3080 | ||
3081 | if (e1.isConst() != 1) | |
3082 | { | |
3083 | // The following should really be an assert() | |
3084 | e1.error("CTFE internal error: non-constant value `%s`", e1.toChars()); | |
9c7d5e88 | 3085 | emplaceExp!CTFEExp(&ue, EXP.cantExpression); |
5fee5ec3 IB |
3086 | return ue; |
3087 | } | |
3088 | if (e2.isConst() != 1) | |
3089 | { | |
3090 | e2.error("CTFE internal error: non-constant value `%s`", e2.toChars()); | |
9c7d5e88 | 3091 | emplaceExp!CTFEExp(&ue, EXP.cantExpression); |
5fee5ec3 IB |
3092 | return ue; |
3093 | } | |
3094 | ||
3095 | return (*fp)(loc, type, e1, e2); | |
3096 | } | |
3097 | ||
3098 | *pue = evaluate(e.loc, e.type, e1, e2); | |
3099 | result = (*pue).exp(); | |
3100 | if (CTFEExp.isCantExp(result)) | |
3101 | e.error("`%s` cannot be interpreted at compile time", e.toChars()); | |
3102 | } | |
3103 | ||
3104 | extern (D) private void interpretCompareCommon(BinExp e, fp2_t fp) | |
3105 | { | |
3106 | debug (LOG) | |
3107 | { | |
3108 | printf("%s BinExp::interpretCompareCommon() %s\n", e.loc.toChars(), e.toChars()); | |
3109 | } | |
3110 | UnionExp ue1 = void; | |
3111 | UnionExp ue2 = void; | |
3112 | if (e.e1.type.ty == Tpointer && e.e2.type.ty == Tpointer) | |
3113 | { | |
3114 | Expression e1 = interpret(&ue1, e.e1, istate); | |
3115 | if (exceptionOrCant(e1)) | |
3116 | return; | |
3117 | Expression e2 = interpret(&ue2, e.e2, istate); | |
3118 | if (exceptionOrCant(e2)) | |
3119 | return; | |
3120 | //printf("e1 = %s %s, e2 = %s %s\n", e1.type.toChars(), e1.toChars(), e2.type.toChars(), e2.toChars()); | |
3121 | dinteger_t ofs1, ofs2; | |
3122 | Expression agg1 = getAggregateFromPointer(e1, &ofs1); | |
3123 | Expression agg2 = getAggregateFromPointer(e2, &ofs2); | |
3124 | //printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1.toChars(), agg2, agg2.toChars()); | |
3125 | const cmp = comparePointers(e.op, agg1, ofs1, agg2, ofs2); | |
3126 | if (cmp == -1) | |
3127 | { | |
9c7d5e88 | 3128 | char dir = (e.op == EXP.greaterThan || e.op == EXP.greaterOrEqual) ? '<' : '>'; |
5fee5ec3 IB |
3129 | e.error("the ordering of pointers to unrelated memory blocks is indeterminate in CTFE. To check if they point to the same memory block, use both `>` and `<` inside `&&` or `||`, eg `%s && %s %c= %s + 1`", e.toChars(), e.e1.toChars(), dir, e.e2.toChars()); |
3130 | result = CTFEExp.cantexp; | |
3131 | return; | |
3132 | } | |
3133 | if (e.type.equals(Type.tbool)) | |
3134 | result = IntegerExp.createBool(cmp != 0); | |
3135 | else | |
3136 | { | |
3137 | emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type); | |
3138 | result = (*pue).exp(); | |
3139 | } | |
3140 | return; | |
3141 | } | |
3142 | Expression e1 = interpret(&ue1, e.e1, istate); | |
3143 | if (exceptionOrCant(e1)) | |
3144 | return; | |
3145 | if (!isCtfeComparable(e1)) | |
3146 | { | |
3147 | e.error("cannot compare `%s` at compile time", e1.toChars()); | |
3148 | result = CTFEExp.cantexp; | |
3149 | return; | |
3150 | } | |
3151 | Expression e2 = interpret(&ue2, e.e2, istate); | |
3152 | if (exceptionOrCant(e2)) | |
3153 | return; | |
3154 | if (!isCtfeComparable(e2)) | |
3155 | { | |
3156 | e.error("cannot compare `%s` at compile time", e2.toChars()); | |
3157 | result = CTFEExp.cantexp; | |
3158 | return; | |
3159 | } | |
3160 | const cmp = (*fp)(e.loc, e.op, e1, e2); | |
3161 | if (e.type.equals(Type.tbool)) | |
3162 | result = IntegerExp.createBool(cmp); | |
3163 | else | |
3164 | { | |
3165 | emplaceExp!(IntegerExp)(pue, e.loc, cmp, e.type); | |
3166 | result = (*pue).exp(); | |
3167 | } | |
3168 | } | |
3169 | ||
3170 | override void visit(BinExp e) | |
3171 | { | |
3172 | switch (e.op) | |
3173 | { | |
9c7d5e88 | 3174 | case EXP.add: |
5fee5ec3 IB |
3175 | interpretCommon(e, &Add); |
3176 | return; | |
3177 | ||
9c7d5e88 | 3178 | case EXP.min: |
5fee5ec3 IB |
3179 | interpretCommon(e, &Min); |
3180 | return; | |
3181 | ||
9c7d5e88 | 3182 | case EXP.mul: |
5fee5ec3 IB |
3183 | interpretCommon(e, &Mul); |
3184 | return; | |
3185 | ||
9c7d5e88 | 3186 | case EXP.div: |
5fee5ec3 IB |
3187 | interpretCommon(e, &Div); |
3188 | return; | |
3189 | ||
9c7d5e88 | 3190 | case EXP.mod: |
5fee5ec3 IB |
3191 | interpretCommon(e, &Mod); |
3192 | return; | |
3193 | ||
9c7d5e88 | 3194 | case EXP.leftShift: |
5fee5ec3 IB |
3195 | interpretCommon(e, &Shl); |
3196 | return; | |
3197 | ||
9c7d5e88 | 3198 | case EXP.rightShift: |
5fee5ec3 IB |
3199 | interpretCommon(e, &Shr); |
3200 | return; | |
3201 | ||
9c7d5e88 | 3202 | case EXP.unsignedRightShift: |
5fee5ec3 IB |
3203 | interpretCommon(e, &Ushr); |
3204 | return; | |
3205 | ||
9c7d5e88 | 3206 | case EXP.and: |
5fee5ec3 IB |
3207 | interpretCommon(e, &And); |
3208 | return; | |
3209 | ||
9c7d5e88 | 3210 | case EXP.or: |
5fee5ec3 IB |
3211 | interpretCommon(e, &Or); |
3212 | return; | |
3213 | ||
9c7d5e88 | 3214 | case EXP.xor: |
5fee5ec3 IB |
3215 | interpretCommon(e, &Xor); |
3216 | return; | |
3217 | ||
9c7d5e88 | 3218 | case EXP.pow: |
5fee5ec3 IB |
3219 | interpretCommon(e, &Pow); |
3220 | return; | |
3221 | ||
9c7d5e88 IB |
3222 | case EXP.equal: |
3223 | case EXP.notEqual: | |
5fee5ec3 IB |
3224 | interpretCompareCommon(e, &ctfeEqual); |
3225 | return; | |
3226 | ||
9c7d5e88 IB |
3227 | case EXP.identity: |
3228 | case EXP.notIdentity: | |
5fee5ec3 IB |
3229 | interpretCompareCommon(e, &ctfeIdentity); |
3230 | return; | |
3231 | ||
9c7d5e88 IB |
3232 | case EXP.lessThan: |
3233 | case EXP.lessOrEqual: | |
3234 | case EXP.greaterThan: | |
3235 | case EXP.greaterOrEqual: | |
5fee5ec3 IB |
3236 | interpretCompareCommon(e, &ctfeCmp); |
3237 | return; | |
3238 | ||
3239 | default: | |
9c7d5e88 | 3240 | printf("be = '%s' %s at [%s]\n", EXPtoString(e.op).ptr, e.toChars(), e.loc.toChars()); |
5fee5ec3 IB |
3241 | assert(0); |
3242 | } | |
3243 | } | |
3244 | ||
3245 | /* Helper functions for BinExp::interpretAssignCommon | |
3246 | */ | |
3247 | // Returns the variable which is eventually modified, or NULL if an rvalue. | |
3248 | // thisval is the current value of 'this'. | |
d6679fa2 | 3249 | static VarDeclaration findParentVar(Expression e) @safe |
5fee5ec3 IB |
3250 | { |
3251 | for (;;) | |
3252 | { | |
3253 | if (auto ve = e.isVarExp()) | |
3254 | { | |
3255 | VarDeclaration v = ve.var.isVarDeclaration(); | |
3256 | assert(v); | |
3257 | return v; | |
3258 | } | |
3259 | if (auto ie = e.isIndexExp()) | |
3260 | e = ie.e1; | |
3261 | else if (auto dve = e.isDotVarExp()) | |
3262 | e = dve.e1; | |
3263 | else if (auto dtie = e.isDotTemplateInstanceExp()) | |
3264 | e = dtie.e1; | |
3265 | else if (auto se = e.isSliceExp()) | |
3266 | e = se.e1; | |
3267 | else | |
3268 | return null; | |
3269 | } | |
3270 | } | |
3271 | ||
3272 | extern (D) private void interpretAssignCommon(BinExp e, fp_t fp, int post = 0) | |
3273 | { | |
3274 | debug (LOG) | |
3275 | { | |
3276 | printf("%s BinExp::interpretAssignCommon() %s\n", e.loc.toChars(), e.toChars()); | |
3277 | } | |
3278 | result = CTFEExp.cantexp; | |
3279 | ||
3280 | Expression e1 = e.e1; | |
3281 | if (!istate) | |
3282 | { | |
3283 | e.error("value of `%s` is not known at compile time", e1.toChars()); | |
3284 | return; | |
3285 | } | |
3286 | ||
3287 | ++ctfeGlobals.numAssignments; | |
3288 | ||
3289 | /* Before we begin, we need to know if this is a reference assignment | |
3290 | * (dynamic array, AA, or class) or a value assignment. | |
3291 | * Determining this for slice assignments are tricky: we need to know | |
3292 | * if it is a block assignment (a[] = e) rather than a direct slice | |
3293 | * assignment (a[] = b[]). Note that initializers of multi-dimensional | |
3294 | * static arrays can have 2D block assignments (eg, int[7][7] x = 6;). | |
3295 | * So we need to recurse to determine if it is a block assignment. | |
3296 | */ | |
3297 | bool isBlockAssignment = false; | |
9c7d5e88 | 3298 | if (e1.op == EXP.slice) |
5fee5ec3 IB |
3299 | { |
3300 | // a[] = e can have const e. So we compare the naked types. | |
3301 | Type tdst = e1.type.toBasetype(); | |
3302 | Type tsrc = e.e2.type.toBasetype(); | |
3303 | while (tdst.ty == Tsarray || tdst.ty == Tarray) | |
3304 | { | |
3305 | tdst = (cast(TypeArray)tdst).next.toBasetype(); | |
3306 | if (tsrc.equivalent(tdst)) | |
3307 | { | |
3308 | isBlockAssignment = true; | |
3309 | break; | |
3310 | } | |
3311 | } | |
3312 | } | |
3313 | ||
3314 | // --------------------------------------- | |
3315 | // Deal with reference assignment | |
3316 | // --------------------------------------- | |
3317 | // If it is a construction of a ref variable, it is a ref assignment | |
9c7d5e88 | 3318 | if ((e.op == EXP.construct || e.op == EXP.blit) && |
5fee5ec3 IB |
3319 | ((cast(AssignExp)e).memset == MemorySet.referenceInit)) |
3320 | { | |
3321 | assert(!fp); | |
3322 | ||
3323 | Expression newval = interpretRegion(e.e2, istate, CTFEGoal.LValue); | |
3324 | if (exceptionOrCant(newval)) | |
3325 | return; | |
3326 | ||
235d5a96 | 3327 | VarDeclaration v = e1.isVarExp().var.isVarDeclaration(); |
5fee5ec3 IB |
3328 | setValue(v, newval); |
3329 | ||
3330 | // Get the value to return. Note that 'newval' is an Lvalue, | |
3331 | // so if we need an Rvalue, we have to interpret again. | |
3332 | if (goal == CTFEGoal.RValue) | |
3333 | result = interpretRegion(newval, istate); | |
3334 | else | |
3335 | result = e1; // VarExp is a CTFE reference | |
3336 | return; | |
3337 | } | |
3338 | ||
3339 | if (fp) | |
3340 | { | |
9c7d5e88 | 3341 | while (e1.op == EXP.cast_) |
5fee5ec3 | 3342 | { |
235d5a96 | 3343 | CastExp ce = e1.isCastExp(); |
5fee5ec3 IB |
3344 | e1 = ce.e1; |
3345 | } | |
3346 | } | |
3347 | ||
3348 | // --------------------------------------- | |
3349 | // Interpret left hand side | |
3350 | // --------------------------------------- | |
3351 | AssocArrayLiteralExp existingAA = null; | |
3352 | Expression lastIndex = null; | |
3353 | Expression oldval = null; | |
235d5a96 | 3354 | if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) |
5fee5ec3 IB |
3355 | { |
3356 | // --------------------------------------- | |
3357 | // Deal with AA index assignment | |
3358 | // --------------------------------------- | |
3359 | /* This needs special treatment if the AA doesn't exist yet. | |
3360 | * There are two special cases: | |
3361 | * (1) If the AA is itself an index of another AA, we may need to create | |
3362 | * multiple nested AA literals before we can insert the new value. | |
3363 | * (2) If the ultimate AA is null, no insertion happens at all. Instead, | |
3364 | * we create nested AA literals, and change it into a assignment. | |
3365 | */ | |
235d5a96 | 3366 | IndexExp ie = e1.isIndexExp(); |
5fee5ec3 | 3367 | int depth = 0; // how many nested AA indices are there? |
235d5a96 | 3368 | while (ie.e1.op == EXP.index && ie.e1.isIndexExp().e1.type.toBasetype().ty == Taarray) |
5fee5ec3 IB |
3369 | { |
3370 | assert(ie.modifiable); | |
235d5a96 | 3371 | ie = ie.e1.isIndexExp(); |
5fee5ec3 IB |
3372 | ++depth; |
3373 | } | |
3374 | ||
3375 | // Get the AA value to be modified. | |
3376 | Expression aggregate = interpretRegion(ie.e1, istate); | |
3377 | if (exceptionOrCant(aggregate)) | |
3378 | return; | |
3379 | if ((existingAA = aggregate.isAssocArrayLiteralExp()) !is null) | |
3380 | { | |
3381 | // Normal case, ultimate parent AA already exists | |
3382 | // We need to walk from the deepest index up, checking that an AA literal | |
3383 | // already exists on each level. | |
235d5a96 | 3384 | lastIndex = interpretRegion(e1.isIndexExp().e2, istate); |
5fee5ec3 IB |
3385 | lastIndex = resolveSlice(lastIndex); // only happens with AA assignment |
3386 | if (exceptionOrCant(lastIndex)) | |
3387 | return; | |
3388 | ||
3389 | while (depth > 0) | |
3390 | { | |
3391 | // Walk the syntax tree to find the indexExp at this depth | |
235d5a96 | 3392 | IndexExp xe = e1.isIndexExp(); |
5fee5ec3 | 3393 | foreach (d; 0 .. depth) |
235d5a96 | 3394 | xe = xe.e1.isIndexExp(); |
5fee5ec3 IB |
3395 | |
3396 | Expression ekey = interpretRegion(xe.e2, istate); | |
3397 | if (exceptionOrCant(ekey)) | |
3398 | return; | |
3399 | UnionExp ekeyTmp = void; | |
3400 | ekey = resolveSlice(ekey, &ekeyTmp); // only happens with AA assignment | |
3401 | ||
3402 | // Look up this index in it up in the existing AA, to get the next level of AA. | |
3403 | AssocArrayLiteralExp newAA = cast(AssocArrayLiteralExp)findKeyInAA(e.loc, existingAA, ekey); | |
3404 | if (exceptionOrCant(newAA)) | |
3405 | return; | |
3406 | if (!newAA) | |
3407 | { | |
3408 | // Doesn't exist yet, create an empty AA... | |
3409 | auto keysx = new Expressions(); | |
3410 | auto valuesx = new Expressions(); | |
3411 | newAA = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); | |
3412 | newAA.type = xe.type; | |
3413 | newAA.ownedByCtfe = OwnedBy.ctfe; | |
3414 | //... and insert it into the existing AA. | |
3415 | existingAA.keys.push(ekey); | |
3416 | existingAA.values.push(newAA); | |
3417 | } | |
3418 | existingAA = newAA; | |
3419 | --depth; | |
3420 | } | |
3421 | ||
3422 | if (fp) | |
3423 | { | |
3424 | oldval = findKeyInAA(e.loc, existingAA, lastIndex); | |
3425 | if (!oldval) | |
3426 | oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy(); | |
3427 | } | |
3428 | } | |
3429 | else | |
3430 | { | |
3431 | /* The AA is currently null. 'aggregate' is actually a reference to | |
3432 | * whatever contains it. It could be anything: var, dotvarexp, ... | |
3433 | * We rewrite the assignment from: | |
3434 | * aa[i][j] op= newval; | |
3435 | * into: | |
3436 | * aa = [i:[j:T.init]]; | |
3437 | * aa[j] op= newval; | |
3438 | */ | |
3439 | oldval = copyLiteral(e.e1.type.defaultInitLiteral(e.loc)).copy(); | |
3440 | ||
3441 | Expression newaae = oldval; | |
235d5a96 | 3442 | while (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) |
5fee5ec3 | 3443 | { |
235d5a96 | 3444 | Expression ekey = interpretRegion(e1.isIndexExp().e2, istate); |
5fee5ec3 IB |
3445 | if (exceptionOrCant(ekey)) |
3446 | return; | |
3447 | ekey = resolveSlice(ekey); // only happens with AA assignment | |
3448 | ||
3449 | auto keysx = new Expressions(); | |
3450 | auto valuesx = new Expressions(); | |
3451 | keysx.push(ekey); | |
3452 | valuesx.push(newaae); | |
3453 | ||
3454 | auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); | |
235d5a96 | 3455 | aae.type = e1.isIndexExp().e1.type; |
5fee5ec3 IB |
3456 | aae.ownedByCtfe = OwnedBy.ctfe; |
3457 | if (!existingAA) | |
3458 | { | |
3459 | existingAA = aae; | |
3460 | lastIndex = ekey; | |
3461 | } | |
3462 | newaae = aae; | |
235d5a96 | 3463 | e1 = e1.isIndexExp().e1; |
5fee5ec3 IB |
3464 | } |
3465 | ||
3466 | // We must set to aggregate with newaae | |
3467 | e1 = interpretRegion(e1, istate, CTFEGoal.LValue); | |
3468 | if (exceptionOrCant(e1)) | |
3469 | return; | |
3470 | e1 = assignToLvalue(e, e1, newaae); | |
3471 | if (exceptionOrCant(e1)) | |
3472 | return; | |
3473 | } | |
3474 | assert(existingAA && lastIndex); | |
3475 | e1 = null; // stomp | |
3476 | } | |
9c7d5e88 | 3477 | else if (e1.op == EXP.arrayLength) |
5fee5ec3 IB |
3478 | { |
3479 | oldval = interpretRegion(e1, istate); | |
3480 | if (exceptionOrCant(oldval)) | |
3481 | return; | |
3482 | } | |
9c7d5e88 | 3483 | else if (e.op == EXP.construct || e.op == EXP.blit) |
5fee5ec3 IB |
3484 | { |
3485 | // Unless we have a simple var assignment, we're | |
3486 | // only modifying part of the variable. So we need to make sure | |
3487 | // that the parent variable exists. | |
3488 | VarDeclaration ultimateVar = findParentVar(e1); | |
3489 | if (auto ve = e1.isVarExp()) | |
3490 | { | |
3491 | VarDeclaration v = ve.var.isVarDeclaration(); | |
3492 | assert(v); | |
3493 | if (v.storage_class & STC.out_) | |
3494 | goto L1; | |
3495 | } | |
3496 | else if (ultimateVar && !getValue(ultimateVar)) | |
3497 | { | |
3498 | Expression ex = interpretRegion(ultimateVar.type.defaultInitLiteral(e.loc), istate); | |
3499 | if (exceptionOrCant(ex)) | |
3500 | return; | |
3501 | setValue(ultimateVar, ex); | |
3502 | } | |
3503 | else | |
3504 | goto L1; | |
3505 | } | |
3506 | else | |
3507 | { | |
3508 | L1: | |
3509 | e1 = interpretRegion(e1, istate, CTFEGoal.LValue); | |
3510 | if (exceptionOrCant(e1)) | |
3511 | return; | |
3512 | ||
235d5a96 | 3513 | if (e1.op == EXP.index && e1.isIndexExp().e1.type.toBasetype().ty == Taarray) |
5fee5ec3 | 3514 | { |
235d5a96 | 3515 | IndexExp ie = e1.isIndexExp(); |
9c7d5e88 | 3516 | assert(ie.e1.op == EXP.assocArrayLiteral); |
235d5a96 | 3517 | existingAA = ie.e1.isAssocArrayLiteralExp(); |
5fee5ec3 IB |
3518 | lastIndex = ie.e2; |
3519 | } | |
3520 | } | |
3521 | ||
3522 | // --------------------------------------- | |
3523 | // Interpret right hand side | |
3524 | // --------------------------------------- | |
3525 | Expression newval = interpretRegion(e.e2, istate); | |
3526 | if (exceptionOrCant(newval)) | |
3527 | return; | |
9c7d5e88 | 3528 | if (e.op == EXP.blit && newval.op == EXP.int64) |
5fee5ec3 IB |
3529 | { |
3530 | Type tbn = e.type.baseElemOf(); | |
3531 | if (tbn.ty == Tstruct) | |
3532 | { | |
3533 | /* Look for special case of struct being initialized with 0. | |
3534 | */ | |
3535 | newval = e.type.defaultInitLiteral(e.loc); | |
9c7d5e88 | 3536 | if (newval.op == EXP.error) |
5fee5ec3 IB |
3537 | { |
3538 | result = CTFEExp.cantexp; | |
3539 | return; | |
3540 | } | |
3541 | newval = interpretRegion(newval, istate); // copy and set ownedByCtfe flag | |
3542 | if (exceptionOrCant(newval)) | |
3543 | return; | |
3544 | } | |
3545 | } | |
3546 | ||
3547 | // ---------------------------------------------------- | |
3548 | // Deal with read-modify-write assignments. | |
3549 | // Set 'newval' to the final assignment value | |
3550 | // Also determine the return value (except for slice | |
3551 | // assignments, which are more complicated) | |
3552 | // ---------------------------------------------------- | |
3553 | if (fp) | |
3554 | { | |
3555 | if (!oldval) | |
3556 | { | |
3557 | // Load the left hand side after interpreting the right hand side. | |
3558 | oldval = interpretRegion(e1, istate); | |
3559 | if (exceptionOrCant(oldval)) | |
3560 | return; | |
3561 | } | |
3562 | ||
3563 | if (e.e1.type.ty != Tpointer) | |
3564 | { | |
3565 | // ~= can create new values (see bug 6052) | |
9c7d5e88 | 3566 | if (e.op == EXP.concatenateAssign || e.op == EXP.concatenateElemAssign || e.op == EXP.concatenateDcharAssign) |
5fee5ec3 IB |
3567 | { |
3568 | // We need to dup it and repaint the type. For a dynamic array | |
3569 | // we can skip duplication, because it gets copied later anyway. | |
3570 | if (newval.type.ty != Tarray) | |
3571 | { | |
3572 | newval = copyLiteral(newval).copy(); | |
3573 | newval.type = e.e2.type; // repaint type | |
3574 | } | |
3575 | else | |
3576 | { | |
3577 | newval = paintTypeOntoLiteral(e.e2.type, newval); | |
3578 | newval = resolveSlice(newval); | |
3579 | } | |
3580 | } | |
3581 | oldval = resolveSlice(oldval); | |
3582 | ||
3583 | newval = (*fp)(e.loc, e.type, oldval, newval).copy(); | |
3584 | } | |
3585 | else if (e.e2.type.isintegral() && | |
9c7d5e88 IB |
3586 | (e.op == EXP.addAssign || |
3587 | e.op == EXP.minAssign || | |
3588 | e.op == EXP.plusPlus || | |
3589 | e.op == EXP.minusMinus)) | |
5fee5ec3 | 3590 | { |
fbdaa581 IB |
3591 | newval = pointerArithmetic(pue, e.loc, e.op, e.type, oldval, newval).copy(); |
3592 | if (newval == pue.exp()) | |
3593 | newval = pue.copy(); | |
5fee5ec3 IB |
3594 | } |
3595 | else | |
3596 | { | |
3597 | e.error("pointer expression `%s` cannot be interpreted at compile time", e.toChars()); | |
3598 | result = CTFEExp.cantexp; | |
3599 | return; | |
3600 | } | |
3601 | if (exceptionOrCant(newval)) | |
3602 | { | |
3603 | if (CTFEExp.isCantExp(newval)) | |
3604 | e.error("cannot interpret `%s` at compile time", e.toChars()); | |
3605 | return; | |
3606 | } | |
3607 | } | |
3608 | ||
3609 | if (existingAA) | |
3610 | { | |
3611 | if (existingAA.ownedByCtfe != OwnedBy.ctfe) | |
3612 | { | |
3613 | e.error("cannot modify read-only constant `%s`", existingAA.toChars()); | |
3614 | result = CTFEExp.cantexp; | |
3615 | return; | |
3616 | } | |
3617 | ||
3618 | //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n", | |
3619 | // __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars()); | |
3620 | assignAssocArrayElement(e.loc, existingAA, lastIndex, newval); | |
3621 | ||
3622 | // Determine the return value | |
3623 | result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval); | |
3624 | return; | |
3625 | } | |
9c7d5e88 | 3626 | if (e1.op == EXP.arrayLength) |
5fee5ec3 IB |
3627 | { |
3628 | /* Change the assignment from: | |
3629 | * arr.length = n; | |
3630 | * into: | |
3631 | * arr = new_length_array; (result is n) | |
3632 | */ | |
3633 | ||
3634 | // Determine the return value | |
3635 | result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval); | |
3636 | if (exceptionOrCant(result)) | |
3637 | return; | |
3638 | ||
3639 | if (result == pue.exp()) | |
3640 | result = pue.copy(); | |
3641 | ||
3642 | size_t oldlen = cast(size_t)oldval.toInteger(); | |
3643 | size_t newlen = cast(size_t)newval.toInteger(); | |
3644 | if (oldlen == newlen) // no change required -- we're done! | |
3645 | return; | |
3646 | ||
3647 | // We have changed it into a reference assignment | |
3648 | // Note that returnValue is still the new length. | |
235d5a96 | 3649 | e1 = e1.isArrayLengthExp().e1; |
5fee5ec3 IB |
3650 | Type t = e1.type.toBasetype(); |
3651 | if (t.ty != Tarray) | |
3652 | { | |
3653 | e.error("`%s` is not yet supported at compile time", e.toChars()); | |
3654 | result = CTFEExp.cantexp; | |
3655 | return; | |
3656 | } | |
3657 | e1 = interpretRegion(e1, istate, CTFEGoal.LValue); | |
3658 | if (exceptionOrCant(e1)) | |
3659 | return; | |
3660 | ||
3661 | if (oldlen != 0) // Get the old array literal. | |
3662 | oldval = interpretRegion(e1, istate); | |
3663 | UnionExp utmp = void; | |
3664 | oldval = resolveSlice(oldval, &utmp); | |
3665 | ||
fbdaa581 IB |
3666 | newval = changeArrayLiteralLength(pue, e.loc, cast(TypeArray)t, oldval, oldlen, newlen); |
3667 | if (newval == pue.exp()) | |
3668 | newval = pue.copy(); | |
5fee5ec3 IB |
3669 | |
3670 | e1 = assignToLvalue(e, e1, newval); | |
3671 | if (exceptionOrCant(e1)) | |
3672 | return; | |
3673 | ||
3674 | return; | |
3675 | } | |
3676 | ||
3677 | if (!isBlockAssignment) | |
3678 | { | |
3679 | newval = ctfeCast(pue, e.loc, e.type, e.type, newval); | |
3680 | if (exceptionOrCant(newval)) | |
3681 | return; | |
3682 | if (newval == pue.exp()) | |
3683 | newval = pue.copy(); | |
3684 | ||
3685 | // Determine the return value | |
3686 | if (goal == CTFEGoal.LValue) // https://issues.dlang.org/show_bug.cgi?id=14371 | |
3687 | result = e1; | |
3688 | else | |
3689 | { | |
3690 | result = ctfeCast(pue, e.loc, e.type, e.type, fp && post ? oldval : newval); | |
3691 | if (result == pue.exp()) | |
3692 | result = pue.copy(); | |
3693 | } | |
3694 | if (exceptionOrCant(result)) | |
3695 | return; | |
3696 | } | |
3697 | if (exceptionOrCant(newval)) | |
3698 | return; | |
3699 | ||
3700 | debug (LOGASSIGN) | |
3701 | { | |
3702 | printf("ASSIGN: %s=%s\n", e1.toChars(), newval.toChars()); | |
3703 | showCtfeExpr(newval); | |
3704 | } | |
3705 | ||
3706 | /* Block assignment or element-wise assignment. | |
3707 | */ | |
9c7d5e88 IB |
3708 | if (e1.op == EXP.slice || |
3709 | e1.op == EXP.vector || | |
3710 | e1.op == EXP.arrayLiteral || | |
3711 | e1.op == EXP.string_ || | |
3712 | e1.op == EXP.null_ && e1.type.toBasetype().ty == Tarray) | |
5fee5ec3 IB |
3713 | { |
3714 | // Note that slice assignments don't support things like ++, so | |
3715 | // we don't need to remember 'returnValue'. | |
3716 | result = interpretAssignToSlice(pue, e, e1, newval, isBlockAssignment); | |
3717 | if (exceptionOrCant(result)) | |
3718 | return; | |
3719 | if (auto se = e.e1.isSliceExp()) | |
3720 | { | |
3721 | Expression e1x = interpretRegion(se.e1, istate, CTFEGoal.LValue); | |
3722 | if (auto dve = e1x.isDotVarExp()) | |
3723 | { | |
3724 | auto ex = dve.e1; | |
235d5a96 IB |
3725 | auto sle = ex.op == EXP.structLiteral ? ex.isStructLiteralExp() |
3726 | : ex.op == EXP.classReference ? ex.isClassReferenceExp().value | |
5fee5ec3 IB |
3727 | : null; |
3728 | auto v = dve.var.isVarDeclaration(); | |
3729 | if (!sle || !v) | |
3730 | { | |
3731 | e.error("CTFE internal error: dotvar slice assignment"); | |
3732 | result = CTFEExp.cantexp; | |
3733 | return; | |
3734 | } | |
3735 | stompOverlappedFields(sle, v); | |
3736 | } | |
3737 | } | |
3738 | return; | |
3739 | } | |
3740 | assert(result); | |
3741 | ||
3742 | /* Assignment to a CTFE reference. | |
3743 | */ | |
3744 | if (Expression ex = assignToLvalue(e, e1, newval)) | |
3745 | result = ex; | |
3746 | ||
3747 | return; | |
3748 | } | |
3749 | ||
3750 | /* Set all sibling fields which overlap with v to VoidExp. | |
3751 | */ | |
3752 | private void stompOverlappedFields(StructLiteralExp sle, VarDeclaration v) | |
3753 | { | |
3754 | if (!v.overlapped) | |
3755 | return; | |
3756 | foreach (size_t i, v2; sle.sd.fields) | |
3757 | { | |
3758 | if (v is v2 || !v.isOverlappedWith(v2)) | |
3759 | continue; | |
3760 | auto e = (*sle.elements)[i]; | |
9c7d5e88 | 3761 | if (e.op != EXP.void_) |
5fee5ec3 IB |
3762 | (*sle.elements)[i] = voidInitLiteral(e.type, v).copy(); |
3763 | } | |
3764 | } | |
3765 | ||
3766 | private Expression assignToLvalue(BinExp e, Expression e1, Expression newval) | |
3767 | { | |
3768 | //printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); | |
3769 | VarDeclaration vd = null; | |
3770 | Expression* payload = null; // dead-store to prevent spurious warning | |
3771 | Expression oldval; | |
3772 | ||
3773 | if (auto ve = e1.isVarExp()) | |
3774 | { | |
3775 | vd = ve.var.isVarDeclaration(); | |
3776 | oldval = getValue(vd); | |
3777 | } | |
3778 | else if (auto dve = e1.isDotVarExp()) | |
3779 | { | |
3780 | /* Assignment to member variable of the form: | |
3781 | * e.v = newval | |
3782 | */ | |
3783 | auto ex = dve.e1; | |
235d5a96 IB |
3784 | auto sle = ex.op == EXP.structLiteral ? ex.isStructLiteralExp() |
3785 | : ex.op == EXP.classReference ? ex.isClassReferenceExp().value | |
5fee5ec3 | 3786 | : null; |
235d5a96 | 3787 | auto v = e1.isDotVarExp().var.isVarDeclaration(); |
5fee5ec3 IB |
3788 | if (!sle || !v) |
3789 | { | |
3790 | e.error("CTFE internal error: dotvar assignment"); | |
3791 | return CTFEExp.cantexp; | |
3792 | } | |
3793 | if (sle.ownedByCtfe != OwnedBy.ctfe) | |
3794 | { | |
3795 | e.error("cannot modify read-only constant `%s`", sle.toChars()); | |
3796 | return CTFEExp.cantexp; | |
3797 | } | |
3798 | ||
9c7d5e88 | 3799 | int fieldi = ex.op == EXP.structLiteral ? findFieldIndexByName(sle.sd, v) |
235d5a96 | 3800 | : ex.isClassReferenceExp().findFieldIndexByName(v); |
5fee5ec3 IB |
3801 | if (fieldi == -1) |
3802 | { | |
3803 | e.error("CTFE internal error: cannot find field `%s` in `%s`", v.toChars(), ex.toChars()); | |
3804 | return CTFEExp.cantexp; | |
3805 | } | |
6d799f0a | 3806 | assert(0 <= fieldi && fieldi < sle.elements.length); |
5fee5ec3 IB |
3807 | |
3808 | // If it's a union, set all other members of this union to void | |
3809 | stompOverlappedFields(sle, v); | |
3810 | ||
3811 | payload = &(*sle.elements)[fieldi]; | |
3812 | oldval = *payload; | |
6384eff5 IB |
3813 | if (auto ival = newval.isIntegerExp()) |
3814 | { | |
3815 | if (auto bf = v.isBitFieldDeclaration()) | |
3816 | { | |
3817 | sinteger_t value = ival.toInteger(); | |
3818 | if (bf.type.isunsigned()) | |
3819 | value &= (1L << bf.fieldWidth) - 1; // zero extra bits | |
3820 | else | |
3821 | { // sign extend extra bits | |
3822 | value = value << (64 - bf.fieldWidth); | |
3823 | value = value >> (64 - bf.fieldWidth); | |
3824 | } | |
3825 | ival.setInteger(value); | |
3826 | } | |
3827 | } | |
5fee5ec3 IB |
3828 | } |
3829 | else if (auto ie = e1.isIndexExp()) | |
3830 | { | |
3831 | assert(ie.e1.type.toBasetype().ty != Taarray); | |
3832 | ||
3833 | Expression aggregate; | |
3834 | uinteger_t indexToModify; | |
3835 | if (!resolveIndexing(ie, istate, &aggregate, &indexToModify, true)) | |
3836 | { | |
3837 | return CTFEExp.cantexp; | |
3838 | } | |
3839 | size_t index = cast(size_t)indexToModify; | |
3840 | ||
3841 | if (auto existingSE = aggregate.isStringExp()) | |
3842 | { | |
3843 | if (existingSE.ownedByCtfe != OwnedBy.ctfe) | |
3844 | { | |
3845 | e.error("cannot modify read-only string literal `%s`", ie.e1.toChars()); | |
3846 | return CTFEExp.cantexp; | |
3847 | } | |
3848 | existingSE.setCodeUnit(index, cast(dchar)newval.toInteger()); | |
3849 | return null; | |
3850 | } | |
9c7d5e88 | 3851 | if (aggregate.op != EXP.arrayLiteral) |
5fee5ec3 IB |
3852 | { |
3853 | e.error("index assignment `%s` is not yet supported in CTFE ", e.toChars()); | |
3854 | return CTFEExp.cantexp; | |
3855 | } | |
3856 | ||
235d5a96 | 3857 | ArrayLiteralExp existingAE = aggregate.isArrayLiteralExp(); |
5fee5ec3 IB |
3858 | if (existingAE.ownedByCtfe != OwnedBy.ctfe) |
3859 | { | |
3860 | e.error("cannot modify read-only constant `%s`", existingAE.toChars()); | |
3861 | return CTFEExp.cantexp; | |
3862 | } | |
3863 | ||
3864 | payload = &(*existingAE.elements)[index]; | |
3865 | oldval = *payload; | |
3866 | } | |
3867 | else | |
3868 | { | |
3869 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
3870 | return CTFEExp.cantexp; | |
3871 | } | |
3872 | ||
3873 | Type t1b = e1.type.toBasetype(); | |
3874 | bool wantCopy = t1b.baseElemOf().ty == Tstruct; | |
3875 | ||
3876 | if (auto ve = newval.isVectorExp()) | |
3877 | { | |
3878 | // Ensure ve is an array literal, and not a broadcast | |
9c7d5e88 | 3879 | if (ve.e1.op == EXP.int64 || ve.e1.op == EXP.float64) // if broadcast |
5fee5ec3 IB |
3880 | { |
3881 | UnionExp ue = void; | |
3882 | Expression ex = interpretVectorToArray(&ue, ve); | |
3883 | ve.e1 = (ex == ue.exp()) ? ue.copy() : ex; | |
3884 | } | |
3885 | } | |
3886 | ||
9c7d5e88 | 3887 | if (newval.op == EXP.structLiteral && oldval) |
5fee5ec3 | 3888 | { |
9c7d5e88 | 3889 | assert(oldval.op == EXP.structLiteral || oldval.op == EXP.arrayLiteral || oldval.op == EXP.string_); |
5fee5ec3 IB |
3890 | newval = copyLiteral(newval).copy(); |
3891 | assignInPlace(oldval, newval); | |
3892 | } | |
e9251fea | 3893 | else if (wantCopy && (e.op == EXP.assign || e.op == EXP.loweredAssignExp)) |
5fee5ec3 IB |
3894 | { |
3895 | // Currently postblit/destructor calls on static array are done | |
3896 | // in the druntime internal functions so they don't appear in AST. | |
3897 | // Therefore interpreter should handle them specially. | |
3898 | ||
3899 | assert(oldval); | |
3900 | version (all) // todo: instead we can directly access to each elements of the slice | |
3901 | { | |
3902 | newval = resolveSlice(newval); | |
3903 | if (CTFEExp.isCantExp(newval)) | |
3904 | { | |
3905 | e.error("CTFE internal error: assignment `%s`", e.toChars()); | |
3906 | return CTFEExp.cantexp; | |
3907 | } | |
3908 | } | |
9c7d5e88 IB |
3909 | assert(oldval.op == EXP.arrayLiteral); |
3910 | assert(newval.op == EXP.arrayLiteral); | |
5fee5ec3 | 3911 | |
235d5a96 IB |
3912 | Expressions* oldelems = oldval.isArrayLiteralExp().elements; |
3913 | Expressions* newelems = newval.isArrayLiteralExp().elements; | |
6d799f0a | 3914 | assert(oldelems.length == newelems.length); |
5fee5ec3 IB |
3915 | |
3916 | Type elemtype = oldval.type.nextOf(); | |
3917 | foreach (i, ref oldelem; *oldelems) | |
3918 | { | |
3919 | Expression newelem = paintTypeOntoLiteral(elemtype, (*newelems)[i]); | |
3920 | // https://issues.dlang.org/show_bug.cgi?id=9245 | |
3921 | if (e.e2.isLvalue()) | |
3922 | { | |
3923 | if (Expression ex = evaluatePostblit(istate, newelem)) | |
3924 | return ex; | |
3925 | } | |
3926 | // https://issues.dlang.org/show_bug.cgi?id=13661 | |
3927 | if (Expression ex = evaluateDtor(istate, oldelem)) | |
3928 | return ex; | |
3929 | oldelem = newelem; | |
3930 | } | |
3931 | } | |
3932 | else | |
3933 | { | |
3934 | // e1 has its own payload, so we have to create a new literal. | |
3935 | if (wantCopy) | |
3936 | newval = copyLiteral(newval).copy(); | |
3937 | ||
9c7d5e88 | 3938 | if (t1b.ty == Tsarray && e.op == EXP.construct && e.e2.isLvalue()) |
5fee5ec3 IB |
3939 | { |
3940 | // https://issues.dlang.org/show_bug.cgi?id=9245 | |
3941 | if (Expression ex = evaluatePostblit(istate, newval)) | |
3942 | return ex; | |
3943 | } | |
3944 | ||
3945 | oldval = newval; | |
3946 | } | |
3947 | ||
3948 | if (vd) | |
3949 | setValue(vd, oldval); | |
3950 | else | |
3951 | *payload = oldval; | |
3952 | ||
3953 | // Blit assignment should return the newly created value. | |
9c7d5e88 | 3954 | if (e.op == EXP.blit) |
5fee5ec3 IB |
3955 | return oldval; |
3956 | ||
3957 | return null; | |
3958 | } | |
3959 | ||
3960 | /************* | |
3961 | * Deal with assignments of the form: | |
3962 | * dest[] = newval | |
3963 | * dest[low..upp] = newval | |
3964 | * where newval has already been interpreted | |
3965 | * | |
3966 | * This could be a slice assignment or a block assignment, and | |
3967 | * dest could be either an array literal, or a string. | |
3968 | * | |
9c7d5e88 | 3969 | * Returns EXP.cantExpression on failure. If there are no errors, |
5fee5ec3 IB |
3970 | * it returns aggregate[low..upp], except that as an optimisation, |
3971 | * if goal == CTFEGoal.Nothing, it will return NULL | |
3972 | */ | |
3973 | private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment) | |
3974 | { | |
3b007164 IB |
3975 | //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); |
3976 | ||
5fee5ec3 IB |
3977 | dinteger_t lowerbound; |
3978 | dinteger_t upperbound; | |
3979 | dinteger_t firstIndex; | |
3980 | ||
3981 | Expression aggregate; | |
3982 | ||
3983 | if (auto se = e1.isSliceExp()) | |
3984 | { | |
3985 | // ------------------------------ | |
3986 | // aggregate[] = newval | |
3987 | // aggregate[low..upp] = newval | |
3988 | // ------------------------------ | |
fbdaa581 IB |
3989 | aggregate = interpretRegion(se.e1, istate); |
3990 | lowerbound = se.lwr ? se.lwr.toInteger() : 0; | |
3991 | upperbound = se.upr ? se.upr.toInteger() : resolveArrayLength(aggregate); | |
5fee5ec3 | 3992 | |
fbdaa581 | 3993 | // Slice of a slice --> change the bounds |
5fee5ec3 IB |
3994 | if (auto oldse = aggregate.isSliceExp()) |
3995 | { | |
5fee5ec3 IB |
3996 | aggregate = oldse.e1; |
3997 | firstIndex = lowerbound + oldse.lwr.toInteger(); | |
3998 | } | |
fbdaa581 IB |
3999 | else |
4000 | firstIndex = lowerbound; | |
5fee5ec3 IB |
4001 | } |
4002 | else | |
4003 | { | |
4004 | if (auto ale = e1.isArrayLiteralExp()) | |
4005 | { | |
4006 | lowerbound = 0; | |
6d799f0a | 4007 | upperbound = ale.elements.length; |
5fee5ec3 IB |
4008 | } |
4009 | else if (auto se = e1.isStringExp()) | |
4010 | { | |
4011 | lowerbound = 0; | |
4012 | upperbound = se.len; | |
4013 | } | |
9c7d5e88 | 4014 | else if (e1.op == EXP.null_) |
5fee5ec3 IB |
4015 | { |
4016 | lowerbound = 0; | |
4017 | upperbound = 0; | |
4018 | } | |
4019 | else if (VectorExp ve = e1.isVectorExp()) | |
4020 | { | |
4021 | // ve is not handled but a proper error message is returned | |
4022 | // this is to prevent https://issues.dlang.org/show_bug.cgi?id=20042 | |
4023 | lowerbound = 0; | |
4024 | upperbound = ve.dim; | |
4025 | } | |
4026 | else | |
4027 | assert(0); | |
4028 | ||
4029 | aggregate = e1; | |
4030 | firstIndex = lowerbound; | |
4031 | } | |
4032 | if (upperbound == lowerbound) | |
4033 | return newval; | |
4034 | ||
4035 | // For slice assignment, we check that the lengths match. | |
3b007164 | 4036 | if (!isBlockAssignment && e1.type.ty != Tpointer) |
5fee5ec3 IB |
4037 | { |
4038 | const srclen = resolveArrayLength(newval); | |
4039 | if (srclen != (upperbound - lowerbound)) | |
4040 | { | |
4041 | e.error("array length mismatch assigning `[0..%llu]` to `[%llu..%llu]`", | |
4042 | ulong(srclen), ulong(lowerbound), ulong(upperbound)); | |
4043 | return CTFEExp.cantexp; | |
4044 | } | |
4045 | } | |
4046 | ||
4047 | if (auto existingSE = aggregate.isStringExp()) | |
4048 | { | |
4049 | if (existingSE.ownedByCtfe != OwnedBy.ctfe) | |
4050 | { | |
4051 | e.error("cannot modify read-only string literal `%s`", existingSE.toChars()); | |
4052 | return CTFEExp.cantexp; | |
4053 | } | |
4054 | ||
4055 | if (auto se = newval.isSliceExp()) | |
4056 | { | |
4057 | auto aggr2 = se.e1; | |
4058 | const srclower = se.lwr.toInteger(); | |
4059 | const srcupper = se.upr.toInteger(); | |
4060 | ||
4061 | if (aggregate == aggr2 && | |
4062 | lowerbound < srcupper && srclower < upperbound) | |
4063 | { | |
4064 | e.error("overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`", | |
4065 | ulong(lowerbound), ulong(upperbound), ulong(srclower), ulong(srcupper)); | |
4066 | return CTFEExp.cantexp; | |
4067 | } | |
4068 | version (all) // todo: instead we can directly access to each elements of the slice | |
4069 | { | |
4070 | Expression orignewval = newval; | |
4071 | newval = resolveSlice(newval); | |
4072 | if (CTFEExp.isCantExp(newval)) | |
4073 | { | |
4074 | e.error("CTFE internal error: slice `%s`", orignewval.toChars()); | |
4075 | return CTFEExp.cantexp; | |
4076 | } | |
4077 | } | |
9c7d5e88 | 4078 | assert(newval.op != EXP.slice); |
5fee5ec3 IB |
4079 | } |
4080 | if (auto se = newval.isStringExp()) | |
4081 | { | |
4082 | sliceAssignStringFromString(existingSE, se, cast(size_t)firstIndex); | |
4083 | return newval; | |
4084 | } | |
4085 | if (auto ale = newval.isArrayLiteralExp()) | |
4086 | { | |
4087 | /* Mixed slice: it was initialized as a string literal. | |
4088 | * Now a slice of it is being set with an array literal. | |
4089 | */ | |
4090 | sliceAssignStringFromArrayLiteral(existingSE, ale, cast(size_t)firstIndex); | |
4091 | return newval; | |
4092 | } | |
4093 | ||
4094 | // String literal block slice assign | |
4095 | const value = cast(dchar)newval.toInteger(); | |
4096 | foreach (i; 0 .. upperbound - lowerbound) | |
4097 | { | |
4098 | existingSE.setCodeUnit(cast(size_t)(i + firstIndex), value); | |
4099 | } | |
4100 | if (goal == CTFEGoal.Nothing) | |
4101 | return null; // avoid creating an unused literal | |
4102 | auto retslice = ctfeEmplaceExp!SliceExp(e.loc, existingSE, | |
4103 | ctfeEmplaceExp!IntegerExp(e.loc, firstIndex, Type.tsize_t), | |
4104 | ctfeEmplaceExp!IntegerExp(e.loc, firstIndex + upperbound - lowerbound, Type.tsize_t)); | |
4105 | retslice.type = e.type; | |
4106 | return interpret(pue, retslice, istate); | |
4107 | } | |
4108 | if (auto existingAE = aggregate.isArrayLiteralExp()) | |
4109 | { | |
4110 | if (existingAE.ownedByCtfe != OwnedBy.ctfe) | |
4111 | { | |
4112 | e.error("cannot modify read-only constant `%s`", existingAE.toChars()); | |
4113 | return CTFEExp.cantexp; | |
4114 | } | |
4115 | ||
9c7d5e88 | 4116 | if (newval.op == EXP.slice && !isBlockAssignment) |
5fee5ec3 | 4117 | { |
235d5a96 | 4118 | auto se = newval.isSliceExp(); |
5fee5ec3 IB |
4119 | auto aggr2 = se.e1; |
4120 | const srclower = se.lwr.toInteger(); | |
4121 | const srcupper = se.upr.toInteger(); | |
4122 | const wantCopy = (newval.type.toBasetype().nextOf().baseElemOf().ty == Tstruct); | |
4123 | ||
4124 | //printf("oldval = %p %s[%d..%u]\nnewval = %p %s[%llu..%llu] wantCopy = %d\n", | |
4125 | // aggregate, aggregate.toChars(), lowerbound, upperbound, | |
4126 | // aggr2, aggr2.toChars(), srclower, srcupper, wantCopy); | |
4127 | if (wantCopy) | |
4128 | { | |
4129 | // Currently overlapping for struct array is allowed. | |
4130 | // The order of elements processing depends on the overlapping. | |
4131 | // https://issues.dlang.org/show_bug.cgi?id=14024 | |
9c7d5e88 | 4132 | assert(aggr2.op == EXP.arrayLiteral); |
5fee5ec3 | 4133 | Expressions* oldelems = existingAE.elements; |
235d5a96 | 4134 | Expressions* newelems = aggr2.isArrayLiteralExp().elements; |
5fee5ec3 IB |
4135 | |
4136 | Type elemtype = aggregate.type.nextOf(); | |
4137 | bool needsPostblit = e.e2.isLvalue(); | |
4138 | ||
4139 | if (aggregate == aggr2 && srclower < lowerbound && lowerbound < srcupper) | |
4140 | { | |
4141 | // reverse order | |
4142 | for (auto i = upperbound - lowerbound; 0 < i--;) | |
4143 | { | |
4144 | Expression oldelem = (*oldelems)[cast(size_t)(i + firstIndex)]; | |
4145 | Expression newelem = (*newelems)[cast(size_t)(i + srclower)]; | |
4146 | newelem = copyLiteral(newelem).copy(); | |
4147 | newelem.type = elemtype; | |
4148 | if (needsPostblit) | |
4149 | { | |
4150 | if (Expression x = evaluatePostblit(istate, newelem)) | |
4151 | return x; | |
4152 | } | |
4153 | if (Expression x = evaluateDtor(istate, oldelem)) | |
4154 | return x; | |
4155 | (*oldelems)[cast(size_t)(lowerbound + i)] = newelem; | |
4156 | } | |
4157 | } | |
4158 | else | |
4159 | { | |
4160 | // normal order | |
4161 | for (auto i = 0; i < upperbound - lowerbound; i++) | |
4162 | { | |
4163 | Expression oldelem = (*oldelems)[cast(size_t)(i + firstIndex)]; | |
4164 | Expression newelem = (*newelems)[cast(size_t)(i + srclower)]; | |
4165 | newelem = copyLiteral(newelem).copy(); | |
4166 | newelem.type = elemtype; | |
4167 | if (needsPostblit) | |
4168 | { | |
4169 | if (Expression x = evaluatePostblit(istate, newelem)) | |
4170 | return x; | |
4171 | } | |
4172 | if (Expression x = evaluateDtor(istate, oldelem)) | |
4173 | return x; | |
4174 | (*oldelems)[cast(size_t)(lowerbound + i)] = newelem; | |
4175 | } | |
4176 | } | |
4177 | ||
4178 | //assert(0); | |
4179 | return newval; // oldval? | |
4180 | } | |
4181 | if (aggregate == aggr2 && | |
4182 | lowerbound < srcupper && srclower < upperbound) | |
4183 | { | |
4184 | e.error("overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`", | |
4185 | ulong(lowerbound), ulong(upperbound), ulong(srclower), ulong(srcupper)); | |
4186 | return CTFEExp.cantexp; | |
4187 | } | |
4188 | version (all) // todo: instead we can directly access to each elements of the slice | |
4189 | { | |
4190 | Expression orignewval = newval; | |
4191 | newval = resolveSlice(newval); | |
4192 | if (CTFEExp.isCantExp(newval)) | |
4193 | { | |
4194 | e.error("CTFE internal error: slice `%s`", orignewval.toChars()); | |
4195 | return CTFEExp.cantexp; | |
4196 | } | |
4197 | } | |
4198 | // no overlapping | |
4199 | //length? | |
9c7d5e88 | 4200 | assert(newval.op != EXP.slice); |
5fee5ec3 | 4201 | } |
9c7d5e88 | 4202 | if (newval.op == EXP.string_ && !isBlockAssignment) |
5fee5ec3 IB |
4203 | { |
4204 | /* Mixed slice: it was initialized as an array literal of chars/integers. | |
4205 | * Now a slice of it is being set with a string. | |
4206 | */ | |
235d5a96 | 4207 | sliceAssignArrayLiteralFromString(existingAE, newval.isStringExp(), cast(size_t)firstIndex); |
5fee5ec3 IB |
4208 | return newval; |
4209 | } | |
9c7d5e88 | 4210 | if (newval.op == EXP.arrayLiteral && !isBlockAssignment) |
5fee5ec3 IB |
4211 | { |
4212 | Expressions* oldelems = existingAE.elements; | |
235d5a96 | 4213 | Expressions* newelems = newval.isArrayLiteralExp().elements; |
5fee5ec3 | 4214 | Type elemtype = existingAE.type.nextOf(); |
9c7d5e88 | 4215 | bool needsPostblit = e.op != EXP.blit && e.e2.isLvalue(); |
5fee5ec3 IB |
4216 | foreach (j, newelem; *newelems) |
4217 | { | |
4218 | newelem = paintTypeOntoLiteral(elemtype, newelem); | |
4219 | if (needsPostblit) | |
4220 | { | |
4221 | Expression x = evaluatePostblit(istate, newelem); | |
4222 | if (exceptionOrCantInterpret(x)) | |
4223 | return x; | |
4224 | } | |
4225 | (*oldelems)[cast(size_t)(j + firstIndex)] = newelem; | |
4226 | } | |
4227 | return newval; | |
4228 | } | |
4229 | ||
4230 | /* Block assignment, initialization of static arrays | |
4231 | * x[] = newval | |
4232 | * x may be a multidimensional static array. (Note that this | |
4233 | * only happens with array literals, never with strings). | |
4234 | */ | |
4235 | struct RecursiveBlock | |
4236 | { | |
4237 | InterState* istate; | |
4238 | Expression newval; | |
4239 | bool refCopy; | |
4240 | bool needsPostblit; | |
4241 | bool needsDtor; | |
4242 | ||
f99303eb | 4243 | Expression assignTo(ArrayLiteralExp ae) |
5fee5ec3 | 4244 | { |
6d799f0a | 4245 | return assignTo(ae, 0, ae.elements.length); |
5fee5ec3 IB |
4246 | } |
4247 | ||
f99303eb | 4248 | Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) |
5fee5ec3 IB |
4249 | { |
4250 | Expressions* w = ae.elements; | |
3b007164 IB |
4251 | assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); |
4252 | bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); | |
5fee5ec3 IB |
4253 | for (size_t k = lwr; k < upr; k++) |
4254 | { | |
9c7d5e88 | 4255 | if (!directblk && (*w)[k].op == EXP.arrayLiteral) |
5fee5ec3 IB |
4256 | { |
4257 | // Multidimensional array block assign | |
235d5a96 | 4258 | if (Expression ex = assignTo((*w)[k].isArrayLiteralExp())) |
5fee5ec3 IB |
4259 | return ex; |
4260 | } | |
4261 | else if (refCopy) | |
4262 | { | |
4263 | (*w)[k] = newval; | |
4264 | } | |
4265 | else if (!needsPostblit && !needsDtor) | |
4266 | { | |
4267 | assignInPlace((*w)[k], newval); | |
4268 | } | |
4269 | else | |
4270 | { | |
4271 | Expression oldelem = (*w)[k]; | |
4272 | Expression tmpelem = needsDtor ? copyLiteral(oldelem).copy() : null; | |
4273 | assignInPlace(oldelem, newval); | |
4274 | if (needsPostblit) | |
4275 | { | |
4276 | if (Expression ex = evaluatePostblit(istate, oldelem)) | |
4277 | return ex; | |
4278 | } | |
4279 | if (needsDtor) | |
4280 | { | |
4281 | // https://issues.dlang.org/show_bug.cgi?id=14860 | |
4282 | if (Expression ex = evaluateDtor(istate, tmpelem)) | |
4283 | return ex; | |
4284 | } | |
4285 | } | |
4286 | } | |
4287 | return null; | |
4288 | } | |
4289 | } | |
4290 | ||
4291 | Type tn = newval.type.toBasetype(); | |
4292 | bool wantRef = (tn.ty == Tarray || isAssocArray(tn) || tn.ty == Tclass); | |
9c7d5e88 | 4293 | bool cow = newval.op != EXP.structLiteral && newval.op != EXP.arrayLiteral && newval.op != EXP.string_; |
5fee5ec3 IB |
4294 | Type tb = tn.baseElemOf(); |
4295 | StructDeclaration sd = (tb.ty == Tstruct ? (cast(TypeStruct)tb).sym : null); | |
4296 | ||
4297 | RecursiveBlock rb; | |
4298 | rb.istate = istate; | |
4299 | rb.newval = newval; | |
4300 | rb.refCopy = wantRef || cow; | |
9c7d5e88 | 4301 | rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue(); |
e9251fea | 4302 | rb.needsDtor = sd && sd.dtor && (e.op == EXP.assign || e.op == EXP.loweredAssignExp); |
5fee5ec3 IB |
4303 | if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound)) |
4304 | return ex; | |
4305 | ||
4306 | if (goal == CTFEGoal.Nothing) | |
4307 | return null; // avoid creating an unused literal | |
4308 | auto retslice = ctfeEmplaceExp!SliceExp(e.loc, existingAE, | |
4309 | ctfeEmplaceExp!IntegerExp(e.loc, firstIndex, Type.tsize_t), | |
4310 | ctfeEmplaceExp!IntegerExp(e.loc, firstIndex + upperbound - lowerbound, Type.tsize_t)); | |
4311 | retslice.type = e.type; | |
4312 | return interpret(pue, retslice, istate); | |
4313 | } | |
4314 | ||
4315 | e.error("slice operation `%s = %s` cannot be evaluated at compile time", e1.toChars(), newval.toChars()); | |
4316 | return CTFEExp.cantexp; | |
4317 | } | |
4318 | ||
4319 | override void visit(AssignExp e) | |
4320 | { | |
4321 | interpretAssignCommon(e, null); | |
4322 | } | |
4323 | ||
4324 | override void visit(BinAssignExp e) | |
4325 | { | |
4326 | switch (e.op) | |
4327 | { | |
9c7d5e88 | 4328 | case EXP.addAssign: |
5fee5ec3 IB |
4329 | interpretAssignCommon(e, &Add); |
4330 | return; | |
4331 | ||
9c7d5e88 | 4332 | case EXP.minAssign: |
5fee5ec3 IB |
4333 | interpretAssignCommon(e, &Min); |
4334 | return; | |
4335 | ||
9c7d5e88 IB |
4336 | case EXP.concatenateAssign: |
4337 | case EXP.concatenateElemAssign: | |
4338 | case EXP.concatenateDcharAssign: | |
5fee5ec3 IB |
4339 | interpretAssignCommon(e, &ctfeCat); |
4340 | return; | |
4341 | ||
9c7d5e88 | 4342 | case EXP.mulAssign: |
5fee5ec3 IB |
4343 | interpretAssignCommon(e, &Mul); |
4344 | return; | |
4345 | ||
9c7d5e88 | 4346 | case EXP.divAssign: |
5fee5ec3 IB |
4347 | interpretAssignCommon(e, &Div); |
4348 | return; | |
4349 | ||
9c7d5e88 | 4350 | case EXP.modAssign: |
5fee5ec3 IB |
4351 | interpretAssignCommon(e, &Mod); |
4352 | return; | |
4353 | ||
9c7d5e88 | 4354 | case EXP.leftShiftAssign: |
5fee5ec3 IB |
4355 | interpretAssignCommon(e, &Shl); |
4356 | return; | |
4357 | ||
9c7d5e88 | 4358 | case EXP.rightShiftAssign: |
5fee5ec3 IB |
4359 | interpretAssignCommon(e, &Shr); |
4360 | return; | |
4361 | ||
9c7d5e88 | 4362 | case EXP.unsignedRightShiftAssign: |
5fee5ec3 IB |
4363 | interpretAssignCommon(e, &Ushr); |
4364 | return; | |
4365 | ||
9c7d5e88 | 4366 | case EXP.andAssign: |
5fee5ec3 IB |
4367 | interpretAssignCommon(e, &And); |
4368 | return; | |
4369 | ||
9c7d5e88 | 4370 | case EXP.orAssign: |
5fee5ec3 IB |
4371 | interpretAssignCommon(e, &Or); |
4372 | return; | |
4373 | ||
9c7d5e88 | 4374 | case EXP.xorAssign: |
5fee5ec3 IB |
4375 | interpretAssignCommon(e, &Xor); |
4376 | return; | |
4377 | ||
9c7d5e88 | 4378 | case EXP.powAssign: |
5fee5ec3 IB |
4379 | interpretAssignCommon(e, &Pow); |
4380 | return; | |
4381 | ||
4382 | default: | |
4383 | assert(0); | |
4384 | } | |
4385 | } | |
4386 | ||
4387 | override void visit(PostExp e) | |
4388 | { | |
4389 | debug (LOG) | |
4390 | { | |
4391 | printf("%s PostExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
4392 | } | |
9c7d5e88 | 4393 | if (e.op == EXP.plusPlus) |
5fee5ec3 IB |
4394 | interpretAssignCommon(e, &Add, 1); |
4395 | else | |
4396 | interpretAssignCommon(e, &Min, 1); | |
4397 | debug (LOG) | |
4398 | { | |
4399 | if (CTFEExp.isCantExp(result)) | |
4400 | printf("PostExp::interpret() CANT\n"); | |
4401 | } | |
4402 | } | |
4403 | ||
4404 | /* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison; | |
4405 | * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison; | |
4406 | * 0 otherwise | |
4407 | */ | |
4408 | static int isPointerCmpExp(Expression e, Expression* p1, Expression* p2) | |
4409 | { | |
4410 | int ret = 1; | |
9c7d5e88 | 4411 | while (e.op == EXP.not) |
5fee5ec3 IB |
4412 | { |
4413 | ret *= -1; | |
235d5a96 | 4414 | e = e.isNotExp().e1; |
5fee5ec3 IB |
4415 | } |
4416 | switch (e.op) | |
4417 | { | |
9c7d5e88 IB |
4418 | case EXP.lessThan: |
4419 | case EXP.lessOrEqual: | |
5fee5ec3 IB |
4420 | ret *= -1; |
4421 | goto case; /+ fall through +/ | |
9c7d5e88 IB |
4422 | case EXP.greaterThan: |
4423 | case EXP.greaterOrEqual: | |
235d5a96 IB |
4424 | *p1 = e.isBinExp().e1; |
4425 | *p2 = e.isBinExp().e2; | |
5fee5ec3 IB |
4426 | if (!(isPointer((*p1).type) && isPointer((*p2).type))) |
4427 | ret = 0; | |
4428 | break; | |
4429 | ||
4430 | default: | |
4431 | ret = 0; | |
4432 | break; | |
4433 | } | |
4434 | return ret; | |
4435 | } | |
4436 | ||
4437 | /** If this is a four pointer relation, evaluate it, else return NULL. | |
4438 | * | |
4439 | * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2) | |
4440 | * where p1, p2 are expressions yielding pointers to memory block p, | |
4441 | * and q1, q2 are expressions yielding pointers to memory block q. | |
4442 | * This expression is valid even if p and q are independent memory | |
4443 | * blocks and are therefore not normally comparable; the && form returns true | |
4444 | * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns | |
4445 | * true if [p1..p2] lies outside [q1..q2], and false otherwise. | |
4446 | * | |
4447 | * Within the expression, any ordering of p1, p2, q1, q2 is permissible; | |
4448 | * the comparison operators can be any of >, <, <=, >=, provided that | |
4449 | * both directions (p > q and p < q) are checked. Additionally the | |
4450 | * relational sub-expressions can be negated, eg | |
4451 | * (!(q1 < p1) && p2 <= q2) is valid. | |
4452 | */ | |
4453 | private void interpretFourPointerRelation(UnionExp* pue, BinExp e) | |
4454 | { | |
9c7d5e88 | 4455 | assert(e.op == EXP.andAnd || e.op == EXP.orOr); |
5fee5ec3 IB |
4456 | |
4457 | /* It can only be an isInside expression, if both e1 and e2 are | |
4458 | * directional pointer comparisons. | |
4459 | * Note that this check can be made statically; it does not depends on | |
4460 | * any runtime values. This allows a JIT implementation to compile a | |
4461 | * special AndAndPossiblyInside, keeping the normal AndAnd case efficient. | |
4462 | */ | |
4463 | ||
4464 | // Save the pointer expressions and the comparison directions, | |
4465 | // so we can use them later. | |
4466 | Expression p1 = null; | |
4467 | Expression p2 = null; | |
4468 | Expression p3 = null; | |
4469 | Expression p4 = null; | |
4470 | int dir1 = isPointerCmpExp(e.e1, &p1, &p2); | |
4471 | int dir2 = isPointerCmpExp(e.e2, &p3, &p4); | |
4472 | if (dir1 == 0 || dir2 == 0) | |
4473 | { | |
4474 | result = null; | |
4475 | return; | |
4476 | } | |
4477 | ||
4478 | //printf("FourPointerRelation %s\n", toChars()); | |
4479 | ||
4480 | UnionExp ue1 = void; | |
4481 | UnionExp ue2 = void; | |
4482 | UnionExp ue3 = void; | |
4483 | UnionExp ue4 = void; | |
4484 | ||
4485 | // Evaluate the first two pointers | |
4486 | p1 = interpret(&ue1, p1, istate); | |
4487 | if (exceptionOrCant(p1)) | |
4488 | return; | |
4489 | p2 = interpret(&ue2, p2, istate); | |
4490 | if (exceptionOrCant(p2)) | |
4491 | return; | |
4492 | dinteger_t ofs1, ofs2; | |
4493 | Expression agg1 = getAggregateFromPointer(p1, &ofs1); | |
4494 | Expression agg2 = getAggregateFromPointer(p2, &ofs2); | |
4495 | ||
9c7d5e88 | 4496 | if (!pointToSameMemoryBlock(agg1, agg2) && agg1.op != EXP.null_ && agg2.op != EXP.null_) |
5fee5ec3 IB |
4497 | { |
4498 | // Here it is either CANT_INTERPRET, | |
4499 | // or an IsInside comparison returning false. | |
4500 | p3 = interpret(&ue3, p3, istate); | |
4501 | if (CTFEExp.isCantExp(p3)) | |
4502 | return; | |
4503 | // Note that it is NOT legal for it to throw an exception! | |
4504 | Expression except = null; | |
4505 | if (exceptionOrCantInterpret(p3)) | |
4506 | except = p3; | |
4507 | else | |
4508 | { | |
4509 | p4 = interpret(&ue4, p4, istate); | |
4510 | if (CTFEExp.isCantExp(p4)) | |
4511 | { | |
4512 | result = p4; | |
4513 | return; | |
4514 | } | |
4515 | if (exceptionOrCantInterpret(p4)) | |
4516 | except = p4; | |
4517 | } | |
4518 | if (except) | |
4519 | { | |
4520 | e.error("comparison `%s` of pointers to unrelated memory blocks remains indeterminate at compile time because exception `%s` was thrown while evaluating `%s`", e.e1.toChars(), except.toChars(), e.e2.toChars()); | |
4521 | result = CTFEExp.cantexp; | |
4522 | return; | |
4523 | } | |
4524 | dinteger_t ofs3, ofs4; | |
4525 | Expression agg3 = getAggregateFromPointer(p3, &ofs3); | |
4526 | Expression agg4 = getAggregateFromPointer(p4, &ofs4); | |
4527 | // The valid cases are: | |
4528 | // p1 > p2 && p3 > p4 (same direction, also for < && <) | |
4529 | // p1 > p2 && p3 < p4 (different direction, also < && >) | |
4530 | // Changing any > into >= doesn't affect the result | |
4531 | if ((dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) && pointToSameMemoryBlock(agg2, agg3)) || | |
4532 | (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) && pointToSameMemoryBlock(agg2, agg4))) | |
4533 | { | |
4534 | // it's a legal two-sided comparison | |
9c7d5e88 | 4535 | emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type); |
5fee5ec3 IB |
4536 | result = pue.exp(); |
4537 | return; | |
4538 | } | |
4539 | // It's an invalid four-pointer comparison. Either the second | |
4540 | // comparison is in the same direction as the first, or else | |
4541 | // more than two memory blocks are involved (either two independent | |
4542 | // invalid comparisons are present, or else agg3 == agg4). | |
4543 | e.error("comparison `%s` of pointers to unrelated memory blocks is indeterminate at compile time, even when combined with `%s`.", e.e1.toChars(), e.e2.toChars()); | |
4544 | result = CTFEExp.cantexp; | |
4545 | return; | |
4546 | } | |
4547 | // The first pointer expression didn't need special treatment, so we | |
4548 | // we need to interpret the entire expression exactly as a normal && or ||. | |
4549 | // This is easy because we haven't evaluated e2 at all yet, and we already | |
4550 | // know it will return a bool. | |
4551 | // But we mustn't evaluate the pointer expressions in e1 again, in case | |
4552 | // they have side-effects. | |
4553 | bool nott = false; | |
4554 | Expression ex = e.e1; | |
4555 | while (1) | |
4556 | { | |
4557 | if (auto ne = ex.isNotExp()) | |
4558 | { | |
4559 | nott = !nott; | |
4560 | ex = ne.e1; | |
4561 | } | |
4562 | else | |
4563 | break; | |
4564 | } | |
4565 | ||
4566 | /** Negate relational operator, eg >= becomes < | |
4567 | * Params: | |
4568 | * op = comparison operator to negate | |
4569 | * Returns: | |
4570 | * negate operator | |
4571 | */ | |
9c7d5e88 | 4572 | static EXP negateRelation(EXP op) pure |
5fee5ec3 IB |
4573 | { |
4574 | switch (op) | |
4575 | { | |
9c7d5e88 IB |
4576 | case EXP.greaterOrEqual: op = EXP.lessThan; break; |
4577 | case EXP.greaterThan: op = EXP.lessOrEqual; break; | |
4578 | case EXP.lessOrEqual: op = EXP.greaterThan; break; | |
4579 | case EXP.lessThan: op = EXP.greaterOrEqual; break; | |
5fee5ec3 IB |
4580 | default: assert(0); |
4581 | } | |
4582 | return op; | |
4583 | } | |
4584 | ||
9c7d5e88 | 4585 | const EXP cmpop = nott ? negateRelation(ex.op) : ex.op; |
5fee5ec3 IB |
4586 | const cmp = comparePointers(cmpop, agg1, ofs1, agg2, ofs2); |
4587 | // We already know this is a valid comparison. | |
4588 | assert(cmp >= 0); | |
9c7d5e88 | 4589 | if (e.op == EXP.andAnd && cmp == 1 || e.op == EXP.orOr && cmp == 0) |
5fee5ec3 IB |
4590 | { |
4591 | result = interpret(pue, e.e2, istate); | |
4592 | return; | |
4593 | } | |
9c7d5e88 | 4594 | emplaceExp!(IntegerExp)(pue, e.loc, (e.op == EXP.andAnd) ? 0 : 1, e.type); |
5fee5ec3 IB |
4595 | result = pue.exp(); |
4596 | } | |
4597 | ||
4598 | override void visit(LogicalExp e) | |
4599 | { | |
4600 | debug (LOG) | |
4601 | { | |
4602 | printf("%s LogicalExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
4603 | } | |
4604 | // Check for an insidePointer expression, evaluate it if so | |
4605 | interpretFourPointerRelation(pue, e); | |
4606 | if (result) | |
4607 | return; | |
4608 | ||
4609 | UnionExp ue1 = void; | |
4610 | result = interpret(&ue1, e.e1, istate); | |
4611 | if (exceptionOrCant(result)) | |
4612 | return; | |
4613 | ||
4614 | bool res; | |
9c7d5e88 IB |
4615 | const andand = e.op == EXP.andAnd; |
4616 | if (andand ? result.toBool().hasValue(false) : isTrueBool(result)) | |
5fee5ec3 | 4617 | res = !andand; |
9c7d5e88 | 4618 | else if (andand ? isTrueBool(result) : result.toBool().hasValue(false)) |
5fee5ec3 IB |
4619 | { |
4620 | UnionExp ue2 = void; | |
4621 | result = interpret(&ue2, e.e2, istate); | |
4622 | if (exceptionOrCant(result)) | |
4623 | return; | |
9c7d5e88 | 4624 | if (result.op == EXP.voidExpression) |
5fee5ec3 IB |
4625 | { |
4626 | assert(e.type.ty == Tvoid); | |
4627 | result = null; | |
4628 | return; | |
4629 | } | |
9c7d5e88 | 4630 | if (result.toBool().hasValue(false)) |
5fee5ec3 IB |
4631 | res = false; |
4632 | else if (isTrueBool(result)) | |
4633 | res = true; | |
4634 | else | |
4635 | { | |
4636 | e.error("`%s` does not evaluate to a `bool`", result.toChars()); | |
4637 | result = CTFEExp.cantexp; | |
4638 | return; | |
4639 | } | |
4640 | } | |
4641 | else | |
4642 | { | |
4643 | e.error("`%s` cannot be interpreted as a `bool`", result.toChars()); | |
4644 | result = CTFEExp.cantexp; | |
4645 | return; | |
4646 | } | |
4647 | incUsageCtfe(istate, e.e2.loc); | |
4648 | ||
4649 | if (goal != CTFEGoal.Nothing) | |
4650 | { | |
4651 | if (e.type.equals(Type.tbool)) | |
4652 | result = IntegerExp.createBool(res); | |
4653 | else | |
4654 | { | |
4655 | emplaceExp!(IntegerExp)(pue, e.loc, res, e.type); | |
4656 | result = pue.exp(); | |
4657 | } | |
4658 | } | |
4659 | } | |
4660 | ||
4661 | ||
4662 | // Print a stack trace, starting from callingExp which called fd. | |
4663 | // To shorten the stack trace, try to detect recursion. | |
4664 | private void showCtfeBackTrace(CallExp callingExp, FuncDeclaration fd) | |
4665 | { | |
4666 | if (ctfeGlobals.stackTraceCallsToSuppress > 0) | |
4667 | { | |
4668 | --ctfeGlobals.stackTraceCallsToSuppress; | |
4669 | return; | |
4670 | } | |
4671 | errorSupplemental(callingExp.loc, "called from here: `%s`", callingExp.toChars()); | |
4672 | // Quit if it's not worth trying to compress the stack trace | |
4673 | if (ctfeGlobals.callDepth < 6 || global.params.verbose) | |
4674 | return; | |
4675 | // Recursion happens if the current function already exists in the call stack. | |
4676 | int numToSuppress = 0; | |
4677 | int recurseCount = 0; | |
4678 | int depthSoFar = 0; | |
4679 | InterState* lastRecurse = istate; | |
4680 | for (InterState* cur = istate; cur; cur = cur.caller) | |
4681 | { | |
4682 | if (cur.fd == fd) | |
4683 | { | |
4684 | ++recurseCount; | |
4685 | numToSuppress = depthSoFar; | |
4686 | lastRecurse = cur; | |
4687 | } | |
4688 | ++depthSoFar; | |
4689 | } | |
4690 | // We need at least three calls to the same function, to make compression worthwhile | |
4691 | if (recurseCount < 2) | |
4692 | return; | |
4693 | // We found a useful recursion. Print all the calls involved in the recursion | |
4694 | errorSupplemental(fd.loc, "%d recursive calls to function `%s`", recurseCount, fd.toChars()); | |
4695 | for (InterState* cur = istate; cur.fd != fd; cur = cur.caller) | |
4696 | { | |
4697 | errorSupplemental(cur.fd.loc, "recursively called from function `%s`", cur.fd.toChars()); | |
4698 | } | |
4699 | // We probably didn't enter the recursion in this function. | |
4700 | // Go deeper to find the real beginning. | |
4701 | InterState* cur = istate; | |
4702 | while (lastRecurse.caller && cur.fd == lastRecurse.caller.fd) | |
4703 | { | |
4704 | cur = cur.caller; | |
4705 | lastRecurse = lastRecurse.caller; | |
4706 | ++numToSuppress; | |
4707 | } | |
4708 | ctfeGlobals.stackTraceCallsToSuppress = numToSuppress; | |
4709 | } | |
4710 | ||
4711 | override void visit(CallExp e) | |
4712 | { | |
4713 | debug (LOG) | |
4714 | { | |
4715 | printf("%s CallExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
4716 | } | |
4717 | Expression pthis = null; | |
4718 | FuncDeclaration fd = null; | |
4719 | ||
4720 | Expression ecall = interpretRegion(e.e1, istate); | |
4721 | if (exceptionOrCant(ecall)) | |
4722 | return; | |
4723 | ||
4724 | if (auto dve = ecall.isDotVarExp()) | |
4725 | { | |
4726 | // Calling a member function | |
4727 | pthis = dve.e1; | |
4728 | fd = dve.var.isFuncDeclaration(); | |
4729 | assert(fd); | |
4730 | ||
4731 | if (auto dte = pthis.isDotTypeExp()) | |
4732 | pthis = dte.e1; | |
4733 | } | |
4734 | else if (auto ve = ecall.isVarExp()) | |
4735 | { | |
4736 | fd = ve.var.isFuncDeclaration(); | |
4737 | assert(fd); | |
4738 | ||
4739 | // If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it. | |
4740 | removeHookTraceImpl(e, fd); | |
4741 | ||
4742 | if (fd.ident == Id.__ArrayPostblit || fd.ident == Id.__ArrayDtor) | |
4743 | { | |
6d799f0a | 4744 | assert(e.arguments.length == 1); |
5fee5ec3 IB |
4745 | Expression ea = (*e.arguments)[0]; |
4746 | // printf("1 ea = %s %s\n", ea.type.toChars(), ea.toChars()); | |
4747 | if (auto se = ea.isSliceExp()) | |
4748 | ea = se.e1; | |
4749 | if (auto ce = ea.isCastExp()) | |
4750 | ea = ce.e1; | |
4751 | ||
9c7d5e88 IB |
4752 | // printf("2 ea = %s, %s %s\n", ea.type.toChars(), EXPtoString(ea.op).ptr, ea.toChars()); |
4753 | if (ea.op == EXP.variable || ea.op == EXP.symbolOffset) | |
5fee5ec3 IB |
4754 | result = getVarExp(e.loc, istate, (cast(SymbolExp)ea).var, CTFEGoal.RValue); |
4755 | else if (auto ae = ea.isAddrExp()) | |
4756 | result = interpretRegion(ae.e1, istate); | |
4757 | ||
4758 | // https://issues.dlang.org/show_bug.cgi?id=18871 | |
4759 | // https://issues.dlang.org/show_bug.cgi?id=18819 | |
4760 | else if (auto ale = ea.isArrayLiteralExp()) | |
4761 | result = interpretRegion(ale, istate); | |
4762 | ||
4763 | else | |
4764 | assert(0); | |
4765 | if (CTFEExp.isCantExp(result)) | |
4766 | return; | |
4767 | ||
4768 | if (fd.ident == Id.__ArrayPostblit) | |
4769 | result = evaluatePostblit(istate, result); | |
4770 | else | |
4771 | result = evaluateDtor(istate, result); | |
4772 | if (!result) | |
4773 | result = CTFEExp.voidexp; | |
4774 | return; | |
4775 | } | |
e9251fea | 4776 | else if (isArrayConstruction(fd.ident)) |
9c7d5e88 | 4777 | { |
e9251fea IB |
4778 | // In expressionsem.d, `T[x] ea = eb;` was lowered to: |
4779 | // `_d_array{,set}ctor(ea[], eb[]);`. | |
4780 | // The following code will rewrite it back to `ea = eb` and | |
b7a586be IB |
4781 | // then interpret that expression. |
4782 | ||
4783 | if (fd.ident == Id._d_arrayctor) | |
6d799f0a | 4784 | assert(e.arguments.length == 3); |
b7a586be | 4785 | else |
6d799f0a | 4786 | assert(e.arguments.length == 2); |
9c7d5e88 IB |
4787 | |
4788 | Expression ea = (*e.arguments)[0]; | |
4789 | if (ea.isCastExp) | |
4790 | ea = ea.isCastExp.e1; | |
4791 | ||
4792 | Expression eb = (*e.arguments)[1]; | |
e9251fea | 4793 | if (eb.isCastExp() && fd.ident == Id._d_arrayctor) |
9c7d5e88 IB |
4794 | eb = eb.isCastExp.e1; |
4795 | ||
e9251fea IB |
4796 | ConstructExp ce = new ConstructExp(e.loc, ea, eb); |
4797 | ce.type = ea.type; | |
b7a586be | 4798 | |
e9251fea IB |
4799 | ce.type = ea.type; |
4800 | result = interpret(ce, istate); | |
9c7d5e88 | 4801 | |
9c7d5e88 IB |
4802 | return; |
4803 | } | |
5eb9927a IB |
4804 | else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace) |
4805 | { | |
4806 | // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`. | |
4807 | // The following code will rewrite it back to `ea ~= eb` and then interpret that expression. | |
4808 | Expression lhs, rhs; | |
4809 | ||
4810 | if (fd.ident == Id._d_arrayappendT) | |
4811 | { | |
6d799f0a | 4812 | assert(e.arguments.length == 2); |
5eb9927a IB |
4813 | lhs = (*e.arguments)[0]; |
4814 | rhs = (*e.arguments)[1]; | |
4815 | } | |
4816 | else | |
4817 | { | |
6d799f0a | 4818 | assert(e.arguments.length == 5); |
5eb9927a IB |
4819 | lhs = (*e.arguments)[3]; |
4820 | rhs = (*e.arguments)[4]; | |
4821 | } | |
4822 | ||
4823 | auto cae = new CatAssignExp(e.loc, lhs, rhs); | |
4824 | cae.type = e.type; | |
4825 | ||
4826 | result = interpretRegion(cae, istate, CTFEGoal.LValue); | |
4827 | return; | |
4828 | } | |
4829 | else if (fd.ident == Id._d_arrayappendcTX) | |
4830 | assert(0, "CTFE cannot interpret _d_arrayappendcTX!"); | |
5fee5ec3 IB |
4831 | } |
4832 | else if (auto soe = ecall.isSymOffExp()) | |
4833 | { | |
4834 | fd = soe.var.isFuncDeclaration(); | |
4835 | assert(fd && soe.offset == 0); | |
4836 | } | |
4837 | else if (auto de = ecall.isDelegateExp()) | |
4838 | { | |
4839 | // Calling a delegate | |
4840 | fd = de.func; | |
4841 | pthis = de.e1; | |
4842 | ||
4843 | // Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc) | |
4844 | if (auto ve = pthis.isVarExp()) | |
4845 | if (ve.var == fd) | |
4846 | pthis = null; // context is not necessary for CTFE | |
4847 | } | |
4848 | else if (auto fe = ecall.isFuncExp()) | |
4849 | { | |
4850 | // Calling a delegate literal | |
4851 | fd = fe.fd; | |
4852 | } | |
4853 | else | |
4854 | { | |
4855 | // delegate.funcptr() | |
4856 | // others | |
4857 | e.error("cannot call `%s` at compile time", e.toChars()); | |
4858 | result = CTFEExp.cantexp; | |
4859 | return; | |
4860 | } | |
4861 | if (!fd) | |
4862 | { | |
4863 | e.error("CTFE internal error: cannot evaluate `%s` at compile time", e.toChars()); | |
4864 | result = CTFEExp.cantexp; | |
4865 | return; | |
4866 | } | |
4867 | if (pthis) | |
4868 | { | |
4869 | // Member function call | |
4870 | ||
4871 | // Currently this is satisfied because closure is not yet supported. | |
4872 | assert(!fd.isNested() || fd.needThis()); | |
4873 | ||
9c7d5e88 | 4874 | if (pthis.op == EXP.typeid_) |
5fee5ec3 IB |
4875 | { |
4876 | pthis.error("static variable `%s` cannot be read at compile time", pthis.toChars()); | |
4877 | result = CTFEExp.cantexp; | |
4878 | return; | |
4879 | } | |
4880 | assert(pthis); | |
4881 | ||
9c7d5e88 | 4882 | if (pthis.op == EXP.null_) |
5fee5ec3 IB |
4883 | { |
4884 | assert(pthis.type.toBasetype().ty == Tclass); | |
4885 | e.error("function call through null class reference `%s`", pthis.toChars()); | |
4886 | result = CTFEExp.cantexp; | |
4887 | return; | |
4888 | } | |
4889 | ||
9c7d5e88 | 4890 | assert(pthis.op == EXP.structLiteral || pthis.op == EXP.classReference || pthis.op == EXP.type); |
5fee5ec3 IB |
4891 | |
4892 | if (fd.isVirtual() && !e.directcall) | |
4893 | { | |
4894 | // Make a virtual function call. | |
4895 | // Get the function from the vtable of the original class | |
4896 | ClassDeclaration cd = pthis.isClassReferenceExp().originalClass(); | |
4897 | ||
4898 | // We can't just use the vtable index to look it up, because | |
4899 | // vtables for interfaces don't get populated until the glue layer. | |
4900 | fd = cd.findFunc(fd.ident, fd.type.isTypeFunction()); | |
4901 | assert(fd); | |
4902 | } | |
4903 | } | |
4904 | ||
235d5a96 | 4905 | if (fd && fd.semanticRun >= PASS.semantic3done && fd.hasSemantic3Errors()) |
5fee5ec3 IB |
4906 | { |
4907 | e.error("CTFE failed because of previous errors in `%s`", fd.toChars()); | |
4908 | result = CTFEExp.cantexp; | |
4909 | return; | |
4910 | } | |
4911 | ||
4912 | // Check for built-in functions | |
4913 | result = evaluateIfBuiltin(pue, istate, e.loc, fd, e.arguments, pthis); | |
4914 | if (result) | |
4915 | return; | |
4916 | ||
4917 | if (!fd.fbody) | |
4918 | { | |
4919 | e.error("`%s` cannot be interpreted at compile time, because it has no available source code", fd.toChars()); | |
4920 | result = CTFEExp.showcontext; | |
4921 | return; | |
4922 | } | |
4923 | ||
4924 | result = interpretFunction(pue, fd, istate, e.arguments, pthis); | |
9c7d5e88 | 4925 | if (result.op == EXP.voidExpression) |
5fee5ec3 IB |
4926 | return; |
4927 | if (!exceptionOrCantInterpret(result)) | |
4928 | { | |
4929 | if (goal != CTFEGoal.LValue) // Peel off CTFE reference if it's unnecessary | |
4930 | { | |
4931 | if (result == pue.exp()) | |
4932 | result = pue.copy(); | |
4933 | result = interpret(pue, result, istate); | |
4934 | } | |
4935 | } | |
4936 | if (!exceptionOrCantInterpret(result)) | |
4937 | { | |
4938 | result = paintTypeOntoLiteral(pue, e.type, result); | |
4939 | result.loc = e.loc; | |
4940 | } | |
4941 | else if (CTFEExp.isCantExp(result) && !global.gag) | |
4942 | showCtfeBackTrace(e, fd); // Print a stack trace. | |
4943 | } | |
4944 | ||
4945 | override void visit(CommaExp e) | |
4946 | { | |
4947 | debug (LOG) | |
4948 | { | |
4949 | printf("%s CommaExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
4950 | } | |
4951 | ||
ec486b73 IB |
4952 | bool isNewThrowableHook() |
4953 | { | |
4954 | auto de = e.e1.isDeclarationExp(); | |
4955 | if (de is null) | |
4956 | return false; | |
4957 | ||
4958 | auto vd = de.declaration.isVarDeclaration(); | |
4959 | if (vd is null) | |
4960 | return false; | |
4961 | ||
4962 | auto ei = vd._init.isExpInitializer(); | |
4963 | if (ei is null) | |
4964 | return false; | |
4965 | ||
4966 | auto ce = ei.exp.isConstructExp(); | |
4967 | if (ce is null) | |
4968 | return false; | |
4969 | ||
4970 | return isRuntimeHook(ce.e2, Id._d_newThrowable) !is null; | |
4971 | } | |
4972 | ||
5eb9927a IB |
4973 | if (auto ce = isRuntimeHook(e.e1, Id._d_arrayappendcTX)) |
4974 | { | |
4975 | // In expressionsem.d `arr ~= elem` was lowered to | |
4976 | // `_d_arrayappendcTX(arr, elem), arr[arr.length - 1] = elem, elem;`. | |
4977 | // The following code will rewrite it back to `arr ~= elem` | |
4978 | // and then interpret that expression. | |
6d799f0a | 4979 | assert(ce.arguments.length == 2); |
5eb9927a IB |
4980 | |
4981 | auto arr = (*ce.arguments)[0]; | |
4982 | auto elem = e.e2.isConstructExp().e2; | |
4983 | assert(elem); | |
4984 | ||
4985 | auto cae = new CatAssignExp(e.loc, arr, elem); | |
4986 | cae.type = arr.type; | |
4987 | ||
4988 | result = interpret(cae, istate); | |
4989 | return; | |
4990 | } | |
ec486b73 IB |
4991 | else if (isNewThrowableHook()) |
4992 | { | |
4993 | // In expressionsem.d `throw new Exception(args)` was lowered to | |
4994 | // `throw (tmp = _d_newThrowable!Exception(), tmp.ctor(args), tmp)`. | |
4995 | // The following code will rewrite it back to `throw new Exception(args)` | |
4996 | // and then interpret this expression instead. | |
4997 | auto ce = e.e2.isCallExp(); | |
4998 | assert(ce); | |
4999 | ||
5000 | auto ne = new NewExp(e.loc, null, e.type, ce.arguments); | |
5001 | ne.type = e.e1.type; | |
5002 | ||
5003 | result = interpret(ne, istate); | |
5004 | return; | |
5005 | } | |
5eb9927a | 5006 | |
5fee5ec3 IB |
5007 | // If it creates a variable, and there's no context for |
5008 | // the variable to be created in, we need to create one now. | |
5009 | InterState istateComma; | |
9c7d5e88 | 5010 | if (!istate && firstComma(e.e1).op == EXP.declaration) |
5fee5ec3 IB |
5011 | { |
5012 | ctfeGlobals.stack.startFrame(null); | |
5013 | istate = &istateComma; | |
5014 | } | |
5015 | ||
5016 | void endTempStackFrame() | |
5017 | { | |
5018 | // If we created a temporary stack frame, end it now. | |
5019 | if (istate == &istateComma) | |
5020 | ctfeGlobals.stack.endFrame(); | |
5021 | } | |
5022 | ||
5023 | result = CTFEExp.cantexp; | |
5024 | ||
5025 | // If the comma returns a temporary variable, it needs to be an lvalue | |
5026 | // (this is particularly important for struct constructors) | |
9c7d5e88 IB |
5027 | if (e.e1.op == EXP.declaration && |
5028 | e.e2.op == EXP.variable && | |
5fee5ec3 IB |
5029 | e.e1.isDeclarationExp().declaration == e.e2.isVarExp().var && |
5030 | e.e2.isVarExp().var.storage_class & STC.ctfe) | |
5031 | { | |
5032 | VarExp ve = e.e2.isVarExp(); | |
5033 | VarDeclaration v = ve.var.isVarDeclaration(); | |
5034 | ctfeGlobals.stack.push(v); | |
5035 | if (!v._init && !getValue(v)) | |
5036 | { | |
5037 | setValue(v, copyLiteral(v.type.defaultInitLiteral(e.loc)).copy()); | |
5038 | } | |
5039 | if (!getValue(v)) | |
5040 | { | |
5041 | Expression newval = v._init.initializerToExpression(); | |
5042 | // Bug 4027. Copy constructors are a weird case where the | |
5043 | // initializer is a void function (the variable is modified | |
5044 | // through a reference parameter instead). | |
5045 | newval = interpretRegion(newval, istate); | |
5046 | if (exceptionOrCant(newval)) | |
5047 | return endTempStackFrame(); | |
9c7d5e88 | 5048 | if (newval.op != EXP.voidExpression) |
5fee5ec3 IB |
5049 | { |
5050 | // v isn't necessarily null. | |
5051 | setValueWithoutChecking(v, copyLiteral(newval).copy()); | |
5052 | } | |
5053 | } | |
5054 | } | |
5055 | else | |
5056 | { | |
5057 | UnionExp ue = void; | |
5058 | auto e1 = interpret(&ue, e.e1, istate, CTFEGoal.Nothing); | |
5059 | if (exceptionOrCant(e1)) | |
5060 | return endTempStackFrame(); | |
5061 | } | |
5062 | result = interpret(pue, e.e2, istate, goal); | |
5063 | return endTempStackFrame(); | |
5064 | } | |
5065 | ||
5066 | override void visit(CondExp e) | |
5067 | { | |
5068 | debug (LOG) | |
5069 | { | |
5070 | printf("%s CondExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5071 | } | |
5072 | UnionExp uecond = void; | |
5073 | Expression econd; | |
5074 | econd = interpret(&uecond, e.econd, istate); | |
5075 | if (exceptionOrCant(econd)) | |
5076 | return; | |
5077 | ||
5078 | if (isPointer(e.econd.type)) | |
5079 | { | |
9c7d5e88 | 5080 | if (econd.op != EXP.null_) |
5fee5ec3 IB |
5081 | { |
5082 | econd = IntegerExp.createBool(true); | |
5083 | } | |
5084 | } | |
5085 | ||
5086 | if (isTrueBool(econd)) | |
5087 | { | |
5088 | result = interpret(pue, e.e1, istate, goal); | |
5089 | incUsageCtfe(istate, e.e1.loc); | |
5090 | } | |
9c7d5e88 | 5091 | else if (econd.toBool().hasValue(false)) |
5fee5ec3 IB |
5092 | { |
5093 | result = interpret(pue, e.e2, istate, goal); | |
5094 | incUsageCtfe(istate, e.e2.loc); | |
5095 | } | |
5096 | else | |
5097 | { | |
5098 | e.error("`%s` does not evaluate to boolean result at compile time", e.econd.toChars()); | |
5099 | result = CTFEExp.cantexp; | |
5100 | } | |
5101 | } | |
5102 | ||
5103 | override void visit(ArrayLengthExp e) | |
5104 | { | |
5105 | debug (LOG) | |
5106 | { | |
5107 | printf("%s ArrayLengthExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5108 | } | |
5109 | UnionExp ue1; | |
5110 | Expression e1 = interpret(&ue1, e.e1, istate); | |
5111 | assert(e1); | |
5112 | if (exceptionOrCant(e1)) | |
5113 | return; | |
9c7d5e88 | 5114 | if (e1.op != EXP.string_ && e1.op != EXP.arrayLiteral && e1.op != EXP.slice && e1.op != EXP.null_) |
5fee5ec3 IB |
5115 | { |
5116 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
5117 | result = CTFEExp.cantexp; | |
5118 | return; | |
5119 | } | |
5120 | emplaceExp!(IntegerExp)(pue, e.loc, resolveArrayLength(e1), e.type); | |
5121 | result = pue.exp(); | |
5122 | } | |
5123 | ||
5124 | /** | |
5125 | * Interpret the vector expression as an array literal. | |
5126 | * Params: | |
5127 | * pue = non-null pointer to temporary storage that can be used to store the return value | |
5128 | * e = Expression to interpret | |
5129 | * Returns: | |
5130 | * resulting array literal or 'e' if unable to interpret | |
5131 | */ | |
5132 | static Expression interpretVectorToArray(UnionExp* pue, VectorExp e) | |
5133 | { | |
5134 | if (auto ale = e.e1.isArrayLiteralExp()) | |
5135 | return ale; // it's already an array literal | |
9c7d5e88 | 5136 | if (e.e1.op == EXP.int64 || e.e1.op == EXP.float64) |
5fee5ec3 IB |
5137 | { |
5138 | // Convert literal __vector(int) -> __vector([array]) | |
5139 | auto elements = new Expressions(e.dim); | |
5140 | foreach (ref element; *elements) | |
5141 | element = copyLiteral(e.e1).copy(); | |
5142 | auto type = (e.type.ty == Tvector) ? e.type.isTypeVector().basetype : e.type.isTypeSArray(); | |
5143 | assert(type); | |
5144 | emplaceExp!(ArrayLiteralExp)(pue, e.loc, type, elements); | |
5145 | auto ale = pue.exp().isArrayLiteralExp(); | |
5146 | ale.ownedByCtfe = OwnedBy.ctfe; | |
5147 | return ale; | |
5148 | } | |
5149 | return e; | |
5150 | } | |
5151 | ||
5152 | override void visit(VectorExp e) | |
5153 | { | |
5154 | debug (LOG) | |
5155 | { | |
5156 | printf("%s VectorExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5157 | } | |
5158 | if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements | |
5159 | { | |
5160 | result = e; | |
5161 | return; | |
5162 | } | |
5163 | Expression e1 = interpret(pue, e.e1, istate); | |
5164 | assert(e1); | |
5165 | if (exceptionOrCant(e1)) | |
5166 | return; | |
9c7d5e88 | 5167 | if (e1.op != EXP.arrayLiteral && e1.op != EXP.int64 && e1.op != EXP.float64) |
5fee5ec3 IB |
5168 | { |
5169 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
5170 | result = CTFEExp.cantexp; | |
5171 | return; | |
5172 | } | |
5173 | if (e1 == pue.exp()) | |
5174 | e1 = pue.copy(); | |
5175 | emplaceExp!(VectorExp)(pue, e.loc, e1, e.to); | |
5176 | auto ve = pue.exp().isVectorExp(); | |
5177 | ve.type = e.type; | |
5178 | ve.dim = e.dim; | |
5179 | ve.ownedByCtfe = OwnedBy.ctfe; | |
5180 | result = ve; | |
5181 | } | |
5182 | ||
5183 | override void visit(VectorArrayExp e) | |
5184 | { | |
5185 | debug (LOG) | |
5186 | { | |
5187 | printf("%s VectorArrayExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5188 | } | |
5189 | Expression e1 = interpret(pue, e.e1, istate); | |
5190 | assert(e1); | |
5191 | if (exceptionOrCant(e1)) | |
5192 | return; | |
5193 | if (auto ve = e1.isVectorExp()) | |
5194 | { | |
5195 | result = interpretVectorToArray(pue, ve); | |
9c7d5e88 | 5196 | if (result.op != EXP.vector) |
5fee5ec3 IB |
5197 | return; |
5198 | } | |
5199 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
5200 | result = CTFEExp.cantexp; | |
5201 | } | |
5202 | ||
5203 | override void visit(DelegatePtrExp e) | |
5204 | { | |
5205 | debug (LOG) | |
5206 | { | |
5207 | printf("%s DelegatePtrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5208 | } | |
5209 | Expression e1 = interpret(pue, e.e1, istate); | |
5210 | assert(e1); | |
5211 | if (exceptionOrCant(e1)) | |
5212 | return; | |
5213 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
5214 | result = CTFEExp.cantexp; | |
5215 | } | |
5216 | ||
5217 | override void visit(DelegateFuncptrExp e) | |
5218 | { | |
5219 | debug (LOG) | |
5220 | { | |
5221 | printf("%s DelegateFuncptrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5222 | } | |
5223 | Expression e1 = interpret(pue, e.e1, istate); | |
5224 | assert(e1); | |
5225 | if (exceptionOrCant(e1)) | |
5226 | return; | |
5227 | e.error("`%s` cannot be evaluated at compile time", e.toChars()); | |
5228 | result = CTFEExp.cantexp; | |
5229 | } | |
5230 | ||
5231 | static bool resolveIndexing(IndexExp e, InterState* istate, Expression* pagg, uinteger_t* pidx, bool modify) | |
5232 | { | |
5233 | assert(e.e1.type.toBasetype().ty != Taarray); | |
5234 | ||
5235 | if (e.e1.type.toBasetype().ty == Tpointer) | |
5236 | { | |
5237 | // Indexing a pointer. Note that there is no $ in this case. | |
5238 | Expression e1 = interpretRegion(e.e1, istate); | |
5239 | if (exceptionOrCantInterpret(e1)) | |
5240 | return false; | |
5241 | ||
5242 | Expression e2 = interpretRegion(e.e2, istate); | |
5243 | if (exceptionOrCantInterpret(e2)) | |
5244 | return false; | |
5245 | sinteger_t indx = e2.toInteger(); | |
5246 | ||
5247 | dinteger_t ofs; | |
5248 | Expression agg = getAggregateFromPointer(e1, &ofs); | |
5249 | ||
9c7d5e88 | 5250 | if (agg.op == EXP.null_) |
5fee5ec3 IB |
5251 | { |
5252 | e.error("cannot index through null pointer `%s`", e.e1.toChars()); | |
5253 | return false; | |
5254 | } | |
9c7d5e88 | 5255 | if (agg.op == EXP.int64) |
5fee5ec3 IB |
5256 | { |
5257 | e.error("cannot index through invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars()); | |
5258 | return false; | |
5259 | } | |
5260 | // Pointer to a non-array variable | |
9c7d5e88 | 5261 | if (agg.op == EXP.symbolOffset) |
5fee5ec3 | 5262 | { |
235d5a96 | 5263 | e.error("mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ? "modified" : "read"), agg.isSymOffExp().var.toChars()); |
5fee5ec3 IB |
5264 | return false; |
5265 | } | |
5266 | ||
9c7d5e88 | 5267 | if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_) |
5fee5ec3 IB |
5268 | { |
5269 | dinteger_t len = resolveArrayLength(agg); | |
5270 | if (ofs + indx >= len) | |
5271 | { | |
5272 | e.error("pointer index `[%lld]` exceeds allocated memory block `[0..%lld]`", ofs + indx, len); | |
5273 | return false; | |
5274 | } | |
5275 | } | |
5276 | else | |
5277 | { | |
5278 | if (ofs + indx != 0) | |
5279 | { | |
5280 | e.error("pointer index `[%lld]` lies outside memory block `[0..1]`", ofs + indx); | |
5281 | return false; | |
5282 | } | |
5283 | } | |
5284 | *pagg = agg; | |
5285 | *pidx = ofs + indx; | |
5286 | return true; | |
5287 | } | |
5288 | ||
5289 | Expression e1 = interpretRegion(e.e1, istate); | |
5290 | if (exceptionOrCantInterpret(e1)) | |
5291 | return false; | |
9c7d5e88 | 5292 | if (e1.op == EXP.null_) |
5fee5ec3 IB |
5293 | { |
5294 | e.error("cannot index null array `%s`", e.e1.toChars()); | |
5295 | return false; | |
5296 | } | |
5297 | if (auto ve = e1.isVectorExp()) | |
5298 | { | |
5299 | UnionExp ue = void; | |
5300 | e1 = interpretVectorToArray(&ue, ve); | |
5301 | e1 = (e1 == ue.exp()) ? ue.copy() : e1; | |
5302 | } | |
5303 | ||
5304 | // Set the $ variable, and find the array literal to modify | |
5305 | dinteger_t len; | |
9c7d5e88 | 5306 | if (e1.op == EXP.variable && e1.type.toBasetype().ty == Tsarray) |
5fee5ec3 IB |
5307 | len = e1.type.toBasetype().isTypeSArray().dim.toInteger(); |
5308 | else | |
5309 | { | |
9c7d5e88 | 5310 | if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.slice && e1.op != EXP.vector) |
5fee5ec3 IB |
5311 | { |
5312 | e.error("cannot determine length of `%s` at compile time", e.e1.toChars()); | |
5313 | return false; | |
5314 | } | |
5315 | len = resolveArrayLength(e1); | |
5316 | } | |
5317 | ||
5318 | if (e.lengthVar) | |
5319 | { | |
5320 | Expression dollarExp = ctfeEmplaceExp!IntegerExp(e.loc, len, Type.tsize_t); | |
5321 | ctfeGlobals.stack.push(e.lengthVar); | |
5322 | setValue(e.lengthVar, dollarExp); | |
5323 | } | |
5324 | Expression e2 = interpretRegion(e.e2, istate); | |
5325 | if (e.lengthVar) | |
5326 | ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside [] | |
5327 | if (exceptionOrCantInterpret(e2)) | |
5328 | return false; | |
9c7d5e88 | 5329 | if (e2.op != EXP.int64) |
5fee5ec3 IB |
5330 | { |
5331 | e.error("CTFE internal error: non-integral index `[%s]`", e.e2.toChars()); | |
5332 | return false; | |
5333 | } | |
5334 | ||
5335 | if (auto se = e1.isSliceExp()) | |
5336 | { | |
5337 | // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] | |
5338 | uinteger_t index = e2.toInteger(); | |
5339 | uinteger_t ilwr = se.lwr.toInteger(); | |
5340 | uinteger_t iupr = se.upr.toInteger(); | |
5341 | ||
5342 | if (index > iupr - ilwr) | |
5343 | { | |
5344 | e.error("index %llu exceeds array length %llu", index, iupr - ilwr); | |
5345 | return false; | |
5346 | } | |
235d5a96 | 5347 | *pagg = e1.isSliceExp().e1; |
5fee5ec3 IB |
5348 | *pidx = index + ilwr; |
5349 | } | |
5350 | else | |
5351 | { | |
5352 | *pagg = e1; | |
5353 | *pidx = e2.toInteger(); | |
5354 | if (len <= *pidx) | |
5355 | { | |
5356 | e.error("array index %lld is out of bounds `[0..%lld]`", *pidx, len); | |
5357 | return false; | |
5358 | } | |
5359 | } | |
5360 | return true; | |
5361 | } | |
5362 | ||
5363 | override void visit(IndexExp e) | |
5364 | { | |
5365 | debug (LOG) | |
5366 | { | |
5367 | printf("%s IndexExp::interpret() %s, goal = %d\n", e.loc.toChars(), e.toChars(), goal); | |
5368 | } | |
5369 | if (e.e1.type.toBasetype().ty == Tpointer) | |
5370 | { | |
5371 | Expression agg; | |
5372 | uinteger_t indexToAccess; | |
5373 | if (!resolveIndexing(e, istate, &agg, &indexToAccess, false)) | |
5374 | { | |
5375 | result = CTFEExp.cantexp; | |
5376 | return; | |
5377 | } | |
9c7d5e88 | 5378 | if (agg.op == EXP.arrayLiteral || agg.op == EXP.string_) |
5fee5ec3 IB |
5379 | { |
5380 | if (goal == CTFEGoal.LValue) | |
5381 | { | |
5382 | // if we need a reference, IndexExp shouldn't be interpreting | |
5383 | // the expression to a value, it should stay as a reference | |
5384 | emplaceExp!(IndexExp)(pue, e.loc, agg, ctfeEmplaceExp!IntegerExp(e.e2.loc, indexToAccess, e.e2.type)); | |
5385 | result = pue.exp(); | |
5386 | result.type = e.type; | |
5387 | return; | |
5388 | } | |
5389 | result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess); | |
5390 | return; | |
5391 | } | |
5392 | else | |
5393 | { | |
5394 | assert(indexToAccess == 0); | |
5395 | result = interpretRegion(agg, istate, goal); | |
5396 | if (exceptionOrCant(result)) | |
5397 | return; | |
5398 | result = paintTypeOntoLiteral(pue, e.type, result); | |
5399 | return; | |
5400 | } | |
5401 | } | |
5402 | ||
5403 | if (e.e1.type.toBasetype().ty == Taarray) | |
5404 | { | |
5405 | Expression e1 = interpretRegion(e.e1, istate); | |
5406 | if (exceptionOrCant(e1)) | |
5407 | return; | |
9c7d5e88 | 5408 | if (e1.op == EXP.null_) |
5fee5ec3 IB |
5409 | { |
5410 | if (goal == CTFEGoal.LValue && e1.type.ty == Taarray && e.modifiable) | |
5411 | { | |
5412 | assert(0); // does not reach here? | |
5413 | } | |
5414 | e.error("cannot index null array `%s`", e.e1.toChars()); | |
5415 | result = CTFEExp.cantexp; | |
5416 | return; | |
5417 | } | |
5418 | Expression e2 = interpretRegion(e.e2, istate); | |
5419 | if (exceptionOrCant(e2)) | |
5420 | return; | |
5421 | ||
5422 | if (goal == CTFEGoal.LValue) | |
5423 | { | |
5424 | // Pointer or reference of a scalar type | |
5425 | if (e1 == e.e1 && e2 == e.e2) | |
5426 | result = e; | |
5427 | else | |
5428 | { | |
5429 | emplaceExp!(IndexExp)(pue, e.loc, e1, e2); | |
5430 | result = pue.exp(); | |
5431 | result.type = e.type; | |
5432 | } | |
5433 | return; | |
5434 | } | |
5435 | ||
9c7d5e88 | 5436 | assert(e1.op == EXP.assocArrayLiteral); |
5fee5ec3 IB |
5437 | UnionExp e2tmp = void; |
5438 | e2 = resolveSlice(e2, &e2tmp); | |
235d5a96 | 5439 | result = findKeyInAA(e.loc, e1.isAssocArrayLiteralExp(), e2); |
5fee5ec3 IB |
5440 | if (!result) |
5441 | { | |
5442 | e.error("key `%s` not found in associative array `%s`", e2.toChars(), e.e1.toChars()); | |
5443 | result = CTFEExp.cantexp; | |
5444 | } | |
5445 | return; | |
5446 | } | |
5447 | ||
5448 | Expression agg; | |
5449 | uinteger_t indexToAccess; | |
5450 | if (!resolveIndexing(e, istate, &agg, &indexToAccess, false)) | |
5451 | { | |
5452 | result = CTFEExp.cantexp; | |
5453 | return; | |
5454 | } | |
5455 | ||
5456 | if (goal == CTFEGoal.LValue) | |
5457 | { | |
5458 | Expression e2 = ctfeEmplaceExp!IntegerExp(e.e2.loc, indexToAccess, Type.tsize_t); | |
5459 | emplaceExp!(IndexExp)(pue, e.loc, agg, e2); | |
5460 | result = pue.exp(); | |
5461 | result.type = e.type; | |
5462 | return; | |
5463 | } | |
5464 | ||
5465 | result = ctfeIndex(pue, e.loc, e.type, agg, indexToAccess); | |
5466 | if (exceptionOrCant(result)) | |
5467 | return; | |
9c7d5e88 | 5468 | if (result.op == EXP.void_) |
5fee5ec3 IB |
5469 | { |
5470 | e.error("`%s` is used before initialized", e.toChars()); | |
5471 | errorSupplemental(result.loc, "originally uninitialized here"); | |
5472 | result = CTFEExp.cantexp; | |
5473 | return; | |
5474 | } | |
5475 | if (result == pue.exp()) | |
5476 | result = result.copy(); | |
5477 | } | |
5478 | ||
5479 | override void visit(SliceExp e) | |
5480 | { | |
5481 | debug (LOG) | |
5482 | { | |
5483 | printf("%s SliceExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5484 | } | |
5485 | if (e.e1.type.toBasetype().ty == Tpointer) | |
5486 | { | |
5487 | // Slicing a pointer. Note that there is no $ in this case. | |
5488 | Expression e1 = interpretRegion(e.e1, istate); | |
5489 | if (exceptionOrCant(e1)) | |
5490 | return; | |
9c7d5e88 | 5491 | if (e1.op == EXP.int64) |
5fee5ec3 IB |
5492 | { |
5493 | e.error("cannot slice invalid pointer `%s` of value `%s`", e.e1.toChars(), e1.toChars()); | |
5494 | result = CTFEExp.cantexp; | |
5495 | return; | |
5496 | } | |
5497 | ||
5498 | /* Evaluate lower and upper bounds of slice | |
5499 | */ | |
5500 | Expression lwr = interpretRegion(e.lwr, istate); | |
5501 | if (exceptionOrCant(lwr)) | |
5502 | return; | |
5503 | Expression upr = interpretRegion(e.upr, istate); | |
5504 | if (exceptionOrCant(upr)) | |
5505 | return; | |
5506 | uinteger_t ilwr = lwr.toInteger(); | |
5507 | uinteger_t iupr = upr.toInteger(); | |
5508 | ||
5509 | dinteger_t ofs; | |
5510 | Expression agg = getAggregateFromPointer(e1, &ofs); | |
5511 | ilwr += ofs; | |
5512 | iupr += ofs; | |
9c7d5e88 | 5513 | if (agg.op == EXP.null_) |
5fee5ec3 IB |
5514 | { |
5515 | if (iupr == ilwr) | |
5516 | { | |
5517 | result = ctfeEmplaceExp!NullExp(e.loc); | |
5518 | result.type = e.type; | |
5519 | return; | |
5520 | } | |
5521 | e.error("cannot slice null pointer `%s`", e.e1.toChars()); | |
5522 | result = CTFEExp.cantexp; | |
5523 | return; | |
5524 | } | |
9c7d5e88 | 5525 | if (agg.op == EXP.symbolOffset) |
5fee5ec3 IB |
5526 | { |
5527 | e.error("slicing pointers to static variables is not supported in CTFE"); | |
5528 | result = CTFEExp.cantexp; | |
5529 | return; | |
5530 | } | |
9c7d5e88 | 5531 | if (agg.op != EXP.arrayLiteral && agg.op != EXP.string_) |
5fee5ec3 IB |
5532 | { |
5533 | e.error("pointer `%s` cannot be sliced at compile time (it does not point to an array)", e.e1.toChars()); | |
5534 | result = CTFEExp.cantexp; | |
5535 | return; | |
5536 | } | |
9c7d5e88 | 5537 | assert(agg.op == EXP.arrayLiteral || agg.op == EXP.string_); |
5fee5ec3 IB |
5538 | dinteger_t len = ArrayLength(Type.tsize_t, agg).exp().toInteger(); |
5539 | //Type *pointee = ((TypePointer *)agg.type)->next; | |
fbdaa581 | 5540 | if (sliceBoundsCheck(0, len, ilwr, iupr)) |
5fee5ec3 IB |
5541 | { |
5542 | e.error("pointer slice `[%lld..%lld]` exceeds allocated memory block `[0..%lld]`", ilwr, iupr, len); | |
5543 | result = CTFEExp.cantexp; | |
5544 | return; | |
5545 | } | |
5546 | if (ofs != 0) | |
5547 | { | |
5548 | lwr = ctfeEmplaceExp!IntegerExp(e.loc, ilwr, lwr.type); | |
5549 | upr = ctfeEmplaceExp!IntegerExp(e.loc, iupr, upr.type); | |
5550 | } | |
5551 | emplaceExp!(SliceExp)(pue, e.loc, agg, lwr, upr); | |
5552 | result = pue.exp(); | |
5553 | result.type = e.type; | |
5554 | return; | |
5555 | } | |
5556 | ||
5557 | CTFEGoal goal1 = CTFEGoal.RValue; | |
5558 | if (goal == CTFEGoal.LValue) | |
5559 | { | |
5560 | if (e.e1.type.toBasetype().ty == Tsarray) | |
5561 | if (auto ve = e.e1.isVarExp()) | |
5562 | if (auto vd = ve.var.isVarDeclaration()) | |
5563 | if (vd.storage_class & STC.ref_) | |
5564 | goal1 = CTFEGoal.LValue; | |
5565 | } | |
5566 | Expression e1 = interpret(e.e1, istate, goal1); | |
5567 | if (exceptionOrCant(e1)) | |
5568 | return; | |
5569 | ||
5570 | if (!e.lwr) | |
5571 | { | |
5572 | result = paintTypeOntoLiteral(pue, e.type, e1); | |
5573 | return; | |
5574 | } | |
5575 | if (auto ve = e1.isVectorExp()) | |
5576 | { | |
5577 | e1 = interpretVectorToArray(pue, ve); | |
5578 | e1 = (e1 == pue.exp()) ? pue.copy() : e1; | |
5579 | } | |
5580 | ||
5581 | /* Set dollar to the length of the array | |
5582 | */ | |
5583 | uinteger_t dollar; | |
9c7d5e88 | 5584 | if ((e1.op == EXP.variable || e1.op == EXP.dotVariable) && e1.type.toBasetype().ty == Tsarray) |
5fee5ec3 IB |
5585 | dollar = e1.type.toBasetype().isTypeSArray().dim.toInteger(); |
5586 | else | |
5587 | { | |
9c7d5e88 | 5588 | if (e1.op != EXP.arrayLiteral && e1.op != EXP.string_ && e1.op != EXP.null_ && e1.op != EXP.slice && e1.op != EXP.vector) |
5fee5ec3 IB |
5589 | { |
5590 | e.error("cannot determine length of `%s` at compile time", e1.toChars()); | |
5591 | result = CTFEExp.cantexp; | |
5592 | return; | |
5593 | } | |
5594 | dollar = resolveArrayLength(e1); | |
5595 | } | |
5596 | ||
5597 | /* Set the $ variable | |
5598 | */ | |
5599 | if (e.lengthVar) | |
5600 | { | |
5601 | auto dollarExp = ctfeEmplaceExp!IntegerExp(e.loc, dollar, Type.tsize_t); | |
5602 | ctfeGlobals.stack.push(e.lengthVar); | |
5603 | setValue(e.lengthVar, dollarExp); | |
5604 | } | |
5605 | ||
5606 | /* Evaluate lower and upper bounds of slice | |
5607 | */ | |
5608 | Expression lwr = interpretRegion(e.lwr, istate); | |
5609 | if (exceptionOrCant(lwr)) | |
5610 | { | |
5611 | if (e.lengthVar) | |
5612 | ctfeGlobals.stack.pop(e.lengthVar); | |
5613 | return; | |
5614 | } | |
5615 | Expression upr = interpretRegion(e.upr, istate); | |
5616 | if (exceptionOrCant(upr)) | |
5617 | { | |
5618 | if (e.lengthVar) | |
5619 | ctfeGlobals.stack.pop(e.lengthVar); | |
5620 | return; | |
5621 | } | |
5622 | if (e.lengthVar) | |
5623 | ctfeGlobals.stack.pop(e.lengthVar); // $ is defined only inside [L..U] | |
5624 | ||
5625 | uinteger_t ilwr = lwr.toInteger(); | |
5626 | uinteger_t iupr = upr.toInteger(); | |
9c7d5e88 | 5627 | if (e1.op == EXP.null_) |
5fee5ec3 IB |
5628 | { |
5629 | if (ilwr == 0 && iupr == 0) | |
5630 | { | |
5631 | result = e1; | |
5632 | return; | |
5633 | } | |
5634 | e1.error("slice `[%llu..%llu]` is out of bounds", ilwr, iupr); | |
5635 | result = CTFEExp.cantexp; | |
5636 | return; | |
5637 | } | |
5638 | if (auto se = e1.isSliceExp()) | |
5639 | { | |
5640 | // Simplify slice of slice: | |
5641 | // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] | |
5642 | uinteger_t lo1 = se.lwr.toInteger(); | |
5643 | uinteger_t up1 = se.upr.toInteger(); | |
fbdaa581 | 5644 | if (sliceBoundsCheck(0, up1 - lo1, ilwr, iupr)) |
5fee5ec3 | 5645 | { |
fbdaa581 | 5646 | e.error("slice `[%llu..%llu]` exceeds array bounds `[0..%llu]`", ilwr, iupr, up1 - lo1); |
5fee5ec3 IB |
5647 | result = CTFEExp.cantexp; |
5648 | return; | |
5649 | } | |
5650 | ilwr += lo1; | |
5651 | iupr += lo1; | |
5652 | emplaceExp!(SliceExp)(pue, e.loc, se.e1, | |
5653 | ctfeEmplaceExp!IntegerExp(e.loc, ilwr, lwr.type), | |
5654 | ctfeEmplaceExp!IntegerExp(e.loc, iupr, upr.type)); | |
5655 | result = pue.exp(); | |
5656 | result.type = e.type; | |
5657 | return; | |
5658 | } | |
9c7d5e88 | 5659 | if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_) |
5fee5ec3 | 5660 | { |
fbdaa581 | 5661 | if (sliceBoundsCheck(0, dollar, ilwr, iupr)) |
5fee5ec3 IB |
5662 | { |
5663 | e.error("slice `[%lld..%lld]` exceeds array bounds `[0..%lld]`", ilwr, iupr, dollar); | |
5664 | result = CTFEExp.cantexp; | |
5665 | return; | |
5666 | } | |
5667 | } | |
5668 | emplaceExp!(SliceExp)(pue, e.loc, e1, lwr, upr); | |
5669 | result = pue.exp(); | |
5670 | result.type = e.type; | |
5671 | } | |
5672 | ||
5673 | override void visit(InExp e) | |
5674 | { | |
5675 | debug (LOG) | |
5676 | { | |
5677 | printf("%s InExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5678 | } | |
5679 | Expression e1 = interpretRegion(e.e1, istate); | |
5680 | if (exceptionOrCant(e1)) | |
5681 | return; | |
5682 | Expression e2 = interpretRegion(e.e2, istate); | |
5683 | if (exceptionOrCant(e2)) | |
5684 | return; | |
9c7d5e88 | 5685 | if (e2.op == EXP.null_) |
5fee5ec3 IB |
5686 | { |
5687 | emplaceExp!(NullExp)(pue, e.loc, e.type); | |
5688 | result = pue.exp(); | |
5689 | return; | |
5690 | } | |
9c7d5e88 | 5691 | if (e2.op != EXP.assocArrayLiteral) |
5fee5ec3 IB |
5692 | { |
5693 | e.error("`%s` cannot be interpreted at compile time", e.toChars()); | |
5694 | result = CTFEExp.cantexp; | |
5695 | return; | |
5696 | } | |
5697 | ||
5698 | e1 = resolveSlice(e1); | |
235d5a96 | 5699 | result = findKeyInAA(e.loc, e2.isAssocArrayLiteralExp(), e1); |
5fee5ec3 IB |
5700 | if (exceptionOrCant(result)) |
5701 | return; | |
5702 | if (!result) | |
5703 | { | |
5704 | emplaceExp!(NullExp)(pue, e.loc, e.type); | |
5705 | result = pue.exp(); | |
5706 | } | |
5707 | else | |
5708 | { | |
5709 | // Create a CTFE pointer &aa[index] | |
5710 | result = ctfeEmplaceExp!IndexExp(e.loc, e2, e1); | |
5711 | result.type = e.type.nextOf(); | |
5712 | emplaceExp!(AddrExp)(pue, e.loc, result, e.type); | |
5713 | result = pue.exp(); | |
5714 | } | |
5715 | } | |
5716 | ||
5717 | override void visit(CatExp e) | |
5718 | { | |
5719 | debug (LOG) | |
5720 | { | |
5721 | printf("%s CatExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5722 | } | |
5723 | ||
5724 | UnionExp ue1 = void; | |
5725 | Expression e1 = interpret(&ue1, e.e1, istate); | |
5726 | if (exceptionOrCant(e1)) | |
5727 | return; | |
5728 | ||
5729 | UnionExp ue2 = void; | |
5730 | Expression e2 = interpret(&ue2, e.e2, istate); | |
5731 | if (exceptionOrCant(e2)) | |
5732 | return; | |
5733 | ||
5734 | UnionExp e1tmp = void; | |
5735 | e1 = resolveSlice(e1, &e1tmp); | |
5736 | ||
5737 | UnionExp e2tmp = void; | |
5738 | e2 = resolveSlice(e2, &e2tmp); | |
5739 | ||
5740 | /* e1 and e2 can't go on the stack because of x~[y] and [x]~y will | |
5741 | * result in [x,y] and then x or y is on the stack. | |
5742 | * But if they are both strings, we can, because it isn't the x~[y] case. | |
5743 | */ | |
9c7d5e88 | 5744 | if (!(e1.op == EXP.string_ && e2.op == EXP.string_)) |
5fee5ec3 IB |
5745 | { |
5746 | if (e1 == ue1.exp()) | |
5747 | e1 = ue1.copy(); | |
5748 | if (e2 == ue2.exp()) | |
5749 | e2 = ue2.copy(); | |
5750 | } | |
5751 | ||
3b007164 IB |
5752 | Expression prepareCatOperand(Expression exp) |
5753 | { | |
5754 | /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an | |
5755 | * array. This is needed because interpreting the `CatExp` calls | |
5756 | * `Cat()`, which cannot handle concatenations between different | |
5757 | * types, except for strings and chars. | |
5758 | */ | |
5759 | auto tb = e.type.toBasetype(); | |
5760 | auto tbNext = tb.nextOf(); | |
5761 | auto expTb = exp.type.toBasetype(); | |
5762 | ||
5763 | if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && | |
5764 | (tb.ty == Tarray || tb.ty == Tsarray) && | |
5765 | (expTb.ty == Tarray || expTb.ty == Tsarray)) | |
5766 | return new ArrayLiteralExp(exp.loc, e.type, exp); | |
5767 | return exp; | |
5768 | } | |
5769 | ||
5770 | *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2)); | |
5fee5ec3 IB |
5771 | result = pue.exp(); |
5772 | ||
5773 | if (CTFEExp.isCantExp(result)) | |
5774 | { | |
5775 | e.error("`%s` cannot be interpreted at compile time", e.toChars()); | |
5776 | return; | |
5777 | } | |
5778 | // We know we still own it, because we interpreted both e1 and e2 | |
5779 | if (auto ale = result.isArrayLiteralExp()) | |
5780 | { | |
5781 | ale.ownedByCtfe = OwnedBy.ctfe; | |
5782 | ||
5783 | // https://issues.dlang.org/show_bug.cgi?id=14686 | |
5784 | foreach (elem; *ale.elements) | |
5785 | { | |
5786 | Expression ex = evaluatePostblit(istate, elem); | |
5787 | if (exceptionOrCant(ex)) | |
5788 | return; | |
5789 | } | |
5790 | } | |
5791 | else if (auto se = result.isStringExp()) | |
5792 | se.ownedByCtfe = OwnedBy.ctfe; | |
5793 | } | |
5794 | ||
5795 | override void visit(DeleteExp e) | |
5796 | { | |
5797 | debug (LOG) | |
5798 | { | |
5799 | printf("%s DeleteExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5800 | } | |
5801 | result = interpretRegion(e.e1, istate); | |
5802 | if (exceptionOrCant(result)) | |
5803 | return; | |
5804 | ||
9c7d5e88 | 5805 | if (result.op == EXP.null_) |
5fee5ec3 IB |
5806 | { |
5807 | result = CTFEExp.voidexp; | |
5808 | return; | |
5809 | } | |
5810 | ||
5811 | auto tb = e.e1.type.toBasetype(); | |
5812 | switch (tb.ty) | |
5813 | { | |
5814 | case Tclass: | |
9c7d5e88 | 5815 | if (result.op != EXP.classReference) |
5fee5ec3 IB |
5816 | { |
5817 | e.error("`delete` on invalid class reference `%s`", result.toChars()); | |
5818 | result = CTFEExp.cantexp; | |
5819 | return; | |
5820 | } | |
5821 | ||
235d5a96 | 5822 | auto cre = result.isClassReferenceExp(); |
5fee5ec3 IB |
5823 | auto cd = cre.originalClass(); |
5824 | ||
5825 | // Find dtor(s) in inheritance chain | |
5826 | do | |
5827 | { | |
5828 | if (cd.dtor) | |
5829 | { | |
5830 | result = interpretFunction(pue, cd.dtor, istate, null, cre); | |
5831 | if (exceptionOrCant(result)) | |
5832 | return; | |
5833 | ||
5834 | // Dtors of Non-extern(D) classes use implicit chaining (like structs) | |
5835 | import dmd.aggregate : ClassKind; | |
5836 | if (cd.classKind != ClassKind.d) | |
5837 | break; | |
5838 | } | |
5839 | ||
5840 | // Emulate manual chaining as done in rt_finalize2 | |
5841 | cd = cd.baseClass; | |
5842 | ||
5843 | } while (cd); // Stop after Object | |
5844 | ||
5845 | break; | |
5846 | ||
5fee5ec3 IB |
5847 | default: |
5848 | assert(0); | |
5849 | } | |
5850 | result = CTFEExp.voidexp; | |
5851 | } | |
5852 | ||
5853 | override void visit(CastExp e) | |
5854 | { | |
5855 | debug (LOG) | |
5856 | { | |
5857 | printf("%s CastExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
5858 | } | |
5859 | Expression e1 = interpretRegion(e.e1, istate, goal); | |
5860 | if (exceptionOrCant(e1)) | |
5861 | return; | |
5862 | // If the expression has been cast to void, do nothing. | |
5863 | if (e.to.ty == Tvoid) | |
5864 | { | |
5865 | result = CTFEExp.voidexp; | |
5866 | return; | |
5867 | } | |
9c7d5e88 | 5868 | if (e.to.ty == Tpointer && e1.op != EXP.null_) |
5fee5ec3 IB |
5869 | { |
5870 | Type pointee = (cast(TypePointer)e.type).next; | |
5871 | // Implement special cases of normally-unsafe casts | |
9c7d5e88 | 5872 | if (e1.op == EXP.int64) |
5fee5ec3 IB |
5873 | { |
5874 | // Happens with Windows HANDLEs, for example. | |
5875 | result = paintTypeOntoLiteral(pue, e.to, e1); | |
5876 | return; | |
5877 | } | |
5878 | ||
5879 | bool castToSarrayPointer = false; | |
5880 | bool castBackFromVoid = false; | |
5881 | if (e1.type.ty == Tarray || e1.type.ty == Tsarray || e1.type.ty == Tpointer) | |
5882 | { | |
5883 | // Check for unsupported type painting operations | |
5884 | // For slices, we need the type being sliced, | |
5885 | // since it may have already been type painted | |
5886 | Type elemtype = e1.type.nextOf(); | |
5887 | if (auto se = e1.isSliceExp()) | |
5888 | elemtype = se.e1.type.nextOf(); | |
5889 | ||
5890 | // Allow casts from X* to void *, and X** to void** for any X. | |
5891 | // But don't allow cast from X* to void**. | |
5892 | // So, we strip all matching * from source and target to find X. | |
5893 | // Allow casts to X* from void* only if the 'void' was originally an X; | |
5894 | // we check this later on. | |
5895 | Type ultimatePointee = pointee; | |
5896 | Type ultimateSrc = elemtype; | |
5897 | while (ultimatePointee.ty == Tpointer && ultimateSrc.ty == Tpointer) | |
5898 | { | |
5899 | ultimatePointee = ultimatePointee.nextOf(); | |
5900 | ultimateSrc = ultimateSrc.nextOf(); | |
5901 | } | |
5902 | if (ultimatePointee.ty == Tsarray && ultimatePointee.nextOf().equivalent(ultimateSrc)) | |
5903 | { | |
5904 | castToSarrayPointer = true; | |
5905 | } | |
5906 | else if (ultimatePointee.ty != Tvoid && ultimateSrc.ty != Tvoid && !isSafePointerCast(elemtype, pointee)) | |
5907 | { | |
5908 | e.error("reinterpreting cast from `%s*` to `%s*` is not supported in CTFE", elemtype.toChars(), pointee.toChars()); | |
5909 | result = CTFEExp.cantexp; | |
5910 | return; | |
5911 | } | |
5912 | if (ultimateSrc.ty == Tvoid) | |
5913 | castBackFromVoid = true; | |
5914 | } | |
5915 | ||
5916 | if (auto se = e1.isSliceExp()) | |
5917 | { | |
9c7d5e88 | 5918 | if (se.e1.op == EXP.null_) |
5fee5ec3 IB |
5919 | { |
5920 | result = paintTypeOntoLiteral(pue, e.type, se.e1); | |
5921 | return; | |
5922 | } | |
5923 | // Create a CTFE pointer &aggregate[1..2] | |
5924 | auto ei = ctfeEmplaceExp!IndexExp(e.loc, se.e1, se.lwr); | |
5925 | ei.type = e.type.nextOf(); | |
5926 | emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); | |
5927 | result = pue.exp(); | |
5928 | return; | |
5929 | } | |
9c7d5e88 | 5930 | if (e1.op == EXP.arrayLiteral || e1.op == EXP.string_) |
5fee5ec3 IB |
5931 | { |
5932 | // Create a CTFE pointer &[1,2,3][0] or &"abc"[0] | |
5933 | auto ei = ctfeEmplaceExp!IndexExp(e.loc, e1, ctfeEmplaceExp!IntegerExp(e.loc, 0, Type.tsize_t)); | |
5934 | ei.type = e.type.nextOf(); | |
5935 | emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); | |
5936 | result = pue.exp(); | |
5937 | return; | |
5938 | } | |
235d5a96 | 5939 | if (e1.op == EXP.index && !e1.isIndexExp().e1.type.equals(e1.type)) |
5fee5ec3 IB |
5940 | { |
5941 | // type painting operation | |
235d5a96 | 5942 | IndexExp ie = e1.isIndexExp(); |
5fee5ec3 IB |
5943 | if (castBackFromVoid) |
5944 | { | |
5945 | // get the original type. For strings, it's just the type... | |
5946 | Type origType = ie.e1.type.nextOf(); | |
5947 | // ..but for arrays of type void*, it's the type of the element | |
9c7d5e88 | 5948 | if (ie.e1.op == EXP.arrayLiteral && ie.e2.op == EXP.int64) |
5fee5ec3 | 5949 | { |
235d5a96 | 5950 | ArrayLiteralExp ale = ie.e1.isArrayLiteralExp(); |
5fee5ec3 | 5951 | const indx = cast(size_t)ie.e2.toInteger(); |
6d799f0a | 5952 | if (indx < ale.elements.length) |
5fee5ec3 IB |
5953 | { |
5954 | if (Expression xx = (*ale.elements)[indx]) | |
5955 | { | |
5956 | if (auto iex = xx.isIndexExp()) | |
5957 | origType = iex.e1.type.nextOf(); | |
5958 | else if (auto ae = xx.isAddrExp()) | |
5959 | origType = ae.e1.type; | |
5960 | else if (auto ve = xx.isVarExp()) | |
5961 | origType = ve.var.type; | |
5962 | } | |
5963 | } | |
5964 | } | |
5965 | if (!isSafePointerCast(origType, pointee)) | |
5966 | { | |
5967 | e.error("using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType.toChars(), pointee.toChars()); | |
5968 | result = CTFEExp.cantexp; | |
5969 | return; | |
5970 | } | |
5971 | } | |
5972 | emplaceExp!(IndexExp)(pue, e1.loc, ie.e1, ie.e2); | |
5973 | result = pue.exp(); | |
5974 | result.type = e.type; | |
5975 | return; | |
5976 | } | |
5977 | ||
5978 | if (auto ae = e1.isAddrExp()) | |
5979 | { | |
5980 | Type origType = ae.e1.type; | |
5981 | if (isSafePointerCast(origType, pointee)) | |
5982 | { | |
5983 | emplaceExp!(AddrExp)(pue, e.loc, ae.e1, e.type); | |
5984 | result = pue.exp(); | |
5985 | return; | |
5986 | } | |
5987 | ||
9c7d5e88 | 5988 | if (castToSarrayPointer && pointee.toBasetype().ty == Tsarray && ae.e1.op == EXP.index) |
5fee5ec3 IB |
5989 | { |
5990 | // &val[idx] | |
5991 | dinteger_t dim = (cast(TypeSArray)pointee.toBasetype()).dim.toInteger(); | |
235d5a96 | 5992 | IndexExp ie = ae.e1.isIndexExp(); |
5fee5ec3 IB |
5993 | Expression lwr = ie.e2; |
5994 | Expression upr = ctfeEmplaceExp!IntegerExp(ie.e2.loc, ie.e2.toInteger() + dim, Type.tsize_t); | |
5995 | ||
5996 | // Create a CTFE pointer &val[idx..idx+dim] | |
5997 | auto er = ctfeEmplaceExp!SliceExp(e.loc, ie.e1, lwr, upr); | |
5998 | er.type = pointee; | |
5999 | emplaceExp!(AddrExp)(pue, e.loc, er, e.type); | |
6000 | result = pue.exp(); | |
6001 | return; | |
6002 | } | |
6003 | } | |
6004 | ||
9c7d5e88 | 6005 | if (e1.op == EXP.variable || e1.op == EXP.symbolOffset) |
5fee5ec3 IB |
6006 | { |
6007 | // type painting operation | |
6008 | Type origType = (cast(SymbolExp)e1).var.type; | |
6009 | if (castBackFromVoid && !isSafePointerCast(origType, pointee)) | |
6010 | { | |
6011 | e.error("using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType.toChars(), pointee.toChars()); | |
6012 | result = CTFEExp.cantexp; | |
6013 | return; | |
6014 | } | |
6015 | if (auto ve = e1.isVarExp()) | |
6016 | emplaceExp!(VarExp)(pue, e.loc, ve.var); | |
6017 | else | |
235d5a96 | 6018 | emplaceExp!(SymOffExp)(pue, e.loc, e1.isSymOffExp().var, e1.isSymOffExp().offset); |
5fee5ec3 IB |
6019 | result = pue.exp(); |
6020 | result.type = e.to; | |
6021 | return; | |
6022 | } | |
6023 | ||
6024 | // Check if we have a null pointer (eg, inside a struct) | |
6025 | e1 = interpretRegion(e1, istate); | |
9c7d5e88 | 6026 | if (e1.op != EXP.null_) |
5fee5ec3 IB |
6027 | { |
6028 | e.error("pointer cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); | |
6029 | result = CTFEExp.cantexp; | |
6030 | return; | |
6031 | } | |
6032 | } | |
6033 | if (e.to.ty == Tsarray && e.e1.type.ty == Tvector) | |
6034 | { | |
6035 | // Special handling for: cast(float[4])__vector([w, x, y, z]) | |
6036 | e1 = interpretRegion(e.e1, istate); | |
6037 | if (exceptionOrCant(e1)) | |
6038 | return; | |
9c7d5e88 | 6039 | assert(e1.op == EXP.vector); |
5fee5ec3 IB |
6040 | e1 = interpretVectorToArray(pue, e1.isVectorExp()); |
6041 | } | |
9c7d5e88 | 6042 | if (e.to.ty == Tarray && e1.op == EXP.slice) |
5fee5ec3 IB |
6043 | { |
6044 | // Note that the slice may be void[], so when checking for dangerous | |
6045 | // casts, we need to use the original type, which is se.e1. | |
235d5a96 | 6046 | SliceExp se = e1.isSliceExp(); |
5fee5ec3 IB |
6047 | if (!isSafePointerCast(se.e1.type.nextOf(), e.to.nextOf())) |
6048 | { | |
6049 | e.error("array cast from `%s` to `%s` is not supported at compile time", se.e1.type.toChars(), e.to.toChars()); | |
6050 | result = CTFEExp.cantexp; | |
6051 | return; | |
6052 | } | |
6053 | emplaceExp!(SliceExp)(pue, e1.loc, se.e1, se.lwr, se.upr); | |
6054 | result = pue.exp(); | |
6055 | result.type = e.to; | |
6056 | return; | |
6057 | } | |
6058 | // Disallow array type painting, except for conversions between built-in | |
6059 | // types of identical size. | |
6060 | if ((e.to.ty == Tsarray || e.to.ty == Tarray) && (e1.type.ty == Tsarray || e1.type.ty == Tarray) && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) | |
6061 | { | |
6062 | e.error("array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); | |
6063 | result = CTFEExp.cantexp; | |
6064 | return; | |
6065 | } | |
6066 | if (e.to.ty == Tsarray) | |
6067 | e1 = resolveSlice(e1); | |
0fb57034 IB |
6068 | |
6069 | auto tobt = e.to.toBasetype(); | |
6070 | if (tobt.ty == Tbool && e1.type.ty == Tpointer) | |
5fee5ec3 | 6071 | { |
9c7d5e88 | 6072 | emplaceExp!(IntegerExp)(pue, e.loc, e1.op != EXP.null_, e.to); |
5fee5ec3 IB |
6073 | result = pue.exp(); |
6074 | return; | |
6075 | } | |
9c7d5e88 | 6076 | else if (tobt.isTypeBasic() && e1.op == EXP.null_) |
0fb57034 IB |
6077 | { |
6078 | if (tobt.isintegral()) | |
6079 | emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to); | |
6080 | else if (tobt.isreal()) | |
6081 | emplaceExp!(RealExp)(pue, e.loc, CTFloat.zero, e.to); | |
6082 | result = pue.exp(); | |
6083 | return; | |
6084 | } | |
fd43568c | 6085 | result = ctfeCast(pue, e.loc, e.type, e.to, e1, true); |
5fee5ec3 IB |
6086 | } |
6087 | ||
6088 | override void visit(AssertExp e) | |
6089 | { | |
6090 | debug (LOG) | |
6091 | { | |
6092 | printf("%s AssertExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
6093 | } | |
6094 | Expression e1 = interpret(pue, e.e1, istate); | |
6095 | if (exceptionOrCant(e1)) | |
6096 | return; | |
6097 | if (isTrueBool(e1)) | |
6098 | { | |
6099 | } | |
9c7d5e88 | 6100 | else if (e1.toBool().hasValue(false)) |
5fee5ec3 IB |
6101 | { |
6102 | if (e.msg) | |
6103 | { | |
6104 | UnionExp ue = void; | |
6105 | result = interpret(&ue, e.msg, istate); | |
6106 | if (exceptionOrCant(result)) | |
6107 | return; | |
d6679fa2 IB |
6108 | if (StringExp se = result.isStringExp()) |
6109 | e.error("%s", se.toStringz().ptr); | |
6110 | else | |
6111 | e.error("%s", result.toChars()); | |
5fee5ec3 IB |
6112 | } |
6113 | else | |
6114 | e.error("`%s` failed", e.toChars()); | |
6115 | result = CTFEExp.cantexp; | |
6116 | return; | |
6117 | } | |
6118 | else | |
6119 | { | |
6120 | e.error("`%s` is not a compile time boolean expression", e1.toChars()); | |
6121 | result = CTFEExp.cantexp; | |
6122 | return; | |
6123 | } | |
6124 | result = e1; | |
6125 | return; | |
6126 | } | |
6127 | ||
d7569187 IB |
6128 | override void visit(ThrowExp te) |
6129 | { | |
6130 | debug (LOG) | |
6131 | { | |
f99303eb | 6132 | printf("%s ThrowExpression::interpret()\n", te.loc.toChars()); |
d7569187 | 6133 | } |
3b007164 | 6134 | interpretThrow(result, te.e1, te.loc, istate); |
d7569187 IB |
6135 | } |
6136 | ||
5fee5ec3 IB |
6137 | override void visit(PtrExp e) |
6138 | { | |
3b007164 IB |
6139 | // Called for both lvalues and rvalues |
6140 | const lvalue = goal == CTFEGoal.LValue; | |
5fee5ec3 IB |
6141 | debug (LOG) |
6142 | { | |
3b007164 | 6143 | printf("%s PtrExp::interpret(%d) %s, %s\n", e.loc.toChars(), lvalue, e.type.toChars(), e.toChars()); |
5fee5ec3 | 6144 | } |
3b007164 | 6145 | |
5fee5ec3 IB |
6146 | // Check for int<->float and long<->double casts. |
6147 | if (auto soe1 = e.e1.isSymOffExp()) | |
6148 | if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type)) | |
6149 | { | |
6150 | // *(cast(int*)&v), where v is a float variable | |
6151 | result = paintFloatInt(pue, getVarExp(e.loc, istate, soe1.var, CTFEGoal.RValue), e.type); | |
6152 | return; | |
6153 | } | |
6154 | ||
6155 | if (auto ce1 = e.e1.isCastExp()) | |
6156 | if (auto ae11 = ce1.e1.isAddrExp()) | |
6157 | { | |
6158 | // *(cast(int*)&x), where x is a float expression | |
6159 | Expression x = ae11.e1; | |
6160 | if (isFloatIntPaint(e.type, x.type)) | |
6161 | { | |
6162 | result = paintFloatInt(pue, interpretRegion(x, istate), e.type); | |
6163 | return; | |
6164 | } | |
6165 | } | |
6166 | ||
6167 | // Constant fold *(&structliteral + offset) | |
6168 | if (auto ae = e.e1.isAddExp()) | |
6169 | { | |
9c7d5e88 | 6170 | if (ae.e1.op == EXP.address && ae.e2.op == EXP.int64) |
5fee5ec3 | 6171 | { |
235d5a96 | 6172 | AddrExp ade = ae.e1.isAddrExp(); |
5fee5ec3 IB |
6173 | Expression ex = interpretRegion(ade.e1, istate); |
6174 | if (exceptionOrCant(ex)) | |
6175 | return; | |
6176 | if (auto se = ex.isStructLiteralExp()) | |
6177 | { | |
6178 | dinteger_t offset = ae.e2.toInteger(); | |
6179 | result = se.getField(e.type, cast(uint)offset); | |
6180 | if (result) | |
6181 | return; | |
6182 | } | |
6183 | } | |
6184 | } | |
6185 | ||
6186 | // It's possible we have an array bounds error. We need to make sure it | |
6187 | // errors with this line number, not the one where the pointer was set. | |
6188 | result = interpretRegion(e.e1, istate); | |
6189 | if (exceptionOrCant(result)) | |
6190 | return; | |
6191 | ||
9c7d5e88 | 6192 | if (result.op == EXP.function_) |
5fee5ec3 IB |
6193 | return; |
6194 | if (auto soe = result.isSymOffExp()) | |
6195 | { | |
6196 | if (soe.offset == 0 && soe.var.isFuncDeclaration()) | |
6197 | return; | |
6198 | e.error("cannot dereference pointer to static variable `%s` at compile time", soe.var.toChars()); | |
6199 | result = CTFEExp.cantexp; | |
6200 | return; | |
6201 | } | |
6202 | ||
3b007164 IB |
6203 | if (!lvalue && result.isArrayLiteralExp() && |
6204 | result.type.isTypePointer()) | |
6205 | { | |
6206 | /* A pointer variable can point to an array literal like `[3]`. | |
6207 | * Dereferencing it means accessing the first element value. | |
6208 | * Dereference it only if result should be an rvalue | |
6209 | */ | |
6210 | auto ae = result.isArrayLiteralExp(); | |
6211 | if (ae.elements.length == 1) | |
6212 | { | |
6213 | result = (*ae.elements)[0]; | |
6214 | return; | |
6215 | } | |
6216 | } | |
5eb9927a | 6217 | if (result.isStringExp() || result.isArrayLiteralExp()) |
5fee5ec3 IB |
6218 | return; |
6219 | ||
9c7d5e88 | 6220 | if (result.op != EXP.address) |
5fee5ec3 | 6221 | { |
9c7d5e88 | 6222 | if (result.op == EXP.null_) |
5fee5ec3 IB |
6223 | e.error("dereference of null pointer `%s`", e.e1.toChars()); |
6224 | else | |
6225 | e.error("dereference of invalid pointer `%s`", result.toChars()); | |
6226 | result = CTFEExp.cantexp; | |
6227 | return; | |
6228 | } | |
6229 | ||
6230 | // *(&x) ==> x | |
235d5a96 | 6231 | result = result.isAddrExp().e1; |
5fee5ec3 | 6232 | |
9c7d5e88 | 6233 | if (result.op == EXP.slice && e.type.toBasetype().ty == Tsarray) |
5fee5ec3 IB |
6234 | { |
6235 | /* aggr[lwr..upr] | |
6236 | * upr may exceed the upper boundary of aggr, but the check is deferred | |
6237 | * until those out-of-bounds elements will be touched. | |
6238 | */ | |
6239 | return; | |
6240 | } | |
6241 | result = interpret(pue, result, istate, goal); | |
6242 | if (exceptionOrCant(result)) | |
6243 | return; | |
6244 | ||
6245 | debug (LOG) | |
6246 | { | |
6247 | if (CTFEExp.isCantExp(result)) | |
6248 | printf("PtrExp::interpret() %s = CTFEExp::cantexp\n", e.toChars()); | |
6249 | } | |
6250 | } | |
6251 | ||
6252 | override void visit(DotVarExp e) | |
6253 | { | |
6254 | void notImplementedYet() | |
6255 | { | |
6256 | e.error("`%s.%s` is not yet implemented at compile time", e.e1.toChars(), e.var.toChars()); | |
6257 | result = CTFEExp.cantexp; | |
6258 | return; | |
6259 | } | |
6260 | ||
6261 | debug (LOG) | |
6262 | { | |
6263 | printf("%s DotVarExp::interpret() %s, goal = %d\n", e.loc.toChars(), e.toChars(), goal); | |
6264 | } | |
6265 | Expression ex = interpretRegion(e.e1, istate); | |
6266 | if (exceptionOrCant(ex)) | |
6267 | return; | |
6268 | ||
6269 | if (FuncDeclaration f = e.var.isFuncDeclaration()) | |
6270 | { | |
6271 | if (ex == e.e1) | |
6272 | result = e; // optimize: reuse this CTFE reference | |
6273 | else | |
6274 | { | |
6275 | emplaceExp!(DotVarExp)(pue, e.loc, ex, f, false); | |
6276 | result = pue.exp(); | |
6277 | result.type = e.type; | |
6278 | } | |
6279 | return; | |
6280 | } | |
6281 | ||
6282 | VarDeclaration v = e.var.isVarDeclaration(); | |
6283 | if (!v) | |
6284 | { | |
6285 | e.error("CTFE internal error: `%s`", e.toChars()); | |
6286 | result = CTFEExp.cantexp; | |
6287 | return; | |
6288 | } | |
6289 | ||
9c7d5e88 | 6290 | if (ex.op == EXP.null_) |
5fee5ec3 IB |
6291 | { |
6292 | if (ex.type.toBasetype().ty == Tclass) | |
6293 | e.error("class `%s` is `null` and cannot be dereferenced", e.e1.toChars()); | |
6294 | else | |
6295 | e.error("CTFE internal error: null this `%s`", e.e1.toChars()); | |
6296 | result = CTFEExp.cantexp; | |
6297 | return; | |
6298 | } | |
6299 | ||
6300 | StructLiteralExp se; | |
6301 | int i; | |
6302 | ||
9c7d5e88 | 6303 | if (ex.op != EXP.structLiteral && ex.op != EXP.classReference && ex.op != EXP.typeid_) |
5fee5ec3 IB |
6304 | { |
6305 | return notImplementedYet(); | |
6306 | } | |
6307 | ||
6308 | // We can't use getField, because it makes a copy | |
9c7d5e88 | 6309 | if (ex.op == EXP.classReference) |
5fee5ec3 | 6310 | { |
235d5a96 IB |
6311 | se = ex.isClassReferenceExp().value; |
6312 | i = ex.isClassReferenceExp().findFieldIndexByName(v); | |
5fee5ec3 | 6313 | } |
9c7d5e88 | 6314 | else if (ex.op == EXP.typeid_) |
5fee5ec3 IB |
6315 | { |
6316 | if (v.ident == Identifier.idPool("name")) | |
6317 | { | |
6318 | if (auto t = isType(ex.isTypeidExp().obj)) | |
6319 | { | |
6320 | auto sym = t.toDsymbol(null); | |
6321 | if (auto ident = (sym ? sym.ident : null)) | |
6322 | { | |
6323 | result = new StringExp(e.loc, ident.toString()); | |
6324 | result.expressionSemantic(null); | |
6325 | return ; | |
6326 | } | |
6327 | } | |
6328 | } | |
6329 | return notImplementedYet(); | |
6330 | } | |
6331 | else | |
6332 | { | |
235d5a96 | 6333 | se = ex.isStructLiteralExp(); |
5fee5ec3 IB |
6334 | i = findFieldIndexByName(se.sd, v); |
6335 | } | |
6336 | if (i == -1) | |
6337 | { | |
6338 | e.error("couldn't find field `%s` of type `%s` in `%s`", v.toChars(), e.type.toChars(), se.toChars()); | |
6339 | result = CTFEExp.cantexp; | |
6340 | return; | |
6341 | } | |
6342 | ||
6343 | // https://issues.dlang.org/show_bug.cgi?id=19897 | |
6344 | // https://issues.dlang.org/show_bug.cgi?id=20710 | |
6345 | // Zero-elements fields don't have an initializer. See: scrubArray function | |
6346 | if ((*se.elements)[i] is null) | |
6347 | (*se.elements)[i] = voidInitLiteral(e.type, v).copy(); | |
6348 | ||
6349 | if (goal == CTFEGoal.LValue) | |
6350 | { | |
6351 | // just return the (simplified) dotvar expression as a CTFE reference | |
6352 | if (e.e1 == ex) | |
6353 | result = e; | |
6354 | else | |
6355 | { | |
6356 | emplaceExp!(DotVarExp)(pue, e.loc, ex, v); | |
6357 | result = pue.exp(); | |
6358 | result.type = e.type; | |
6359 | } | |
6360 | return; | |
6361 | } | |
6362 | ||
6363 | result = (*se.elements)[i]; | |
6364 | if (!result) | |
6365 | { | |
5eb9927a | 6366 | e.error("internal compiler error: null field `%s`", v.toChars()); |
5fee5ec3 IB |
6367 | result = CTFEExp.cantexp; |
6368 | return; | |
6369 | } | |
6370 | if (auto vie = result.isVoidInitExp()) | |
6371 | { | |
6372 | const s = vie.var.toChars(); | |
6373 | if (v.overlapped) | |
6374 | { | |
6375 | e.error("reinterpretation through overlapped field `%s` is not allowed in CTFE", s); | |
6376 | result = CTFEExp.cantexp; | |
6377 | return; | |
6378 | } | |
6379 | e.error("cannot read uninitialized variable `%s` in CTFE", s); | |
6380 | result = CTFEExp.cantexp; | |
6381 | return; | |
6382 | } | |
6383 | ||
6384 | if (v.type.ty != result.type.ty && v.type.ty == Tsarray) | |
6385 | { | |
6386 | // Block assignment from inside struct literals | |
6387 | auto tsa = cast(TypeSArray)v.type; | |
6388 | auto len = cast(size_t)tsa.dim.toInteger(); | |
6389 | UnionExp ue = void; | |
0fb57034 | 6390 | result = createBlockDuplicatedArrayLiteral(&ue, e.loc, v.type, result, len); |
5fee5ec3 IB |
6391 | if (result == ue.exp()) |
6392 | result = ue.copy(); | |
6393 | (*se.elements)[i] = result; | |
6394 | } | |
6395 | debug (LOG) | |
6396 | { | |
6397 | if (CTFEExp.isCantExp(result)) | |
6398 | printf("DotVarExp::interpret() %s = CTFEExp::cantexp\n", e.toChars()); | |
6399 | } | |
6400 | } | |
6401 | ||
6402 | override void visit(RemoveExp e) | |
6403 | { | |
6404 | debug (LOG) | |
6405 | { | |
6406 | printf("%s RemoveExp::interpret() %s\n", e.loc.toChars(), e.toChars()); | |
6407 | } | |
6408 | Expression agg = interpret(e.e1, istate); | |
6409 | if (exceptionOrCant(agg)) | |
6410 | return; | |
6411 | Expression index = interpret(e.e2, istate); | |
6412 | if (exceptionOrCant(index)) | |
6413 | return; | |
9c7d5e88 | 6414 | if (agg.op == EXP.null_) |
5fee5ec3 IB |
6415 | { |
6416 | result = CTFEExp.voidexp; | |
6417 | return; | |
6418 | } | |
6419 | ||
6420 | AssocArrayLiteralExp aae = agg.isAssocArrayLiteralExp(); | |
6421 | Expressions* keysx = aae.keys; | |
6422 | Expressions* valuesx = aae.values; | |
6423 | size_t removed = 0; | |
6424 | foreach (j, evalue; *valuesx) | |
6425 | { | |
6426 | Expression ekey = (*keysx)[j]; | |
9c7d5e88 | 6427 | int eq = ctfeEqual(e.loc, EXP.equal, ekey, index); |
5fee5ec3 IB |
6428 | if (eq) |
6429 | ++removed; | |
6430 | else if (removed != 0) | |
6431 | { | |
6432 | (*keysx)[j - removed] = ekey; | |
6433 | (*valuesx)[j - removed] = evalue; | |
6434 | } | |
6435 | } | |
6d799f0a IB |
6436 | valuesx.length = valuesx.length - removed; |
6437 | keysx.length = keysx.length - removed; | |
5fee5ec3 IB |
6438 | result = IntegerExp.createBool(removed != 0); |
6439 | } | |
6440 | ||
6441 | override void visit(ClassReferenceExp e) | |
6442 | { | |
6443 | //printf("ClassReferenceExp::interpret() %s\n", e.value.toChars()); | |
6444 | result = e; | |
6445 | } | |
6446 | ||
6447 | override void visit(VoidInitExp e) | |
6448 | { | |
6449 | e.error("CTFE internal error: trying to read uninitialized variable"); | |
6450 | assert(0); | |
6451 | } | |
6452 | ||
6453 | override void visit(ThrownExceptionExp e) | |
6454 | { | |
6455 | assert(0); // This should never be interpreted | |
6456 | } | |
3b007164 | 6457 | } |
5eb9927a | 6458 | |
3b007164 IB |
6459 | /// Interpret `throw <exp>` found at the specified location `loc` |
6460 | private | |
6461 | void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) | |
6462 | { | |
6463 | incUsageCtfe(istate, loc); | |
6464 | ||
6465 | Expression e = interpretRegion(exp, istate); | |
6466 | if (exceptionOrCantInterpret(e)) | |
6467 | { | |
6468 | // Make sure e is not pointing to a stack temporary | |
6469 | result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; | |
6470 | } | |
6471 | else if (e.op == EXP.classReference) | |
5eb9927a | 6472 | { |
3b007164 IB |
6473 | result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); |
6474 | } | |
6475 | else | |
6476 | { | |
6477 | exp.error("to be thrown `%s` must be non-null", exp.toChars()); | |
6478 | result = ErrorExp.get(); | |
6479 | } | |
6480 | } | |
6481 | ||
6482 | /********************************************* | |
6483 | * Checks if the given expresion is a call to the runtime hook `id`. | |
6484 | * | |
6485 | * Params: | |
6486 | * e = the expression to check | |
6487 | * id = the identifier of the runtime hook | |
6488 | * Returns: | |
6489 | * `e` cast to `CallExp` if it's the hook, `null` otherwise | |
6490 | */ | |
6491 | public CallExp isRuntimeHook(Expression e, Identifier id) | |
6492 | { | |
6493 | if (auto ce = e.isCallExp()) | |
6494 | { | |
6495 | if (auto ve = ce.e1.isVarExp()) | |
5eb9927a | 6496 | { |
3b007164 | 6497 | if (auto fd = ve.var.isFuncDeclaration()) |
5eb9927a | 6498 | { |
3b007164 IB |
6499 | // If `_d_HookTraceImpl` is found, resolve the underlying hook |
6500 | // and replace `e` and `fd` with it. | |
6501 | removeHookTraceImpl(ce, fd); | |
6502 | return fd.ident == id ? ce : null; | |
5eb9927a IB |
6503 | } |
6504 | } | |
5eb9927a | 6505 | } |
3b007164 IB |
6506 | |
6507 | return null; | |
5fee5ec3 IB |
6508 | } |
6509 | ||
6510 | /******************************************** | |
6511 | * Interpret the expression. | |
6512 | * Params: | |
6513 | * pue = non-null pointer to temporary storage that can be used to store the return value | |
6514 | * e = Expression to interpret | |
6515 | * istate = context | |
6516 | * goal = what the result will be used for | |
6517 | * Returns: | |
6518 | * resulting expression | |
6519 | */ | |
6520 | ||
6521 | Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue) | |
6522 | { | |
6523 | if (!e) | |
6524 | return null; | |
3b007164 | 6525 | //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars()); |
5fee5ec3 IB |
6526 | scope Interpreter v = new Interpreter(pue, istate, goal); |
6527 | e.accept(v); | |
6528 | Expression ex = v.result; | |
6529 | assert(goal == CTFEGoal.Nothing || ex !is null); | |
3b007164 | 6530 | //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n"); |
5fee5ec3 IB |
6531 | return ex; |
6532 | } | |
6533 | ||
6534 | /// | |
6535 | Expression interpret(Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue) | |
6536 | { | |
6537 | UnionExp ue = void; | |
6538 | auto result = interpret(&ue, e, istate, goal); | |
6539 | if (result == ue.exp()) | |
6540 | result = ue.copy(); | |
6541 | return result; | |
6542 | } | |
6543 | ||
6544 | /***************************** | |
6545 | * Same as interpret(), but return result allocated in Region. | |
6546 | * Params: | |
6547 | * e = Expression to interpret | |
6548 | * istate = context | |
6549 | * goal = what the result will be used for | |
6550 | * Returns: | |
6551 | * resulting expression | |
6552 | */ | |
6553 | Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTFEGoal.RValue) | |
6554 | { | |
6555 | UnionExp ue = void; | |
6556 | auto result = interpret(&ue, e, istate, goal); | |
6557 | auto uexp = ue.exp(); | |
6558 | if (result != uexp) | |
6559 | return result; | |
6560 | if (mem.isGCEnabled) | |
6561 | return ue.copy(); | |
6562 | ||
6563 | // mimicking UnionExp.copy, but with region allocation | |
6564 | switch (uexp.op) | |
6565 | { | |
9c7d5e88 IB |
6566 | case EXP.cantExpression: return CTFEExp.cantexp; |
6567 | case EXP.voidExpression: return CTFEExp.voidexp; | |
6568 | case EXP.break_: return CTFEExp.breakexp; | |
6569 | case EXP.continue_: return CTFEExp.continueexp; | |
6570 | case EXP.goto_: return CTFEExp.gotoexp; | |
5fee5ec3 IB |
6571 | default: break; |
6572 | } | |
6573 | auto p = ctfeGlobals.region.malloc(uexp.size); | |
6574 | return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size); | |
6575 | } | |
6576 | ||
3b007164 IB |
6577 | private |
6578 | Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) | |
6579 | { | |
6580 | if (exps is original) | |
6581 | { | |
6582 | if (!original) | |
6583 | exps = new Expressions(); | |
6584 | else | |
6585 | exps = original.copy(); | |
6586 | ++ctfeGlobals.numArrayAllocs; | |
6587 | } | |
6588 | return exps; | |
6589 | } | |
6590 | ||
6591 | /** | |
6592 | Given an expression e which is about to be returned from the current | |
6593 | function, generate an error if it contains pointers to local variables. | |
6594 | ||
6595 | Only checks expressions passed by value (pointers to local variables | |
6596 | may already be stored in members of classes, arrays, or AAs which | |
6597 | were passed as mutable function parameters). | |
6598 | Returns: | |
6599 | true if it is safe to return, false if an error was generated. | |
5fee5ec3 | 6600 | */ |
3b007164 IB |
6601 | private |
6602 | bool stopPointersEscaping(const ref Loc loc, Expression e) | |
5fee5ec3 | 6603 | { |
3b007164 IB |
6604 | if (!e.type.hasPointers()) |
6605 | return true; | |
6606 | if (isPointer(e.type)) | |
6607 | { | |
6608 | Expression x = e; | |
6609 | if (auto eaddr = e.isAddrExp()) | |
6610 | x = eaddr.e1; | |
6611 | VarDeclaration v; | |
6612 | while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) | |
6613 | { | |
6614 | if (v.storage_class & STC.ref_) | |
6615 | { | |
6616 | x = getValue(v); | |
6617 | if (auto eaddr = e.isAddrExp()) | |
6618 | eaddr.e1 = x; | |
6619 | continue; | |
6620 | } | |
6621 | if (ctfeGlobals.stack.isInCurrentFrame(v)) | |
6622 | { | |
6623 | error(loc, "returning a pointer to a local stack variable"); | |
6624 | return false; | |
6625 | } | |
6626 | else | |
6627 | break; | |
6628 | } | |
6629 | // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not | |
6630 | // pointing to a local struct or static array. | |
6631 | } | |
6632 | if (auto se = e.isStructLiteralExp()) | |
6633 | { | |
6634 | return stopPointersEscapingFromArray(loc, se.elements); | |
6635 | } | |
6636 | if (auto ale = e.isArrayLiteralExp()) | |
6637 | { | |
6638 | return stopPointersEscapingFromArray(loc, ale.elements); | |
6639 | } | |
6640 | if (auto aae = e.isAssocArrayLiteralExp()) | |
6641 | { | |
6642 | if (!stopPointersEscapingFromArray(loc, aae.keys)) | |
6643 | return false; | |
6644 | return stopPointersEscapingFromArray(loc, aae.values); | |
6645 | } | |
6646 | return true; | |
5fee5ec3 IB |
6647 | } |
6648 | ||
3b007164 IB |
6649 | // Check all elements of an array for escaping local variables. Return false if error |
6650 | private | |
6651 | bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) | |
5fee5ec3 | 6652 | { |
3b007164 IB |
6653 | foreach (e; *elems) |
6654 | { | |
6655 | if (e && !stopPointersEscaping(loc, e)) | |
6656 | return false; | |
6657 | } | |
6658 | return true; | |
6659 | } | |
6660 | ||
6661 | private | |
6662 | Statement findGotoTarget(InterState* istate, Identifier ident) | |
6663 | { | |
6664 | Statement target = null; | |
6665 | if (ident) | |
6666 | { | |
6667 | LabelDsymbol label = istate.fd.searchLabel(ident); | |
6668 | assert(label && label.statement); | |
6669 | LabelStatement ls = label.statement; | |
6670 | target = ls.gotoTarget ? ls.gotoTarget : ls.statement; | |
6671 | } | |
6672 | return target; | |
6673 | } | |
6674 | ||
6675 | private | |
6676 | ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) | |
6677 | { | |
6678 | debug (LOG) | |
6679 | { | |
6680 | printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); | |
6681 | } | |
6682 | // Little sanity check to make sure it's really a Throwable | |
6683 | ClassReferenceExp boss = oldest.thrown; | |
6684 | const next = 5; // index of Throwable.next | |
6685 | assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next | |
6686 | ClassReferenceExp collateral = newest.thrown; | |
6687 | if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) | |
6688 | { | |
6689 | /* Find the index of the Error.bypassException field | |
6690 | */ | |
6691 | auto bypass = next + 1; | |
6692 | if ((*collateral.value.elements)[bypass].type.ty == Tuns32) | |
6693 | bypass += 1; // skip over _refcount field | |
6694 | assert((*collateral.value.elements)[bypass].type.ty == Tclass); | |
6695 | ||
6696 | // The new exception bypass the existing chain | |
6697 | (*collateral.value.elements)[bypass] = boss; | |
6698 | return newest; | |
6699 | } | |
6700 | while ((*boss.value.elements)[next].op == EXP.classReference) | |
6701 | { | |
6702 | boss = (*boss.value.elements)[next].isClassReferenceExp(); | |
6703 | } | |
6704 | (*boss.value.elements)[next] = collateral; | |
6705 | return oldest; | |
5fee5ec3 IB |
6706 | } |
6707 | ||
6708 | /** | |
6709 | * All results destined for use outside of CTFE need to have their CTFE-specific | |
6710 | * features removed. | |
6711 | * In particular, | |
6712 | * 1. all slices must be resolved. | |
6713 | * 2. all .ownedByCtfe set to OwnedBy.code | |
6714 | */ | |
6715 | private Expression scrubReturnValue(const ref Loc loc, Expression e) | |
6716 | { | |
6717 | /* Returns: true if e is void, | |
6718 | * or is an array literal or struct literal of void elements. | |
6719 | */ | |
6720 | static bool isVoid(const Expression e, bool checkArrayType = false) pure | |
6721 | { | |
9c7d5e88 | 6722 | if (e.op == EXP.void_) |
5fee5ec3 IB |
6723 | return true; |
6724 | ||
6725 | static bool isEntirelyVoid(const Expressions* elems) | |
6726 | { | |
6727 | foreach (e; *elems) | |
6728 | { | |
6729 | // It can be NULL for performance reasons, | |
6730 | // see StructLiteralExp::interpret(). | |
6731 | if (e && !isVoid(e)) | |
6732 | return false; | |
6733 | } | |
6734 | return true; | |
6735 | } | |
6736 | ||
6737 | if (auto sle = e.isStructLiteralExp()) | |
6738 | return isEntirelyVoid(sle.elements); | |
6739 | ||
6740 | if (checkArrayType && e.type.ty != Tsarray) | |
6741 | return false; | |
6742 | ||
6743 | if (auto ale = e.isArrayLiteralExp()) | |
6744 | return isEntirelyVoid(ale.elements); | |
6745 | ||
6746 | return false; | |
6747 | } | |
6748 | ||
6749 | ||
6750 | /* Scrub all elements of elems[]. | |
6751 | * Returns: null for success, error Expression for failure | |
6752 | */ | |
6753 | Expression scrubArray(Expressions* elems, bool structlit = false) | |
6754 | { | |
6755 | foreach (ref e; *elems) | |
6756 | { | |
6757 | // It can be NULL for performance reasons, | |
6758 | // see StructLiteralExp::interpret(). | |
6759 | if (!e) | |
6760 | continue; | |
6761 | ||
6762 | // A struct .init may contain void members. | |
6763 | // Static array members are a weird special case https://issues.dlang.org/show_bug.cgi?id=10994 | |
6764 | if (structlit && isVoid(e, true)) | |
6765 | { | |
6766 | e = null; | |
6767 | } | |
6768 | else | |
6769 | { | |
6770 | e = scrubReturnValue(loc, e); | |
9c7d5e88 | 6771 | if (CTFEExp.isCantExp(e) || e.op == EXP.error) |
5fee5ec3 IB |
6772 | return e; |
6773 | } | |
6774 | } | |
6775 | return null; | |
6776 | } | |
6777 | ||
6778 | Expression scrubSE(StructLiteralExp sle) | |
6779 | { | |
6780 | sle.ownedByCtfe = OwnedBy.code; | |
6781 | if (!(sle.stageflags & stageScrub)) | |
6782 | { | |
6783 | const old = sle.stageflags; | |
6784 | sle.stageflags |= stageScrub; // prevent infinite recursion | |
6785 | if (auto ex = scrubArray(sle.elements, true)) | |
6786 | return ex; | |
6787 | sle.stageflags = old; | |
6788 | } | |
6789 | return null; | |
6790 | } | |
6791 | ||
9c7d5e88 | 6792 | if (e.op == EXP.classReference) |
5fee5ec3 | 6793 | { |
235d5a96 | 6794 | StructLiteralExp sle = e.isClassReferenceExp().value; |
5fee5ec3 IB |
6795 | if (auto ex = scrubSE(sle)) |
6796 | return ex; | |
6797 | } | |
6798 | else if (auto vie = e.isVoidInitExp()) | |
6799 | { | |
6800 | error(loc, "uninitialized variable `%s` cannot be returned from CTFE", vie.var.toChars()); | |
6801 | return ErrorExp.get(); | |
6802 | } | |
6803 | ||
6804 | e = resolveSlice(e); | |
6805 | ||
6806 | if (auto sle = e.isStructLiteralExp()) | |
6807 | { | |
6808 | if (auto ex = scrubSE(sle)) | |
6809 | return ex; | |
6810 | } | |
6811 | else if (auto se = e.isStringExp()) | |
6812 | { | |
6813 | se.ownedByCtfe = OwnedBy.code; | |
6814 | } | |
6815 | else if (auto ale = e.isArrayLiteralExp()) | |
6816 | { | |
6817 | ale.ownedByCtfe = OwnedBy.code; | |
6818 | if (auto ex = scrubArray(ale.elements)) | |
6819 | return ex; | |
6820 | } | |
6821 | else if (auto aae = e.isAssocArrayLiteralExp()) | |
6822 | { | |
6823 | aae.ownedByCtfe = OwnedBy.code; | |
6824 | if (auto ex = scrubArray(aae.keys)) | |
6825 | return ex; | |
6826 | if (auto ex = scrubArray(aae.values)) | |
6827 | return ex; | |
6828 | aae.type = toBuiltinAAType(aae.type); | |
6829 | } | |
6830 | else if (auto ve = e.isVectorExp()) | |
6831 | { | |
6832 | ve.ownedByCtfe = OwnedBy.code; | |
6833 | if (auto ale = ve.e1.isArrayLiteralExp()) | |
6834 | { | |
6835 | ale.ownedByCtfe = OwnedBy.code; | |
6836 | if (auto ex = scrubArray(ale.elements)) | |
6837 | return ex; | |
6838 | } | |
6839 | } | |
6840 | return e; | |
6841 | } | |
6842 | ||
6843 | /************************************** | |
6844 | * Transitively set all .ownedByCtfe to OwnedBy.cache | |
6845 | */ | |
6846 | private Expression scrubCacheValue(Expression e) | |
6847 | { | |
6848 | if (!e) | |
6849 | return e; | |
6850 | ||
6851 | Expression scrubArrayCache(Expressions* elems) | |
6852 | { | |
6853 | foreach (ref e; *elems) | |
6854 | e = scrubCacheValue(e); | |
6855 | return null; | |
6856 | } | |
6857 | ||
6858 | Expression scrubSE(StructLiteralExp sle) | |
6859 | { | |
6860 | sle.ownedByCtfe = OwnedBy.cache; | |
6861 | if (!(sle.stageflags & stageScrub)) | |
6862 | { | |
6863 | const old = sle.stageflags; | |
6864 | sle.stageflags |= stageScrub; // prevent infinite recursion | |
6865 | if (auto ex = scrubArrayCache(sle.elements)) | |
6866 | return ex; | |
6867 | sle.stageflags = old; | |
6868 | } | |
6869 | return null; | |
6870 | } | |
6871 | ||
9c7d5e88 | 6872 | if (e.op == EXP.classReference) |
5fee5ec3 | 6873 | { |
235d5a96 | 6874 | if (auto ex = scrubSE(e.isClassReferenceExp().value)) |
5fee5ec3 IB |
6875 | return ex; |
6876 | } | |
6877 | else if (auto sle = e.isStructLiteralExp()) | |
6878 | { | |
6879 | if (auto ex = scrubSE(sle)) | |
6880 | return ex; | |
6881 | } | |
6882 | else if (auto se = e.isStringExp()) | |
6883 | { | |
6884 | se.ownedByCtfe = OwnedBy.cache; | |
6885 | } | |
6886 | else if (auto ale = e.isArrayLiteralExp()) | |
6887 | { | |
6888 | ale.ownedByCtfe = OwnedBy.cache; | |
6889 | if (Expression ex = scrubArrayCache(ale.elements)) | |
6890 | return ex; | |
6891 | } | |
6892 | else if (auto aae = e.isAssocArrayLiteralExp()) | |
6893 | { | |
6894 | aae.ownedByCtfe = OwnedBy.cache; | |
6895 | if (auto ex = scrubArrayCache(aae.keys)) | |
6896 | return ex; | |
6897 | if (auto ex = scrubArrayCache(aae.values)) | |
6898 | return ex; | |
6899 | } | |
6900 | else if (auto ve = e.isVectorExp()) | |
6901 | { | |
6902 | ve.ownedByCtfe = OwnedBy.cache; | |
6903 | if (auto ale = ve.e1.isArrayLiteralExp()) | |
6904 | { | |
6905 | ale.ownedByCtfe = OwnedBy.cache; | |
6906 | if (auto ex = scrubArrayCache(ale.elements)) | |
6907 | return ex; | |
6908 | } | |
6909 | } | |
6910 | return e; | |
6911 | } | |
6912 | ||
6913 | /******************************************** | |
6914 | * Transitively replace all Expressions allocated in ctfeGlobals.region | |
6915 | * with Mem owned copies. | |
6916 | * Params: | |
6917 | * e = possible ctfeGlobals.region owned expression | |
6918 | * Returns: | |
6919 | * Mem owned expression | |
6920 | */ | |
6921 | private Expression copyRegionExp(Expression e) | |
6922 | { | |
6923 | if (!e) | |
6924 | return e; | |
6925 | ||
6926 | static void copyArray(Expressions* elems) | |
6927 | { | |
6928 | foreach (ref e; *elems) | |
6929 | { | |
6930 | auto ex = e; | |
6931 | e = null; | |
6932 | e = copyRegionExp(ex); | |
6933 | } | |
6934 | } | |
6935 | ||
6936 | static void copySE(StructLiteralExp sle) | |
6937 | { | |
6938 | if (1 || !(sle.stageflags & stageScrub)) | |
6939 | { | |
6940 | const old = sle.stageflags; | |
6941 | sle.stageflags |= stageScrub; // prevent infinite recursion | |
6942 | copyArray(sle.elements); | |
6943 | sle.stageflags = old; | |
6944 | } | |
6945 | } | |
6946 | ||
6947 | switch (e.op) | |
6948 | { | |
9c7d5e88 | 6949 | case EXP.classReference: |
5fee5ec3 IB |
6950 | { |
6951 | auto cre = e.isClassReferenceExp(); | |
6952 | cre.value = copyRegionExp(cre.value).isStructLiteralExp(); | |
6953 | break; | |
6954 | } | |
6955 | ||
9c7d5e88 | 6956 | case EXP.structLiteral: |
5fee5ec3 IB |
6957 | { |
6958 | auto sle = e.isStructLiteralExp(); | |
6959 | ||
6960 | /* The following is to take care of updating sle.origin correctly, | |
6961 | * which may have multiple objects pointing to it. | |
6962 | */ | |
6963 | if (sle.isOriginal && !ctfeGlobals.region.contains(cast(void*)sle.origin)) | |
6964 | { | |
6965 | /* This means sle has already been moved out of the region, | |
6966 | * and sle.origin is the new location. | |
6967 | */ | |
6968 | return sle.origin; | |
6969 | } | |
6970 | copySE(sle); | |
6971 | sle.isOriginal = sle is sle.origin; | |
6972 | ||
6973 | auto slec = ctfeGlobals.region.contains(cast(void*)e) | |
6974 | ? e.copy().isStructLiteralExp() // move sle out of region to slec | |
6975 | : sle; | |
6976 | ||
6977 | if (ctfeGlobals.region.contains(cast(void*)sle.origin)) | |
6978 | { | |
6979 | auto sleo = sle.origin == sle ? slec : sle.origin.copy().isStructLiteralExp(); | |
6980 | sle.origin = sleo; | |
6981 | slec.origin = sleo; | |
6982 | } | |
6983 | return slec; | |
6984 | } | |
6985 | ||
9c7d5e88 | 6986 | case EXP.arrayLiteral: |
5fee5ec3 IB |
6987 | { |
6988 | auto ale = e.isArrayLiteralExp(); | |
6989 | ale.basis = copyRegionExp(ale.basis); | |
6990 | copyArray(ale.elements); | |
6991 | break; | |
6992 | } | |
6993 | ||
9c7d5e88 | 6994 | case EXP.assocArrayLiteral: |
5fee5ec3 IB |
6995 | copyArray(e.isAssocArrayLiteralExp().keys); |
6996 | copyArray(e.isAssocArrayLiteralExp().values); | |
6997 | break; | |
6998 | ||
9c7d5e88 | 6999 | case EXP.slice: |
5fee5ec3 IB |
7000 | { |
7001 | auto se = e.isSliceExp(); | |
7002 | se.e1 = copyRegionExp(se.e1); | |
7003 | se.upr = copyRegionExp(se.upr); | |
7004 | se.lwr = copyRegionExp(se.lwr); | |
7005 | break; | |
7006 | } | |
7007 | ||
9c7d5e88 | 7008 | case EXP.tuple: |
5fee5ec3 IB |
7009 | { |
7010 | auto te = e.isTupleExp(); | |
7011 | te.e0 = copyRegionExp(te.e0); | |
7012 | copyArray(te.exps); | |
7013 | break; | |
7014 | } | |
7015 | ||
9c7d5e88 IB |
7016 | case EXP.address: |
7017 | case EXP.delegate_: | |
7018 | case EXP.vector: | |
7019 | case EXP.dotVariable: | |
5fee5ec3 | 7020 | { |
235d5a96 | 7021 | UnaExp ue = e.isUnaExp(); |
5fee5ec3 IB |
7022 | ue.e1 = copyRegionExp(ue.e1); |
7023 | break; | |
7024 | } | |
7025 | ||
9c7d5e88 | 7026 | case EXP.index: |
5fee5ec3 | 7027 | { |
235d5a96 | 7028 | BinExp be = e.isBinExp(); |
5fee5ec3 IB |
7029 | be.e1 = copyRegionExp(be.e1); |
7030 | be.e2 = copyRegionExp(be.e2); | |
7031 | break; | |
7032 | } | |
7033 | ||
9c7d5e88 IB |
7034 | case EXP.this_: |
7035 | case EXP.super_: | |
7036 | case EXP.variable: | |
7037 | case EXP.type: | |
7038 | case EXP.function_: | |
7039 | case EXP.typeid_: | |
7040 | case EXP.string_: | |
7041 | case EXP.int64: | |
7042 | case EXP.error: | |
7043 | case EXP.float64: | |
7044 | case EXP.complex80: | |
7045 | case EXP.null_: | |
7046 | case EXP.void_: | |
7047 | case EXP.symbolOffset: | |
5fee5ec3 IB |
7048 | break; |
7049 | ||
9c7d5e88 IB |
7050 | case EXP.cantExpression: |
7051 | case EXP.voidExpression: | |
7052 | case EXP.showCtfeContext: | |
5fee5ec3 IB |
7053 | return e; |
7054 | ||
7055 | default: | |
9c7d5e88 | 7056 | printf("e: %s, %s\n", EXPtoString(e.op).ptr, e.toChars()); |
5fee5ec3 IB |
7057 | assert(0); |
7058 | } | |
7059 | ||
7060 | if (ctfeGlobals.region.contains(cast(void*)e)) | |
7061 | { | |
7062 | return e.copy(); | |
7063 | } | |
7064 | return e; | |
7065 | } | |
7066 | ||
7067 | /******************************* Special Functions ***************************/ | |
7068 | ||
7069 | private Expression interpret_length(UnionExp* pue, InterState* istate, Expression earg) | |
7070 | { | |
7071 | //printf("interpret_length()\n"); | |
7072 | earg = interpret(pue, earg, istate); | |
7073 | if (exceptionOrCantInterpret(earg)) | |
7074 | return earg; | |
7075 | dinteger_t len = 0; | |
7076 | if (auto aae = earg.isAssocArrayLiteralExp()) | |
6d799f0a | 7077 | len = aae.keys.length; |
5fee5ec3 | 7078 | else |
9c7d5e88 | 7079 | assert(earg.op == EXP.null_); |
5fee5ec3 IB |
7080 | emplaceExp!(IntegerExp)(pue, earg.loc, len, Type.tsize_t); |
7081 | return pue.exp(); | |
7082 | } | |
7083 | ||
7084 | private Expression interpret_keys(UnionExp* pue, InterState* istate, Expression earg, Type returnType) | |
7085 | { | |
7086 | debug (LOG) | |
7087 | { | |
7088 | printf("interpret_keys()\n"); | |
7089 | } | |
7090 | earg = interpret(pue, earg, istate); | |
7091 | if (exceptionOrCantInterpret(earg)) | |
7092 | return earg; | |
9c7d5e88 | 7093 | if (earg.op == EXP.null_) |
5fee5ec3 IB |
7094 | { |
7095 | emplaceExp!(NullExp)(pue, earg.loc, earg.type); | |
7096 | return pue.exp(); | |
7097 | } | |
9c7d5e88 | 7098 | if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray) |
5fee5ec3 IB |
7099 | return null; |
7100 | AssocArrayLiteralExp aae = earg.isAssocArrayLiteralExp(); | |
7101 | auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.keys); | |
7102 | ae.ownedByCtfe = aae.ownedByCtfe; | |
7103 | *pue = copyLiteral(ae); | |
7104 | return pue.exp(); | |
7105 | } | |
7106 | ||
7107 | private Expression interpret_values(UnionExp* pue, InterState* istate, Expression earg, Type returnType) | |
7108 | { | |
7109 | debug (LOG) | |
7110 | { | |
7111 | printf("interpret_values()\n"); | |
7112 | } | |
7113 | earg = interpret(pue, earg, istate); | |
7114 | if (exceptionOrCantInterpret(earg)) | |
7115 | return earg; | |
9c7d5e88 | 7116 | if (earg.op == EXP.null_) |
5fee5ec3 IB |
7117 | { |
7118 | emplaceExp!(NullExp)(pue, earg.loc, earg.type); | |
7119 | return pue.exp(); | |
7120 | } | |
9c7d5e88 | 7121 | if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray) |
5fee5ec3 IB |
7122 | return null; |
7123 | auto aae = earg.isAssocArrayLiteralExp(); | |
7124 | auto ae = ctfeEmplaceExp!ArrayLiteralExp(aae.loc, returnType, aae.values); | |
7125 | ae.ownedByCtfe = aae.ownedByCtfe; | |
7126 | //printf("result is %s\n", e.toChars()); | |
7127 | *pue = copyLiteral(ae); | |
7128 | return pue.exp(); | |
7129 | } | |
7130 | ||
7131 | private Expression interpret_dup(UnionExp* pue, InterState* istate, Expression earg) | |
7132 | { | |
7133 | debug (LOG) | |
7134 | { | |
7135 | printf("interpret_dup()\n"); | |
7136 | } | |
7137 | earg = interpret(pue, earg, istate); | |
7138 | if (exceptionOrCantInterpret(earg)) | |
7139 | return earg; | |
9c7d5e88 | 7140 | if (earg.op == EXP.null_) |
5fee5ec3 IB |
7141 | { |
7142 | emplaceExp!(NullExp)(pue, earg.loc, earg.type); | |
7143 | return pue.exp(); | |
7144 | } | |
9c7d5e88 | 7145 | if (earg.op != EXP.assocArrayLiteral && earg.type.toBasetype().ty != Taarray) |
5fee5ec3 IB |
7146 | return null; |
7147 | auto aae = copyLiteral(earg).copy().isAssocArrayLiteralExp(); | |
6d799f0a | 7148 | for (size_t i = 0; i < aae.keys.length; i++) |
5fee5ec3 IB |
7149 | { |
7150 | if (Expression e = evaluatePostblit(istate, (*aae.keys)[i])) | |
7151 | return e; | |
7152 | if (Expression e = evaluatePostblit(istate, (*aae.values)[i])) | |
7153 | return e; | |
7154 | } | |
7155 | aae.type = earg.type.mutableOf(); // repaint type from const(int[int]) to const(int)[int] | |
7156 | //printf("result is %s\n", aae.toChars()); | |
7157 | return aae; | |
7158 | } | |
7159 | ||
7160 | // signature is int delegate(ref Value) OR int delegate(ref Key, ref Value) | |
7161 | private Expression interpret_aaApply(UnionExp* pue, InterState* istate, Expression aa, Expression deleg) | |
7162 | { | |
7163 | aa = interpret(aa, istate); | |
7164 | if (exceptionOrCantInterpret(aa)) | |
7165 | return aa; | |
9c7d5e88 | 7166 | if (aa.op != EXP.assocArrayLiteral) |
5fee5ec3 IB |
7167 | { |
7168 | emplaceExp!(IntegerExp)(pue, deleg.loc, 0, Type.tsize_t); | |
7169 | return pue.exp(); | |
7170 | } | |
7171 | ||
7172 | FuncDeclaration fd = null; | |
7173 | Expression pthis = null; | |
7174 | if (auto de = deleg.isDelegateExp()) | |
7175 | { | |
7176 | fd = de.func; | |
7177 | pthis = de.e1; | |
7178 | } | |
7179 | else if (auto fe = deleg.isFuncExp()) | |
7180 | fd = fe.fd; | |
7181 | ||
7182 | assert(fd && fd.fbody); | |
7183 | assert(fd.parameters); | |
6d799f0a | 7184 | size_t numParams = fd.parameters.length; |
5fee5ec3 IB |
7185 | assert(numParams == 1 || numParams == 2); |
7186 | ||
7187 | Parameter fparam = fd.type.isTypeFunction().parameterList[numParams - 1]; | |
7188 | const wantRefValue = fparam.isReference(); | |
7189 | ||
7190 | Expressions args = Expressions(numParams); | |
7191 | ||
235d5a96 | 7192 | AssocArrayLiteralExp ae = aa.isAssocArrayLiteralExp(); |
6d799f0a | 7193 | if (!ae.keys || ae.keys.length == 0) |
5fee5ec3 IB |
7194 | return ctfeEmplaceExp!IntegerExp(deleg.loc, 0, Type.tsize_t); |
7195 | Expression eresult; | |
7196 | ||
6d799f0a | 7197 | for (size_t i = 0; i < ae.keys.length; ++i) |
5fee5ec3 IB |
7198 | { |
7199 | Expression ekey = (*ae.keys)[i]; | |
7200 | Expression evalue = (*ae.values)[i]; | |
7201 | if (wantRefValue) | |
7202 | { | |
7203 | Type t = evalue.type; | |
7204 | evalue = ctfeEmplaceExp!IndexExp(deleg.loc, ae, ekey); | |
7205 | evalue.type = t; | |
7206 | } | |
7207 | args[numParams - 1] = evalue; | |
7208 | if (numParams == 2) | |
7209 | args[0] = ekey; | |
7210 | ||
7211 | UnionExp ue = void; | |
7212 | eresult = interpretFunction(&ue, fd, istate, &args, pthis); | |
7213 | if (eresult == ue.exp()) | |
7214 | eresult = ue.copy(); | |
7215 | if (exceptionOrCantInterpret(eresult)) | |
7216 | return eresult; | |
7217 | ||
7218 | if (eresult.isIntegerExp().getInteger() != 0) | |
7219 | return eresult; | |
7220 | } | |
7221 | return eresult; | |
7222 | } | |
7223 | ||
7224 | /* Decoding UTF strings for foreach loops. Duplicates the functionality of | |
7225 | * the twelve _aApplyXXn functions in aApply.d in the runtime. | |
7226 | */ | |
7227 | private Expression foreachApplyUtf(UnionExp* pue, InterState* istate, Expression str, Expression deleg, bool rvs) | |
7228 | { | |
7229 | debug (LOG) | |
7230 | { | |
7231 | printf("foreachApplyUtf(%s, %s)\n", str.toChars(), deleg.toChars()); | |
7232 | } | |
7233 | FuncDeclaration fd = null; | |
7234 | Expression pthis = null; | |
7235 | if (auto de = deleg.isDelegateExp()) | |
7236 | { | |
7237 | fd = de.func; | |
7238 | pthis = de.e1; | |
7239 | } | |
7240 | else if (auto fe = deleg.isFuncExp()) | |
7241 | fd = fe.fd; | |
7242 | ||
7243 | assert(fd && fd.fbody); | |
7244 | assert(fd.parameters); | |
6d799f0a | 7245 | size_t numParams = fd.parameters.length; |
5fee5ec3 IB |
7246 | assert(numParams == 1 || numParams == 2); |
7247 | Type charType = (*fd.parameters)[numParams - 1].type; | |
7248 | Type indexType = numParams == 2 ? (*fd.parameters)[0].type : Type.tsize_t; | |
7249 | size_t len = cast(size_t)resolveArrayLength(str); | |
7250 | if (len == 0) | |
7251 | { | |
7252 | emplaceExp!(IntegerExp)(pue, deleg.loc, 0, indexType); | |
7253 | return pue.exp(); | |
7254 | } | |
7255 | ||
7256 | UnionExp strTmp = void; | |
7257 | str = resolveSlice(str, &strTmp); | |
7258 | ||
7259 | auto se = str.isStringExp(); | |
7260 | auto ale = str.isArrayLiteralExp(); | |
7261 | if (!se && !ale) | |
7262 | { | |
7263 | str.error("CTFE internal error: cannot foreach `%s`", str.toChars()); | |
7264 | return CTFEExp.cantexp; | |
7265 | } | |
7266 | Expressions args = Expressions(numParams); | |
7267 | ||
7268 | Expression eresult = null; // ded-store to prevent spurious warning | |
7269 | ||
7270 | // Buffers for encoding; also used for decoding array literals | |
7271 | char[4] utf8buf = void; | |
7272 | wchar[2] utf16buf = void; | |
7273 | ||
7274 | size_t start = rvs ? len : 0; | |
7275 | size_t end = rvs ? 0 : len; | |
7276 | for (size_t indx = start; indx != end;) | |
7277 | { | |
7278 | // Step 1: Decode the next dchar from the string. | |
7279 | ||
7280 | string errmsg = null; // Used for reporting decoding errors | |
7281 | dchar rawvalue; // Holds the decoded dchar | |
7282 | size_t currentIndex = indx; // The index of the decoded character | |
7283 | ||
7284 | if (ale) | |
7285 | { | |
7286 | // If it is an array literal, copy the code points into the buffer | |
7287 | size_t buflen = 1; // #code points in the buffer | |
7288 | size_t n = 1; // #code points in this char | |
7289 | size_t sz = cast(size_t)ale.type.nextOf().size(); | |
7290 | ||
7291 | switch (sz) | |
7292 | { | |
7293 | case 1: | |
7294 | if (rvs) | |
7295 | { | |
7296 | // find the start of the string | |
7297 | --indx; | |
7298 | buflen = 1; | |
7299 | while (indx > 0 && buflen < 4) | |
7300 | { | |
7301 | Expression r = (*ale.elements)[indx]; | |
7302 | char x = cast(char)r.isIntegerExp().getInteger(); | |
7303 | if ((x & 0xC0) != 0x80) | |
7304 | break; | |
7305 | --indx; | |
7306 | ++buflen; | |
7307 | } | |
7308 | } | |
7309 | else | |
7310 | buflen = (indx + 4 > len) ? len - indx : 4; | |
7311 | for (size_t i = 0; i < buflen; ++i) | |
7312 | { | |
7313 | Expression r = (*ale.elements)[indx + i]; | |
7314 | utf8buf[i] = cast(char)r.isIntegerExp().getInteger(); | |
7315 | } | |
7316 | n = 0; | |
7317 | errmsg = utf_decodeChar(utf8buf[0 .. buflen], n, rawvalue); | |
7318 | break; | |
7319 | ||
7320 | case 2: | |
7321 | if (rvs) | |
7322 | { | |
7323 | // find the start of the string | |
7324 | --indx; | |
7325 | buflen = 1; | |
7326 | Expression r = (*ale.elements)[indx]; | |
7327 | ushort x = cast(ushort)r.isIntegerExp().getInteger(); | |
7328 | if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF) | |
7329 | { | |
7330 | --indx; | |
7331 | ++buflen; | |
7332 | } | |
7333 | } | |
7334 | else | |
7335 | buflen = (indx + 2 > len) ? len - indx : 2; | |
7336 | for (size_t i = 0; i < buflen; ++i) | |
7337 | { | |
7338 | Expression r = (*ale.elements)[indx + i]; | |
7339 | utf16buf[i] = cast(ushort)r.isIntegerExp().getInteger(); | |
7340 | } | |
7341 | n = 0; | |
7342 | errmsg = utf_decodeWchar(utf16buf[0 .. buflen], n, rawvalue); | |
7343 | break; | |
7344 | ||
7345 | case 4: | |
7346 | { | |
7347 | if (rvs) | |
7348 | --indx; | |
7349 | Expression r = (*ale.elements)[indx]; | |
7350 | rawvalue = cast(dchar)r.isIntegerExp().getInteger(); | |
7351 | n = 1; | |
7352 | } | |
7353 | break; | |
7354 | ||
7355 | default: | |
7356 | assert(0); | |
7357 | } | |
7358 | if (!rvs) | |
7359 | indx += n; | |
7360 | } | |
7361 | else | |
7362 | { | |
7363 | // String literals | |
7364 | size_t saveindx; // used for reverse iteration | |
7365 | ||
7366 | switch (se.sz) | |
7367 | { | |
7368 | case 1: | |
7369 | { | |
7370 | if (rvs) | |
7371 | { | |
7372 | // find the start of the string | |
7373 | --indx; | |
7374 | while (indx > 0 && ((se.getCodeUnit(indx) & 0xC0) == 0x80)) | |
7375 | --indx; | |
7376 | saveindx = indx; | |
7377 | } | |
7378 | auto slice = se.peekString(); | |
7379 | errmsg = utf_decodeChar(slice, indx, rawvalue); | |
7380 | if (rvs) | |
7381 | indx = saveindx; | |
7382 | break; | |
7383 | } | |
7384 | ||
7385 | case 2: | |
7386 | if (rvs) | |
7387 | { | |
7388 | // find the start | |
7389 | --indx; | |
7390 | auto wc = se.getCodeUnit(indx); | |
7391 | if (wc >= 0xDC00 && wc <= 0xDFFF) | |
7392 | --indx; | |
7393 | saveindx = indx; | |
7394 | } | |
7395 | const slice = se.peekWstring(); | |
7396 | errmsg = utf_decodeWchar(slice, indx, rawvalue); | |
7397 | if (rvs) | |
7398 | indx = saveindx; | |
7399 | break; | |
7400 | ||
7401 | case 4: | |
7402 | if (rvs) | |
7403 | --indx; | |
7404 | rawvalue = se.getCodeUnit(indx); | |
7405 | if (!rvs) | |
7406 | ++indx; | |
7407 | break; | |
7408 | ||
7409 | default: | |
7410 | assert(0); | |
7411 | } | |
7412 | } | |
7413 | if (errmsg) | |
7414 | { | |
7415 | deleg.error("`%.*s`", cast(int)errmsg.length, errmsg.ptr); | |
7416 | return CTFEExp.cantexp; | |
7417 | } | |
7418 | ||
7419 | // Step 2: encode the dchar in the target encoding | |
7420 | ||
7421 | int charlen = 1; // How many codepoints are involved? | |
7422 | switch (charType.size()) | |
7423 | { | |
7424 | case 1: | |
7425 | charlen = utf_codeLengthChar(rawvalue); | |
7426 | utf_encodeChar(&utf8buf[0], rawvalue); | |
7427 | break; | |
7428 | case 2: | |
7429 | charlen = utf_codeLengthWchar(rawvalue); | |
7430 | utf_encodeWchar(&utf16buf[0], rawvalue); | |
7431 | break; | |
7432 | case 4: | |
7433 | break; | |
7434 | default: | |
7435 | assert(0); | |
7436 | } | |
7437 | if (rvs) | |
7438 | currentIndex = indx; | |
7439 | ||
7440 | // Step 3: call the delegate once for each code point | |
7441 | ||
7442 | // The index only needs to be set once | |
7443 | if (numParams == 2) | |
7444 | args[0] = ctfeEmplaceExp!IntegerExp(deleg.loc, currentIndex, indexType); | |
7445 | ||
7446 | Expression val = null; | |
7447 | ||
7448 | foreach (k; 0 .. charlen) | |
7449 | { | |
7450 | dchar codepoint; | |
7451 | switch (charType.size()) | |
7452 | { | |
7453 | case 1: | |
7454 | codepoint = utf8buf[k]; | |
7455 | break; | |
7456 | case 2: | |
7457 | codepoint = utf16buf[k]; | |
7458 | break; | |
7459 | case 4: | |
7460 | codepoint = rawvalue; | |
7461 | break; | |
7462 | default: | |
7463 | assert(0); | |
7464 | } | |
7465 | val = ctfeEmplaceExp!IntegerExp(str.loc, codepoint, charType); | |
7466 | ||
7467 | args[numParams - 1] = val; | |
7468 | ||
7469 | UnionExp ue = void; | |
7470 | eresult = interpretFunction(&ue, fd, istate, &args, pthis); | |
7471 | if (eresult == ue.exp()) | |
7472 | eresult = ue.copy(); | |
7473 | if (exceptionOrCantInterpret(eresult)) | |
7474 | return eresult; | |
7475 | if (eresult.isIntegerExp().getInteger() != 0) | |
7476 | return eresult; | |
7477 | } | |
7478 | } | |
7479 | return eresult; | |
7480 | } | |
7481 | ||
7482 | /* If this is a built-in function, return the interpreted result, | |
7483 | * Otherwise, return NULL. | |
7484 | */ | |
7485 | private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis) | |
7486 | { | |
7487 | Expression e = null; | |
6d799f0a | 7488 | size_t nargs = arguments ? arguments.length : 0; |
5fee5ec3 IB |
7489 | if (!pthis) |
7490 | { | |
7491 | if (isBuiltin(fd) != BUILTIN.unimp) | |
7492 | { | |
7493 | Expressions args = Expressions(nargs); | |
7494 | foreach (i, ref arg; args) | |
7495 | { | |
7496 | Expression earg = (*arguments)[i]; | |
7497 | earg = interpret(earg, istate); | |
7498 | if (exceptionOrCantInterpret(earg)) | |
7499 | return earg; | |
7500 | arg = earg; | |
7501 | } | |
7502 | e = eval_builtin(loc, fd, &args); | |
7503 | if (!e) | |
7504 | { | |
7505 | error(loc, "cannot evaluate unimplemented builtin `%s` at compile time", fd.toChars()); | |
7506 | e = CTFEExp.cantexp; | |
7507 | } | |
7508 | } | |
7509 | } | |
7510 | if (!pthis) | |
7511 | { | |
7512 | if (nargs == 1 || nargs == 3) | |
7513 | { | |
7514 | Expression firstarg = (*arguments)[0]; | |
7515 | if (auto firstAAtype = firstarg.type.toBasetype().isTypeAArray()) | |
7516 | { | |
7517 | const id = fd.ident; | |
7518 | if (nargs == 1) | |
7519 | { | |
7520 | if (id == Id.aaLen) | |
7521 | return interpret_length(pue, istate, firstarg); | |
7522 | ||
7523 | if (fd.toParent2().ident == Id.object) | |
7524 | { | |
7525 | if (id == Id.keys) | |
7526 | return interpret_keys(pue, istate, firstarg, firstAAtype.index.arrayOf()); | |
7527 | if (id == Id.values) | |
7528 | return interpret_values(pue, istate, firstarg, firstAAtype.nextOf().arrayOf()); | |
7529 | if (id == Id.rehash) | |
7530 | return interpret(pue, firstarg, istate); | |
7531 | if (id == Id.dup) | |
7532 | return interpret_dup(pue, istate, firstarg); | |
7533 | } | |
7534 | } | |
7535 | else // (nargs == 3) | |
7536 | { | |
7537 | if (id == Id._aaApply) | |
7538 | return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]); | |
7539 | if (id == Id._aaApply2) | |
7540 | return interpret_aaApply(pue, istate, firstarg, (*arguments)[2]); | |
7541 | } | |
7542 | } | |
7543 | } | |
7544 | } | |
7545 | if (pthis && !fd.fbody && fd.isCtorDeclaration() && fd.parent && fd.parent.parent && fd.parent.parent.ident == Id.object) | |
7546 | { | |
9c7d5e88 | 7547 | if (pthis.op == EXP.classReference && fd.parent.ident == Id.Throwable) |
5fee5ec3 IB |
7548 | { |
7549 | // At present, the constructors just copy their arguments into the struct. | |
7550 | // But we might need some magic if stack tracing gets added to druntime. | |
235d5a96 | 7551 | StructLiteralExp se = pthis.isClassReferenceExp().value; |
6d799f0a | 7552 | assert(arguments.length <= se.elements.length); |
5fee5ec3 IB |
7553 | foreach (i, arg; *arguments) |
7554 | { | |
7555 | auto elem = interpret(arg, istate); | |
7556 | if (exceptionOrCantInterpret(elem)) | |
7557 | return elem; | |
7558 | (*se.elements)[i] = elem; | |
7559 | } | |
7560 | return CTFEExp.voidexp; | |
7561 | } | |
7562 | } | |
7563 | if (nargs == 1 && !pthis && (fd.ident == Id.criticalenter || fd.ident == Id.criticalexit)) | |
7564 | { | |
7565 | // Support synchronized{} as a no-op | |
7566 | return CTFEExp.voidexp; | |
7567 | } | |
7568 | if (!pthis) | |
7569 | { | |
7570 | const idlen = fd.ident.toString().length; | |
7571 | const id = fd.ident.toChars(); | |
7572 | if (nargs == 2 && (idlen == 10 || idlen == 11) && !strncmp(id, "_aApply", 7)) | |
7573 | { | |
7574 | // Functions from aApply.d and aApplyR.d in the runtime | |
7575 | bool rvs = (idlen == 11); // true if foreach_reverse | |
7576 | char c = id[idlen - 3]; // char width: 'c', 'w', or 'd' | |
7577 | char s = id[idlen - 2]; // string width: 'c', 'w', or 'd' | |
7578 | char n = id[idlen - 1]; // numParams: 1 or 2. | |
7579 | // There are 12 combinations | |
7580 | if ((n == '1' || n == '2') && | |
7581 | (c == 'c' || c == 'w' || c == 'd') && | |
7582 | (s == 'c' || s == 'w' || s == 'd') && | |
7583 | c != s) | |
7584 | { | |
7585 | Expression str = (*arguments)[0]; | |
7586 | str = interpret(str, istate); | |
7587 | if (exceptionOrCantInterpret(str)) | |
7588 | return str; | |
7589 | return foreachApplyUtf(pue, istate, str, (*arguments)[1], rvs); | |
7590 | } | |
7591 | } | |
7592 | } | |
7593 | return e; | |
7594 | } | |
7595 | ||
7596 | private Expression evaluatePostblit(InterState* istate, Expression e) | |
7597 | { | |
7598 | auto ts = e.type.baseElemOf().isTypeStruct(); | |
7599 | if (!ts) | |
7600 | return null; | |
7601 | StructDeclaration sd = ts.sym; | |
7602 | if (!sd.postblit) | |
7603 | return null; | |
7604 | ||
7605 | if (auto ale = e.isArrayLiteralExp()) | |
7606 | { | |
7607 | foreach (elem; *ale.elements) | |
7608 | { | |
7609 | if (auto ex = evaluatePostblit(istate, elem)) | |
7610 | return ex; | |
7611 | } | |
7612 | return null; | |
7613 | } | |
9c7d5e88 | 7614 | if (e.op == EXP.structLiteral) |
5fee5ec3 IB |
7615 | { |
7616 | // e.__postblit() | |
7617 | UnionExp ue = void; | |
7618 | e = interpretFunction(&ue, sd.postblit, istate, null, e); | |
7619 | if (e == ue.exp()) | |
7620 | e = ue.copy(); | |
7621 | if (exceptionOrCantInterpret(e)) | |
7622 | return e; | |
7623 | return null; | |
7624 | } | |
7625 | assert(0); | |
7626 | } | |
7627 | ||
7628 | private Expression evaluateDtor(InterState* istate, Expression e) | |
7629 | { | |
7630 | auto ts = e.type.baseElemOf().isTypeStruct(); | |
7631 | if (!ts) | |
7632 | return null; | |
7633 | StructDeclaration sd = ts.sym; | |
7634 | if (!sd.dtor) | |
7635 | return null; | |
7636 | ||
7637 | UnionExp ue = void; | |
7638 | if (auto ale = e.isArrayLiteralExp()) | |
7639 | { | |
7640 | foreach_reverse (elem; *ale.elements) | |
7641 | e = evaluateDtor(istate, elem); | |
7642 | } | |
9c7d5e88 | 7643 | else if (e.op == EXP.structLiteral) |
5fee5ec3 IB |
7644 | { |
7645 | // e.__dtor() | |
7646 | e = interpretFunction(&ue, sd.dtor, istate, null, e); | |
7647 | } | |
7648 | else | |
7649 | assert(0); | |
7650 | if (exceptionOrCantInterpret(e)) | |
7651 | { | |
7652 | if (e == ue.exp()) | |
7653 | e = ue.copy(); | |
7654 | return e; | |
7655 | } | |
7656 | return null; | |
7657 | } | |
7658 | ||
7659 | /*************************** CTFE Sanity Checks ***************************/ | |
7660 | /* Setter functions for CTFE variable values. | |
7661 | * These functions exist to check for compiler CTFE bugs. | |
7662 | */ | |
7663 | private bool hasValue(VarDeclaration vd) | |
7664 | { | |
7665 | return vd.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone && | |
7666 | getValue(vd) !is null; | |
7667 | } | |
7668 | ||
7669 | // Don't check for validity | |
7670 | private void setValueWithoutChecking(VarDeclaration vd, Expression newval) | |
7671 | { | |
7672 | ctfeGlobals.stack.setValue(vd, newval); | |
7673 | } | |
7674 | ||
7675 | private void setValue(VarDeclaration vd, Expression newval) | |
7676 | { | |
7677 | //printf("setValue() vd: %s newval: %s\n", vd.toChars(), newval.toChars()); | |
7678 | version (none) | |
7679 | { | |
7680 | if (!((vd.storage_class & (STC.out_ | STC.ref_)) ? isCtfeReferenceValid(newval) : isCtfeValueValid(newval))) | |
7681 | { | |
7682 | printf("[%s] vd = %s %s, newval = %s\n", vd.loc.toChars(), vd.type.toChars(), vd.toChars(), newval.toChars()); | |
7683 | } | |
7684 | } | |
7685 | assert((vd.storage_class & (STC.out_ | STC.ref_)) ? isCtfeReferenceValid(newval) : isCtfeValueValid(newval)); | |
7686 | ctfeGlobals.stack.setValue(vd, newval); | |
7687 | } | |
7688 | ||
7689 | /** | |
7690 | * Removes `_d_HookTraceImpl` if found from `ce` and `fd`. | |
7691 | * This is needed for the CTFE interception code to be able to find hooks that are called though the hook's `*Trace` | |
7692 | * wrapper. | |
7693 | * | |
7694 | * This is done by replacing `_d_HookTraceImpl!(T, Hook, errMsg)(..., parameters)` with `Hook(parameters)`. | |
7695 | * Parameters: | |
7696 | * ce = The CallExp that possible will be be replaced | |
7697 | * fd = Fully resolve function declaration that `ce` would call | |
7698 | */ | |
7699 | private void removeHookTraceImpl(ref CallExp ce, ref FuncDeclaration fd) | |
7700 | { | |
7701 | if (fd.ident != Id._d_HookTraceImpl) | |
7702 | return; | |
7703 | ||
7704 | auto oldCE = ce; | |
7705 | ||
7706 | // Get the Hook from the second template parameter | |
7707 | TemplateInstance templateInstance = fd.parent.isTemplateInstance; | |
7708 | RootObject hook = (*templateInstance.tiargs)[1]; | |
d6679fa2 | 7709 | assert(hook.isDsymbol(), "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!"); |
5fee5ec3 IB |
7710 | fd = (cast(Dsymbol)hook).isFuncDeclaration; |
7711 | ||
7712 | // Remove the first three trace parameters | |
7713 | auto arguments = new Expressions(); | |
6d799f0a | 7714 | arguments.reserve(ce.arguments.length - 3); |
5fee5ec3 IB |
7715 | arguments.pushSlice((*ce.arguments)[3 .. $]); |
7716 | ||
7717 | ce = ctfeEmplaceExp!CallExp(ce.loc, ctfeEmplaceExp!VarExp(ce.loc, fd, false), arguments); | |
7718 | ||
7719 | if (global.params.verbose) | |
7720 | message("strip %s =>\n %s", oldCE.toChars(), ce.toChars()); | |
7721 | } |