Internally these would treat the cast same as a normal conversion from int[7] to int[], which allows code at CTFE to erroneously succeed where it would raise a SEGV at run-time.
1 |
/**
|
|
2 |
* The entry point for CTFE.
|
|
3 |
*
|
|
4 |
* Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE))
|
|
5 |
*
|
|
6 |
* Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
|
|
7 |
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
|
|
8 |
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
9 |
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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; |
|
19 |
import dmd.apply; |
|
20 |
import dmd.arraytypes; |
|
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; |
|
36 |
import dmd.id; |
|
37 |
import dmd.identifier; |
|
38 |
import dmd.init; |
|
39 |
import dmd.initsem; |
|
40 |
import dmd.mtype; |
|
41 |
import dmd.root.rmem; |
|
42 |
import dmd.root.array; |
|
43 |
import dmd.root.region; |
|
44 |
import dmd.root.rootobject; |
|
45 |
import dmd.statement; |
|
46 |
import dmd.tokens; |
|
47 |
import dmd.utf; |
|
48 |
import dmd.visitor; |
|
49 |
|
|
50 |
/*************************************
|
|
51 |
* Entry point for CTFE.
|
|
52 |
* A compile-time result is required. Give an error if not possible.
|
|
53 |
*
|
|
54 |
* `e` must be semantically valid expression. In other words, it should not
|
|
55 |
* contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
|
|
56 |
* functions and may invoke a function that contains `ErrorStatement` in its body.
|
|
57 |
* If that, the "CTFE failed because of previous errors" error is raised.
|
|
58 |
*/
|
|
59 |
public Expression ctfeInterpret(Expression e) |
|
60 |
{
|
|
61 | 1 |
switch (e.op) |
62 |
{
|
|
63 | 1 |
case TOK.int64: |
64 | 1 |
case TOK.float64: |
65 | 1 |
case TOK.complex80: |
66 | 1 |
case TOK.null_: |
67 | 1 |
case TOK.void_: |
68 | 1 |
case TOK.string_: |
69 | 1 |
case TOK.this_: |
70 | 1 |
case TOK.super_: |
71 | 1 |
case TOK.type: |
72 | 1 |
case TOK.typeid_: |
73 | 1 |
if (e.type.ty == Terror) |
74 | 1 |
return ErrorExp.get(); |
75 | 1 |
goto case TOK.error; |
76 |
|
|
77 | 1 |
case TOK.error: |
78 | 1 |
return e; |
79 |
|
|
80 | 1 |
default: |
81 | 1 |
break; |
82 |
}
|
|
83 |
|
|
84 | 1 |
assert(e.type); // https://issues.dlang.org/show_bug.cgi?id=14642 |
85 |
//assert(e.type.ty != Terror); // FIXME
|
|
86 | 1 |
if (e.type.ty == Terror) |
87 |
return ErrorExp.get(); |
|
88 |
|
|
89 | 1 |
auto rgnpos = ctfeGlobals.region.savePos(); |
90 |
|
|
91 | 1 |
Expression result = interpret(e, null); |
92 |
|
|
93 | 1 |
result = copyRegionExp(result); |
94 |
|
|
95 | 1 |
if (!CTFEExp.isCantExp(result)) |
96 | 1 |
result = scrubReturnValue(e.loc, result); |
97 | 1 |
if (CTFEExp.isCantExp(result)) |
98 | 1 |
result = ErrorExp.get(); |
99 |
|
|
100 | 1 |
ctfeGlobals.region.release(rgnpos); |
101 |
|
|
102 | 1 |
return result; |
103 |
}
|
|
104 |
|
|
105 |
/* Run CTFE on the expression, but allow the expression to be a TypeExp
|
|
106 |
* or a tuple containing a TypeExp. (This is required by pragma(msg)).
|
|
107 |
*/
|
|
108 |
public Expression ctfeInterpretForPragmaMsg(Expression e) |
|
109 |
{
|
|
110 | 1 |
if (e.op == TOK.error || e.op == TOK.type) |
111 | 1 |
return e; |
112 |
|
|
113 |
// It's also OK for it to be a function declaration (happens only with
|
|
114 |
// __traits(getOverloads))
|
|
115 | 1 |
if (auto ve = e.isVarExp()) |
116 | 1 |
if (ve.var.isFuncDeclaration()) |
117 |
{
|
|
118 | 1 |
return e; |
119 |
}
|
|
120 |
|
|
121 | 1 |
auto tup = e.isTupleExp(); |
122 | 1 |
if (!tup) |
123 | 1 |
return e.ctfeInterpret(); |
124 |
|
|
125 |
// Tuples need to be treated separately, since they are
|
|
126 |
// allowed to contain a TypeExp in this case.
|
|
127 |
|
|
128 | 1 |
Expressions* expsx = null; |
129 | 1 |
foreach (i, g; *tup.exps) |
130 |
{
|
|
131 | 1 |
auto h = ctfeInterpretForPragmaMsg(g); |
132 | 1 |
if (h != g) |
133 |
{
|
|
134 | 1 |
if (!expsx) |
135 |
{
|
|
136 | 1 |
expsx = tup.exps.copy(); |
137 |
}
|
|
138 | 1 |
(*expsx)[i] = h; |
139 |
}
|
|
140 |
}
|
|
141 | 1 |
if (expsx) |
142 |
{
|
|
143 | 1 |
auto te = new TupleExp(e.loc, expsx); |
144 | 1 |
expandTuples(te.exps); |
145 | 1 |
te.type = new TypeTuple(te.exps); |
146 | 1 |
return te; |
147 |
}
|
|
148 | 1 |
return e; |
149 |
}
|
|
150 |
|
|
151 |
public extern (C++) Expression getValue(VarDeclaration vd) |
|
152 |
{
|
|
153 | 1 |
return ctfeGlobals.stack.getValue(vd); |
154 |
}
|
|
155 |
|
|
156 |
/*************************************************
|
|
157 |
* Allocate an Expression in the ctfe region.
|
|
158 |
* Params:
|
|
159 |
* T = type of Expression to allocate
|
|
160 |
* args = arguments to Expression's constructor
|
|
161 |
* Returns:
|
|
162 |
* allocated Expression
|
|
163 |
*/
|
|
164 |
T ctfeEmplaceExp(T : Expression, Args...)(Args args) |
|
165 |
{
|
|
166 | 1 |
if (mem.isGCEnabled) |
167 | 1 |
return new T(args); |
168 | 1 |
auto p = ctfeGlobals.region.malloc(__traits(classInstanceSize, T)); |
169 | 1 |
emplaceExp!T(p, args); |
170 | 1 |
return cast(T)p; |
171 |
}
|
|
172 |
|
|
173 |
// CTFE diagnostic information
|
|
174 |
public extern (C++) void printCtfePerformanceStats() |
|
175 |
{
|
|
176 |
debug (SHOWPERFORMANCE) |
|
177 |
{
|
|
178 |
printf(" ---- CTFE Performance ----\n"); |
|
179 |
printf("max call depth = %d\tmax stack = %d\n", ctfeGlobals.maxCallDepth, ctfeGlobals.stack.maxStackUsage()); |
|
180 |
printf("array allocs = %d\tassignments = %d\n\n", ctfeGlobals.numArrayAllocs, ctfeGlobals.numAssignments); |
|
181 |
}
|
|
182 |
}
|
|
183 |
|
|
184 |
/**************************
|
|
185 |
*/
|
|
186 |
|
|
187 |
void incArrayAllocs() |
|
188 |
{
|
|
189 | 1 |
++ctfeGlobals.numArrayAllocs; |
190 |
}
|
|
191 |
|
|
192 |
/* ================================================ Implementation ======================================= */
|
|
193 |
|
|
194 |
private: |
|
195 |
|
|
196 |
/***************
|
|
197 |
* Collect together globals used by CTFE
|
|
198 |
*/
|
|
199 |
struct CtfeGlobals |
|
200 |
{
|
|
201 |
Region region; |
|
202 |
|
|
203 |
CtfeStack stack; |
|
204 |
|
|
205 |
int callDepth = 0; // current number of recursive calls |
|
206 |
|
|
207 |
// When printing a stack trace, suppress this number of calls
|
|
208 |
int stackTraceCallsToSuppress = 0; |
|
209 |
|
|
210 |
int maxCallDepth = 0; // highest number of recursive calls |
|
211 |
int numArrayAllocs = 0; // Number of allocated arrays |
|
212 |
int numAssignments = 0; // total number of assignments executed |
|
213 |
}
|
|
214 |
|
|
215 |
__gshared CtfeGlobals ctfeGlobals; |
|
216 |
|
|
217 |
enum CTFEGoal : int |
|
218 |
{
|
|
219 |
RValue, /// Must return an Rvalue (== CTFE value) |
|
220 |
LValue, /// Must return an Lvalue (== CTFE reference) |
|
221 |
Nothing, /// The return value is not required |
|
222 |
}
|
|
223 |
|
|
224 |
//debug = LOG;
|
|
225 |
//debug = LOGASSIGN;
|
|
226 |
//debug = LOGCOMPILE;
|
|
227 |
//debug = SHOWPERFORMANCE;
|
|
228 |
|
|
229 |
// Maximum allowable recursive function calls in CTFE
|
|
230 |
enum CTFE_RECURSION_LIMIT = 1000; |
|
231 |
|
|
232 |
/**
|
|
233 |
The values of all CTFE variables
|
|
234 |
*/
|
|
235 |
struct CtfeStack |
|
236 |
{
|
|
237 |
private: |
|
238 |
/* The stack. Every declaration we encounter is pushed here,
|
|
239 |
* together with the VarDeclaration, and the previous
|
|
240 |
* stack address of that variable, so that we can restore it
|
|
241 |
* when we leave the stack frame.
|
|
242 |
* Note that when a function is forward referenced, the interpreter must
|
|
243 |
* run semantic3, and that may start CTFE again with a NULL istate. Thus
|
|
244 |
* the stack might not be empty when CTFE begins.
|
|
245 |
*
|
|
246 |
* Ctfe Stack addresses are just 0-based integers, but we save
|
|
247 |
* them as 'void *' because Array can only do pointers.
|
|
248 |
*/
|
|
249 |
Expressions values; // values on the stack |
|
250 |
VarDeclarations vars; // corresponding variables |
|
251 |
Array!(void*) savedId; // id of the previous state of that var |
|
252 |
|
|
253 |
Array!(void*) frames; // all previous frame pointers |
|
254 |
Expressions savedThis; // all previous values of localThis |
|
255 |
|
|
256 |
/* Global constants get saved here after evaluation, so we never
|
|
257 |
* have to redo them. This saves a lot of time and memory.
|
|
258 |
*/
|
|
259 |
Expressions globalValues; // values of global constants |
|
260 |
|
|
261 |
size_t framepointer; // current frame pointer |
|
262 |
size_t maxStackPointer; // most stack we've ever used |
|
263 |
Expression localThis; // value of 'this', or NULL if none |
|
264 |
|
|
265 |
public: |
|
266 |
extern (C++) size_t stackPointer() |
|
267 |
{
|
|
268 | 1 |
return values.dim; |
269 |
}
|
|
270 |
|
|
271 |
// The current value of 'this', or NULL if none
|
|
272 |
extern (C++) Expression getThis() |
|
273 |
{
|
|
274 | 1 |
return localThis; |
275 |
}
|
|
276 |
|
|
277 |
// Largest number of stack positions we've used
|
|
278 |
extern (C++) size_t maxStackUsage() |
|
279 |
{
|
|
280 |
return maxStackPointer; |
|
281 |
}
|
|
282 |
|
|
283 |
// Start a new stack frame, using the provided 'this'.
|
|
284 |
extern (C++) void startFrame(Expression thisexp) |
|
285 |
{
|
|
286 | 1 |
frames.push(cast(void*)cast(size_t)framepointer); |
287 | 1 |
savedThis.push(localThis); |
288 | 1 |
framepointer = stackPointer(); |
289 | 1 |
localThis = thisexp; |
290 |
}
|
|
291 |
|
|
292 |
extern (C++) void endFrame() |
|
293 |
{
|
|
294 | 1 |
size_t oldframe = cast(size_t)frames[frames.dim - 1]; |
295 | 1 |
localThis = savedThis[savedThis.dim - 1]; |
296 | 1 |
popAll(framepointer); |
297 | 1 |
framepointer = oldframe; |
298 | 1 |
frames.setDim(frames.dim - 1); |
299 | 1 |
savedThis.setDim(savedThis.dim - 1); |
300 |
}
|
|
301 |
|
|
302 |
extern (C++) bool isInCurrentFrame(VarDeclaration v) |
|
303 |
{
|
|
304 | 1 |
if (v.isDataseg() && !v.isCTFE()) |
305 |
return false; // It's a global |
|
306 | 1 |
return v.ctfeAdrOnStack >= framepointer; |
307 |
}
|
|
308 |
|
|
309 |
extern (C++) Expression getValue(VarDeclaration v) |
|
310 |
{
|
|
311 | 1 |
if ((v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE()) |
312 |
{
|
|
313 | 1 |
assert(v.ctfeAdrOnStack < globalValues.dim); |
314 | 1 |
return globalValues[v.ctfeAdrOnStack]; |
315 |
}
|
|
316 | 1 |
assert(v.ctfeAdrOnStack < stackPointer()); |
317 | 1 |
return values[v.ctfeAdrOnStack]; |
318 |
}
|
|
319 |
|
|
320 |
extern (C++) void setValue(VarDeclaration v, Expression e) |
|
321 |
{
|
|
322 | 1 |
assert(!v.isDataseg() || v.isCTFE()); |
323 | 1 |
assert(v.ctfeAdrOnStack < stackPointer()); |
324 | 1 |
values[v.ctfeAdrOnStack] = e; |
325 |
}
|
|
326 |
|
|
327 |
extern (C++) void push(VarDeclaration v) |
|
328 |
{
|
|
329 | 1 |
assert(!v.isDataseg() || v.isCTFE()); |
330 | 1 |
if (v.ctfeAdrOnStack != VarDeclaration.AdrOnStackNone && v.ctfeAdrOnStack >= framepointer) |
331 |
{
|
|
332 |
// Already exists in this frame, reuse it.
|
|
333 | 1 |
values[v.ctfeAdrOnStack] = null; |
334 | 1 |
return; |
335 |
}
|
|
336 | 1 |
savedId.push(cast(void*)cast(size_t)v.ctfeAdrOnStack); |
337 | 1 |
v.ctfeAdrOnStack = cast(uint)values.dim; |
338 | 1 |
vars.push(v); |
339 | 1 |
values.push(null); |
340 |
}
|
|
341 |
|
|
342 |
extern (C++) void pop(VarDeclaration v) |
|
343 |
{
|
|
344 | 1 |
assert(!v.isDataseg() || v.isCTFE()); |
345 | 1 |
assert(!(v.storage_class & (STC.ref_ | STC.out_))); |
346 | 1 |
const oldid = v.ctfeAdrOnStack; |
347 | 1 |
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[oldid]; |
348 | 1 |
if (v.ctfeAdrOnStack == values.dim - 1) |
349 |
{
|
|
350 |
values.pop(); |
|
351 |
vars.pop(); |
|
352 |
savedId.pop(); |
|
353 |
}
|
|
354 |
}
|
|
355 |
|
|
356 |
extern (C++) void popAll(size_t stackpointer) |
|
357 |
{
|
|
358 | 1 |
if (stackPointer() > maxStackPointer) |
359 | 1 |
maxStackPointer = stackPointer(); |
360 | 1 |
assert(values.dim >= stackpointer); |
361 | 1 |
for (size_t i = stackpointer; i < values.dim; ++i) |
362 |
{
|
|
363 | 1 |
VarDeclaration v = vars[i]; |
364 | 1 |
v.ctfeAdrOnStack = cast(uint)cast(size_t)savedId[i]; |
365 |
}
|
|
366 | 1 |
values.setDim(stackpointer); |
367 | 1 |
vars.setDim(stackpointer); |
368 | 1 |
savedId.setDim(stackpointer); |
369 |
}
|
|
370 |
|
|
371 |
extern (C++) void saveGlobalConstant(VarDeclaration v, Expression e) |
|
372 |
{
|
|
373 | 1 |
assert(v._init && (v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !v.isCTFE()); |
374 | 1 |
v.ctfeAdrOnStack = cast(uint)globalValues.dim; |
375 | 1 |
globalValues.push(copyRegionExp(e)); |
376 |
}
|
|
377 |
}
|
|
378 |
|
|
379 |
private struct InterState |
|
380 |
{
|
|
381 |
InterState* caller; // calling function's InterState |
|
382 |
FuncDeclaration fd; // function being interpreted |
|
383 |
Statement start; // if !=NULL, start execution at this statement |
|
384 |
|
|
385 |
/* target of CTFEExp result; also
|
|
386 |
* target of labelled CTFEExp or
|
|
387 |
* CTFEExp. (null if no label).
|
|
388 |
*/
|
|
389 |
Statement gotoTarget; |
|
390 |
}
|
|
391 |
|
|
392 |
/*************************************
|
|
393 |
* Attempt to interpret a function given the arguments.
|
|
394 |
* Params:
|
|
395 |
* pue = storage for result
|
|
396 |
* fd = function being called
|
|
397 |
* istate = state for calling function (NULL if none)
|
|
398 |
* arguments = function arguments
|
|
399 |
* thisarg = 'this', if a needThis() function, NULL if not.
|
|
400 |
*
|
|
401 |
* Returns:
|
|
402 |
* result expression if successful, TOK.cantExpression if not,
|
|
403 |
* or CTFEExp if function returned void.
|
|
404 |
*/
|
|
405 |
private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterState* istate, Expressions* arguments, Expression thisarg) |
|
406 |
{
|
|
407 |
debug (LOG) |
|
408 |
{
|
|
409 |
printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars()); |
|
410 |
}
|
|
411 | 1 |
assert(pue); |
412 | 1 |
if (fd.semanticRun == PASS.semantic3) |
413 |
{
|
|
414 | 1 |
fd.error("circular dependency. Functions cannot be interpreted while being compiled"); |
415 | 1 |
return CTFEExp.cantexp; |
416 |
}
|
|
417 | 1 |
if (!fd.functionSemantic3()) |
418 | 1 |
return CTFEExp.cantexp; |
419 | 1 |
if (fd.semanticRun < PASS.semantic3done) |
420 |
return CTFEExp.cantexp; |
|
421 |
|
|
422 | 1 |
Type tb = fd.type.toBasetype(); |
423 | 1 |
assert(tb.ty == Tfunction); |
424 | 1 |
TypeFunction tf = cast(TypeFunction)tb; |
425 | 1 |
if (tf.parameterList.varargs != VarArg.none && arguments && |
426 | 1 |
((fd.parameters && arguments.dim != fd.parameters.dim) || (!fd.parameters && arguments.dim))) |
427 |
{
|
|
428 | 1 |
fd.error("C-style variadic functions are not yet implemented in CTFE"); |
429 | 1 |
return CTFEExp.cantexp; |
430 |
}
|
|
431 |
|
|
432 |
// Nested functions always inherit the 'this' pointer from the parent,
|
|
433 |
// except for delegates. (Note that the 'this' pointer may be null).
|
|
434 |
// Func literals report isNested() even if they are in global scope,
|
|
435 |
// so we need to check that the parent is a function.
|
|
436 | 1 |
if (fd.isNested() && fd.toParentLocal().isFuncDeclaration() && !thisarg && istate) |
437 | 1 |
thisarg = ctfeGlobals.stack.getThis(); |
438 |
|
|
439 | 1 |
if (fd.needThis() && !thisarg) |
440 |
{
|
|
441 |
// error, no this. Prevent segfault.
|
|
442 |
// Here should be unreachable by the strict 'this' check in front-end.
|
|
443 |
fd.error("need `this` to access member `%s`", fd.toChars()); |
|
444 |
return CTFEExp.cantexp; |
|
445 |
}
|
|
446 |
|
|
447 |
// Place to hold all the arguments to the function while
|
|
448 |
// we are evaluating them.
|
|
449 | 1 |
size_t dim = arguments ? arguments.dim : 0; |
450 | 1 |
assert((fd.parameters ? fd.parameters.dim : 0) == dim); |
451 |
|
|
452 |
/* Evaluate all the arguments to the function,
|
|
453 |
* store the results in eargs[]
|
|
454 |
*/
|
|
455 | 1 |
Expressions eargs = Expressions(dim); |
456 | 1 |
for (size_t i = 0; i < dim; i++) |
457 |
{
|
|
458 | 1 |
Expression earg = (*arguments)[i]; |
459 | 1 |
Parameter fparam = tf.parameterList[i]; |
460 |
|
|
461 | 1 |
if (fparam.isReference()) |
462 |
{
|
|
463 | 1 |
if (!istate && (fparam.storageClass & STC.out_)) |
464 |
{
|
|
465 |
// initializing an out parameter involves writing to it.
|
|
466 |
earg.error("global `%s` cannot be passed as an `out` parameter at compile time", earg.toChars()); |
|
467 |
return CTFEExp.cantexp; |
|
468 |
}
|
|
469 |
// Convert all reference arguments into lvalue references
|
|
470 | 1 |
earg = interpretRegion(earg, istate, CTFEGoal.LValue); |
471 | 1 |
if (CTFEExp.isCantExp(earg)) |
472 | 1 |
return earg; |
473 |
}
|
|
474 | 1 |
else if (fparam.storageClass & STC.lazy_) |
475 |
{
|
|
476 |
}
|
|
477 |
else
|
|
478 |
{
|
|
479 |
/* Value parameters
|
|
480 |
*/
|
|
481 | 1 |
Type ta = fparam.type.toBasetype(); |
482 | 1 |
if (ta.ty == Tsarray) |
483 | 1 |
if (auto eaddr = earg.isAddrExp()) |
484 |
{
|
|
485 |
/* Static arrays are passed by a simple pointer.
|
|
486 |
* Skip past this to get at the actual arg.
|
|
487 |
*/
|
|
488 |
earg = eaddr.e1; |
|
489 |
}
|
|
490 |
|
|
491 | 1 |
earg = interpretRegion(earg, istate); |
492 | 1 |
if (CTFEExp.isCantExp(earg)) |
493 | 1 |
return earg; |
494 |
|
|
495 |
/* Struct literals are passed by value, but we don't need to
|
|
496 |
* copy them if they are passed as const
|
|
497 |
*/
|
|
498 | 1 |
if (earg.op == TOK.structLiteral && !(fparam.storageClass & (STC.const_ | STC.immutable_))) |
499 | 1 |
earg = copyLiteral(earg).copy(); |
500 |
}
|
|
501 | 1 |
if (earg.op == TOK.thrownException) |
502 |
{
|
|
503 | 1 |
if (istate) |
504 | 1 |
return earg; |
505 |
(cast(ThrownExceptionExp)earg).generateUncaughtError(); |
|
506 |
return CTFEExp.cantexp; |
|
507 |
}
|
|
508 | 1 |
eargs[i] = earg; |
509 |
}
|
|
510 |
|
|
511 |
// Now that we've evaluated all the arguments, we can start the frame
|
|
512 |
// (this is the moment when the 'call' actually takes place).
|
|
513 | 1 |
InterState istatex; |
514 | 1 |
istatex.caller = istate; |
515 | 1 |
istatex.fd = fd; |
516 |
|
|
517 | 1 |
if (fd.isThis2) |
518 |
{
|
|
519 | 1 |
Expression arg0 = thisarg; |
520 | 1 |
if (arg0 && arg0.type.ty == Tstruct) |
521 |
{
|
|
522 | 1 |
Type t = arg0.type.pointerTo(); |
523 | 1 |
arg0 = ctfeEmplaceExp!AddrExp(arg0.loc, arg0); |
524 | 1 |
arg0.type = t; |
525 |
}
|
|
526 | 1 |
auto elements = new Expressions(2); |
527 | 1 |
(*elements)[0] = arg0; |
528 | 1 |
(*elements)[1] = ctfeGlobals.stack.getThis(); |
529 | 1 |
Type t2 = Type.tvoidptr.sarrayOf(2); |
530 | 1 |
const loc = thisarg ? thisarg.loc : fd.loc; |
531 | 1 |
thisarg = ctfeEmplaceExp!ArrayLiteralExp(loc, t2, elements); |
532 | 1 |
thisarg = ctfeEmplaceExp!AddrExp(loc, thisarg); |
533 | 1 |
thisarg.type = t2.pointerTo(); |
534 |
}
|
|
535 |
|
|
536 | 1 |
ctfeGlobals.stack.startFrame(thisarg); |
537 | 1 |
if (fd.vthis && thisarg) |
538 |
{
|
|
539 | 1 |
ctfeGlobals.stack.push(fd.vthis); |
540 | 1 |
setValue(fd.vthis, thisarg); |
541 |
}
|
|
542 |
|
|
543 | 1 |
for (size_t i = 0; i < dim; i++) |
544 |
{
|
|
545 | 1 |
Expression earg = eargs[i]; |
546 | 1 |
Parameter fparam = tf.parameterList[i]; |
547 | 1 |
VarDeclaration v = (*fd.parameters)[i]; |
548 |
debug (LOG) |
|
549 |
{
|
|
550 |
printf("arg[%zu] = %s\n", i, earg.toChars()); |
|
551 |
}
|
|
552 | 1 |
ctfeGlobals.stack.push(v); |
553 |
|
|
554 | 1 |
if (fparam.isReference() && earg.op == TOK.variable && |
555 | 1 |
(cast(VarExp)earg).var.toParent2() == fd) |
556 |
{
|
|
557 | 1 |
VarDeclaration vx = (cast(VarExp)earg).var.isVarDeclaration(); |
558 | 1 |
if (!vx) |
559 |
{
|
|
560 |
fd.error("cannot interpret `%s` as a `ref` parameter", earg.toChars()); |
|
561 |
return CTFEExp.cantexp; |
|
562 |
}
|
|
563 |
|
|
564 |
/* vx is a variable that is declared in fd.
|
|
565 |
* It means that fd is recursively called. e.g.
|
|
566 |
*
|
|
567 |
* void fd(int n, ref int v = dummy) {
|
|
568 |
* int vx;
|
|
569 |
* if (n == 1) fd(2, vx);
|
|
570 |
* }
|
|
571 |
* fd(1);
|
|
572 |
*
|
|
573 |
* The old value of vx on the stack in fd(1)
|
|
574 |
* should be saved at the start of fd(2, vx) call.
|
|
575 |
*/
|
|
576 | 1 |
const oldadr = vx.ctfeAdrOnStack; |
577 |
|
|
578 | 1 |
ctfeGlobals.stack.push(vx); |
579 | 1 |
assert(!hasValue(vx)); // vx is made uninitialized |
580 |
|
|
581 |
// https://issues.dlang.org/show_bug.cgi?id=14299
|
|
582 |
// v.ctfeAdrOnStack should be saved already
|
|
583 |
// in the stack before the overwrite.
|
|
584 | 1 |
v.ctfeAdrOnStack = oldadr; |
585 | 1 |
assert(hasValue(v)); // ref parameter v should refer existing value. |
586 |
}
|
|
587 |
else
|
|
588 |
{
|
|
589 |
// Value parameters and non-trivial references
|
|
590 | 1 |
setValueWithoutChecking(v, earg); |
591 |
}
|
|
592 |
debug (LOG) |
|
593 |
{
|
|
594 |
printf("interpreted arg[%zu] = %s\n", i, earg.toChars()); |
|
595 |
showCtfeExpr(earg); |
|
596 |
}
|
|
597 |
debug (LOGASSIGN) |
|
598 |
{
|
|
599 |
printf("interpreted arg[%zu] = %s\n", i, earg.toChars()); |
|
600 |
showCtfeExpr(earg); |
|
601 |
}
|
|
602 |
}
|
|
603 |
|
|
604 | 1 |
if (fd.vresult) |
605 | 1 |
ctfeGlobals.stack.push(fd.vresult); |
606 |
|
|
607 |
// Enter the function
|
|
608 | 1 |
++ctfeGlobals.callDepth; |
609 | 1 |
if (ctfeGlobals.callDepth > ctfeGlobals.maxCallDepth) |
610 | 1 |
ctfeGlobals.maxCallDepth = ctfeGlobals.callDepth; |
611 |
|
|
612 | 1 |
Expression e = null; |
613 | 1 |
while (1) |
614 |
{
|
|
615 | 1 |
if (ctfeGlobals.callDepth > CTFE_RECURSION_LIMIT) |
616 |
{
|
|
617 |
// This is a compiler error. It must not be suppressed.
|
|
618 | 1 |
global.gag = 0; |
619 | 1 |
fd.error("CTFE recursion limit exceeded"); |
620 | 1 |
e = CTFEExp.cantexp; |
621 | 1 |
break; |
622 |
}
|
|
623 | 1 |
e = interpret(pue, fd.fbody, &istatex); |
624 | 1 |
if (CTFEExp.isCantExp(e)) |
625 |
{
|
|
626 |
debug (LOG) |
|
627 |
{
|
|
628 |
printf("function body failed to interpret\n"); |
|
629 |
}
|
|
630 |
}
|
|
631 |
|
|
632 | 1 |
if (istatex.start) |
633 |
{
|
|
634 |
fd.error("CTFE internal error: failed to resume at statement `%s`", istatex.start.toChars()); |
|
635 |
return CTFEExp.cantexp; |
|
636 |
}
|
|
637 |
|
|
638 |
/* This is how we deal with a recursive statement AST
|
|
639 |
* that has arbitrary goto statements in it.
|
|
640 |
* Bubble up a 'result' which is the target of the goto
|
|
641 |
* statement, then go recursively down the AST looking
|
|
642 |
* for that statement, then execute starting there.
|
|
643 |
*/
|
|
644 | 1 |
if (CTFEExp.isGotoExp(e)) |
645 |
{
|
|
646 | 1 |
istatex.start = istatex.gotoTarget; // set starting statement |
647 | 1 |
istatex.gotoTarget = null; |
648 |
}
|
|
649 |
else
|
|
650 |
{
|
|
651 | 1 |
assert(!e || (e.op != TOK.continue_ && e.op != TOK.break_)); |
652 | 1 |
break; |
653 |
}
|
|
654 |
}
|
|
655 |
// If fell off the end of a void function, return void
|
|
656 | 1 |
if (!e && tf.next.ty == Tvoid) |
657 | 1 |
e = CTFEExp.voidexp; |
658 | 1 |
if (tf.isref && e.op == TOK.variable && (cast(VarExp)e).var == fd.vthis) |
659 | 1 |
e = thisarg; |
660 | 1 |
if (tf.isref && fd.isThis2 && e.op == TOK.index) |
661 |
{
|
|
662 | 1 |
auto ie = cast(IndexExp)e; |
663 | 1 |
auto pe = ie.e1.isPtrExp(); |
664 | 1 |
auto ve = !pe ? null : pe.e1.isVarExp(); |
665 | 1 |
if (ve && ve.var == fd.vthis) |
666 |
{
|
|
667 | 1 |
auto ne = ie.e2.isIntegerExp(); |
668 | 1 |
assert(ne); |
669 | 1 |
assert(thisarg.op == TOK.address); |
670 | 1 |
e = (cast(AddrExp)thisarg).e1; |
671 | 1 |
e = (*(cast(ArrayLiteralExp)e).elements)[cast(size_t)ne.getInteger()]; |
672 | 1 |
if (e.op == TOK.address) |
673 |
{
|
|
674 | 1 |
e = (cast(AddrExp)e).e1; |
675 |
}
|
|
676 |
}
|
|
677 |
}
|
|
678 | 1 |
assert(e !is null); |
679 |
|
|
680 |
// Leave the function
|
|
681 | 1 |
--ctfeGlobals.callDepth; |
682 |
|
|
683 | 1 |
ctfeGlobals.stack.endFrame(); |
684 |
|
|
685 |
// If it generated an uncaught exception, report error.
|
|
686 | 1 |
if (!istate && e.op == TOK.thrownException) |
687 |
{
|
|
688 | 1 |
if (e == pue.exp()) |
689 |
e = pue.copy(); |
|
690 | 1 |
(cast(ThrownExceptionExp)e).generateUncaughtError(); |
691 | 1 |
e = CTFEExp.cantexp; |
692 |
}
|
|
693 |
|
|
694 | 1 |
return e; |
695 |
}
|
|
696 |
|
|
697 |
/// used to collect coverage information in ctfe
|
|
698 |
void incUsageCtfe(InterState* istate, const ref Loc loc) |
|
699 |
{
|
|
700 | 1 |
if (global.params.ctfe_cov && istate) |
701 |
{
|
|
702 | 1 |
auto line = loc.linnum; |
703 | 1 |
auto mod = istate.fd.getModule(); |
704 |
|
|
705 | 1 |
++mod.ctfe_cov[line]; |
706 |
}
|
|
707 |
}
|
|
708 |
|
|
709 |
private extern (C++) final class Interpreter : Visitor |
|
710 |
{
|
|
711 |
alias visit = Visitor.visit; |
|
712 |
public: |
|
713 |
InterState* istate; |
|
714 |
CTFEGoal goal; |
|
715 |
Expression result; |
|
716 |
UnionExp* pue; // storage for `result` |
|
717 |
|
|
718 | 1 |
extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) |
719 |
{
|
|
720 | 1 |
this.pue = pue; |
721 | 1 |
this.istate = istate; |
722 | 1 |
this.goal = goal; |
723 |
}
|
|
724 |
|
|
725 |
// If e is TOK.throw_exception or TOK.cantExpression,
|
|
726 |
// set it to 'result' and returns true.
|
|
727 |
bool exceptionOrCant(Expression e) |
|
728 |
{
|
|
729 | 1 |
if (exceptionOrCantInterpret(e)) |
730 |
{
|
|
731 |
// Make sure e is not pointing to a stack temporary
|
|
732 | 1 |
result = (e.op == TOK.cantExpression) ? CTFEExp.cantexp : e; |
733 | 1 |
return true; |
734 |
}
|
|
735 | 1 |
return false; |
736 |
}
|
|
737 |
|
|
738 |
static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) |
|
739 |
{
|
|
740 | 1 |
if (exps is original) |
741 |
{
|
|
742 | 1 |
if (!original) |
743 |
exps = new Expressions(); |
|
744 |
else
|
|
745 | 1 |
exps = original.copy(); |
746 | 1 |
++ctfeGlobals.numArrayAllocs; |
747 |
}
|
|
748 | 1 |
return exps; |
749 |
}
|
|
750 |
|
|
751 |
/******************************** Statement ***************************/
|
|
752 |
|
|
753 |
override void visit(Statement s) |
|
754 |
{
|
|
755 |
debug (LOG) |
|
756 |
{
|
|
757 |
printf("%s Statement::interpret()\n", s.loc.toChars()); |
|
758 |
}
|
|
759 | 1 |
if (istate.start) |
760 |
{
|
|
761 | 1 |
if (istate.start != s) |
762 | 1 |
return; |
763 |
istate.start = null; |
|
764 |
}
|
|
765 |
|
|
766 |
s.error("statement `%s` cannot be interpreted at compile time", s.toChars()); |
|
767 |
result = CTFEExp.cantexp; |
|
768 |
}
|
|
769 |
|
|
770 |
override void visit(ExpStatement s) |
|
771 |
{
|
|
772 |
debug (LOG) |
|
773 |
{
|
|
774 |
printf("%s ExpStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : ""); |
|
775 |
}
|
|
776 | 1 |
if (istate.start) |
777 |
{
|
|
778 | 1 |
if (istate.start != s) |
779 | 1 |
return; |
780 |
istate.start = null; |
|
781 |
}
|
|
782 | 1 |
if (s.exp && s.exp.hasCode) |
783 | 1 |
incUsageCtfe(istate, s.loc); |
784 |
|
|
785 | 1 |
Expression e = interpret(pue, s.exp, istate, CTFEGoal.Nothing); |
786 | 1 |
if (exceptionOrCant(e)) |
787 | 1 |
return; |
788 |
}
|
|
789 |
|
|
790 |
override void visit(CompoundStatement s) |
|
791 |
{
|
|
792 |
debug (LOG) |
|
793 |
{
|
|
794 |
printf("%s CompoundStatement::interpret()\n", s.loc.toChars()); |
|
795 |
}
|
|
796 | 1 |
if (istate.start == s) |
797 |
istate.start = null; |
|
798 |
|
|
799 | 1 |
const dim = s.statements ? s.statements.dim : 0; |
800 | 1 |
foreach (i; 0 .. dim) |
801 |
{
|
|
802 | 1 |
Statement sx = (*s.statements)[i]; |
803 | 1 |
result = interpret(pue, sx, istate); |
804 | 1 |
if (result) |
805 | 1 |
break; |
806 |
}
|
|
807 |
debug (LOG) |
|
808 |
{
|
|
809 |
printf("%s -CompoundStatement::interpret() %p\n", s.loc.toChars(), result); |
|
810 |
}
|
|
811 |
}
|
|
812 |
|
|
813 |
override void visit(UnrolledLoopStatement s) |
|
814 |
{
|
|
815 |
debug (LOG) |
|
816 |
{
|
|
817 |
printf("%s UnrolledLoopStatement::interpret()\n", s.loc.toChars()); |
|
818 |
}
|
|
819 | 1 |
if (istate.start == s) |
820 |
istate.start = null; |
|
821 |
|
|
822 | 1 |
const dim = s.statements ? s.statements.dim : 0; |
823 | 1 |
foreach (i; 0 .. dim) |
824 |
{
|
|
825 | 1 |
Statement sx = (*s.statements)[i]; |
826 | 1 |
Expression e = interpret(pue, sx, istate); |
827 | 1 |
if (!e) // succeeds to interpret, or goto target was not found |
828 | 1 |
continue; |
829 | 1 |
if (exceptionOrCant(e)) |
830 |
return; |
|
831 | 1 |
if (e.op == TOK.break_) |
832 |
{
|
|
833 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
834 |
{
|
|
835 | 1 |
result = e; // break at a higher level |
836 | 1 |
return; |
837 |
}
|
|
838 | 1 |
istate.gotoTarget = null; |
839 | 1 |
result = null; |
840 | 1 |
return; |
841 |
}
|
|
842 | 1 |
if (e.op == TOK.continue_) |
843 |
{
|
|
844 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
845 |
{
|
|
846 |
result = e; // continue at a higher level |
|
847 |
return; |
|
848 |
}
|
|
849 | 1 |
istate.gotoTarget = null; |
850 | 1 |
continue; |
851 |
}
|
|
852 |
|
|
853 |
// expression from return statement, or thrown exception
|
|
854 | 1 |
result = e; |
855 | 1 |
break; |
856 |
}
|
|
857 |
}
|
|
858 |
|
|
859 |
override void visit(IfStatement s) |
|
860 |
{
|
|
861 |
debug (LOG) |
|
862 |
{
|
|
863 |
printf("%s IfStatement::interpret(%s)\n", s.loc.toChars(), s.condition.toChars()); |
|
864 |
}
|
|
865 | 1 |
incUsageCtfe(istate, s.loc); |
866 | 1 |
if (istate.start == s) |
867 |
istate.start = null; |
|
868 | 1 |
if (istate.start) |
869 |
{
|
|
870 | 1 |
Expression e = null; |
871 | 1 |
e = interpret(s.ifbody, istate); |
872 | 1 |
if (!e && istate.start) |
873 | 1 |
e = interpret(s.elsebody, istate); |
874 | 1 |
result = e; |
875 | 1 |
return; |
876 |
}
|
|
877 |
|
|
878 | 1 |
UnionExp ue = void; |
879 | 1 |
Expression e = interpret(&ue, s.condition, istate); |
880 | 1 |
assert(e); |
881 | 1 |
if (exceptionOrCant(e)) |
882 |
return; |
|
883 |
|
|
884 | 1 |
if (isTrueBool(e)) |
885 | 1 |
result = interpret(pue, s.ifbody, istate); |
886 | 1 |
else if (e.isBool(false)) |
887 | 1 |
result = interpret(pue, s.elsebody, istate); |
888 |
else
|
|
889 |
{
|
|
890 |
// no error, or assert(0)?
|
|
891 |
result = CTFEExp.cantexp; |
|
892 |
}
|
|
893 |
}
|
|
894 |
|
|
895 |
override void visit(ScopeStatement s) |
|
896 |
{
|
|
897 |
debug (LOG) |
|
898 |
{
|
|
899 |
printf("%s ScopeStatement::interpret()\n", s.loc.toChars()); |
|
900 |
}
|
|
901 | 1 |
if (istate.start == s) |
902 |
istate.start = null; |
|
903 |
|
|
904 | 1 |
result = interpret(pue, s.statement, istate); |
905 |
}
|
|
906 |
|
|
907 |
/**
|
|
908 |
Given an expression e which is about to be returned from the current
|
|
909 |
function, generate an error if it contains pointers to local variables.
|
|
910 |
|
|
911 |
Only checks expressions passed by value (pointers to local variables
|
|
912 |
may already be stored in members of classes, arrays, or AAs which
|
|
913 |
were passed as mutable function parameters).
|
|
914 |
Returns:
|
|
915 |
true if it is safe to return, false if an error was generated.
|
|
916 |
*/
|
|
917 |
static bool stopPointersEscaping(const ref Loc loc, Expression e) |
|
918 |
{
|
|
919 | 1 |
if (!e.type.hasPointers()) |
920 | 1 |
return true; |
921 | 1 |
if (isPointer(e.type)) |
922 |
{
|
|
923 | 1 |
Expression x = e; |
924 | 1 |
if (auto eaddr = e.isAddrExp()) |
925 | 1 |
x = eaddr.e1; |
926 | 1 |
VarDeclaration v; |
927 | 1 |
while (x.op == TOK.variable && (v = (cast(VarExp)x).var.isVarDeclaration()) !is null) |
928 |
{
|
|
929 | 1 |
if (v.storage_class & STC.ref_) |
930 |
{
|
|
931 |
x = getValue(v); |
|
932 |
if (auto eaddr = e.isAddrExp()) |
|
933 |
eaddr.e1 = x; |
|
934 |
continue; |
|
935 |
}
|
|
936 | 1 |
if (ctfeGlobals.stack.isInCurrentFrame(v)) |
937 |
{
|
|
938 | 1 |
error(loc, "returning a pointer to a local stack variable"); |
939 | 1 |
return false; |
940 |
}
|
|
941 |
else
|
|
942 | 1 |
break; |
943 |
}
|
|
944 |
// TODO: If it is a TOK.dotVariable or TOK.index, we should check that it is not
|
|
945 |
// pointing to a local struct or static array.
|
|
946 |
}
|
|
947 | 1 |
if (auto se = e.isStructLiteralExp()) |
948 |
{
|
|
949 | 1 |
return stopPointersEscapingFromArray(loc, se.elements); |
950 |
}
|
|
951 | 1 |
if (auto ale = e.isArrayLiteralExp()) |
952 |
{
|
|
953 | 1 |
return stopPointersEscapingFromArray(loc, ale.elements); |
954 |
}
|
|
955 | 1 |
if (auto aae = e.isAssocArrayLiteralExp()) |
956 |
{
|
|
957 | 1 |
if (!stopPointersEscapingFromArray(loc, aae.keys)) |
958 |
return false; |
|
959 | 1 |
return stopPointersEscapingFromArray(loc, aae.values); |
960 |
}
|
|
961 | 1 |
return true; |
962 |
}
|
|
963 |
|
|
964 |
// Check all elements of an array for escaping local variables. Return false if error
|
|
965 |
static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) |
|
966 |
{
|
|
967 | 1 |
foreach (e; *elems) |
968 |
{
|
|
969 | 1 |
if (e && !stopPointersEscaping(loc, e)) |
970 | 1 |
return false; |
971 |
}
|
|
972 | 1 |
return true; |
973 |
}
|
|
974 |
|
|
975 |
override void visit(ReturnStatement s) |
|
976 |
{
|
|
977 |
debug (LOG) |
|
978 |
{
|
|
979 |
printf("%s ReturnStatement::interpret(%s)\n", s.loc.toChars(), s.exp ? s.exp.toChars() : ""); |
|
980 |
}
|
|
981 | 1 |
if (istate.start) |
982 |
{
|
|
983 | 1 |
if (istate.start != s) |
984 | 1 |
return; |
985 |
istate.start = null; |
|
986 |
}
|
|
987 |
|
|
988 | 1 |
if (!s.exp) |
989 |
{
|
|
990 | 1 |
result = CTFEExp.voidexp; |
991 | 1 |
return; |
992 |
}
|
|
993 |
|
|
994 | 1 |
incUsageCtfe(istate, s.loc); |
995 | 1 |
assert(istate && istate.fd && istate.fd.type && istate.fd.type.ty == Tfunction); |
996 | 1 |
TypeFunction tf = cast(TypeFunction)istate.fd.type; |
997 |
|
|
998 |
/* If the function returns a ref AND it's been called from an assignment,
|
|
999 |
* we need to return an lvalue. Otherwise, just do an (rvalue) interpret.
|
|
1000 |
*/
|
|
1001 | 1 |
if (tf.isref) |
1002 |
{
|
|
1003 | 1 |
result = interpret(pue, s.exp, istate, CTFEGoal.LValue); |
1004 | 1 |
return; |
1005 |
}
|
|
1006 | 1 |
if (tf.next && tf.next.ty == Tdelegate && istate.fd.closureVars.dim > 0) |
1007 |
{
|
|
1008 |
// To support this, we need to copy all the closure vars
|
|
1009 |
// into the delegate literal.
|
|
1010 | 1 |
s.error("closures are not yet supported in CTFE"); |
1011 | 1 |
result = CTFEExp.cantexp; |
1012 | 1 |
return; |
1013 |
}
|
|
1014 |
|
|
1015 |
// We need to treat pointers specially, because TOK.symbolOffset can be used to
|
|
1016 |
// return a value OR a pointer
|
|
1017 | 1 |
Expression e = interpret(pue, s.exp, istate); |
1018 | 1 |
if (exceptionOrCant(e)) |
1019 | 1 |
return; |
1020 |
|
|
1021 |
// Disallow returning pointers to stack-allocated variables (bug 7876)
|
|
1022 | 1 |
if (!stopPointersEscaping(s.loc, e)) |
1023 |
{
|
|
1024 | 1 |
result = CTFEExp.cantexp; |
1025 | 1 |
return; |
1026 |
}
|
|
1027 |
|
|
1028 | 1 |
if (needToCopyLiteral(e)) |
1029 | 1 |
e = copyLiteral(e).copy(); |
1030 |
debug (LOGASSIGN) |
|
1031 |
{
|
|
1032 |
printf("RETURN %s\n", s.loc.toChars()); |
|
1033 |
showCtfeExpr(e); |
|
1034 |
}
|
|
1035 | 1 |
result = e; |
1036 |
}
|
|
1037 |
|
|
1038 |
static Statement findGotoTarget(InterState* istate, Identifier ident) |
|
1039 |
{
|
|
1040 | 1 |
Statement target = null; |
1041 | 1 |
if (ident) |
1042 |
{
|
|
1043 | 1 |
LabelDsymbol label = istate.fd.searchLabel(ident); |
1044 | 1 |
assert(label && label.statement); |
1045 | 1 |
LabelStatement ls = label.statement; |
1046 | 1 |
target = ls.gotoTarget ? ls.gotoTarget : ls.statement; |
1047 |
}
|
|
1048 | 1 |
return target; |
1049 |
}
|
|
1050 |
|
|
1051 |
override void visit(BreakStatement s) |
|
1052 |
{
|
|
1053 |
debug (LOG) |
|
1054 |
{
|
|
1055 |
printf("%s BreakStatement::interpret()\n", s.loc.toChars()); |
|
1056 |
}
|
|
1057 | 1 |
incUsageCtfe(istate, s.loc); |
1058 | 1 |
if (istate.start) |
1059 |
{
|
|
1060 | 1 |
if (istate.start != s) |
1061 | 1 |
return; |
1062 |
istate.start = null; |
|
1063 |
}
|
|
1064 |
|
|
1065 | 1 |
istate.gotoTarget = findGotoTarget(istate, s.ident); |
1066 | 1 |
result = CTFEExp.breakexp; |
1067 |
}
|
|
1068 |
|
|
1069 |
override void visit(ContinueStatement s) |
|
1070 |
{
|
|
1071 |
debug (LOG) |
|
1072 |
{
|
|
1073 |
printf("%s ContinueStatement::interpret()\n", s.loc.toChars()); |
|
1074 |
}
|
|
1075 | 1 |
incUsageCtfe(istate, s.loc); |
1076 | 1 |
if (istate.start) |
1077 |
{
|
|
1078 | 1 |
if (istate.start != s) |
1079 | 1 |
return; |
1080 |
istate.start = null; |
|
1081 |
}
|
|
1082 |
|
|
1083 | 1 |
istate.gotoTarget = findGotoTarget(istate, s.ident); |
1084 | 1 |
result = CTFEExp.continueexp; |
1085 |
}
|
|
1086 |
|
|
1087 |
override void visit(WhileStatement s) |
|
1088 |
{
|
|
1089 |
debug (LOG) |
|
1090 |
{
|
|
1091 |
printf("WhileStatement::interpret()\n"); |
|
1092 |
}
|
|
1093 |
assert(0); // rewritten to ForStatement |
|
1094 |
}
|
|
1095 |
|
|
1096 |
override void visit(DoStatement s) |
|
1097 |
{
|
|
1098 |
debug (LOG) |
|
1099 |
{
|
|
1100 |
printf("%s DoStatement::interpret()\n", s.loc.toChars()); |
|
1101 |
}
|
|
1102 | 1 |
if (istate.start == s) |
1103 |
istate.start = null; |
|
1104 |
|
|
1105 | 1 |
while (1) |
1106 |
{
|
|
1107 | 1 |
Expression e = interpret(s._body, istate); |
1108 | 1 |
if (!e && istate.start) // goto target was not found |
1109 | 1 |
return; |
1110 | 1 |
assert(!istate.start); |
1111 |
|
|
1112 | 1 |
if (exceptionOrCant(e)) |
1113 |
return; |
|
1114 | 1 |
if (e && e.op == TOK.break_) |
1115 |
{
|
|
1116 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1117 |
{
|
|
1118 |
result = e; // break at a higher level |
|
1119 |
return; |
|
1120 |
}
|
|
1121 | 1 |
istate.gotoTarget = null; |
1122 | 1 |
break; |
1123 |
}
|
|
1124 | 1 |
if (e && e.op == TOK.continue_) |
1125 |
{
|
|
1126 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1127 |
{
|
|
1128 |
result = e; // continue at a higher level |
|
1129 |
return; |
|
1130 |
}
|
|
1131 | 1 |
istate.gotoTarget = null; |
1132 | 1 |
e = null; |
1133 |
}
|
|
1134 | 1 |
if (e) |
1135 |
{
|
|
1136 | 1 |
result = e; // bubbled up from ReturnStatement |
1137 | 1 |
return; |
1138 |
}
|
|
1139 |
|
|
1140 | 1 |
UnionExp ue = void; |
1141 | 1 |
incUsageCtfe(istate, s.condition.loc); |
1142 | 1 |
e = interpret(&ue, s.condition, istate); |
1143 | 1 |
if (exceptionOrCant(e)) |
1144 |
return; |
|
1145 | 1 |
if (!e.isConst()) |
1146 |
{
|
|
1147 |
result = CTFEExp.cantexp; |
|
1148 |
return; |
|
1149 |
}
|
|
1150 | 1 |
if (e.isBool(false)) |
1151 | 1 |
break; |
1152 | 1 |
assert(isTrueBool(e)); |
1153 |
}
|
|
1154 | 1 |
assert(result is null); |
1155 |
}
|
|
1156 |
|
|
1157 |
override void visit(ForStatement s) |
|
1158 |
{
|
|
1159 |
debug (LOG) |
|
1160 |
{
|
|
1161 |
printf("%s ForStatement::interpret()\n", s.loc.toChars()); |
|
1162 |
}
|
|
1163 | 1 |
if (istate.start == s) |
1164 |
istate.start = null; |
|
1165 |
|
|
1166 | 1 |
UnionExp ueinit = void; |
1167 | 1 |
Expression ei = interpret(&ueinit, s._init, istate); |
1168 | 1 |
if (exceptionOrCant(ei)) |
1169 |
return; |
|
1170 | 1 |
assert(!ei); // s.init never returns from function, or jumps out from it |
1171 |
|
|
1172 | 1 |
while (1) |
1173 |
{
|
|
1174 | 1 |
if (s.condition && !istate.start) |
1175 |
{
|
|
1176 | 1 |
UnionExp ue = void; |
1177 | 1 |
incUsageCtfe(istate, s.condition.loc); |
1178 | 1 |
Expression e = interpret(&ue, s.condition, istate); |
1179 | 1 |
if (exceptionOrCant(e)) |
1180 |
return; |
|
1181 | 1 |
if (e.isBool(false)) |
1182 | 1 |
break; |
1183 | 1 |
assert(isTrueBool(e)); |
1184 |
}
|
|
1185 |
|
|
1186 | 1 |
Expression e = interpret(pue, s._body, istate); |
1187 | 1 |
if (!e && istate.start) // goto target was not found |
1188 | 1 |
return; |
1189 | 1 |
assert(!istate.start); |
1190 |
|
|
1191 | 1 |
if (exceptionOrCant(e)) |
1192 |
return; |
|
1193 | 1 |
if (e && e.op == TOK.break_) |
1194 |
{
|
|
1195 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1196 |
{
|
|
1197 |
result = e; // break at a higher level |
|
1198 |
return; |
|
1199 |
}
|
|
1200 | 1 |
istate.gotoTarget = null; |
1201 | 1 |
break; |
1202 |
}
|
|
1203 | 1 |
if (e && e.op == TOK.continue_) |
1204 |
{
|
|
1205 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1206 |
{
|
|
1207 | 1 |
result = e; // continue at a higher level |
1208 | 1 |
return; |
1209 |
}
|
|
1210 | 1 |
istate.gotoTarget = null; |
1211 | 1 |
e = null; |
1212 |
}
|
|
1213 | 1 |
if (e) |
1214 |
{
|
|
1215 | 1 |
result = e; // bubbled up from ReturnStatement |
1216 | 1 |
return; |
1217 |
}
|
|
1218 |
|
|
1219 | 1 |
UnionExp uei = void; |
1220 | 1 |
if (s.increment) |
1221 | 1 |
incUsageCtfe(istate, s.increment.loc); |
1222 | 1 |
e = interpret(&uei, s.increment, istate, CTFEGoal.Nothing); |
1223 | 1 |
if (exceptionOrCant(e)) |
1224 |
return; |
|
1225 |
}
|
|
1226 | 1 |
assert(result is null); |
1227 |
}
|
|
1228 |
|
|
1229 |
override void visit(ForeachStatement s) |
|
1230 |
{
|
|
1231 |
assert(0); // rewritten to ForStatement |
|
1232 |
}
|
|
1233 |
|
|
1234 |
override void visit(ForeachRangeStatement s) |
|
1235 |
{
|
|
1236 |
assert(0); // rewritten to ForStatement |
|
1237 |
}
|
|
1238 |
|
|
1239 |
override void visit(SwitchStatement s) |
|
1240 |
{
|
|
1241 |
debug (LOG) |
|
1242 |
{
|
|
1243 |
printf("%s SwitchStatement::interpret()\n", s.loc.toChars()); |
|
1244 |
}
|
|
1245 | 1 |
incUsageCtfe(istate, s.loc); |
1246 | 1 |
if (istate.start == s) |
1247 |
istate.start = null; |
|
1248 | 1 |
if (istate.start) |
1249 |
{
|
|
1250 | 1 |
Expression e = interpret(s._body, istate); |
1251 | 1 |
if (istate.start) // goto target was not found |
1252 | 1 |
return; |
1253 | 1 |
if (exceptionOrCant(e)) |
1254 |
return; |
|
1255 | 1 |
if (e && e.op == TOK.break_) |
1256 |
{
|
|
1257 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1258 |
{
|
|
1259 | 1 |
result = e; // break at a higher level |
1260 | 1 |
return; |
1261 |
}
|
|
1262 | 1 |
istate.gotoTarget = null; |
1263 | 1 |
e = null; |
1264 |
}
|
|
1265 | 1 |
result = e; |
1266 | 1 |
return; |
1267 |
}
|
|
1268 |
|
|
1269 | 1 |
UnionExp uecond = void; |
1270 | 1 |
Expression econdition = interpret(&uecond, s.condition, istate); |
1271 | 1 |
if (exceptionOrCant(econdition)) |
1272 |
return; |
|
1273 |
|
|
1274 | 1 |
Statement scase = null; |
1275 | 1 |
if (s.cases) |
1276 | 1 |
foreach (cs; *s.cases) |
1277 |
{
|
|
1278 | 1 |
UnionExp uecase = void; |
1279 | 1 |
Expression ecase = interpret(&uecase, cs.exp, istate); |
1280 | 1 |
if (exceptionOrCant(ecase)) |
1281 |
return; |
|
1282 | 1 |
if (ctfeEqual(cs.exp.loc, TOK.equal, econdition, ecase)) |
1283 |
{
|
|
1284 | 1 |
scase = cs; |
1285 | 1 |
break; |
1286 |
}
|
|
1287 |
}
|
|
1288 | 1 |
if (!scase) |
1289 |
{
|
|
1290 | 1 |
if (s.hasNoDefault) |
1291 |
s.error("no `default` or `case` for `%s` in `switch` statement", econdition.toChars()); |
|
1292 | 1 |
scase = s.sdefault; |
1293 |
}
|
|
1294 |
|
|
1295 | 1 |
assert(scase); |
1296 |
|
|
1297 |
/* Jump to scase
|
|
1298 |
*/
|
|
1299 | 1 |
istate.start = scase; |
1300 | 1 |
Expression e = interpret(pue, s._body, istate); |
1301 | 1 |
assert(!istate.start); // jump must not fail |
1302 | 1 |
if (e && e.op == TOK.break_) |
1303 |
{
|
|
1304 | 1 |
if (istate.gotoTarget && istate.gotoTarget != s) |
1305 |
{
|
|
1306 | 1 |
result = e; // break at a higher level |
1307 | 1 |
return; |
1308 |
}
|
|
1309 | 1 |
istate.gotoTarget = null; |
1310 | 1 |
e = null; |
1311 |
}
|
|
1312 | 1 |
result = e; |
1313 |
}
|
|
1314 |
|
|
1315 |
override void visit(CaseStatement s) |
|
1316 |
{
|
|
1317 |
debug (LOG) |
|
1318 |
{
|
|
1319 |
printf("%s CaseStatement::interpret(%s) this = %p\n", s.loc.toChars(), s.exp.toChars(), s); |
|
1320 |
}
|
|
1321 | 1 |
incUsageCtfe(istate, s.loc); |
1322 | 1 |
if (istate.start == s) |
1323 | 1 |
istate.start = null; |
1324 |
|
|
1325 | 1 |
result = interpret(pue, s.statement, istate); |
1326 |
}
|
|
1327 |
|
|
1328 |
override void visit(DefaultStatement s) |
|
1329 |
{
|
|
1330 |
debug (LOG) |
|
1331 |
{
|
|
1332 |
printf("%s DefaultStatement::interpret()\n", s.loc.toChars()); |
|
1333 |
}
|
|
1334 | 1 |
incUsageCtfe(istate, s.loc); |
1335 | 1 |
if (istate.start == s) |
1336 | 1 |
istate.start = null; |
1337 |
|
|
1338 | 1 |
result = interpret(pue, s.statement, istate); |
1339 |
}
|
|
1340 |
|
|
1341 |
override void visit(GotoStatement s) |
|
1342 |
{
|
|
1343 |
debug (LOG) |
|
1344 |
{
|
|
1345 |
printf("%s GotoStatement::interpret()\n", s.loc.toChars()); |
|
1346 |
}
|
|
1347 | 1 |
if (istate.start) |
1348 |
{
|
|
1349 | 1 |
if (istate.start != s) |
1350 | 1 |
return; |
1351 |
istate.start = null; |
|
1352 |
}
|
|
1353 | 1 |
incUsageCtfe(istate, s.loc); |
1354 |
|
|
1355 | 1 |
assert(s.label && s.label.statement); |
1356 | 1 |
istate.gotoTarget = s.label.statement; |
1357 | 1 |
result = CTFEExp.gotoexp; |
1358 |
}
|
|
1359 |
|
|
1360 |
override void visit(GotoCaseStatement s) |
|
1361 |
{
|
|
1362 |
debug (LOG) |
|
1363 |
{
|
|
1364 |
printf("%s GotoCaseStatement::interpret()\n", s.loc.toChars()); |
|
1365 |
}
|
|
1366 | 1 |
if (istate.start) |
1367 |
{
|
|
1368 | 1 |
if (istate.start != s) |
1369 | 1 |
return; |
1370 |
istate.start = null; |
|
1371 |
}
|
|
1372 | 1 |
incUsageCtfe(istate, s.loc); |
1373 |
|
|
1374 | 1 |
assert(s.cs); |
1375 | 1 |
istate.gotoTarget = s.cs; |
1376 | 1 |
result = CTFEExp.gotoexp; |
1377 |
}
|
|
1378 |
|
|
1379 |
override void visit(GotoDefaultStatement s) |
|
1380 |
{
|
|
1381 |
debug (LOG) |
|
1382 |
{
|
|
1383 |
printf("%s GotoDefaultStatement::interpret()\n", s.loc.toChars()); |
|
1384 |
}
|
|
1385 | 1 |
if (istate.start) |
1386 |
{
|
|
1387 | 1 |
if (istate.start != s) |
1388 | 1 |
return; |
1389 |
istate.start = null; |
|
1390 |
}
|
|
1391 | 1 |
incUsageCtfe(istate, s.loc); |
1392 |
|
|
1393 | 1 |
assert(s.sw && s.sw.sdefault); |
1394 | 1 |
istate.gotoTarget = s.sw.sdefault; |
1395 | 1 |
result = CTFEExp.gotoexp; |
1396 |
}
|
|
1397 |
|
|
1398 |
override void visit(LabelStatement s) |
|
1399 |
{
|
|
1400 |
debug (LOG) |
|
1401 |
{
|
|
1402 |
printf("%s LabelStatement::interpret()\n", s.loc.toChars()); |
|
1403 |
}
|
|
1404 | 1 |
if (istate.start == s) |
1405 | 1 |
istate.start = null; |
1406 |
|
|
1407 | 1 |
result = interpret(pue, s.statement, istate); |
1408 |
}
|
|
1409 |
|
|
1410 |
override void visit(TryCatchStatement s) |
|
1411 |
{
|
|
1412 |
debug (LOG) |
|
1413 |
{
|
|
1414 |
printf("%s TryCatchStatement::interpret()\n", s.loc.toChars()); |
|
1415 |
}
|
|
1416 | 1 |
if (istate.start == s) |
1417 |
istate.start = null; |
|
1418 | 1 |
if (istate.start) |
1419 |
{
|
|
1420 | 1 |
Expression e = null; |
1421 | 1 |
e = interpret(pue, s._body, istate); |
1422 | 1 |
foreach (ca; *s.catches) |
1423 |
{
|
|
1424 | 1 |
if (e || !istate.start) // goto target was found |
1425 | 1 |
break; |
1426 | 1 |
e = interpret(pue, ca.handler, istate); |
1427 |
}
|
|
1428 | 1 |
result = e; |
1429 | 1 |
return; |
1430 |
}
|
|
1431 |
|
|
1432 | 1 |
Expression e = interpret(s._body, istate); |
1433 |
|
|
1434 |
// An exception was thrown
|
|
1435 | 1 |
if (e && e.op == TOK.thrownException) |
1436 |
{
|
|
1437 | 1 |
ThrownExceptionExp ex = cast(ThrownExceptionExp)e; |
1438 | 1 |
Type extype = ex.thrown.originalClass().type; |
1439 |
|
|
1440 |
// Search for an appropriate catch clause.
|
|
1441 | 1 |
foreach (ca; *s.catches) |
1442 |
{
|
|
1443 | 1 |
Type catype = ca.type; |
1444 | 1 |
if (!catype.equals(extype) && !catype.isBaseOf(extype, null)) |
1445 | 1 |
continue; |
1446 |
|
|
1447 |
// Execute the handler
|
|
1448 | 1 |
if (ca.var) |
1449 |
{
|
|
1450 | 1 |
ctfeGlobals.stack.push(ca.var); |
1451 | 1 |
setValue(ca.var, ex.thrown); |
1452 |
}
|
|
1453 | 1 |
e = interpret(ca.handler, istate); |
1454 | 1 |
if (CTFEExp.isGotoExp(e)) |
1455 |
{
|
|
1456 |
/* This is an optimization that relies on the locality of the jump target.
|
|
1457 |
* If the label is in the same catch handler, the following scan
|
|
1458 |
* would find it quickly and can reduce jump cost.
|
|
1459 |
* Otherwise, the catch block may be unnnecessary scanned again
|
|
1460 |
* so it would make CTFE speed slower.
|
|
1461 |
*/
|
|
1462 | 1 |
InterState istatex = *istate; |
1463 | 1 |
istatex.start = istate.gotoTarget; // set starting statement |
1464 | 1 |
istatex.gotoTarget = null; |
1465 | 1 |
Expression eh = interpret(ca.handler, &istatex); |
1466 | 1 |
if (!istatex.start) |
1467 |
{
|
|
1468 | 1 |
istate.gotoTarget = null; |
1469 | 1 |
e = eh; |
1470 |
}
|
|
1471 |
}
|
|
1472 | 1 |
break; |
1473 |
}
|
|
1474 |
}
|
|
1475 | 1 |
result = e; |
1476 |
}
|
|
1477 |
|
|
1478 |
static bool isAnErrorException(ClassDeclaration cd) |
|
1479 |
{
|
|
1480 | 1 |
return cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null); |
1481 |
}
|
|
1482 |
|
|
1483 |
static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) |
|
1484 |
{
|
|
1485 |
debug (LOG) |
|
1486 |
{
|
|
1487 |
printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); |
|
1488 |
}
|
|
1489 |
// Little sanity check to make sure it's really a Throwable
|
|
1490 | 1 |
ClassReferenceExp boss = oldest.thrown; |
1491 | 1 |
const next = 4; // index of Throwable.next |
1492 | 1 |
assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next |
1493 | 1 |
ClassReferenceExp collateral = newest.thrown; |
1494 | 1 |
if (isAnErrorException(collateral.originalClass()) && !isAnErrorException(boss.originalClass())) |
1495 |
{
|
|
1496 |
/* Find the index of the Error.bypassException field
|
|
1497 |
*/
|
|
1498 | 1 |
auto bypass = next + 1; |
1499 | 1 |
if ((*collateral.value.elements)[bypass].type.ty == Tuns32) |
1500 | 1 |
bypass += 1; // skip over _refcount field |
1501 | 1 |
assert((*collateral.value.elements)[bypass].type.ty == Tclass); |
1502 |
|
|
1503 |
// The new exception bypass the existing chain
|
|
1504 | 1 |
(*collateral.value.elements)[bypass] = boss; |
1505 | 1 |
return newest; |
1506 |
}
|
|
1507 | 1 |
while ((*boss.value.elements)[next].op == TOK.classReference) |
1508 |
{
|
|
1509 | 1 |
boss = cast(ClassReferenceExp)(*boss.value.elements)[next]; |
1510 |
}
|
|
1511 | 1 |
(*boss.value.elements)[next] = collateral; |
1512 | 1 |
return oldest; |
1513 |
}
|
|
1514 |
|
|
1515 |
override void visit(TryFinallyStatement s) |
|
1516 |
{
|
|
1517 |
debug (LOG) |
|
1518 |
{
|
|
1519 |
printf("%s TryFinallyStatement::interpret()\n", s.loc.toChars()); |
|
1520 |
}
|
|
1521 | 1 |
if (istate.start == s) |
1522 |
istate.start = null; |
|
1523 | 1 |
if (istate.start) |
1524 |
{
|
|
1525 | 1 |
Expression e = null; |
1526 | 1 |
e = interpret(pue, s._body, istate); |
1527 |
// Jump into/out from finalbody is disabled in semantic analysis.
|
|
1528 |
// and jump inside will be handled by the ScopeStatement == finalbody.
|
|
1529 | 1 |
result = e; |
1530 | 1 |
return; |
1531 |
}
|
|
1532 |
|
|
1533 | 1 |
Expression ex = interpret(s._body, istate); |
1534 | 1 |
if (CTFEExp.isCantExp(ex)) |
1535 |
{
|
|
1536 |
result = ex; |
|
1537 |
return; |
|
1538 |
}
|
|
1539 | 1 |
while (CTFEExp.isGotoExp(ex)) |
1540 |
{
|
|
1541 |
// If the goto target is within the body, we must not interpret the finally statement,
|
|
1542 |
// because that will call destructors for objects within the scope, which we should not do.
|
|
1543 | 1 |
InterState istatex = *istate; |
1544 | 1 |
istatex.start = istate.gotoTarget; // set starting statement |
1545 | 1 |
istatex.gotoTarget = null; |
1546 | 1 |
Expression bex = interpret(s._body, &istatex); |
1547 | 1 |
if (istatex.start) |
1548 |
{
|
|
1549 |
// The goto target is outside the current scope.
|
|
1550 | 1 |
break; |
1551 |
}
|
|
1552 |
// The goto target was within the body.
|
|
1553 | 1 |
if (CTFEExp.isCantExp(bex)) |
1554 |
{
|
|
1555 |
result = bex; |
|
1556 |
return; |
|
1557 |
}
|
|
1558 | 1 |
*istate = istatex; |
1559 | 1 |
ex = bex; |
1560 |
}
|
|
1561 |
|
|
1562 | 1 |
Expression ey = interpret(s.finalbody, istate); |
1563 | 1 |
if (CTFEExp.isCantExp(ey)) |
1564 |
{
|
|
1565 |
result = ey; |
|
1566 |
return; |
|
1567 |
}
|
|
1568 | 1 |
if (ey && ey.op == TOK.thrownException) |
1569 |
{
|
|
1570 |
// Check for collided exceptions
|
|
1571 | 1 |
if (ex && ex.op == TOK.thrownException) |
1572 | 1 |
ex = chainExceptions(cast(ThrownExceptionExp)ex, cast(ThrownExceptionExp)ey); |
1573 |
else
|
|
1574 |
ex = ey; |
|
1575 |
}
|
|
1576 | 1 |
result = ex; |
1577 |
}
|
|
1578 |
|
|
1579 |
override void visit(ThrowStatement s) |
|
1580 |
{
|
|
1581 |
debug (LOG) |
|
1582 |
{
|
|
1583 |
printf("%s ThrowStatement::interpret()\n", s.loc.toChars()); |
|
1584 |
}
|
|
1585 | 1 |
if (istate.start) |
1586 |
{
|
|
1587 | 1 |
if (istate.start != s) |
1588 | 1 |
return; |
1589 |
istate.start = null; |
|
1590 |
}
|
|
1591 |
|
|
1592 | 1 |
incUsageCtfe(istate, s.loc); |
1593 |
|
|
1594 | 1 |
Expression e = interpretRegion(s.exp, istate); |
1595 | 1 |
if (exceptionOrCant(e)) |
1596 |
return; |
|
1597 |
|
|
1598 | 1 |
assert(e.op == TOK.classReference); |
1599 | 1 |
result = ctfeEmplaceExp!ThrownExceptionExp(s.loc, e.isClassReferenceExp()); |
1600 |
}
|
|
1601 |
|
|
1602 |
override void visit(ScopeGuardStatement s) |
|
1603 |
{
|
|
1604 |
assert(0); |
|
1605 |
}
|
|
1606 |
|
|
1607 |
override void visit(WithStatement s) |
|
1608 |
{
|
|
1609 |
debug (LOG) |
|
1610 |
{
|
|
1611 |
printf("%s WithStatement::interpret()\n", s.loc.toChars()); |
|
1612 |
}
|
|
1613 | 1 |
if (istate.start == s) |
1614 |
istate.start = null; |
|
1615 | 1 |
if (istate.start) |
1616 |
{
|
|
1617 | 1 |
result = s._body ? interpret(s._body, istate) : null; |
1618 | 1 |
return; |
1619 |
}
|
|
1620 |
|
|
1621 |
// If it is with(Enum) {...}, just execute the body.
|
|
1622 | 1 |
if (s.exp.op == TOK.scope_ || s.exp.op == TOK.type) |
1623 |
{
|
|
1624 | 1 |
result = interpret(pue, s._body, istate); |
1625 | 1 |
return; |
1626 |
}
|
|
1627 |
|
|
1628 | 1 |
incUsageCtfe(istate, s.loc); |
1629 |
|
|
1630 | 1 |
Expression e = interpret(s.exp, istate); |
1631 | 1 |
if (exceptionOrCant(e)) |
1632 |
return; |
|
1633 |
|
|
1634 | 1 |
if (s.wthis.type.ty == Tpointer && s.exp.type.ty != Tpointer) |
1635 |
{
|
|
1636 | 1 |
e = ctfeEmplaceExp!AddrExp(s.loc, e, s.wthis.type); |
1637 |
}
|
|
1638 | 1 |
ctfeGlobals.stack.push(s.wthis); |
1639 | 1 |
setValue(s.wthis, e); |
1640 | 1 |
e = interpret(s._body, istate); |
1641 | 1 |
if (CTFEExp.isGotoExp(e)) |
1642 |
{
|
|
1643 |
/* This is an optimization that relies on the locality of the jump target.
|
|
1644 |
* If the label is in the same WithStatement, the following scan
|
|
1645 |
* would find it quickly and can reduce jump cost.
|
|
1646 |
* Otherwise, the statement body may be unnnecessary scanned again
|
|
1647 |
* so it would make CTFE speed slower.
|
|
1648 |
*/
|
|
1649 | 1 |
InterState istatex = *istate; |
1650 | 1 |
istatex.start = istate.gotoTarget; // set starting statement |
1651 | 1 |
istatex.gotoTarget = null; |
1652 | 1 |
Expression ex = interpret(s._body, &istatex); |
1653 | 1 |
if (!istatex.start) |
1654 |
{
|
|
1655 | 1 |
istate.gotoTarget = null; |
1656 | 1 |
e = ex; |
1657 |
}
|
|
1658 |
}
|
|
1659 | 1 |
ctfeGlobals.stack.pop(s.wthis); |
1660 | 1 |
result = e; |
1661 |
}
|
|
1662 |
|
|
1663 |
override void visit(AsmStatement s) |
|
1664 |
{
|
|
1665 |
debug (LOG) |
|
1666 |
{
|
|
1667 |
printf("%s AsmStatement::interpret()\n", s.loc.toChars()); |
|
1668 |
}
|
|
1669 |
if (istate.start) |
|
1670 |
{
|
|
1671 |
if (istate.start != s) |
|
1672 |
return; |
|
1673 |
istate.start = null; |
|
1674 |
}
|
|
1675 |
s.error("`asm` statements cannot be interpreted at compile time"); |
|
1676 |
result = CTFEExp.cantexp; |
|
1677 |
}
|
|
1678 |
|
|
1679 |
override void visit(ImportStatement s) |
|
1680 |
{
|
|
1681 |
debug (LOG) |
|
1682 |
{
|
|
1683 |
printf("ImportStatement::interpret()\n"); |
|
1684 |
}
|
|
1685 | 1 |
if (istate.start) |
1686 |
{
|
|
1687 | 1 |
if (istate.start != s) |
1688 | 1 |
return; |
1689 |
istate.start = null; |
|
1690 |
}
|
|
1691 |
}
|
|
1692 |
|
|
1693 |
/******************************** Expression ***************************/
|
|
1694 |
|
|
1695 |
override void visit(Expression e) |
|
1696 |
{
|
|
1697 |
debug (LOG) |
|
1698 |
{
|
|
1699 |
printf("%s Expression::interpret() '%s' %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars()); |
|
1700 |
printf("type = %s\n", e.type.toChars()); |
|
1701 |
showCtfeExpr(e); |
|
1702 |
}
|
|
1703 | 1 |
e.error("cannot interpret `%s` at compile time", e.toChars()); |
1704 | 1 |
result = CTFEExp.cantexp; |
1705 |
}
|
|
1706 |
|
|
1707 |
override void visit(TypeExp e) |
|
1708 |
{
|
|
1709 |
debug (LOG) |
|
1710 |
{
|
|
1711 |
printf("%s TypeExp.interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1712 |
}
|
|
1713 | 1 |
result = e; |
1714 |
}
|
|
1715 |
|
|
1716 |
override void visit(ThisExp e) |
|
1717 |
{
|
|
1718 |
debug (LOG) |
|
1719 |
{
|
|
1720 |
printf("%s ThisExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1721 |
}
|
|
1722 | 1 |
if (goal == CTFEGoal.LValue) |
1723 |
{
|
|
1724 |
// We might end up here with istate being zero
|
|
1725 |
// https://issues.dlang.org/show_bug.cgi?id=16382
|
|
1726 | 1 |
if (istate && istate.fd.vthis) |
1727 |
{
|
|
1728 | 1 |
result = ctfeEmplaceExp!VarExp(e.loc, istate.fd.vthis); |
1729 | 1 |
if (istate.fd.isThis2) |
1730 |
{
|
|
1731 | 1 |
result = ctfeEmplaceExp!PtrExp(e.loc, result); |
1732 | 1 |
result.type = Type.tvoidptr.sarrayOf(2); |
1733 | 1 |
result = ctfeEmplaceExp!IndexExp(e.loc, result, IntegerExp.literal!0); |
1734 |
}
|
|
1735 | 1 |
result.type = e.type; |
1736 |
}
|
|
1737 |
else
|
|
1738 | 1 |
result = e; |
1739 | 1 |
return; |
1740 |
}
|
|
1741 |
|
|
1742 | 1 |
result = ctfeGlobals.stack.getThis(); |
1743 | 1 |
if (result) |
1744 |
{
|
|
1745 | 1 |
if (istate && istate.fd.isThis2) |
1746 |
{
|
|
1747 | 1 |
assert(result.op == TOK.address); |
1748 | 1 |
result = (cast(AddrExp)result).e1; |
1749 | 1 |
assert(result.op == TOK.arrayLiteral); |
1750 | 1 |
result = (*(cast(ArrayLiteralExp)result).elements)[0]; |
1751 | 1 |
if (e.type.ty == Tstruct) |
1752 |
{
|
|
1753 | 1 |
result = (cast(AddrExp)result).e1; |
1754 |
}
|
|
1755 | 1 |
return; |
1756 |
}
|
|
1757 | 1 |
assert(result.op == TOK.structLiteral || result.op == TOK.classReference || result.op == TOK.type); |
1758 | 1 |
return; |
1759 |
}
|
|
1760 | 1 |
e.error("value of `this` is not known at compile time"); |
1761 | 1 |
result = CTFEExp.cantexp; |
1762 |
}
|
|
1763 |
|
|
1764 |
override void visit(NullExp e) |
|
1765 |
{
|
|
1766 | 1 |
result = e; |
1767 |
}
|
|
1768 |
|
|
1769 |
override void visit(IntegerExp e) |
|
1770 |
{
|
|
1771 |
debug (LOG) |
|
1772 |
{
|
|
1773 |
printf("%s IntegerExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1774 |
}
|
|
1775 | 1 |
result = e; |
1776 |
}
|
|
1777 |
|
|
1778 |
override void visit(RealExp e) |
|
1779 |
{
|
|
1780 |
debug (LOG) |
|
1781 |
{
|
|
1782 |
printf("%s RealExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1783 |
}
|
|
1784 | 1 |
result = e; |
1785 |
}
|
|
1786 |
|
|
1787 |
override void visit(ComplexExp e) |
|
1788 |
{
|
|
1789 | 1 |
result = e; |
1790 |
}
|
|
1791 |
|
|
1792 |
override void visit(StringExp e) |
|
1793 |
{
|
|
1794 |
debug (LOG) |
|
1795 |
{
|
|
1796 |
printf("%s StringExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1797 |
}
|
|
1798 |
/* Attempts to modify string literals are prevented
|
|
1799 |
* in BinExp::interpretAssignCommon.
|
|
1800 |
*/
|
|
1801 | 1 |
result = e; |
1802 |
}
|
|
1803 |
|
|
1804 |
override void visit(FuncExp e) |
|
1805 |
{
|
|
1806 |
debug (LOG) |
|
1807 |
{
|
|
1808 |
printf("%s FuncExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1809 |
}
|
|
1810 | 1 |
result = e; |
1811 |
}
|
|
1812 |
|
|
1813 |
override void visit(SymOffExp e) |
|
1814 |
{
|
|
1815 |
debug (LOG) |
|
1816 |
{
|
|
1817 |
printf("%s SymOffExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1818 |
}
|
|
1819 | 1 |
if (e.var.isFuncDeclaration() && e.offset == 0) |
1820 |
{
|
|
1821 | 1 |
result = e; |
1822 | 1 |
return; |
1823 |
}
|
|
1824 | 1 |
if (isTypeInfo_Class(e.type) && e.offset == 0) |
1825 |
{
|
|
1826 | 1 |
result = e; |
1827 | 1 |
return; |
1828 |
}
|
|
1829 | 1 |
if (e.type.ty != Tpointer) |
1830 |
{
|
|
1831 |
// Probably impossible
|
|
1832 |
e.error("cannot interpret `%s` at compile time", e.toChars()); |
|
1833 |
result = CTFEExp.cantexp; |
|
1834 |
return; |
|
1835 |
}
|
|
1836 | 1 |
Type pointee = (cast(TypePointer)e.type).next; |
1837 | 1 |
if (e.var.isThreadlocal()) |
1838 |
{
|
|
1839 | 1 |
e.error("cannot take address of thread-local variable %s at compile time", e.var.toChars()); |
1840 | 1 |
result = CTFEExp.cantexp; |
1841 | 1 |
return; |
1842 |
}
|
|
1843 |
// Check for taking an address of a shared variable.
|
|
1844 |
// If the shared variable is an array, the offset might not be zero.
|
|
1845 | 1 |
Type fromType = null; |
1846 | 1 |
if (e.var.type.ty == Tarray || e.var.type.ty == Tsarray) |
1847 |
{
|
|
1848 | 1 |
fromType = (cast(TypeArray)e.var.type).next; |
1849 |
}
|
|
1850 | 1 |
if (e.var.isDataseg() && ((e.offset == 0 && isSafePointerCast(e.var.type, pointee)) || (fromType && isSafePointerCast(fromType, pointee)))) |
1851 |
{
|
|
1852 | 1 |
result = e; |
1853 | 1 |
return; |
1854 |
}
|
|
1855 |
|
|
1856 | 1 |
Expression val = getVarExp(e.loc, istate, e.var, goal); |
1857 | 1 |
if (exceptionOrCant(val)) |
1858 | 1 |
return; |
1859 | 1 |
if (val.type.ty == Tarray || val.type.ty == Tsarray) |
1860 |
{
|
|
1861 |
// Check for unsupported type painting operations
|
|
1862 | 1 |
Type elemtype = (cast(TypeArray)val.type).next; |
1863 | 1 |
d_uns64 elemsize = elemtype.size(); |
1864 |
|
|
1865 |
// It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*.
|
|
1866 | 1 |
if (val.type.ty == Tsarray && pointee.ty == Tsarray && elemsize == pointee.nextOf().size()) |
1867 |
{
|
|
1868 | 1 |
size_t d = cast(size_t)(cast(TypeSArray)pointee).dim.toInteger(); |
1869 | 1 |
Expression elwr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize, Type.tsize_t); |
1870 | 1 |
Expression eupr = ctfeEmplaceExp!IntegerExp(e.loc, e.offset / elemsize + d, Type.tsize_t); |
1871 |
|
|
1872 |
// Create a CTFE pointer &val[ofs..ofs+d]
|
|
1873 | 1 |
auto se = ctfeEmplaceExp!SliceExp(e.loc, val, elwr, eupr); |
1874 | 1 |
se.type = pointee; |
1875 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, se, e.type); |
1876 | 1 |
result = pue.exp(); |
1877 | 1 |
return; |
1878 |
}
|
|
1879 |
|
|
1880 | 1 |
if (!isSafePointerCast(elemtype, pointee)) |
1881 |
{
|
|
1882 |
// It's also OK to cast from &string to string*.
|
|
1883 | 1 |
if (e.offset == 0 && isSafePointerCast(e.var.type, pointee)) |
1884 |
{
|
|
1885 |
// Create a CTFE pointer &var
|
|
1886 | 1 |
auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var); |
1887 | 1 |
ve.type = elemtype; |
1888 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); |
1889 | 1 |
result = pue.exp(); |
1890 | 1 |
return; |
1891 |
}
|
|
1892 | 1 |
e.error("reinterpreting cast from `%s` to `%s` is not supported in CTFE", val.type.toChars(), e.type.toChars()); |
1893 | 1 |
result = CTFEExp.cantexp; |
1894 | 1 |
return; |
1895 |
}
|
|
1896 |
|
|
1897 | 1 |
const dinteger_t sz = pointee.size(); |
1898 | 1 |
dinteger_t indx = e.offset / sz; |
1899 | 1 |
assert(sz * indx == e.offset); |
1900 | 1 |
Expression aggregate = null; |
1901 | 1 |
if (val.op == TOK.arrayLiteral || val.op == TOK.string_) |
1902 |
{
|
|
1903 | 1 |
aggregate = val; |
1904 |
}
|
|
1905 |
else if (auto se = val.isSliceExp()) |
|
1906 |
{
|
|
1907 |
aggregate = se.e1; |
|
1908 |
UnionExp uelwr = void; |
|
1909 |
Expression lwr = interpret(&uelwr, se.lwr, istate); |
|
1910 |
indx += lwr.toInteger(); |
|
1911 |
}
|
|
1912 | 1 |
if (aggregate) |
1913 |
{
|
|
1914 |
// Create a CTFE pointer &aggregate[ofs]
|
|
1915 | 1 |
auto ofs = ctfeEmplaceExp!IntegerExp(e.loc, indx, Type.tsize_t); |
1916 | 1 |
auto ei = ctfeEmplaceExp!IndexExp(e.loc, aggregate, ofs); |
1917 | 1 |
ei.type = elemtype; |
1918 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, ei, e.type); |
1919 | 1 |
result = pue.exp(); |
1920 | 1 |
return; |
1921 |
}
|
|
1922 |
}
|
|
1923 | 1 |
else if (e.offset == 0 && isSafePointerCast(e.var.type, pointee)) |
1924 |
{
|
|
1925 |
// Create a CTFE pointer &var
|
|
1926 | 1 |
auto ve = ctfeEmplaceExp!VarExp(e.loc, e.var); |
1927 | 1 |
ve.type = e.var.type; |
1928 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, ve, e.type); |
1929 | 1 |
result = pue.exp(); |
1930 | 1 |
return; |
1931 |
}
|
|
1932 |
|
|
1933 | 1 |
e.error("cannot convert `&%s` to `%s` at compile time", e.var.type.toChars(), e.type.toChars()); |
1934 | 1 |
result = CTFEExp.cantexp; |
1935 |
}
|
|
1936 |
|
|
1937 |
override void visit(AddrExp e) |
|
1938 |
{
|
|
1939 |
debug (LOG) |
|
1940 |
{
|
|
1941 |
printf("%s AddrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1942 |
}
|
|
1943 | 1 |
if (auto ve = e.e1.isVarExp()) |
1944 |
{
|
|
1945 | 1 |
Declaration decl = ve.var; |
1946 |
|
|
1947 |
// We cannot take the address of an imported symbol at compile time
|
|
1948 | 1 |
if (decl.isImportedSymbol()) { |
1949 | 1 |
e.error("cannot take address of imported symbol `%s` at compile time", decl.toChars()); |
1950 | 1 |
result = CTFEExp.cantexp; |
1951 | 1 |
return; |
1952 |
}
|
|
1953 |
|
|
1954 | 1 |
if (decl.isDataseg()) { |
1955 |
// Normally this is already done by optimize()
|
|
1956 |
// Do it here in case optimize(WANTvalue) wasn't run before CTFE
|
|
1957 | 1 |
emplaceExp!(SymOffExp)(pue, e.loc, (cast(VarExp)e.e1).var, 0); |
1958 | 1 |
result = pue.exp(); |
1959 | 1 |
result.type = e.type; |
1960 | 1 |
return; |
1961 |
}
|
|
1962 |
}
|
|
1963 | 1 |
auto er = interpret(e.e1, istate, CTFEGoal.LValue); |
1964 | 1 |
if (auto ve = er.isVarExp()) |
1965 | 1 |
if (ve.var == istate.fd.vthis) |
1966 | 1 |
er = interpret(er, istate); |
1967 |
|
|
1968 | 1 |
if (exceptionOrCant(er)) |
1969 | 1 |
return; |
1970 |
|
|
1971 |
// Return a simplified address expression
|
|
1972 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, er, e.type); |
1973 | 1 |
result = pue.exp(); |
1974 |
}
|
|
1975 |
|
|
1976 |
override void visit(DelegateExp e) |
|
1977 |
{
|
|
1978 |
debug (LOG) |
|
1979 |
{
|
|
1980 |
printf("%s DelegateExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
1981 |
}
|
|
1982 |
// TODO: Really we should create a CTFE-only delegate expression
|
|
1983 |
// of a pointer and a funcptr.
|
|
1984 |
|
|
1985 |
// If it is &nestedfunc, just return it
|
|
1986 |
// TODO: We should save the context pointer
|
|
1987 | 1 |
if (auto ve1 = e.e1.isVarExp()) |
1988 | 1 |
if (ve1.var == e.func) |
1989 |
{
|
|
1990 | 1 |
result = e; |
1991 | 1 |
return; |
1992 |
}
|
|
1993 |
|
|
1994 | 1 |
auto er = interpret(pue, e.e1, istate); |
1995 | 1 |
if (exceptionOrCant(er)) |
1996 |
return; |
|
1997 | 1 |
if (er == e.e1) |
1998 |
{
|
|
1999 |
// If it has already been CTFE'd, just return it
|
|
2000 | 1 |
result = e; |
2001 |
}
|
|
2002 |
else
|
|
2003 |
{
|
|
2004 | 1 |
er = (er == pue.exp()) ? pue.copy() : er; |
2005 | 1 |
emplaceExp!(DelegateExp)(pue, e.loc, er, e.func, false); |
2006 | 1 |
result = pue.exp(); |
2007 | 1 |
result.type = e.type; |
2008 |
}
|
|
2009 |
}
|
|
2010 |
|
|
2011 |
static Expression getVarExp(const ref Loc loc, InterState* istate, Declaration d, CTFEGoal goal) |
|
2012 |
{
|
|
2013 | 1 |
Expression e = CTFEExp.cantexp; |
2014 | 1 |
if (VarDeclaration v = d.isVarDeclaration()) |
2015 |
{
|
|
2016 |
/* Magic variable __ctfe always returns true when interpreting
|
|
2017 |
*/
|
|
2018 | 1 |
if (v.ident == Id.ctfe) |
2019 | 1 |
return IntegerExp.createBool(true); |
2020 |
|
|
2021 | 1 |
if (!v.originalType && v.semanticRun < PASS.semanticdone) // semantic() not yet run |
2022 |
{
|
|
2023 | 1 |
v.dsymbolSemantic(null); |
2024 | 1 |
if (v.type.ty == Terror) |
2025 |
return CTFEExp.cantexp; |
|
2026 |
}
|
|
2027 |
|
|
2028 | 1 |
if ((v.isConst() || v.isImmutable() || v.storage_class & STC.manifest) && !hasValue(v) && v._init && !v.isCTFE()) |
2029 |
{
|
|
2030 | 1 |
if (v.inuse) |
2031 |
{
|
|
2032 | 1 |
error(loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); |
2033 | 1 |
return CTFEExp.cantexp; |
2034 |
}
|
|
2035 | 1 |
if (v._scope) |
2036 |
{
|
|
2037 | 1 |
v.inuse++; |
2038 | 1 |
v._init = v._init.initializerSemantic(v._scope, v.type, INITinterpret); // might not be run on aggregate members |
2039 | 1 |
v.inuse--; |
2040 |
}
|
|
2041 | 1 |
e = v._init.initializerToExpression(v.type); |
2042 | 1 |
if (!e) |
2043 |
return CTFEExp.cantexp; |
|
2044 | 1 |
assert(e.type); |
2045 |
|
|
2046 | 1 |
if (e.op == TOK.construct || e.op == TOK.blit) |
2047 |
{
|
|
2048 | 1 |
AssignExp ae = cast(AssignExp)e; |
2049 | 1 |
e = ae.e2; |
2050 |
}
|
|
2051 |
|
|
2052 | 1 |
if (e.op == TOK.error) |
2053 |
{
|
|
2054 |
// FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
|
|
2055 |
}
|
|
2056 | 1 |
else if (v.isDataseg() || (v.storage_class & STC.manifest)) |
2057 |
{
|
|
2058 |
/* https://issues.dlang.org/show_bug.cgi?id=14304
|
|
2059 |
* e is a value that is not yet owned by CTFE.
|
|
2060 |
* Mark as "cached", and use it directly during interpretation.
|
|
2061 |
*/
|
|
2062 | 1 |
e = scrubCacheValue(e); |
2063 | 1 |
ctfeGlobals.stack.saveGlobalConstant(v, e); |
2064 |
}
|
|
2065 |
else
|
|
2066 |
{
|
|
2067 | 1 |
v.inuse++; |
2068 | 1 |
e = interpret(e, istate); |
2069 | 1 |
v.inuse--; |
2070 | 1 |
if (CTFEExp.isCantExp(e) && !global.gag && !ctfeGlobals.stackTraceCallsToSuppress) |
2071 |
errorSupplemental(loc, "while evaluating %s.init", v.toChars()); |
|
2072 | 1 |
if (exceptionOrCantInterpret(e)) |
2073 | 1 |
return e; |
2074 |
}
|
|
2075 |
}
|
|
2076 | 1 |
else if (v.isCTFE() && !hasValue(v)) |
2077 |
{
|
|
2078 | 1 |
if (v._init && v.type.size() != 0) |
2079 |
{
|
|
2080 | 1 |
if (v._init.isVoidInitializer()) |
2081 |
{
|
|
2082 |
// var should have been initialized when it was created
|
|
2083 |
error(loc, "CTFE internal error: trying to access uninitialized var"); |
|
2084 |
assert(0); |
|
2085 |
}
|
|
2086 | 1 |
e = v._init.initializerToExpression(); |
2087 |
}
|
|
2088 |
else
|
|
2089 | 1 |
e = v.type.defaultInitLiteral(e.loc); |
2090 |
|
|
2091 | 1 |
e = interpret(e, istate); |
2092 |
}
|
|
2093 | 1 |
else if (!(v.isDataseg() || v.storage_class & STC.manifest) && !v.isCTFE() && !istate) |
2094 |
{
|
|
2095 | 1 |
error(loc, "variable `%s` cannot be read at compile time", v.toChars()); |
2096 | 1 |
return CTFEExp.cantexp; |
2097 |
}
|
|
2098 |
else
|
|
2099 |
{
|
|
2100 | 1 |
e = hasValue(v) ? getValue(v) : null; |
2101 | 1 |
if (!e && !v.isCTFE() && v.isDataseg()) |
2102 |
{
|
|
2103 | 1 |
error(loc, "static variable `%s` cannot be read at compile time", v.toChars()); |
2104 | 1 |
return CTFEExp.cantexp; |
2105 |
}
|
|
2106 | 1 |
if (!e) |
2107 |
{
|
|
2108 | 1 |
assert(!(v._init && v._init.isVoidInitializer())); |
2109 |
// CTFE initiated from inside a function
|
|
2110 | 1 |
error(loc, "variable `%s` cannot be read at compile time", v.toChars()); |
2111 | 1 |
return CTFEExp.cantexp; |
2112 |
}
|
|
2113 | 1 |
if (auto vie = e.isVoidInitExp()) |
2114 |
{
|
|
2115 | 1 |
error(loc, "cannot read uninitialized variable `%s` in ctfe", v.toPrettyChars()); |
2116 | 1 |
errorSupplemental(vie.var.loc, "`%s` was uninitialized and used before set", vie.var.toChars()); |
2117 | 1 |
return CTFEExp.cantexp; |
2118 |
}
|
|
2119 | 1 |
if (goal != CTFEGoal.LValue && (v.isRef() || v.isOut())) |
2120 | 1 |
e = interpret(e, istate, goal); |
2121 |
}
|
|
2122 | 1 |
if (!e) |
2123 |
e = CTFEExp.cantexp; |
|
2124 |
}
|
|
2125 | 1 |
else if (SymbolDeclaration s = d.isSymbolDeclaration()) |
2126 |
{
|
|
2127 |
// Struct static initializers, for example
|
|
2128 | 1 |
e = s.dsym.type.defaultInitLiteral(loc); |
2129 | 1 |
if (e.op == TOK.error) |
2130 | 1 |
error(loc, "CTFE failed because of previous errors in `%s.init`", s.toChars()); |
2131 | 1 |
e = e.expressionSemantic(null); |
2132 | 1 |
if (e.op == TOK.error) |
2133 | 1 |
e = CTFEExp.cantexp; |
2134 |
else // Convert NULL to CTFEExp |
|
2135 | 1 |
e = interpret(e, istate, goal); |
2136 |
}
|
|
2137 |
else
|
|
2138 |
error(loc, "cannot interpret declaration `%s` at compile time", d.toChars()); |
|
2139 | 1 |
return e; |
2140 |
}
|
|
2141 |
|
|
2142 |
override void visit(VarExp e) |
|
2143 |
{
|
|
2144 |
debug (LOG) |
|
2145 |
{
|
|
2146 |
printf("%s VarExp::interpret() `%s`, goal = %d\n", e.loc.toChars(), e.toChars(), goal); |
|
2147 |
}
|
|
2148 | 1 |
if (e.var.isFuncDeclaration()) |
2149 |
{
|
|
2150 | 1 |
result = e; |
2151 | 1 |
return; |
2152 |
}
|
|
2153 |
|
|
2154 | 1 |
if (goal == CTFEGoal.LValue) |
2155 |
{
|
|
2156 | 1 |
if (auto v = e.var.isVarDeclaration()) |
2157 |
{
|
|
2158 | 1 |
if (!v.isDataseg() && !v.isCTFE() && !istate) |
2159 |
{
|
|
2160 | 1 |
e.error("variable `%s` cannot be read at compile time", v.toChars()); |
2161 | 1 |
result = CTFEExp.cantexp; |
2162 | 1 |
return; |
2163 |
}
|
|
2164 | 1 |
if (!hasValue(v)) |
2165 |
{
|
|
2166 | 1 |
if (!v.isCTFE() && v.isDataseg()) |
2167 | 1 |
e.error("static variable `%s` cannot be read at compile time", v.toChars()); |
2168 |
else // CTFE initiated from inside a function |
|
2169 | 1 |
e.error("variable `%s` cannot be read at compile time", v.toChars()); |
2170 | 1 |
result = CTFEExp.cantexp; |
2171 | 1 |
return; |
2172 |
}
|
|
2173 |
|
|
2174 | 1 |
if (v.storage_class & (STC.out_ | STC.ref_)) |
2175 |
{
|
|
2176 |
// Strip off the nest of ref variables
|
|
2177 | 1 |
Expression ev = getValue(v); |
2178 | 1 |
if (ev.op == TOK.variable || |
2179 | 1 |
ev.op == TOK.index || |
2180 | 1 |
ev.op == TOK.slice || |
2181 | 1 |
ev.op == TOK.dotVariable) |
2182 |
{
|
|
2183 | 1 |
result = interpret(pue, ev, istate, goal); |
2184 | 1 |
return; |
2185 |
}
|
|
2186 |
}
|
|
2187 |
}
|
|
2188 | 1 |
result = e; |
2189 | 1 |
return; |
2190 |
}
|
|
2191 | 1 |
result = getVarExp(e.loc, istate, e.var, goal); |
2192 | 1 |
if (exceptionOrCant(result)) |
2193 | 1 |
return; |
2194 | 1 |
if ((e.var.storage_class & (STC.ref_ | STC.out_)) == 0 && e.type.baseElemOf().ty != Tstruct) |
2195 |
{
|
|
2196 |
/* Ultimately, STC.ref_|STC.out_ check should be enough to see the
|
|
2197 |
* necessity of type repainting. But currently front-end paints
|
|
2198 |
* non-ref struct variables by the const type.
|
|
2199 |
*
|
|
2200 |
* auto foo(ref const S cs);
|
|
2201 |
* S s;
|
|
2202 |
* foo(s); // VarExp('s') will have const(S)
|
|
2203 |
*/
|
|
2204 |
// A VarExp may include an implicit cast. It must be done explicitly.
|
|
2205 | 1 |
result = paintTypeOntoLiteral(pue, e.type, result); |
2206 |
}
|
|
2207 |
}
|
|
2208 |
|
|
2209 |
override void visit(DeclarationExp e) |
|
2210 |
{
|
|
2211 |
debug (LOG) |
|
2212 |
{
|
|
2213 |
printf("%s DeclarationExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2214 |
}
|
|
2215 | 1 |
Dsymbol s = e.declaration; |
2216 | 1 |
if (VarDeclaration v = s.isVarDeclaration()) |
2217 |
{
|
|
2218 | 1 |
if (TupleDeclaration td = v.toAlias().isTupleDeclaration()) |
2219 |
{
|
|
2220 | 1 |
result = null; |
2221 |
|
|
2222 |
// Reserve stack space for all tuple members
|
|
2223 | 1 |
if (!td.objects) |
2224 |
return; |
|
2225 | 1 |
foreach (o; *td.objects) |
2226 |
{
|
|
2227 | 1 |
Expression ex = isExpression(o); |
2228 | 1 |
DsymbolExp ds = ex ? ex.isDsymbolExp() : null; |
2229 | 1 |
VarDeclaration v2 = ds ? ds.s.isVarDeclaration() : null; |
2230 | 1 |
assert(v2); |
2231 | 1 |
if (v2.isDataseg() && !v2.isCTFE()) |
2232 |
continue; |
|
2233 |
|
|
2234 | 1 |
ctfeGlobals.stack.push(v2); |
2235 | 1 |
if (v2._init) |
2236 |
{
|
|
2237 | 1 |
Expression einit; |
2238 | 1 |
if (ExpInitializer ie = v2._init.isExpInitializer()) |
2239 |
{
|
|
2240 | 1 |
einit = interpretRegion(ie.exp, istate, goal); |
2241 | 1 |
if (exceptionOrCant(einit)) |
2242 |
return; |
|
2243 |
}
|
|
2244 |
else if (v2._init.isVoidInitializer()) |
|
2245 |
{
|
|
2246 |
einit = voidInitLiteral(v2.type, v2).copy(); |
|
2247 |
}
|
|
2248 |
else
|
|
2249 |
{
|
|
2250 |
e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); |
|
2251 |
result = CTFEExp.cantexp; |
|
2252 |
return; |
|
2253 |
}
|
|
2254 | 1 |
setValue(v2, einit); |
2255 |
}
|
|
2256 |
}
|
|
2257 | 1 |
return; |
2258 |
}
|
|
2259 | 1 |
if (v.isStatic()) |
2260 |
{
|
|
2261 |
// Just ignore static variables which aren't read or written yet
|
|
2262 | 1 |
result = null; |
2263 | 1 |
return; |
2264 |
}
|
|
2265 | 1 |
if (!(v.isDataseg() || v.storage_class & STC.manifest) || v.isCTFE()) |
2266 | 1 |
ctfeGlobals.stack.push(v); |
2267 | 1 |
if (v._init) |
2268 |
{
|
|
2269 | 1 |
if (ExpInitializer ie = v._init.isExpInitializer()) |
2270 |
{
|
|
2271 | 1 |
result = interpretRegion(ie.exp, istate, goal); |
2272 |
}
|
|
2273 | 1 |
else if (v._init.isVoidInitializer()) |
2274 |
{
|
|
2275 | 1 |
result = voidInitLiteral(v.type, v).copy(); |
2276 |
// There is no AssignExp for void initializers,
|
|
2277 |
// so set it here.
|
|
2278 | 1 |
setValue(v, result); |
2279 |
}
|
|
2280 |
else
|
|
2281 |
{
|
|
2282 |
e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); |
|
2283 |
result = CTFEExp.cantexp; |
|
2284 |
}
|
|
2285 |
}
|
|
2286 | 1 |
else if (v.type.size() == 0) |
2287 |
{
|
|
2288 |
// Zero-length arrays don't need an initializer
|
|
2289 | 1 |
result = v.type.defaultInitLiteral(e.loc); |
2290 |
}
|
|
2291 |
else
|
|
2292 |
{
|
|
2293 |
e.error("variable `%s` cannot be modified at compile time", v.toChars()); |
|
2294 |
result = CTFEExp.cantexp; |
|
2295 |
}
|
|
2296 | 1 |
return; |
2297 |
}
|
|
2298 | 1 |
if (s.isAttribDeclaration() || s.isTemplateMixin() || s.isTupleDeclaration()) |
2299 |
{
|
|
2300 |
// Check for static struct declarations, which aren't executable
|
|
2301 | 1 |
AttribDeclaration ad = e.declaration.isAttribDeclaration(); |
2302 | 1 |
if (ad && ad.decl && ad.decl.dim == 1) |
2303 |
{
|
|
2304 | 1 |
Dsymbol sparent = (*ad.decl)[0]; |
2305 | 1 |
if (sparent.isAggregateDeclaration() || sparent.isTemplateDeclaration() || sparent.isAliasDeclaration()) |
2306 |
{
|
|
2307 | 1 |
result = null; |
2308 | 1 |
return; // static (template) struct declaration. Nothing to do. |
2309 |
}
|
|
2310 |
}
|
|
2311 |
|
|
2312 |
// These can be made to work, too lazy now
|
|
2313 |
e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); |
|
2314 |
result = CTFEExp.cantexp; |
|
2315 |
return; |
|
2316 |
}
|
|
2317 |
|
|
2318 |
// Others should not contain executable code, so are trivial to evaluate
|
|
2319 | 1 |
result = null; |
2320 |
debug (LOG) |
|
2321 |
{
|
|
2322 |
printf("-DeclarationExp::interpret(%s): %p\n", e.toChars(), result); |
|
2323 |
}
|
|
2324 |
}
|
|
2325 |
|
|
2326 |
override void visit(TypeidExp e) |
|
2327 |
{
|
|
2328 |
debug (LOG) |
|
2329 |
{
|
|
2330 |
printf("%s TypeidExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2331 |
}
|
|
2332 | 1 |
if (Type t = isType(e.obj)) |
2333 |
{
|
|
2334 | 1 |
result = e; |
2335 | 1 |
return; |
2336 |
}
|
|
2337 | 1 |
if (Expression ex = isExpression(e.obj)) |
2338 |
{
|
|
2339 | 1 |
result = interpret(pue, ex, istate); |
2340 | 1 |
if (exceptionOrCant(ex)) |
2341 |
return; |
|
2342 |
|
|
2343 | 1 |
if (result.op == TOK.null_) |
2344 |
{
|
|
2345 | 1 |
e.error("null pointer dereference evaluating typeid. `%s` is `null`", ex.toChars()); |
2346 | 1 |
result = CTFEExp.cantexp; |
2347 | 1 |
return; |
2348 |
}
|
|
2349 | 1 |
if (result.op != TOK.classReference) |
2350 |
{
|
|
2351 |
e.error("CTFE internal error: determining classinfo"); |
|
2352 |
result = CTFEExp.cantexp; |
|
2353 |
return; |
|
2354 |
}
|
|
2355 |
|
|
2356 | 1 |
ClassDeclaration cd = (cast(ClassReferenceExp)result).originalClass(); |
2357 | 1 |
assert(cd); |
2358 |
|
|
2359 | 1 |
emplaceExp!(TypeidExp)(pue, e.loc, cd.type); |
2360 | 1 |
result = pue.exp(); |
2361 | 1 |
result.type = e.type; |
2362 | 1 |
return; |
2363 |
}
|
|
2364 |
visit(cast(Expression)e); |
|
2365 |
}
|
|
2366 |
|
|
2367 |
override void visit(TupleExp e) |
|
2368 |
{
|
|
2369 |
debug (LOG) |
|
2370 |
{
|
|
2371 |
printf("%s TupleExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2372 |
}
|
|
2373 | 1 |
if (exceptionOrCant(interpretRegion(e.e0, istate, CTFEGoal.Nothing))) |
2374 |
return; |
|
2375 |
|
|
2376 | 1 |
auto expsx = e.exps; |
2377 | 1 |
foreach (i, exp; *expsx) |
2378 |
{
|
|
2379 | 1 |
Expression ex = interpretRegion(exp, istate); |
2380 | 1 |
if (exceptionOrCant(ex)) |
2381 |
return; |
|
2382 |
|
|
2383 |
// A tuple of assignments can contain void (Bug 5676).
|
|
2384 | 1 |
if (goal == CTFEGoal.Nothing) |
2385 | 1 |
continue; |
2386 | 1 |
if (ex.op == TOK.voidExpression) |
2387 |
{
|
|
2388 |
e.error("CTFE internal error: void element `%s` in tuple", exp.toChars()); |
|
2389 |
assert(0); |
|
2390 |
}
|
|
2391 |
|
|
2392 |
/* If any changes, do Copy On Write
|
|
2393 |
*/
|
|
2394 | 1 |
if (ex !is exp) |
2395 |
{
|
|
2396 |
expsx = copyArrayOnWrite(expsx, e.exps); |
|
2397 |
(*expsx)[i] = copyRegionExp(ex); |
|
2398 |
}
|
|
2399 |
}
|
|
2400 |
|
|
2401 | 1 |
if (expsx !is e.exps) |
2402 |
{
|
|
2403 |
expandTuples(expsx); |
|
2404 |
emplaceExp!(TupleExp)(pue, e.loc, expsx); |
|
2405 |
result = pue.exp(); |
|
2406 |
result.type = new TypeTuple(expsx); |
|
2407 |
}
|
|
2408 |
else
|
|
2409 | 1 |
result = e; |
2410 |
}
|
|
2411 |
|
|
2412 |
override void visit(ArrayLiteralExp e) |
|
2413 |
{
|
|
2414 |
debug (LOG) |
|
2415 |
{
|
|
2416 |
printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2417 |
}
|
|
2418 | 1 |
if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements |
2419 |
{
|
|
2420 | 1 |
result = e; |
2421 | 1 |
return; |
2422 |
}
|
|
2423 |
|
|
2424 | 1 |
Type tn = e.type.toBasetype().nextOf().toBasetype(); |
2425 | 1 |
bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct); |
2426 |
|
|
2427 | 1 |
auto basis = interpretRegion(e.basis, istate); |
2428 | 1 |
if (exceptionOrCant(basis)) |
2429 |
return; |
|
2430 |
|
|
2431 | 1 |
auto expsx = e.elements; |
2432 | 1 |
size_t dim = expsx ? expsx.dim : 0; |
2433 | 1 |
for (size_t i = 0; i < dim; i++) |
2434 |
{
|
|
2435 | 1 |
Expression exp = (*expsx)[i]; |
2436 | 1 |
Expression ex; |
2437 | 1 |
if (!exp) |
2438 |
{
|
|
2439 | 1 |
ex = copyLiteral(basis).copy(); |
2440 |
}
|
|
2441 |
else
|
|
2442 |
{
|
|
2443 |
// segfault bug 6250
|
|
2444 | 1 |
assert(exp.op != TOK.index || (cast(IndexExp)exp).e1 != e); |
2445 |
|
|
2446 | 1 |
ex = interpretRegion(exp, istate); |
2447 | 1 |
if (exceptionOrCant(ex)) |
2448 | 1 |
return; |
2449 |
|
|
2450 |
/* Each elements should have distinct CTFE memory.
|
|
2451 |
* int[1] z = 7;
|
|
2452 |
* int[1][] pieces = [z,z]; // here
|
|
2453 |
*/
|
|
2454 | 1 |
if (wantCopy) |
2455 | 1 |
ex = copyLiteral(ex).copy(); |
2456 |
}
|
|
2457 |
|
|
2458 |
/* If any changes, do Copy On Write
|
|
2459 |
*/
|
|
2460 | 1 |
if (ex !is exp) |
2461 |
{
|
|
2462 | 1 |
expsx = copyArrayOnWrite(expsx, e.elements); |
2463 | 1 |
(*expsx)[i] = ex; |
2464 |
}
|
|
2465 |
}
|
|
2466 |
|
|
2467 | 1 |
if (expsx !is e.elements) |
2468 |
{
|
|
2469 |
// todo: all tuple expansions should go in semantic phase.
|
|
2470 | 1 |
expandTuples(expsx); |
2471 | 1 |
if (expsx.dim != dim) |
2472 |
{
|
|
2473 |
e.error("CTFE internal error: invalid array literal"); |
|
2474 |
result = CTFEExp.cantexp; |
|
2475 |
return; |
|
2476 |
}
|
|
2477 | 1 |
emplaceExp!(ArrayLiteralExp)(pue, e.loc, e.type, basis, expsx); |
2478 | 1 |
auto ale = cast(ArrayLiteralExp)pue.exp(); |
2479 | 1 |
ale.ownedByCtfe = OwnedBy.ctfe; |
2480 | 1 |
result = ale; |
2481 |
}
|
|
2482 | 1 |
else if ((cast(TypeNext)e.type).next.mod & (MODFlags.const_ | MODFlags.immutable_)) |
2483 |
{
|
|
2484 |
// If it's immutable, we don't need to dup it
|
|
2485 | 1 |
result = e; |
2486 |
}
|
|
2487 |
else
|
|
2488 |
{
|
|
2489 | 1 |
*pue = copyLiteral(e); |
2490 | 1 |
result = pue.exp(); |
2491 |
}
|
|
2492 |
}
|
|
2493 |
|
|
2494 |
override void visit(AssocArrayLiteralExp e) |
|
2495 |
{
|
|
2496 |
debug (LOG) |
|
2497 |
{
|
|
2498 |
printf("%s AssocArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2499 |
}
|
|
2500 | 1 |
if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements |
2501 |
{
|
|
2502 | 1 |
result = e; |
2503 | 1 |
return; |
2504 |
}
|
|
2505 |
|
|
2506 | 1 |
auto keysx = e.keys; |
2507 | 1 |
auto valuesx = e.values; |
2508 | 1 |
foreach (i, ekey; *keysx) |
2509 |
{
|
|
2510 | 1 |
auto evalue = (*valuesx)[i]; |
2511 |
|
|
2512 | 1 |
auto ek = interpretRegion(ekey, istate); |
2513 | 1 |
if (exceptionOrCant(ek)) |
2514 |
return; |
|
2515 | 1 |
auto ev = interpretRegion(evalue, istate); |
2516 | 1 |
if (exceptionOrCant(ev)) |
2517 |
return; |
|
2518 |
|
|
2519 |
/* If any changes, do Copy On Write
|
|
2520 |
*/
|
|
2521 | 1 |
if (ek !is ekey || |
2522 | 1 |
ev !is evalue) |
2523 |
{
|
|
2524 | 1 |
keysx = copyArrayOnWrite(keysx, e.keys); |
2525 | 1 |
valuesx = copyArrayOnWrite(valuesx, e.values); |
2526 | 1 |
(*keysx)[i] = ek; |
2527 | 1 |
(*valuesx)[i] = ev; |
2528 |
}
|
|
2529 |
}
|
|
2530 | 1 |
if (keysx !is e.keys) |
2531 | 1 |
expandTuples(keysx); |
2532 | 1 |
if (valuesx !is e.values) |
2533 | 1 |
expandTuples(valuesx); |
2534 | 1 |
if (keysx.dim != valuesx.dim) |
2535 |
{
|
|
2536 |
e.error("CTFE internal error: invalid AA"); |
|
2537 |
result = CTFEExp.cantexp; |
|
2538 |
return; |
|
2539 |
}
|
|
2540 |
|
|
2541 |
/* Remove duplicate keys
|
|
2542 |
*/
|
|
2543 | 1 |
for (size_t i = 1; i < keysx.dim; i++) |
2544 |
{
|
|
2545 | 1 |
auto ekey = (*keysx)[i - 1]; |
2546 | 1 |
for (size_t j = i; j < keysx.dim; j++) |
2547 |
{
|
|
2548 | 1 |
auto ekey2 = (*keysx)[j]; |
2549 | 1 |
if (!ctfeEqual(e.loc, TOK.equal, ekey, ekey2)) |
2550 | 1 |
continue; |
2551 |
|
|
2552 |
// Remove ekey
|
|
2553 | 1 |
keysx = copyArrayOnWrite(keysx, e.keys); |
2554 | 1 |
valuesx = copyArrayOnWrite(valuesx, e.values); |
2555 | 1 |
keysx.remove(i - 1); |
2556 | 1 |
valuesx.remove(i - 1); |
2557 |
|
|
2558 | 1 |
i -= 1; // redo the i'th iteration |
2559 | 1 |
break; |
2560 |
}
|
|
2561 |
}
|
|
2562 |
|
|
2563 | 1 |
if (keysx !is e.keys || |
2564 | 1 |
valuesx !is e.values) |
2565 |
{
|
|
2566 | 1 |
assert(keysx !is e.keys && |
2567 | 1 |
valuesx !is e.values); |
2568 | 1 |
auto aae = ctfeEmplaceExp!AssocArrayLiteralExp(e.loc, keysx, valuesx); |
2569 | 1 |
aae.type = e.type; |
2570 | 1 |
aae.ownedByCtfe = OwnedBy.ctfe; |
2571 | 1 |
result = aae; |
2572 |
}
|
|
2573 |
else
|
|
2574 |
{
|
|
2575 | 1 |
*pue = copyLiteral(e); |
2576 | 1 |
result = pue.exp(); |
2577 |
}
|
|
2578 |
}
|
|
2579 |
|
|
2580 |
override void visit(StructLiteralExp e) |
|
2581 |
{
|
|
2582 |
debug (LOG) |
|
2583 |
{
|
|
2584 |
printf("%s StructLiteralExp::interpret() %s ownedByCtfe = %d\n", e.loc.toChars(), e.toChars(), e.ownedByCtfe); |
|
2585 |
}
|
|
2586 | 1 |
if (e.ownedByCtfe >= OwnedBy.ctfe) |
2587 |
{
|
|
2588 | 1 |
result = e; |
2589 | 1 |
return; |
2590 |
}
|
|
2591 |
|
|
2592 | 1 |
size_t dim = e.elements ? e.elements.dim : 0; |
2593 | 1 |
auto expsx = e.elements; |
2594 |
|
|
2595 | 1 |
if (dim != e.sd.fields.dim) |
2596 |
{
|
|
2597 |
// guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
|
|
2598 | 1 |
const nvthis = e.sd.fields.dim - e.sd.nonHiddenFields(); |
2599 | 1 |
assert(e.sd.fields.dim - dim == nvthis); |
2600 |
|
|
2601 |
/* If a nested struct has no initialized hidden pointer,
|
|
2602 |
* set it to null to match the runtime behaviour.
|
|
2603 |
*/
|
|
2604 | 1 |
foreach (const i; 0 .. nvthis) |
2605 |
{
|
|
2606 | 1 |
auto ne = ctfeEmplaceExp!NullExp(e.loc); |
2607 | 1 |
auto vthis = i == 0 ? e.sd.vthis : e.sd.vthis2; |
2608 | 1 |
ne.type = vthis.type; |
2609 |
|
|
2610 | 1 |
expsx = copyArrayOnWrite(expsx, e.elements); |
2611 | 1 |
expsx.push(ne); |
2612 | 1 |
++dim; |
2613 |
}
|
|
2614 |
}
|
|
2615 | 1 |
assert(dim == e.sd.fields.dim); |
2616 |
|
|
2617 | 1 |
foreach (i; 0 .. dim) |
2618 |
{
|
|
2619 | 1 |
auto v = e.sd.fields[i]; |
2620 | 1 |
Expression exp = (*expsx)[i]; |
2621 | 1 |
Expression ex; |
2622 | 1 |
if (!exp) |
2623 |
{
|
|
2624 | 1 |
ex = voidInitLiteral(v.type, v).copy(); |
2625 |
}
|
|
2626 |
else
|
|
2627 |
{
|
|
2628 | 1 |
ex = interpretRegion(exp, istate); |
2629 | 1 |
if (exceptionOrCant(ex)) |
2630 | 1 |
return; |
2631 | 1 |
if ((v.type.ty != ex.type.ty) && v.type.ty == Tsarray) |
2632 |
{
|
|
2633 |
// Block assignment from inside struct literals
|
|
2634 | 1 |
auto tsa = cast(TypeSArray)v.type; |
2635 | 1 |
auto len = cast(size_t)tsa.dim.toInteger(); |
2636 | 1 |
UnionExp ue = void; |
2637 | 1 |
ex = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len); |
2638 | 1 |
if (ex == ue.exp()) |
2639 | 1 |
ex = ue.copy(); |
2640 |
}
|
|
2641 |
}
|
|
2642 |
|
|
2643 |
/* If any changes, do Copy On Write
|
|
2644 |
*/
|
|
2645 | 1 |
if (ex !is exp) |
2646 |
{
|
|
2647 | 1 |
expsx = copyArrayOnWrite(expsx, e.elements); |
2648 | 1 |
(*expsx)[i] = ex; |
2649 |
}
|
|
2650 |
}
|
|
2651 |
|
|
2652 | 1 |
if (expsx !is e.elements) |
2653 |
{
|
|
2654 | 1 |
expandTuples(expsx); |
2655 | 1 |
if (expsx.dim != e.sd.fields.dim) |
2656 |
{
|
|
2657 |
e.error("CTFE internal error: invalid struct literal"); |
|
2658 |
result = CTFEExp.cantexp; |
|
2659 |
return; |
|
2660 |
}
|
|
2661 | 1 |
emplaceExp!(StructLiteralExp)(pue, e.loc, e.sd, expsx); |
2662 | 1 |
auto sle = cast(StructLiteralExp)pue.exp(); |
2663 | 1 |
sle.type = e.type; |
2664 | 1 |
sle.ownedByCtfe = OwnedBy.ctfe; |
2665 | 1 |
sle.origin = e.origin; |
2666 | 1 |
result = sle; |
2667 |
}
|
|
2668 |
else
|
|
2669 |
{
|
|
2670 | 1 |
*pue = copyLiteral(e); |
2671 | 1 |
result = pue.exp(); |
2672 |
}
|
|
2673 |
}
|
|
2674 |
|
|
2675 |
// Create an array literal of type 'newtype' with dimensions given by
|
|
2676 |
// 'arguments'[argnum..$]
|
|
2677 |
static Expression recursivelyCreateArrayLiteral(UnionExp* pue, const ref Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) |
|
2678 |
{
|
|
2679 | 1 |
Expression lenExpr = interpret(pue, (*arguments)[argnum], istate); |
2680 | 1 |
if (exceptionOrCantInterpret(lenExpr)) |
2681 |
return lenExpr; |
|
2682 | 1 |
size_t len = cast(size_t)lenExpr.toInteger(); |
2683 | 1 |
Type elemType = (cast(TypeArray)newtype).next; |
2684 | 1 |
if (elemType.ty == Tarray && argnum < arguments.dim - 1) |
2685 |
{
|
|
2686 | 1 |
Expression elem = recursivelyCreateArrayLiteral(pue, loc, elemType, istate, arguments, argnum + 1); |
2687 | 1 |
if (exceptionOrCantInterpret(elem)) |
2688 |
return elem; |
|
2689 |
|
|
2690 | 1 |
auto elements = new Expressions(len); |
2691 | 1 |
foreach (ref element; *elements) |
2692 | 1 |
element = copyLiteral(elem).copy(); |
2693 | 1 |
emplaceExp!(ArrayLiteralExp)(pue, loc, newtype, elements); |
2694 | 1 |
auto ae = cast(ArrayLiteralExp)pue.exp(); |
2695 | 1 |
ae.ownedByCtfe = OwnedBy.ctfe; |
2696 | 1 |
return ae; |
2697 |
}
|
|
2698 | 1 |
assert(argnum == arguments.dim - 1); |
2699 | 1 |
if (elemType.ty.isSomeChar) |
2700 |
{
|
|
2701 | 1 |
const ch = cast(dchar)elemType.defaultInitLiteral(loc).toInteger(); |
2702 | 1 |
const sz = cast(ubyte)elemType.size(); |
2703 | 1 |
return createBlockDuplicatedStringLiteral(pue, loc, newtype, ch, len, sz); |
2704 |
}
|
|
2705 |
else
|
|
2706 |
{
|
|
2707 | 1 |
auto el = interpret(elemType.defaultInitLiteral(loc), istate); |
2708 | 1 |
return createBlockDuplicatedArrayLiteral(pue, loc, newtype, el, len); |
2709 |
}
|
|
2710 |
}
|
|
2711 |
|
|
2712 |
override void visit(NewExp e) |
|
2713 |
{
|
|
2714 |
debug (LOG) |
|
2715 |
{
|
|
2716 |
printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); |
|
2717 |
}
|
|
2718 | 1 |
if (e.allocator) |
2719 |
{
|
|
2720 |
e.error("member allocators not supported by CTFE"); |
|
2721 |
result = CTFEExp.cantexp; |
|
2722 |
return; |
|
2723 |
}
|
|
2724 |
|
|
2725 | 1 |
Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing); |
2726 | 1 |
if (exceptionOrCant(epre)) |
2727 |
return; |
|
2728 |
|
|
2729 | 1 |
if (e.newtype.ty == Tarray && e.arguments) |
2730 |
{
|
|
2731 | 1 |
result = recursivelyCreateArrayLiteral(pue, e.loc, e.newtype, istate, e.arguments, 0); |
2732 | 1 |
return; |
2733 |
}
|
|
2734 | 1 |
if (auto ts = e.newtype.toBasetype().isTypeStruct()) |
2735 |
{
|
|
2736 | 1 |
if (e.member) |
2737 |
{
|
|
2738 | 1 |
Expression se = e.newtype.defaultInitLiteral(e.loc); |
2739 | 1 |
se = interpret(se, istate); |
2740 | 1 |
if (exceptionOrCant(se)) |
2741 |
return; |
|
2742 | 1 |
result = interpretFunction(pue, e.member, istate, e.arguments, se); |
2743 |
|
|
2744 |
// Repaint as same as CallExp::interpret() does.
|
|
2745 | 1 |
result.loc = e.loc; |
2746 |
}
|
|
2747 |
else
|
|
2748 |
{
|
|
2749 | 1 |
StructDeclaration sd = ts.sym; |
2750 | 1 |
auto exps = new Expressions(); |
2751 | 1 |
exps.reserve(sd.fields.dim); |
2752 | 1 |
if (e.arguments) |
2753 |
{
|
|
2754 | 1 |
exps.setDim(e.arguments.dim); |
2755 | 1 |
foreach (i, ex; *e.arguments) |
2756 |
{
|
|
2757 | 1 |
ex = interpretRegion(ex, istate); |
2758 | 1 |
if (exceptionOrCant(ex)) |
2759 |
return; |
|
2760 | 1 |
(*exps)[i] = ex; |
2761 |
}
|
|
2762 |
}
|
|
2763 | 1 |
sd.fill(e.loc, exps, false); |
2764 |
|
|
2765 | 1 |
auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, sd, exps, e.newtype); |
2766 | 1 |
se.origin = se; |
2767 | 1 |
se.type = e.newtype; |
2768 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
2769 | 1 |
result = interpret(pue, se, istate); |
2770 |
}
|
|
2771 | 1 |
if (exceptionOrCant(result)) |
2772 |
return; |
|
2773 | 1 |
Expression ev = (result == pue.exp()) ? pue.copy() : result; |
2774 | 1 |
emplaceExp!(AddrExp)(pue, e.loc, ev, e.type); |
2775 | 1 |
result = pue.exp(); |
2776 | 1 |
return; |
2777 |
}
|
|
2778 | 1 |
if (auto tc = e.newtype.toBasetype().isTypeClass()) |
2779 |
{
|
|
2780 | 1 |
ClassDeclaration cd = tc.sym; |
2781 | 1 |
size_t totalFieldCount = 0; |
2782 | 1 |
for (ClassDeclaration c = cd; c; c = c.baseClass) |
2783 | 1 |
totalFieldCount += c.fields.dim; |
2784 | 1 |
auto elems = new Expressions(totalFieldCount); |
2785 | 1 |
size_t fieldsSoFar = totalFieldCount; |
2786 | 1 |
for (ClassDeclaration c = cd; c; c = c.baseClass) |
2787 |
{
|
|
2788 | 1 |
fieldsSoFar -= c.fields.dim; |
2789 | 1 |
foreach (i, v; c.fields) |
2790 |
{
|
|
2791 | 1 |
if (v.inuse) |
2792 |
{
|
|
2793 | 1 |
e.error("circular reference to `%s`", v.toPrettyChars()); |
2794 | 1 |
result = CTFEExp.cantexp; |
2795 | 1 |
return; |
2796 |
}
|
|
2797 | 1 |
Expression m; |
2798 | 1 |
if (v._init) |
2799 |
{
|
|
2800 | 1 |
if (v._init.isVoidInitializer()) |
2801 |
m = voidInitLiteral(v.type, v).copy(); |
|
2802 |
else
|
|
2803 | 1 |
m = v.getConstInitializer(true); |
2804 |
}
|
|
2805 |
else
|
|
2806 | 1 |
m = v.type.defaultInitLiteral(e.loc); |
2807 | 1 |
if (exceptionOrCant(m)) |
2808 |
return; |
|
2809 | 1 |
(*elems)[fieldsSoFar + i] = copyLiteral(m).copy(); |
2810 |
}
|
|
2811 |
}
|
|
2812 |
// Hack: we store a ClassDeclaration instead of a StructDeclaration.
|
|
2813 |
// We probably won't get away with this.
|
|
2814 |
// auto se = new StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
|
|
2815 | 1 |
auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype); |
2816 | 1 |
se.origin = se; |
2817 | 1 |
se.ownedByCtfe = OwnedBy.ctfe; |
2818 | 1 |
emplaceExp!(ClassReferenceExp)(pue, e.loc, se, e.type); |
2819 | 1 |
Expression eref = pue.exp(); |
2820 | 1 |
if (e.member) |
2821 |
{
|
|
2822 |
// Call constructor
|
|
2823 | 1 |
if (!e.member.fbody) |
2824 |
{
|
|
2825 |
Expression ctorfail = evaluateIfBuiltin(pue, istate, e.loc, e.member, e.arguments, eref); |
|
2826 |
if (ctorfail) |
|
2827 |
{
|
|
2828 |
if (exceptionOrCant(ctorfail)) |
|
2829 |
return; |
|
2830 |
result = eref; |
|
2831 |
return; |
|
2832 |
}
|
|
2833 |
e.member.error("`%s` cannot be constructed at compile time, because the constructor has no available source code", e.newtype.toChars()); |
|
2834 |
result = CTFEExp.cantexp; |
|
2835 |
return; |
|
2836 |
}
|
|
2837 | 1 |
UnionExp ue = void; |
2838 | 1 |
Expression ctorfail = interpretFunction(&ue, e.member, istate, e.arguments, eref); |
2839 | 1 |
if (exceptionOrCant(ctorfail)) |
2840 | 1 |
return; |
2841 |
|
|
2842 |
/* https://issues.dlang.org/show_bug.cgi?id=14465
|
|
2843 |
* Repaint the loc, because a super() call
|
|
2844 |
* in the constructor modifies the loc of ClassReferenceExp
|
|
2845 |
* in CallExp::interpret().
|
|
2846 |
*/
|
|
2847 | 1 |
eref.loc = e. |