2 * The entry point for CTFE.
4 * Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE))
6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
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
14 module dmd
.dinterpret
;
16 import core
.stdc
.stdio
;
17 import core
.stdc
.stdlib
;
18 import core
.stdc
.string
;
19 import dmd
.arraytypes
;
26 import dmd
.declaration
;
29 import dmd
.dsymbolsem
;
32 import dmd
.expression
;
33 import dmd
.expressionsem
;
38 import dmd
.identifier
;
44 import dmd
.root
.array
;
45 import dmd
.root
.ctfloat
;
46 import dmd
.root
.region
;
47 import dmd
.root
.rootobject
;
53 /*************************************
54 * Entry point for CTFE.
55 * A compile-time result is required. Give an error if not possible.
57 * `e` must be semantically valid expression. In other words, it should not
58 * contain any `ErrorExp`s in it. But, CTFE interpretation will cross over
59 * functions and may invoke a function that contains `ErrorStatement` in its body.
60 * If that, the "CTFE failed because of previous errors" error is raised.
62 public Expression
ctfeInterpret(Expression e
)
76 case EXP
.template_
: // non-eponymous template/instance
77 case EXP
.scope_
: // ditto
78 case EXP
.dotTemplateDeclaration
: // ditto, e.e1 doesn't matter here
79 case EXP
.dotTemplateInstance
: // ditto
80 case EXP
.dot
: // ditto
81 if (e
.type
.ty
== Terror
)
82 return ErrorExp
.get();
92 assert(e
.type
); // https://issues.dlang.org/show_bug.cgi?id=14642
93 //assert(e.type.ty != Terror); // FIXME
94 if (e
.type
.ty
== Terror
)
95 return ErrorExp
.get();
97 auto rgnpos
= ctfeGlobals
.region
.savePos();
99 Expression result
= interpret(e
, null);
101 // Report an error if the expression contained a `ThrowException` and
102 // hence generated an uncaught exception
103 if (auto tee
= result
.isThrownExceptionExp())
105 tee
.generateUncaughtError();
106 result
= CTFEExp
.cantexp
;
109 result
= copyRegionExp(result
);
111 if (!CTFEExp
.isCantExp(result
))
112 result
= scrubReturnValue(e
.loc
, result
);
113 if (CTFEExp
.isCantExp(result
))
114 result
= ErrorExp
.get();
116 ctfeGlobals
.region
.release(rgnpos
);
121 /* Run CTFE on the expression, but allow the expression to be a TypeExp
122 * or a tuple containing a TypeExp. (This is required by pragma(msg)).
124 public Expression
ctfeInterpretForPragmaMsg(Expression e
)
126 if (e
.op
== EXP
.error || e
.op
== EXP
.type
)
129 // It's also OK for it to be a function declaration (happens only with
130 // __traits(getOverloads))
131 if (auto ve
= e
.isVarExp())
132 if (ve
.var
.isFuncDeclaration())
137 auto tup
= e
.isTupleExp();
139 return e
.ctfeInterpret();
141 // Tuples need to be treated separately, since they are
142 // allowed to contain a TypeExp in this case.
144 Expressions
* expsx
= null;
145 foreach (i
, g
; *tup
.exps
)
147 auto h
= ctfeInterpretForPragmaMsg(g
);
152 expsx
= tup
.exps
.copy();
159 auto te
= new TupleExp(e
.loc
, expsx
);
160 expandTuples(te
.exps
);
161 te
.type
= new TypeTuple(te
.exps
);
167 public Expression
getValue(VarDeclaration vd
)
169 return ctfeGlobals
.stack
.getValue(vd
);
172 /*************************************************
173 * Allocate an Expression in the ctfe region.
175 * T = type of Expression to allocate
176 * args = arguments to Expression's constructor
178 * allocated Expression
180 T
ctfeEmplaceExp(T
: Expression
, Args
...)(Args args
)
184 auto p
= ctfeGlobals
.region
.malloc(__traits(classInstanceSize
, T
));
185 emplaceExp
!T(p
, args
);
189 // CTFE diagnostic information
190 public extern (C
++) void printCtfePerformanceStats()
192 debug (SHOWPERFORMANCE
)
194 printf(" ---- CTFE Performance ----\n");
195 printf("max call depth = %d\tmax stack = %d\n", ctfeGlobals
.maxCallDepth
, ctfeGlobals
.stack
.maxStackUsage());
196 printf("array allocs = %d\tassignments = %d\n\n", ctfeGlobals
.numArrayAllocs
, ctfeGlobals
.numAssignments
);
200 /**************************
203 void incArrayAllocs()
205 ++ctfeGlobals
.numArrayAllocs
;
208 /* ================================================ Implementation ======================================= */
213 * Collect together globals used by CTFE
221 int callDepth
= 0; // current number of recursive calls
223 // When printing a stack trace, suppress this number of calls
224 int stackTraceCallsToSuppress
= 0;
226 int maxCallDepth
= 0; // highest number of recursive calls
227 int numArrayAllocs
= 0; // Number of allocated arrays
228 int numAssignments
= 0; // total number of assignments executed
231 __gshared CtfeGlobals ctfeGlobals
;
235 RValue
, /// Must return an Rvalue (== CTFE value)
236 LValue
, /// Must return an Lvalue (== CTFE reference)
237 Nothing
, /// The return value is not required
242 //debug = LOGCOMPILE;
243 //debug = SHOWPERFORMANCE;
245 // Maximum allowable recursive function calls in CTFE
246 enum CTFE_RECURSION_LIMIT
= 1000;
249 The values of all CTFE variables
254 /* The stack. Every declaration we encounter is pushed here,
255 * together with the VarDeclaration, and the previous
256 * stack address of that variable, so that we can restore it
257 * when we leave the stack frame.
258 * Note that when a function is forward referenced, the interpreter must
259 * run semantic3, and that may start CTFE again with a NULL istate. Thus
260 * the stack might not be empty when CTFE begins.
262 * Ctfe Stack addresses are just 0-based integers, but we save
263 * them as 'void *' because Array can only do pointers.
265 Expressions values
; // values on the stack
266 VarDeclarations vars
; // corresponding variables
267 Array
!(void*) savedId
; // id of the previous state of that var
269 Array
!(void*) frames
; // all previous frame pointers
270 Expressions savedThis
; // all previous values of localThis
272 /* Global constants get saved here after evaluation, so we never
273 * have to redo them. This saves a lot of time and memory.
275 Expressions globalValues
; // values of global constants
277 size_t framepointer
; // current frame pointer
278 size_t maxStackPointer
; // most stack we've ever used
279 Expression localThis
; // value of 'this', or NULL if none
282 size_t
stackPointer() @safe
284 return values
.length
;
287 // The current value of 'this', or NULL if none
288 Expression
getThis() @safe
293 // Largest number of stack positions we've used
294 size_t
maxStackUsage() @safe
296 return maxStackPointer
;
299 // Start a new stack frame, using the provided 'this'.
300 void startFrame(Expression thisexp
)
302 frames
.push(cast(void*)cast(size_t
)framepointer
);
303 savedThis
.push(localThis
);
304 framepointer
= stackPointer();
310 size_t oldframe
= cast(size_t
)frames
[frames
.length
- 1];
311 localThis
= savedThis
[savedThis
.length
- 1];
312 popAll(framepointer
);
313 framepointer
= oldframe
;
314 frames
.setDim(frames
.length
- 1);
315 savedThis
.setDim(savedThis
.length
- 1);
318 bool isInCurrentFrame(VarDeclaration v
)
320 if (v
.isDataseg() && !v
.isCTFE())
321 return false; // It's a global
322 return v
.ctfeAdrOnStack
>= framepointer
;
325 Expression
getValue(VarDeclaration v
)
327 //printf("getValue() %s\n", v.toChars());
328 if ((v
.isDataseg() || v
.storage_class
& STC
.manifest
) && !v
.isCTFE())
330 assert(v
.ctfeAdrOnStack
< globalValues
.length
);
331 return globalValues
[v
.ctfeAdrOnStack
];
333 assert(v
.ctfeAdrOnStack
< stackPointer());
334 return values
[v
.ctfeAdrOnStack
];
337 void setValue(VarDeclaration v
, Expression e
)
339 //printf("setValue() %s : %s\n", v.toChars(), e.toChars());
340 assert(!v
.isDataseg() || v
.isCTFE());
341 assert(v
.ctfeAdrOnStack
< stackPointer());
342 values
[v
.ctfeAdrOnStack
] = e
;
345 void push(VarDeclaration v
)
347 //printf("push() %s\n", v.toChars());
348 assert(!v
.isDataseg() || v
.isCTFE());
349 if (v
.ctfeAdrOnStack
!= VarDeclaration
.AdrOnStackNone
&& v
.ctfeAdrOnStack
>= framepointer
)
351 // Already exists in this frame, reuse it.
352 values
[v
.ctfeAdrOnStack
] = null;
355 savedId
.push(cast(void*)cast(size_t
)v
.ctfeAdrOnStack
);
356 v
.ctfeAdrOnStack
= cast(uint)values
.length
;
361 void pop(VarDeclaration v
)
363 assert(!v
.isDataseg() || v
.isCTFE());
364 assert(!v
.isReference());
365 const oldid
= v
.ctfeAdrOnStack
;
366 v
.ctfeAdrOnStack
= cast(uint)cast(size_t
)savedId
[oldid
];
367 if (v
.ctfeAdrOnStack
== values
.length
- 1)
375 void popAll(size_t stackpointer
)
377 if (stackPointer() > maxStackPointer
)
378 maxStackPointer
= stackPointer();
379 assert(values
.length
>= stackpointer
);
380 for (size_t i
= stackpointer
; i
< values
.length
; ++i
)
382 VarDeclaration v
= vars
[i
];
383 v
.ctfeAdrOnStack
= cast(uint)cast(size_t
)savedId
[i
];
385 values
.setDim(stackpointer
);
386 vars
.setDim(stackpointer
);
387 savedId
.setDim(stackpointer
);
390 void saveGlobalConstant(VarDeclaration v
, Expression e
)
392 assert(v
._init
&& (v
.isConst() || v
.isImmutable() || v
.storage_class
& STC
.manifest
) && !v
.isCTFE());
393 v
.ctfeAdrOnStack
= cast(uint)globalValues
.length
;
394 globalValues
.push(copyRegionExp(e
));
398 private struct InterState
400 InterState
* caller
; // calling function's InterState
401 FuncDeclaration fd
; // function being interpreted
402 Statement start
; // if !=NULL, start execution at this statement
404 /* target of CTFEExp result; also
405 * target of labelled CTFEExp or
406 * CTFEExp. (null if no label).
408 Statement gotoTarget
;
411 /*************************************
412 * Attempt to interpret a function given the arguments.
414 * pue = storage for result
415 * fd = function being called
416 * istate = state for calling function (NULL if none)
417 * arguments = function arguments
418 * thisarg = 'this', if a needThis() function, NULL if not.
421 * result expression if successful, EXP.cantExpression if not,
422 * or CTFEExp if function returned void.
424 private Expression
interpretFunction(UnionExp
* pue
, FuncDeclaration fd
, InterState
* istate
, Expressions
* arguments
, Expression thisarg
)
428 printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd
.loc
.toChars(), istate
, fd
.toChars());
431 void fdError(const(char)* msg
)
433 error(fd
.loc
, "%s `%s` %s", fd
.kind
, fd
.toPrettyChars
, msg
);
437 if (fd
.semanticRun
== PASS
.semantic3
)
439 fdError("circular dependency. Functions cannot be interpreted while being compiled");
440 return CTFEExp
.cantexp
;
442 if (!fd
.functionSemantic3())
443 return CTFEExp
.cantexp
;
444 if (fd
.semanticRun
< PASS
.semantic3done
)
446 fdError("circular dependency. Functions cannot be interpreted while being compiled");
447 return CTFEExp
.cantexp
;
450 auto tf
= fd
.type
.toBasetype().isTypeFunction();
451 if (tf
.parameterList
.varargs
!= VarArg
.none
&& arguments
&&
452 ((fd
.parameters
&& arguments
.length
!= fd
.parameters
.length
) ||
(!fd
.parameters
&& arguments
.length
)))
454 fdError("C-style variadic functions are not yet implemented in CTFE");
455 return CTFEExp
.cantexp
;
458 // Nested functions always inherit the 'this' pointer from the parent,
459 // except for delegates. (Note that the 'this' pointer may be null).
460 // Func literals report isNested() even if they are in global scope,
461 // so we need to check that the parent is a function.
462 if (fd
.isNested() && fd
.toParentLocal().isFuncDeclaration() && !thisarg
&& istate
)
463 thisarg
= ctfeGlobals
.stack
.getThis();
465 if (fd
.needThis() && !thisarg
)
467 // error, no this. Prevent segfault.
468 // Here should be unreachable by the strict 'this' check in front-end.
469 error(fd
.loc
, "%s `%s` need `this` to access member `%s`", fd
.kind
, fd
.toPrettyChars
, fd
.toChars());
470 return CTFEExp
.cantexp
;
473 // Place to hold all the arguments to the function while
474 // we are evaluating them.
475 size_t dim
= arguments ? arguments
.length
: 0;
476 assert((fd
.parameters ? fd
.parameters
.length
: 0) == dim
);
478 /* Evaluate all the arguments to the function,
479 * store the results in eargs[]
481 Expressions eargs
= Expressions(dim
);
482 for (size_t i
= 0; i
< dim
; i
++)
484 Expression earg
= (*arguments
)[i
];
485 Parameter fparam
= tf
.parameterList
[i
];
487 if (fparam
.isReference())
489 if (!istate
&& (fparam
.storageClass
& STC
.out_
))
491 // initializing an out parameter involves writing to it.
492 error(earg
.loc
, "global `%s` cannot be passed as an `out` parameter at compile time", earg
.toChars());
493 return CTFEExp
.cantexp
;
495 // Convert all reference arguments into lvalue references
496 earg
= interpretRegion(earg
, istate
, CTFEGoal
.LValue
);
497 if (CTFEExp
.isCantExp(earg
))
500 else if (fparam
.isLazy())
507 Type ta
= fparam
.type
.toBasetype();
508 if (ta
.ty
== Tsarray
)
509 if (auto eaddr
= earg
.isAddrExp())
511 /* Static arrays are passed by a simple pointer.
512 * Skip past this to get at the actual arg.
517 earg
= interpretRegion(earg
, istate
);
518 if (CTFEExp
.isCantExp(earg
))
521 /* Struct literals are passed by value, but we don't need to
522 * copy them if they are passed as const
524 if (earg
.op
== EXP
.structLiteral
&& !(fparam
.storageClass
& (STC
.const_ | STC
.immutable_
)))
525 earg
= copyLiteral(earg
).copy();
527 if (auto tee
= earg
.isThrownExceptionExp())
531 tee
.generateUncaughtError();
532 return CTFEExp
.cantexp
;
537 // Now that we've evaluated all the arguments, we can start the frame
538 // (this is the moment when the 'call' actually takes place).
540 istatex
.caller
= istate
;
543 if (fd
.hasDualContext())
545 Expression arg0
= thisarg
;
546 if (arg0
&& arg0
.type
.ty
== Tstruct
)
548 Type t
= arg0
.type
.pointerTo();
549 arg0
= ctfeEmplaceExp
!AddrExp(arg0
.loc
, arg0
);
552 auto elements
= new Expressions(2);
553 (*elements
)[0] = arg0
;
554 (*elements
)[1] = ctfeGlobals
.stack
.getThis();
555 Type t2
= Type
.tvoidptr
.sarrayOf(2);
556 const loc
= thisarg ? thisarg
.loc
: fd
.loc
;
557 thisarg
= ctfeEmplaceExp
!ArrayLiteralExp(loc
, t2
, elements
);
558 thisarg
= ctfeEmplaceExp
!AddrExp(loc
, thisarg
);
559 thisarg
.type
= t2
.pointerTo();
562 ctfeGlobals
.stack
.startFrame(thisarg
);
563 if (fd
.vthis
&& thisarg
)
565 ctfeGlobals
.stack
.push(fd
.vthis
);
566 setValue(fd
.vthis
, thisarg
);
569 for (size_t i
= 0; i
< dim
; i
++)
571 Expression earg
= eargs
[i
];
572 Parameter fparam
= tf
.parameterList
[i
];
573 VarDeclaration v
= (*fd
.parameters
)[i
];
576 printf("arg[%zu] = %s\n", i
, earg
.toChars());
578 ctfeGlobals
.stack
.push(v
);
580 if (fparam
.isReference() && earg
.op
== EXP
.variable
&&
581 earg
.isVarExp().var
.toParent2() == fd
)
583 VarDeclaration vx
= earg
.isVarExp().var
.isVarDeclaration();
586 error(fd
.loc
, "%s `%s` cannot interpret `%s` as a `ref` parameter", fd
.kind
, fd
.toPrettyChars
, earg
.toChars());
587 return CTFEExp
.cantexp
;
590 /* vx is a variable that is declared in fd.
591 * It means that fd is recursively called. e.g.
593 * void fd(int n, ref int v = dummy) {
595 * if (n == 1) fd(2, vx);
599 * The old value of vx on the stack in fd(1)
600 * should be saved at the start of fd(2, vx) call.
602 const oldadr
= vx
.ctfeAdrOnStack
;
604 ctfeGlobals
.stack
.push(vx
);
605 assert(!hasValue(vx
)); // vx is made uninitialized
607 // https://issues.dlang.org/show_bug.cgi?id=14299
608 // v.ctfeAdrOnStack should be saved already
609 // in the stack before the overwrite.
610 v
.ctfeAdrOnStack
= oldadr
;
611 assert(hasValue(v
)); // ref parameter v should refer existing value.
615 // Value parameters and non-trivial references
616 setValueWithoutChecking(v
, earg
);
620 printf("interpreted arg[%zu] = %s\n", i
, earg
.toChars());
625 printf("interpreted arg[%zu] = %s\n", i
, earg
.toChars());
631 ctfeGlobals
.stack
.push(fd
.vresult
);
633 // Enter the function
634 ++ctfeGlobals
.callDepth
;
635 if (ctfeGlobals
.callDepth
> ctfeGlobals
.maxCallDepth
)
636 ctfeGlobals
.maxCallDepth
= ctfeGlobals
.callDepth
;
641 if (ctfeGlobals
.callDepth
> CTFE_RECURSION_LIMIT
)
643 fdError("CTFE recursion limit exceeded");
647 e
= interpretStatement(pue
, fd
.fbody
, &istatex
);
648 if (CTFEExp
.isCantExp(e
))
652 printf("function body failed to interpret\n");
658 error(fd
.loc
, "%s `%s` CTFE internal error: failed to resume at statement `%s`", fd
.kind
, fd
.toPrettyChars
, istatex
.start
.toChars());
659 return CTFEExp
.cantexp
;
662 /* This is how we deal with a recursive statement AST
663 * that has arbitrary goto statements in it.
664 * Bubble up a 'result' which is the target of the goto
665 * statement, then go recursively down the AST looking
666 * for that statement, then execute starting there.
668 if (CTFEExp
.isGotoExp(e
))
670 istatex
.start
= istatex
.gotoTarget
; // set starting statement
671 istatex
.gotoTarget
= null;
675 assert(!e ||
(e
.op
!= EXP
.continue_
&& e
.op
!= EXP
.break_
));
679 // If fell off the end of a void function, return void
682 if (tf
.next
.ty
== Tvoid
)
686 /* missing a return statement can happen with C functions
687 * https://issues.dlang.org/show_bug.cgi?id=23056
689 fdError("no return value from function");
694 if (tf
.isref
&& e
.op
== EXP
.variable
&& e
.isVarExp().var
== fd
.vthis
)
696 if (tf
.isref
&& fd
.hasDualContext() && e
.op
== EXP
.index
)
698 auto ie
= e
.isIndexExp();
699 auto pe
= ie
.e1
.isPtrExp();
700 auto ve
= !pe ?
null : pe
.e1
.isVarExp();
701 if (ve
&& ve
.var
== fd
.vthis
)
703 auto ne
= ie
.e2
.isIntegerExp();
705 auto ale
= thisarg
.isAddrExp().e1
.isArrayLiteralExp();
706 e
= (*ale
.elements
)[cast(size_t
)ne
.getInteger()];
707 if (auto ae
= e
.isAddrExp())
714 // Leave the function
715 --ctfeGlobals
.callDepth
;
717 ctfeGlobals
.stack
.endFrame();
719 // If it generated an uncaught exception, report error.
720 if (!istate
&& e
.isThrownExceptionExp())
724 e
.isThrownExceptionExp().generateUncaughtError();
731 /// used to collect coverage information in ctfe
732 void incUsageCtfe(InterState
* istate
, const ref Loc loc
)
734 if (global
.params
.ctfe_cov
&& istate
)
736 auto line
= loc
.linnum
;
737 auto mod
= istate
.fd
.getModule();
739 ++mod
.ctfe_cov
[line
];
743 /***********************************
744 * Interpret the statement.
746 * s = Statement to interpret
749 * NULL continue to next statement
750 * EXP.cantExpression cannot interpret statement at compile time
751 * !NULL expression from return statement, or thrown exception
754 Expression
interpretStatement(Statement s
, InterState
* istate
)
757 auto result
= interpretStatement(&ue
, s
, istate
);
758 if (result
== ue
.exp())
764 Expression
interpretStatement(UnionExp
* pue
, Statement s
, InterState
* istate
)
768 // If e is EXP.throw_exception or EXP.cantExpression,
769 // set it to 'result' and returns true.
770 bool exceptionOrCant(Expression e
)
772 if (exceptionOrCantInterpret(e
))
774 // Make sure e is not pointing to a stack temporary
775 result
= (e
.op
== EXP
.cantExpression
) ? CTFEExp
.cantexp
: e
;
781 /******************************** Statement ***************************/
783 void visitDefaultCase(Statement s
)
787 printf("%s Statement::interpret() %s\n", s
.loc
.toChars(), s
.toChars());
791 if (istate
.start
!= s
)
796 error(s
.loc
, "statement `%s` cannot be interpreted at compile time", s
.toChars());
797 result
= CTFEExp
.cantexp
;
800 void visitExp(ExpStatement s
)
804 printf("%s ExpStatement::interpret(%s)\n", s
.loc
.toChars(), s
.exp ? s
.exp
.toChars() : "");
808 if (istate
.start
!= s
)
812 if (s
.exp
&& s
.exp
.hasCode
)
813 incUsageCtfe(istate
, s
.loc
);
815 Expression e
= interpret(pue
, s
.exp
, istate
, CTFEGoal
.Nothing
);
816 if (exceptionOrCant(e
))
820 void visitDtorExp(DtorExpStatement s
)
825 void visitCompound(CompoundStatement s
)
829 printf("%s CompoundStatement::interpret()\n", s
.loc
.toChars());
831 if (istate
.start
== s
)
834 const dim
= s
.statements ? s
.statements
.length
: 0;
835 foreach (i
; 0 .. dim
)
837 Statement sx
= (*s
.statements
)[i
];
838 result
= interpretStatement(pue
, sx
, istate
);
844 printf("%s -CompoundStatement::interpret() %p\n", s
.loc
.toChars(), result
);
848 void visitCompoundAsm(CompoundAsmStatement s
)
853 void visitUnrolledLoop(UnrolledLoopStatement s
)
857 printf("%s UnrolledLoopStatement::interpret()\n", s
.loc
.toChars());
859 if (istate
.start
== s
)
862 const dim
= s
.statements ? s
.statements
.length
: 0;
863 foreach (i
; 0 .. dim
)
865 Statement sx
= (*s
.statements
)[i
];
866 Expression e
= interpretStatement(pue
, sx
, istate
);
867 if (!e
) // succeeds to interpret, or goto target was not found
869 if (exceptionOrCant(e
))
871 if (e
.op
== EXP
.break_
)
873 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
875 result
= e
; // break at a higher level
878 istate
.gotoTarget
= null;
882 if (e
.op
== EXP
.continue_
)
884 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
886 result
= e
; // continue at a higher level
889 istate
.gotoTarget
= null;
893 // expression from return statement, or thrown exception
899 void visitIf(IfStatement s
)
903 printf("%s IfStatement::interpret(%s)\n", s
.loc
.toChars(), s
.condition
.toChars());
905 incUsageCtfe(istate
, s
.loc
);
906 if (istate
.start
== s
)
911 e
= interpretStatement(s
.ifbody
, istate
);
912 if (!e
&& istate
.start
)
913 e
= interpretStatement(s
.elsebody
, istate
);
919 Expression e
= interpret(&ue
, s
.condition
, istate
);
921 if (exceptionOrCant(e
))
925 result
= interpretStatement(pue
, s
.ifbody
, istate
);
926 else if (e
.toBool().hasValue(false))
927 result
= interpretStatement(pue
, s
.elsebody
, istate
);
930 // no error, or assert(0)?
931 result
= CTFEExp
.cantexp
;
935 void visitScope(ScopeStatement s
)
939 printf("%s ScopeStatement::interpret()\n", s
.loc
.toChars());
941 if (istate
.start
== s
)
944 result
= interpretStatement(pue
, s
.statement
, istate
);
947 void visitReturn(ReturnStatement s
)
951 printf("%s ReturnStatement::interpret(%s)\n", s
.loc
.toChars(), s
.exp ? s
.exp
.toChars() : "");
955 if (istate
.start
!= s
)
962 result
= CTFEExp
.voidexp
;
966 incUsageCtfe(istate
, s
.loc
);
967 assert(istate
&& istate
.fd
&& istate
.fd
.type
&& istate
.fd
.type
.ty
== Tfunction
);
968 TypeFunction tf
= cast(TypeFunction
)istate
.fd
.type
;
970 /* If the function returns a ref AND it's been called from an assignment,
971 * we need to return an lvalue. Otherwise, just do an (rvalue) interpret.
975 result
= interpret(pue
, s
.exp
, istate
, CTFEGoal
.LValue
);
978 if (tf
.next
&& tf
.next
.ty
== Tdelegate
&& istate
.fd
.closureVars
.length
> 0)
980 // To support this, we need to copy all the closure vars
981 // into the delegate literal.
982 error(s
.loc
, "closures are not yet supported in CTFE");
983 result
= CTFEExp
.cantexp
;
987 // We need to treat pointers specially, because EXP.symbolOffset can be used to
988 // return a value OR a pointer
989 Expression e
= interpret(pue
, s
.exp
, istate
);
990 if (exceptionOrCant(e
))
994 * Interpret `return a ~= b` (i.e. `return _d_arrayappendT{,Trace}(a, b)`) as:
997 * This is needed because `a ~= b` has to be interpreted as an lvalue, in order to avoid
998 * assigning a larger array into a smaller one, such as:
999 * `a = [1, 2], a ~= [3]` => `[1, 2] ~= [3]` => `[1, 2] = [1, 2, 3]`
1001 if (isRuntimeHook(s
.exp
, Id
._d_arrayappendT
) ||
isRuntimeHook(s
.exp
, Id
._d_arrayappendTTrace
))
1003 auto rs
= new ReturnStatement(s
.loc
, e
);
1008 // Disallow returning pointers to stack-allocated variables (bug 7876)
1009 if (!stopPointersEscaping(s
.loc
, e
))
1011 result
= CTFEExp
.cantexp
;
1015 if (needToCopyLiteral(e
))
1016 e
= copyLiteral(e
).copy();
1019 printf("RETURN %s\n", s
.loc
.toChars());
1025 void visitBreak(BreakStatement s
)
1029 printf("%s BreakStatement::interpret()\n", s
.loc
.toChars());
1031 incUsageCtfe(istate
, s
.loc
);
1034 if (istate
.start
!= s
)
1036 istate
.start
= null;
1039 istate
.gotoTarget
= findGotoTarget(istate
, s
.ident
);
1040 result
= CTFEExp
.breakexp
;
1043 void visitContinue(ContinueStatement s
)
1047 printf("%s ContinueStatement::interpret()\n", s
.loc
.toChars());
1049 incUsageCtfe(istate
, s
.loc
);
1052 if (istate
.start
!= s
)
1054 istate
.start
= null;
1057 istate
.gotoTarget
= findGotoTarget(istate
, s
.ident
);
1058 result
= CTFEExp
.continueexp
;
1061 void visitWhile(WhileStatement s
)
1065 printf("WhileStatement::interpret()\n");
1067 assert(0); // rewritten to ForStatement
1070 void visitDo(DoStatement s
)
1074 printf("%s DoStatement::interpret()\n", s
.loc
.toChars());
1076 if (istate
.start
== s
)
1077 istate
.start
= null;
1081 Expression e
= interpretStatement(s
._body
, istate
);
1082 if (!e
&& istate
.start
) // goto target was not found
1084 assert(!istate
.start
);
1086 if (exceptionOrCant(e
))
1088 if (e
&& e
.op
== EXP
.break_
)
1090 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1092 result
= e
; // break at a higher level
1095 istate
.gotoTarget
= null;
1098 if (e
&& e
.op
== EXP
.continue_
)
1100 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1102 result
= e
; // continue at a higher level
1105 istate
.gotoTarget
= null;
1110 result
= e
; // bubbled up from ReturnStatement
1115 incUsageCtfe(istate
, s
.condition
.loc
);
1116 e
= interpret(&ue
, s
.condition
, istate
);
1117 if (exceptionOrCant(e
))
1121 result
= CTFEExp
.cantexp
;
1124 if (e
.toBool().hasValue(false))
1126 assert(isTrueBool(e
));
1128 assert(result
is null);
1131 void visitFor(ForStatement s
)
1135 printf("%s ForStatement::interpret()\n", s
.loc
.toChars());
1137 if (istate
.start
== s
)
1138 istate
.start
= null;
1140 UnionExp ueinit
= void;
1141 Expression ei
= interpretStatement(&ueinit
, s
._init
, istate
);
1142 if (exceptionOrCant(ei
))
1144 assert(!ei
); // s.init never returns from function, or jumps out from it
1148 if (s
.condition
&& !istate
.start
)
1151 incUsageCtfe(istate
, s
.condition
.loc
);
1152 Expression e
= interpret(&ue
, s
.condition
, istate
);
1153 if (exceptionOrCant(e
))
1155 if (e
.toBool().hasValue(false))
1157 assert(isTrueBool(e
));
1160 Expression e
= interpretStatement(pue
, s
._body
, istate
);
1161 if (!e
&& istate
.start
) // goto target was not found
1163 assert(!istate
.start
);
1165 if (exceptionOrCant(e
))
1167 if (e
&& e
.op
== EXP
.break_
)
1169 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1171 result
= e
; // break at a higher level
1174 istate
.gotoTarget
= null;
1177 if (e
&& e
.op
== EXP
.continue_
)
1179 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1181 result
= e
; // continue at a higher level
1184 istate
.gotoTarget
= null;
1189 result
= e
; // bubbled up from ReturnStatement
1193 UnionExp uei
= void;
1195 incUsageCtfe(istate
, s
.increment
.loc
);
1196 e
= interpret(&uei
, s
.increment
, istate
, CTFEGoal
.Nothing
);
1197 if (exceptionOrCant(e
))
1200 assert(result
is null);
1203 void visitForeach(ForeachStatement s
)
1205 assert(0); // rewritten to ForStatement
1208 void visitForeachRange(ForeachRangeStatement s
)
1210 assert(0); // rewritten to ForStatement
1213 void visitSwitch(SwitchStatement s
)
1217 printf("%s SwitchStatement::interpret()\n", s
.loc
.toChars());
1219 incUsageCtfe(istate
, s
.loc
);
1220 if (istate
.start
== s
)
1221 istate
.start
= null;
1224 Expression e
= interpretStatement(s
._body
, istate
);
1225 if (istate
.start
) // goto target was not found
1227 if (exceptionOrCant(e
))
1229 if (e
&& e
.op
== EXP
.break_
)
1231 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1233 result
= e
; // break at a higher level
1236 istate
.gotoTarget
= null;
1243 UnionExp uecond
= void;
1244 Expression econdition
= interpret(&uecond
, s
.condition
, istate
);
1245 if (exceptionOrCant(econdition
))
1248 Statement scase
= null;
1250 foreach (cs
; *s
.cases
)
1252 UnionExp uecase
= void;
1253 Expression ecase
= interpret(&uecase
, cs
.exp
, istate
);
1254 if (exceptionOrCant(ecase
))
1256 if (ctfeEqual(cs
.exp
.loc
, EXP
.equal
, econdition
, ecase
))
1265 error(s
.loc
, "no `default` or `case` for `%s` in `switch` statement", econdition
.toChars());
1273 istate
.start
= scase
;
1274 Expression e
= interpretStatement(pue
, s
._body
, istate
);
1275 assert(!istate
.start
); // jump must not fail
1276 if (e
&& e
.op
== EXP
.break_
)
1278 if (istate
.gotoTarget
&& istate
.gotoTarget
!= s
)
1280 result
= e
; // break at a higher level
1283 istate
.gotoTarget
= null;
1289 void visitCase(CaseStatement s
)
1293 printf("%s CaseStatement::interpret(%s) this = %p\n", s
.loc
.toChars(), s
.exp
.toChars(), s
);
1295 incUsageCtfe(istate
, s
.loc
);
1296 if (istate
.start
== s
)
1297 istate
.start
= null;
1299 result
= interpretStatement(pue
, s
.statement
, istate
);
1302 void visitDefault(DefaultStatement s
)
1306 printf("%s DefaultStatement::interpret()\n", s
.loc
.toChars());
1308 incUsageCtfe(istate
, s
.loc
);
1309 if (istate
.start
== s
)
1310 istate
.start
= null;
1312 result
= interpretStatement(pue
, s
.statement
, istate
);
1315 void visitGoto(GotoStatement s
)
1319 printf("%s GotoStatement::interpret()\n", s
.loc
.toChars());
1323 if (istate
.start
!= s
)
1325 istate
.start
= null;
1327 incUsageCtfe(istate
, s
.loc
);
1329 assert(s
.label
&& s
.label
.statement
);
1330 istate
.gotoTarget
= s
.label
.statement
;
1331 result
= CTFEExp
.gotoexp
;
1334 void visitGotoCase(GotoCaseStatement s
)
1338 printf("%s GotoCaseStatement::interpret()\n", s
.loc
.toChars());
1342 if (istate
.start
!= s
)
1344 istate
.start
= null;
1346 incUsageCtfe(istate
, s
.loc
);
1349 istate
.gotoTarget
= s
.cs
;
1350 result
= CTFEExp
.gotoexp
;
1353 void visitGotoDefault(GotoDefaultStatement s
)
1357 printf("%s GotoDefaultStatement::interpret()\n", s
.loc
.toChars());
1361 if (istate
.start
!= s
)
1363 istate
.start
= null;
1365 incUsageCtfe(istate
, s
.loc
);
1367 assert(s
.sw
&& s
.sw
.sdefault
);
1368 istate
.gotoTarget
= s
.sw
.sdefault
;
1369 result
= CTFEExp
.gotoexp
;
1372 void visitLabel(LabelStatement s
)
1376 printf("%s LabelStatement::interpret()\n", s
.loc
.toChars());
1378 if (istate
.start
== s
)
1379 istate
.start
= null;
1381 result
= interpretStatement(pue
, s
.statement
, istate
);
1384 void visitTryCatch(TryCatchStatement s
)
1388 printf("%s TryCatchStatement::interpret()\n", s
.loc
.toChars());
1390 if (istate
.start
== s
)
1391 istate
.start
= null;
1394 Expression e
= null;
1395 e
= interpretStatement(pue
, s
._body
, istate
);
1396 foreach (ca
; *s
.catches
)
1398 if (e ||
!istate
.start
) // goto target was found
1400 e
= interpretStatement(pue
, ca
.handler
, istate
);
1406 Expression e
= interpretStatement(s
._body
, istate
);
1408 // An exception was thrown
1409 if (e
&& e
.isThrownExceptionExp())
1411 ThrownExceptionExp ex
= e
.isThrownExceptionExp();
1412 Type extype
= ex
.thrown
.originalClass().type
;
1414 // Search for an appropriate catch clause.
1415 foreach (ca
; *s
.catches
)
1417 Type catype
= ca
.type
;
1418 if (!catype
.equals(extype
) && !catype
.isBaseOf(extype
, null))
1421 // Execute the handler
1424 ctfeGlobals
.stack
.push(ca
.var
);
1425 setValue(ca
.var
, ex
.thrown
);
1427 e
= interpretStatement(ca
.handler
, istate
);
1428 while (CTFEExp
.isGotoExp(e
))
1430 /* This is an optimization that relies on the locality of the jump target.
1431 * If the label is in the same catch handler, the following scan
1432 * would find it quickly and can reduce jump cost.
1433 * Otherwise, the catch block may be unnnecessary scanned again
1434 * so it would make CTFE speed slower.
1436 InterState istatex
= *istate
;
1437 istatex
.start
= istate
.gotoTarget
; // set starting statement
1438 istatex
.gotoTarget
= null;
1439 Expression eh
= interpretStatement(ca
.handler
, &istatex
);
1442 // The goto target is outside the current scope.
1445 // The goto target was within the body.
1446 if (CTFEExp
.isCantExp(eh
))
1460 void visitTryFinally(TryFinallyStatement s
)
1464 printf("%s TryFinallyStatement::interpret()\n", s
.loc
.toChars());
1466 if (istate
.start
== s
)
1467 istate
.start
= null;
1470 Expression e
= null;
1471 e
= interpretStatement(pue
, s
._body
, istate
);
1472 // Jump into/out from finalbody is disabled in semantic analysis.
1473 // and jump inside will be handled by the ScopeStatement == finalbody.
1478 Expression ex
= interpretStatement(s
._body
, istate
);
1479 if (CTFEExp
.isCantExp(ex
))
1484 while (CTFEExp
.isGotoExp(ex
))
1486 // If the goto target is within the body, we must not interpret the finally statement,
1487 // because that will call destructors for objects within the scope, which we should not do.
1488 InterState istatex
= *istate
;
1489 istatex
.start
= istate
.gotoTarget
; // set starting statement
1490 istatex
.gotoTarget
= null;
1491 Expression bex
= interpretStatement(s
._body
, &istatex
);
1494 // The goto target is outside the current scope.
1497 // The goto target was within the body.
1498 if (CTFEExp
.isCantExp(bex
))
1507 Expression ey
= interpretStatement(s
.finalbody
, istate
);
1508 if (CTFEExp
.isCantExp(ey
))
1513 if (ey
&& ey
.isThrownExceptionExp())
1515 // Check for collided exceptions
1516 if (ex
&& ex
.isThrownExceptionExp())
1517 ex
= chainExceptions(ex
.isThrownExceptionExp(), ey
.isThrownExceptionExp());
1524 void visitThrow(ThrowStatement s
)
1528 printf("%s ThrowStatement::interpret()\n", s
.loc
.toChars());
1532 if (istate
.start
!= s
)
1534 istate
.start
= null;
1537 interpretThrow(result
, s
.exp
, s
.loc
, istate
);
1540 void visitScopeGuard(ScopeGuardStatement s
)
1545 void visitWith(WithStatement s
)
1549 printf("%s WithStatement::interpret()\n", s
.loc
.toChars());
1551 if (istate
.start
== s
)
1552 istate
.start
= null;
1555 result
= s
._body ?
interpretStatement(s
._body
, istate
) : null;
1559 // If it is with(Enum) {...}, just execute the body.
1560 if (s
.exp
.op
== EXP
.scope_ || s
.exp
.op
== EXP
.type
)
1562 result
= interpretStatement(pue
, s
._body
, istate
);
1566 incUsageCtfe(istate
, s
.loc
);
1568 Expression e
= interpret(s
.exp
, istate
);
1569 if (exceptionOrCant(e
))
1572 if (s
.wthis
.type
.ty
== Tpointer
&& s
.exp
.type
.ty
!= Tpointer
)
1574 e
= ctfeEmplaceExp
!AddrExp(s
.loc
, e
, s
.wthis
.type
);
1576 ctfeGlobals
.stack
.push(s
.wthis
);
1577 setValue(s
.wthis
, e
);
1578 e
= interpretStatement(s
._body
, istate
);
1579 while (CTFEExp
.isGotoExp(e
))
1581 /* This is an optimization that relies on the locality of the jump target.
1582 * If the label is in the same WithStatement, the following scan
1583 * would find it quickly and can reduce jump cost.
1584 * Otherwise, the statement body may be unnnecessary scanned again
1585 * so it would make CTFE speed slower.
1587 InterState istatex
= *istate
;
1588 istatex
.start
= istate
.gotoTarget
; // set starting statement
1589 istatex
.gotoTarget
= null;
1590 Expression ex
= interpretStatement(s
._body
, &istatex
);
1593 // The goto target is outside the current scope.
1596 // The goto target was within the body.
1597 if (CTFEExp
.isCantExp(ex
))
1605 ctfeGlobals
.stack
.pop(s
.wthis
);
1609 void visitAsm(AsmStatement s
)
1613 printf("%s AsmStatement::interpret()\n", s
.loc
.toChars());
1617 if (istate
.start
!= s
)
1619 istate
.start
= null;
1621 error(s
.loc
, "`asm` statements cannot be interpreted at compile time");
1622 result
= CTFEExp
.cantexp
;
1625 void visitInlineAsm(InlineAsmStatement s
)
1630 void visitGccAsm(GccAsmStatement s
)
1635 void visitImport(ImportStatement s
)
1639 printf("ImportStatement::interpret()\n");
1643 if (istate
.start
!= s
)
1645 istate
.start
= null;
1652 mixin VisitStatement
!void visit
;
1653 visit
.VisitStatement(s
);
1659 private extern (C
++) final class Interpreter
: Visitor
1661 alias visit
= Visitor
.visit
;
1666 UnionExp
* pue
; // storage for `result`
1668 extern (D
) this(UnionExp
* pue
, InterState
* istate
, CTFEGoal goal
) scope @safe
1671 this.istate
= istate
;
1675 // If e is EXP.throw_exception or EXP.cantExpression,
1676 // set it to 'result' and returns true.
1677 bool exceptionOrCant(Expression e
)
1679 if (exceptionOrCantInterpret(e
))
1681 // Make sure e is not pointing to a stack temporary
1682 result
= (e
.op
== EXP
.cantExpression
) ? CTFEExp
.cantexp
: e
;
1688 /******************************** Expression ***************************/
1690 override void visit(Expression e
)
1694 printf("%s Expression::interpret() '%s' %s\n", e
.loc
.toChars(), EXPtoString(e
.op
).ptr
, e
.toChars());
1695 printf("type = %s\n", e
.type
.toChars());
1698 error(e
.loc
, "cannot interpret `%s` at compile time", e
.toChars());
1699 result
= CTFEExp
.cantexp
;
1702 override void visit(TypeExp e
)
1706 printf("%s TypeExp.interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1711 override void visit(ThisExp e
)
1715 printf("%s ThisExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1717 if (goal
== CTFEGoal
.LValue
)
1719 // We might end up here with istate being zero
1720 // https://issues.dlang.org/show_bug.cgi?id=16382
1721 if (istate
&& istate
.fd
.vthis
)
1723 result
= ctfeEmplaceExp
!VarExp(e
.loc
, istate
.fd
.vthis
);
1724 if (istate
.fd
.hasDualContext())
1726 result
= ctfeEmplaceExp
!PtrExp(e
.loc
, result
);
1727 result
.type
= Type
.tvoidptr
.sarrayOf(2);
1728 result
= ctfeEmplaceExp
!IndexExp(e
.loc
, result
, IntegerExp
.literal
!0);
1730 result
.type
= e
.type
;
1737 result
= ctfeGlobals
.stack
.getThis();
1740 if (istate
&& istate
.fd
.hasDualContext())
1742 assert(result
.op
== EXP
.address
);
1743 result
= result
.isAddrExp().e1
;
1744 assert(result
.op
== EXP
.arrayLiteral
);
1745 result
= (*result
.isArrayLiteralExp().elements
)[0];
1746 if (e
.type
.ty
== Tstruct
)
1748 result
= result
.isAddrExp().e1
;
1752 assert(result
.op
== EXP
.structLiteral || result
.op
== EXP
.classReference || result
.op
== EXP
.type
);
1755 error(e
.loc
, "value of `this` is not known at compile time");
1756 result
= CTFEExp
.cantexp
;
1759 override void visit(NullExp e
)
1764 override void visit(IntegerExp e
)
1768 printf("%s IntegerExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1773 override void visit(RealExp e
)
1777 printf("%s RealExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1782 override void visit(ComplexExp e
)
1787 override void visit(StringExp e
)
1791 printf("%s StringExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1793 if (e
.ownedByCtfe
>= OwnedBy
.ctfe
) // We've already interpreted the string
1799 if (e
.type
.ty
!= Tsarray ||
1800 (cast(TypeNext
)e
.type
).next
.mod
& (MODFlags
.const_ | MODFlags
.immutable_
))
1802 // If it's immutable, we don't need to dup it. Attempts to modify
1803 // string literals are prevented in BinExp::interpretAssignCommon.
1808 // https://issues.dlang.org/show_bug.cgi?id=20811
1809 // Create a copy of mutable string literals, so that any change in
1810 // value via an index or slice will not survive CTFE.
1811 *pue
= copyLiteral(e
);
1816 override void visit(FuncExp e
)
1820 printf("%s FuncExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1825 override void visit(SymOffExp e
)
1829 printf("%s SymOffExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1831 if (e
.var
.isFuncDeclaration() && e
.offset
== 0)
1836 if (isTypeInfo_Class(e
.type
) && e
.offset
== 0)
1841 if (e
.type
.ty
!= Tpointer
)
1843 // Probably impossible
1844 error(e
.loc
, "cannot interpret `%s` at compile time", e
.toChars());
1845 result
= CTFEExp
.cantexp
;
1848 Type pointee
= (cast(TypePointer
)e
.type
).next
;
1849 if (e
.var
.isThreadlocal())
1851 error(e
.loc
, "cannot take address of thread-local variable %s at compile time", e
.var
.toChars());
1852 result
= CTFEExp
.cantexp
;
1855 // Check for taking an address of a shared variable.
1856 // If the shared variable is an array, the offset might not be zero.
1857 Type fromType
= null;
1858 if (e
.var
.type
.ty
== Tarray || e
.var
.type
.ty
== Tsarray
)
1860 fromType
= (cast(TypeArray
)e
.var
.type
).next
;
1862 if (e
.var
.isDataseg() && ((e
.offset
== 0 && isSafePointerCast(e
.var
.type
, pointee
)) ||
1863 (fromType
&& isSafePointerCast(fromType
, pointee
)) ||
1864 (e
.var
.isCsymbol() && e
.offset
+ pointee
.size() <= e
.var
.type
.size())))
1870 Expression val
= getVarExp(e
.loc
, istate
, e
.var
, goal
);
1871 if (exceptionOrCant(val
))
1873 if (val
.type
.ty
== Tarray || val
.type
.ty
== Tsarray
)
1875 // Check for unsupported type painting operations
1876 Type elemtype
= (cast(TypeArray
)val
.type
).next
;
1877 const elemsize
= elemtype
.size();
1879 // It's OK to cast from fixed length to fixed length array, eg &int[n] to int[d]*.
1880 if (val
.type
.ty
== Tsarray
&& pointee
.ty
== Tsarray
&& elemsize
== pointee
.nextOf().size())
1882 size_t d
= cast(size_t
)(cast(TypeSArray
)pointee
).dim
.toInteger();
1883 Expression elwr
= ctfeEmplaceExp
!IntegerExp(e
.loc
, e
.offset
/ elemsize
, Type
.tsize_t
);
1884 Expression eupr
= ctfeEmplaceExp
!IntegerExp(e
.loc
, e
.offset
/ elemsize
+ d
, Type
.tsize_t
);
1886 // Create a CTFE pointer &val[ofs..ofs+d]
1887 auto se
= ctfeEmplaceExp
!SliceExp(e
.loc
, val
, elwr
, eupr
);
1889 emplaceExp
!(AddrExp
)(pue
, e
.loc
, se
, e
.type
);
1894 if (!isSafePointerCast(elemtype
, pointee
))
1896 // It's also OK to cast from &string to string*.
1897 if (e
.offset
== 0 && isSafePointerCast(e
.var
.type
, pointee
))
1899 // Create a CTFE pointer &var
1900 auto ve
= ctfeEmplaceExp
!VarExp(e
.loc
, e
.var
);
1902 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ve
, e
.type
);
1906 error(e
.loc
, "reinterpreting cast from `%s` to `%s` is not supported in CTFE", val
.type
.toChars(), e
.type
.toChars());
1907 result
= CTFEExp
.cantexp
;
1911 const dinteger_t sz
= pointee
.size();
1912 dinteger_t indx
= e
.offset
/ sz
;
1913 assert(sz
* indx
== e
.offset
);
1914 Expression aggregate
= null;
1915 if (val
.op
== EXP
.arrayLiteral || val
.op
== EXP
.string_
)
1919 else if (auto se
= val
.isSliceExp())
1922 UnionExp uelwr
= void;
1923 Expression lwr
= interpret(&uelwr
, se
.lwr
, istate
);
1924 indx
+= lwr
.toInteger();
1928 // Create a CTFE pointer &aggregate[ofs]
1929 auto ofs
= ctfeEmplaceExp
!IntegerExp(e
.loc
, indx
, Type
.tsize_t
);
1930 auto ei
= ctfeEmplaceExp
!IndexExp(e
.loc
, aggregate
, ofs
);
1932 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ei
, e
.type
);
1937 else if (e
.offset
== 0 && isSafePointerCast(e
.var
.type
, pointee
))
1939 // Create a CTFE pointer &var
1940 auto ve
= ctfeEmplaceExp
!VarExp(e
.loc
, e
.var
);
1941 ve
.type
= e
.var
.type
;
1942 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ve
, e
.type
);
1947 error(e
.loc
, "cannot convert `&%s` to `%s` at compile time", e
.var
.type
.toChars(), e
.type
.toChars());
1948 result
= CTFEExp
.cantexp
;
1951 override void visit(AddrExp e
)
1955 printf("%s AddrExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1957 if (auto ve
= e
.e1
.isVarExp())
1959 Declaration decl
= ve
.var
;
1961 // We cannot take the address of an imported symbol at compile time
1962 if (decl
.isImportedSymbol()) {
1963 error(e
.loc
, "cannot take address of imported symbol `%s` at compile time", decl
.toChars());
1964 result
= CTFEExp
.cantexp
;
1968 if (decl
.isDataseg()) {
1969 // Normally this is already done by optimize()
1970 // Do it here in case optimize(WANTvalue) wasn't run before CTFE
1971 emplaceExp
!(SymOffExp
)(pue
, e
.loc
, e
.e1
.isVarExp().var
, 0);
1973 result
.type
= e
.type
;
1977 auto er
= interpret(e
.e1
, istate
, CTFEGoal
.LValue
);
1978 if (auto ve
= er
.isVarExp())
1979 if (istate
&& ve
.var
== istate
.fd
.vthis
)
1980 er
= interpret(er
, istate
);
1982 if (exceptionOrCant(er
))
1985 // Return a simplified address expression
1986 emplaceExp
!(AddrExp
)(pue
, e
.loc
, er
, e
.type
);
1990 override void visit(DelegateExp e
)
1994 printf("%s DelegateExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
1996 // TODO: Really we should create a CTFE-only delegate expression
1997 // of a pointer and a funcptr.
1999 // If it is &nestedfunc, just return it
2000 // TODO: We should save the context pointer
2001 if (auto ve1
= e
.e1
.isVarExp())
2002 if (ve1
.var
== e
.func
)
2008 auto er
= interpret(pue
, e
.e1
, istate
);
2009 if (exceptionOrCant(er
))
2013 // If it has already been CTFE'd, just return it
2018 er
= (er
== pue
.exp()) ? pue
.copy() : er
;
2019 emplaceExp
!(DelegateExp
)(pue
, e
.loc
, er
, e
.func
, false);
2021 result
.type
= e
.type
;
2025 static Expression
getVarExp(const ref Loc loc
, InterState
* istate
, Declaration d
, CTFEGoal goal
)
2027 Expression e
= CTFEExp
.cantexp
;
2028 if (VarDeclaration v
= d
.isVarDeclaration())
2030 /* Magic variable __ctfe always returns true when interpreting
2032 if (v
.ident
== Id
.ctfe
)
2033 return IntegerExp
.createBool(true);
2035 if (!v
.originalType
&& v
.semanticRun
< PASS
.semanticdone
) // semantic() not yet run
2037 v
.dsymbolSemantic(null);
2038 if (v
.type
.ty
== Terror
)
2039 return CTFEExp
.cantexp
;
2042 if ((v
.isConst() || v
.isImmutable() || v
.storage_class
& STC
.manifest
) && !hasValue(v
) && v
._init
&& !v
.isCTFE())
2046 error(loc
, "circular initialization of %s `%s`", v
.kind(), v
.toPrettyChars());
2047 return CTFEExp
.cantexp
;
2052 v
._init
= v
._init
.initializerSemantic(v
._scope
, v
.type
, INITinterpret
); // might not be run on aggregate members
2055 e
= v
._init
.initializerToExpression(v
.type
);
2057 return CTFEExp
.cantexp
;
2060 // There's a terrible hack in `dmd.dsymbolsem` that special case
2061 // a struct with all zeros to an `ExpInitializer(BlitExp(IntegerExp(0)))`
2062 // There's matching code for it in e2ir (toElem's visitAssignExp),
2063 // so we need the same hack here.
2064 // This does not trigger for global as they get a normal initializer.
2065 if (auto ts
= e
.type
.isTypeStruct())
2066 if (auto ae
= e
.isBlitExp())
2067 if (ae
.e2
.op
== EXP
.int64
)
2068 e
= ts
.defaultInitLiteral(loc
);
2070 if (e
.op
== EXP
.construct || e
.op
== EXP
.blit
)
2072 AssignExp ae
= cast(AssignExp
)e
;
2076 if (e
.op
== EXP
.error
)
2078 // FIXME: Ultimately all errors should be detected in prior semantic analysis stage.
2080 else if (v
.isDataseg() ||
(v
.storage_class
& STC
.manifest
))
2082 /* https://issues.dlang.org/show_bug.cgi?id=14304
2083 * e is a value that is not yet owned by CTFE.
2084 * Mark as "cached", and use it directly during interpretation.
2086 e
= scrubCacheValue(e
);
2087 ctfeGlobals
.stack
.saveGlobalConstant(v
, e
);
2092 e
= interpret(e
, istate
);
2094 if (CTFEExp
.isCantExp(e
) && !global
.gag
&& !ctfeGlobals
.stackTraceCallsToSuppress
)
2095 errorSupplemental(loc
, "while evaluating %s.init", v
.toChars());
2096 if (exceptionOrCantInterpret(e
))
2100 else if (v
.isCTFE() && !hasValue(v
))
2102 if (v
._init
&& v
.type
.size() != 0)
2104 if (v
._init
.isVoidInitializer())
2106 // var should have been initialized when it was created
2107 error(loc
, "CTFE internal error: trying to access uninitialized var");
2110 e
= v
._init
.initializerToExpression();
2113 // Zero-length arrays don't have an initializer
2114 e
= v
.type
.defaultInitLiteral(e
.loc
);
2116 e
= interpret(e
, istate
);
2118 else if (!(v
.isDataseg() || v
.storage_class
& STC
.manifest
) && !v
.isCTFE() && !istate
)
2120 error(loc
, "variable `%s` cannot be read at compile time", v
.toChars());
2121 return CTFEExp
.cantexp
;
2125 e
= hasValue(v
) ?
getValue(v
) : null;
2128 // Zero-length arrays don't have an initializer
2129 if (v
.type
.size() == 0)
2130 e
= v
.type
.defaultInitLiteral(loc
);
2131 else if (!v
.isCTFE() && v
.isDataseg())
2133 error(loc
, "static variable `%s` cannot be read at compile time", v
.toChars());
2134 return CTFEExp
.cantexp
;
2138 assert(!(v
._init
&& v
._init
.isVoidInitializer()));
2139 // CTFE initiated from inside a function
2140 error(loc
, "variable `%s` cannot be read at compile time", v
.toChars());
2141 return CTFEExp
.cantexp
;
2144 if (auto vie
= e
.isVoidInitExp())
2146 error(loc
, "cannot read uninitialized variable `%s` in ctfe", v
.toPrettyChars());
2147 errorSupplemental(vie
.var
.loc
, "`%s` was uninitialized and used before set", vie
.var
.toChars());
2148 return CTFEExp
.cantexp
;
2150 if (goal
!= CTFEGoal
.LValue
&& v
.isReference())
2151 e
= interpret(e
, istate
, goal
);
2154 e
= CTFEExp
.cantexp
;
2156 else if (SymbolDeclaration s
= d
.isSymbolDeclaration())
2158 // exclude void[]-typed `__traits(initSymbol)`
2159 if (auto ta
= s
.type
.toBasetype().isTypeDArray())
2161 assert(ta
.next
.ty
== Tvoid
);
2162 error(loc
, "cannot determine the address of the initializer symbol during CTFE");
2163 return CTFEExp
.cantexp
;
2166 // Struct static initializers, for example
2167 e
= s
.dsym
.type
.defaultInitLiteral(loc
);
2168 if (e
.op
== EXP
.error
)
2169 error(loc
, "CTFE failed because of previous errors in `%s.init`", s
.toChars());
2170 e
= e
.expressionSemantic(null);
2171 if (e
.op
== EXP
.error
)
2172 e
= CTFEExp
.cantexp
;
2173 else // Convert NULL to CTFEExp
2174 e
= interpret(e
, istate
, goal
);
2177 error(loc
, "cannot interpret declaration `%s` at compile time", d
.toChars());
2181 override void visit(VarExp e
)
2185 printf("%s VarExp::interpret() `%s`, goal = %d\n", e
.loc
.toChars(), e
.toChars(), goal
);
2187 if (e
.var
.isFuncDeclaration())
2193 if (goal
== CTFEGoal
.LValue
)
2195 if (auto v
= e
.var
.isVarDeclaration())
2199 // Compile-time known non-CTFE variable from an outer context
2200 // e.g. global or from a ref argument
2201 if (v
.isConst() || v
.isImmutable())
2203 result
= getVarExp(e
.loc
, istate
, v
, goal
);
2207 if (!v
.isCTFE() && v
.isDataseg())
2208 error(e
.loc
, "static variable `%s` cannot be read at compile time", v
.toChars());
2209 else // CTFE initiated from inside a function
2210 error(e
.loc
, "variable `%s` cannot be read at compile time", v
.toChars());
2211 result
= CTFEExp
.cantexp
;
2215 if (v
.storage_class
& (STC
.out_ | STC
.ref_
))
2217 // Strip off the nest of ref variables
2218 Expression ev
= getValue(v
);
2219 if (ev
.op
== EXP
.variable ||
2220 ev
.op
== EXP
.index ||
2221 (ev
.op
== EXP
.slice
&& ev
.type
.toBasetype().ty
== Tsarray
) ||
2222 ev
.op
== EXP
.dotVariable
)
2224 result
= interpret(pue
, ev
, istate
, goal
);
2232 result
= getVarExp(e
.loc
, istate
, e
.var
, goal
);
2233 if (exceptionOrCant(result
))
2236 // Visit the default initializer for noreturn variables
2237 // (Custom initializers would abort the current function call and exit above)
2238 if (result
.type
.ty
== Tnoreturn
)
2240 result
.accept(this);
2244 if ((e
.var
.storage_class
& (STC
.ref_ | STC
.out_
)) == 0 && e
.type
.baseElemOf().ty
!= Tstruct
)
2246 /* Ultimately, STC.ref_|STC.out_ check should be enough to see the
2247 * necessity of type repainting. But currently front-end paints
2248 * non-ref struct variables by the const type.
2250 * auto foo(ref const S cs);
2252 * foo(s); // VarExp('s') will have const(S)
2254 // A VarExp may include an implicit cast. It must be done explicitly.
2255 result
= paintTypeOntoLiteral(pue
, e
.type
, result
);
2259 override void visit(DeclarationExp e
)
2263 printf("%s DeclarationExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2265 Dsymbol s
= e
.declaration
;
2266 while (s
.isAttribDeclaration())
2268 auto ad
= cast(AttribDeclaration
)s
;
2269 assert(ad
.decl
&& ad
.decl
.length
== 1); // Currently, only one allowed when parsing
2272 if (VarDeclaration v
= s
.isVarDeclaration())
2274 if (TupleDeclaration td
= v
.toAlias().isTupleDeclaration())
2278 // Reserve stack space for all tuple members
2281 VarDeclaration v2
= s
.isVarDeclaration();
2283 if (v2
.isDataseg() && !v2
.isCTFE())
2286 ctfeGlobals
.stack
.push(v2
);
2290 if (ExpInitializer ie
= v2
._init
.isExpInitializer())
2292 einit
= interpretRegion(ie
.exp
, istate
, goal
);
2293 if (exceptionOrCant(einit
))
2296 else if (v2
._init
.isVoidInitializer())
2298 einit
= voidInitLiteral(v2
.type
, v2
).copy();
2302 error(e
.loc
, "declaration `%s` is not yet implemented in CTFE", e
.toChars());
2303 result
= CTFEExp
.cantexp
;
2306 setValue(v2
, einit
);
2314 // Just ignore static variables which aren't read or written yet
2318 if (!(v
.isDataseg() || v
.storage_class
& STC
.manifest
) || v
.isCTFE())
2319 ctfeGlobals
.stack
.push(v
);
2322 if (ExpInitializer ie
= v
._init
.isExpInitializer())
2324 result
= interpretRegion(ie
.exp
, istate
, goal
);
2327 else if (v
._init
.isVoidInitializer())
2329 result
= voidInitLiteral(v
.type
, v
).copy();
2330 // There is no AssignExp for void initializers,
2332 setValue(v
, result
);
2335 else if (v
._init
.isArrayInitializer())
2337 result
= v
._init
.initializerToExpression(v
.type
);
2338 if (result
!is null)
2341 error(e
.loc
, "declaration `%s` is not yet implemented in CTFE", e
.toChars());
2342 result
= CTFEExp
.cantexp
;
2344 else if (v
.type
.size() == 0)
2346 // Zero-length arrays don't need an initializer
2347 result
= v
.type
.defaultInitLiteral(e
.loc
);
2351 error(e
.loc
, "variable `%s` cannot be modified at compile time", v
.toChars());
2352 result
= CTFEExp
.cantexp
;
2356 if (s
.isTemplateMixin() || s
.isTupleDeclaration())
2358 // These can be made to work, too lazy now
2359 error(e
.loc
, "declaration `%s` is not yet implemented in CTFE", e
.toChars());
2360 result
= CTFEExp
.cantexp
;
2364 // Others should not contain executable code, so are trivial to evaluate
2368 printf("-DeclarationExp::interpret(%s): %p\n", e
.toChars(), result
);
2372 override void visit(TypeidExp e
)
2376 printf("%s TypeidExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2378 if (Type t
= isType(e
.obj
))
2383 if (Expression ex
= isExpression(e
.obj
))
2385 result
= interpret(pue
, ex
, istate
);
2386 if (exceptionOrCant(ex
))
2389 if (result
.op
== EXP
.null_
)
2391 error(e
.loc
, "null pointer dereference evaluating typeid. `%s` is `null`", ex
.toChars());
2392 result
= CTFEExp
.cantexp
;
2395 if (result
.op
!= EXP
.classReference
)
2397 error(e
.loc
, "CTFE internal error: determining classinfo");
2398 result
= CTFEExp
.cantexp
;
2402 ClassDeclaration cd
= result
.isClassReferenceExp().originalClass();
2405 emplaceExp
!(TypeidExp
)(pue
, e
.loc
, cd
.type
);
2407 result
.type
= e
.type
;
2410 visit(cast(Expression
)e
);
2413 override void visit(TupleExp e
)
2417 printf("%s TupleExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2419 if (exceptionOrCant(interpretRegion(e
.e0
, istate
, CTFEGoal
.Nothing
)))
2422 auto expsx
= e
.exps
;
2423 foreach (i
, exp
; *expsx
)
2425 Expression ex
= interpretRegion(exp
, istate
);
2426 if (exceptionOrCant(ex
))
2429 // A tuple of assignments can contain void (Bug 5676).
2430 if (goal
== CTFEGoal
.Nothing
)
2432 if (ex
.op
== EXP
.voidExpression
)
2434 error(e
.loc
, "CTFE internal error: void element `%s` in sequence", exp
.toChars());
2438 /* If any changes, do Copy On Write
2442 expsx
= copyArrayOnWrite(expsx
, e
.exps
);
2443 (*expsx
)[i
] = copyRegionExp(ex
);
2447 if (expsx
!is e
.exps
)
2449 expandTuples(expsx
);
2450 emplaceExp
!(TupleExp
)(pue
, e
.loc
, expsx
);
2452 result
.type
= new TypeTuple(expsx
);
2458 override void visit(ArrayLiteralExp e
)
2462 printf("%s ArrayLiteralExp::interpret() %s, %s\n", e
.loc
.toChars(), e
.type
.toChars(), e
.toChars());
2464 if (e
.ownedByCtfe
>= OwnedBy
.ctfe
) // We've already interpreted all the elements
2470 Type tb
= e
.type
.toBasetype();
2471 Type tn
= tb
.nextOf().toBasetype();
2472 bool wantCopy
= (tn
.ty
== Tsarray || tn
.ty
== Tstruct
);
2474 auto basis
= interpretRegion(e
.basis
, istate
);
2475 if (exceptionOrCant(basis
))
2478 auto expsx
= e
.elements
;
2479 size_t dim
= expsx ? expsx
.length
: 0;
2481 for (size_t i
= 0; i
< dim
; i
++)
2483 Expression exp
= (*expsx
)[i
];
2487 ex
= copyLiteral(basis
).copy();
2491 // segfault bug 6250
2492 assert(exp
.op
!= EXP
.index || exp
.isIndexExp().e1
!= e
);
2494 ex
= interpretRegion(exp
, istate
);
2495 if (exceptionOrCant(ex
))
2498 /* Each elements should have distinct CTFE memory.
2500 * int[1][] pieces = [z,z]; // here
2503 ex
= copyLiteral(ex
).copy();
2506 /* If any changes, do Copy On Write
2510 expsx
= copyArrayOnWrite(expsx
, e
.elements
);
2515 if (expsx
!is e
.elements
)
2517 // todo: all tuple expansions should go in semantic phase.
2518 expandTuples(expsx
);
2519 if (expsx
.length
!= dim
)
2521 error(e
.loc
, "CTFE internal error: invalid array literal");
2522 result
= CTFEExp
.cantexp
;
2525 emplaceExp
!(ArrayLiteralExp
)(pue
, e
.loc
, e
.type
, basis
, expsx
);
2526 auto ale
= pue
.exp().isArrayLiteralExp();
2527 ale
.ownedByCtfe
= OwnedBy
.ctfe
;
2530 else if ((cast(TypeNext
)e
.type
).next
.mod
& (MODFlags
.const_ | MODFlags
.immutable_
))
2532 // If it's immutable, we don't need to dup it
2537 *pue
= copyLiteral(e
);
2542 override void visit(AssocArrayLiteralExp e
)
2546 printf("%s AssocArrayLiteralExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2548 if (e
.ownedByCtfe
>= OwnedBy
.ctfe
) // We've already interpreted all the elements
2554 auto keysx
= e
.keys
;
2555 auto valuesx
= e
.values
;
2556 foreach (i
, ekey
; *keysx
)
2558 auto evalue
= (*valuesx
)[i
];
2560 auto ek
= interpretRegion(ekey
, istate
);
2561 if (exceptionOrCant(ek
))
2563 auto ev
= interpretRegion(evalue
, istate
);
2564 if (exceptionOrCant(ev
))
2567 /* If any changes, do Copy On Write
2572 keysx
= copyArrayOnWrite(keysx
, e
.keys
);
2573 valuesx
= copyArrayOnWrite(valuesx
, e
.values
);
2578 if (keysx
!is e
.keys
)
2579 expandTuples(keysx
);
2580 if (valuesx
!is e
.values
)
2581 expandTuples(valuesx
);
2582 if (keysx
.length
!= valuesx
.length
)
2584 error(e
.loc
, "CTFE internal error: invalid AA");
2585 result
= CTFEExp
.cantexp
;
2589 /* Remove duplicate keys
2591 for (size_t i
= 1; i
< keysx
.length
; i
++)
2593 auto ekey
= (*keysx
)[i
- 1];
2594 for (size_t j
= i
; j
< keysx
.length
; j
++)
2596 auto ekey2
= (*keysx
)[j
];
2597 if (!ctfeEqual(e
.loc
, EXP
.equal
, ekey
, ekey2
))
2601 keysx
= copyArrayOnWrite(keysx
, e
.keys
);
2602 valuesx
= copyArrayOnWrite(valuesx
, e
.values
);
2603 keysx
.remove(i
- 1);
2604 valuesx
.remove(i
- 1);
2606 i
-= 1; // redo the i'th iteration
2611 if (keysx
!is e
.keys ||
2612 valuesx
!is e
.values
)
2614 assert(keysx
!is e
.keys
&&
2615 valuesx
!is e
.values
);
2616 auto aae
= ctfeEmplaceExp
!AssocArrayLiteralExp(e
.loc
, keysx
, valuesx
);
2618 aae
.ownedByCtfe
= OwnedBy
.ctfe
;
2623 *pue
= copyLiteral(e
);
2628 override void visit(StructLiteralExp e
)
2632 printf("%s StructLiteralExp::interpret() %s ownedByCtfe = %d\n", e
.loc
.toChars(), e
.toChars(), e
.ownedByCtfe
);
2634 if (e
.ownedByCtfe
>= OwnedBy
.ctfe
)
2640 size_t dim
= e
.elements ? e
.elements
.length
: 0;
2641 auto expsx
= e
.elements
;
2643 if (dim
!= e
.sd
.fields
.length
)
2645 // guaranteed by AggregateDeclaration.fill and TypeStruct.defaultInitLiteral
2646 const nvthis
= e
.sd
.fields
.length
- e
.sd
.nonHiddenFields();
2647 assert(e
.sd
.fields
.length
- dim
== nvthis
);
2649 /* If a nested struct has no initialized hidden pointer,
2650 * set it to null to match the runtime behaviour.
2652 foreach (const i
; 0 .. nvthis
)
2654 auto ne
= ctfeEmplaceExp
!NullExp(e
.loc
);
2655 auto vthis
= i
== 0 ? e
.sd
.vthis
: e
.sd
.vthis2
;
2656 ne
.type
= vthis
.type
;
2658 expsx
= copyArrayOnWrite(expsx
, e
.elements
);
2663 assert(dim
== e
.sd
.fields
.length
);
2665 foreach (i
; 0 .. dim
)
2667 auto v
= e
.sd
.fields
[i
];
2668 Expression exp
= (*expsx
)[i
];
2672 ex
= voidInitLiteral(v
.type
, v
).copy();
2676 ex
= interpretRegion(exp
, istate
);
2677 if (exceptionOrCant(ex
))
2679 if ((v
.type
.ty
!= ex
.type
.ty
) && v
.type
.ty
== Tsarray
)
2681 // Block assignment from inside struct literals
2682 auto tsa
= cast(TypeSArray
)v
.type
;
2683 auto len
= cast(size_t
)tsa
.dim
.toInteger();
2685 ex
= createBlockDuplicatedArrayLiteral(&ue
, ex
.loc
, v
.type
, ex
, len
);
2691 /* If any changes, do Copy On Write
2695 expsx
= copyArrayOnWrite(expsx
, e
.elements
);
2700 if (expsx
!is e
.elements
)
2702 expandTuples(expsx
);
2703 if (expsx
.length
!= e
.sd
.fields
.length
)
2705 error(e
.loc
, "CTFE internal error: invalid struct literal");
2706 result
= CTFEExp
.cantexp
;
2709 emplaceExp
!(StructLiteralExp
)(pue
, e
.loc
, e
.sd
, expsx
);
2710 auto sle
= pue
.exp().isStructLiteralExp();
2712 sle
.ownedByCtfe
= OwnedBy
.ctfe
;
2713 sle
.origin
= e
.origin
;
2718 *pue
= copyLiteral(e
);
2723 // Create an array literal of type 'newtype' with dimensions given by
2724 // 'arguments'[argnum..$]
2725 static Expression
recursivelyCreateArrayLiteral(UnionExp
* pue
, const ref Loc loc
, Type newtype
, InterState
* istate
, Expressions
* arguments
, int argnum
)
2727 Expression lenExpr
= interpret(pue
, (*arguments
)[argnum
], istate
);
2728 if (exceptionOrCantInterpret(lenExpr
))
2730 size_t len
= cast(size_t
)lenExpr
.toInteger();
2731 Type elemType
= (cast(TypeArray
)newtype
).next
;
2732 if (elemType
.ty
== Tarray
&& argnum
< arguments
.length
- 1)
2734 Expression elem
= recursivelyCreateArrayLiteral(pue
, loc
, elemType
, istate
, arguments
, argnum
+ 1);
2735 if (exceptionOrCantInterpret(elem
))
2738 auto elements
= new Expressions(len
);
2739 foreach (ref element
; *elements
)
2740 element
= copyLiteral(elem
).copy();
2741 emplaceExp
!(ArrayLiteralExp
)(pue
, loc
, newtype
, elements
);
2742 auto ae
= pue
.exp().isArrayLiteralExp();
2743 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
2746 assert(argnum
== arguments
.length
- 1);
2747 if (elemType
.ty
.isSomeChar
)
2749 const ch
= cast(dchar)elemType
.defaultInitLiteral(loc
).toInteger();
2750 const sz
= cast(ubyte)elemType
.size();
2751 return createBlockDuplicatedStringLiteral(pue
, loc
, newtype
, ch
, len
, sz
);
2755 auto el
= interpret(elemType
.defaultInitLiteral(loc
), istate
);
2756 return createBlockDuplicatedArrayLiteral(pue
, loc
, newtype
, el
, len
);
2760 override void visit(NewExp e
)
2764 printf("%s NewExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2767 Expression epre
= interpret(pue
, e
.argprefix
, istate
, CTFEGoal
.Nothing
);
2768 if (exceptionOrCant(epre
))
2771 if (e
.newtype
.ty
== Tarray
&& e
.arguments
)
2773 result
= recursivelyCreateArrayLiteral(pue
, e
.loc
, e
.newtype
, istate
, e
.arguments
, 0);
2776 if (auto ts
= e
.newtype
.toBasetype().isTypeStruct())
2780 Expression se
= e
.newtype
.defaultInitLiteral(e
.loc
);
2781 se
= interpret(se
, istate
);
2782 if (exceptionOrCant(se
))
2784 result
= interpretFunction(pue
, e
.member
, istate
, e
.arguments
, se
);
2786 // Repaint as same as CallExp::interpret() does.
2791 StructDeclaration sd
= ts
.sym
;
2792 auto exps
= new Expressions();
2793 exps
.reserve(sd
.fields
.length
);
2796 exps
.setDim(e
.arguments
.length
);
2797 foreach (i
, ex
; *e
.arguments
)
2799 ex
= interpretRegion(ex
, istate
);
2800 if (exceptionOrCant(ex
))
2805 sd
.fill(e
.loc
, *exps
, false);
2807 auto se
= ctfeEmplaceExp
!StructLiteralExp(e
.loc
, sd
, exps
, e
.newtype
);
2809 se
.type
= e
.newtype
;
2810 se
.ownedByCtfe
= OwnedBy
.ctfe
;
2811 result
= interpret(pue
, se
, istate
);
2813 if (exceptionOrCant(result
))
2815 Expression ev
= (result
== pue
.exp()) ? pue
.copy() : result
;
2816 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ev
, e
.type
);
2820 if (auto tc
= e
.newtype
.toBasetype().isTypeClass())
2822 ClassDeclaration cd
= tc
.sym
;
2823 size_t totalFieldCount
= 0;
2824 for (ClassDeclaration c
= cd
; c
; c
= c
.baseClass
)
2825 totalFieldCount
+= c
.fields
.length
;
2826 auto elems
= new Expressions(totalFieldCount
);
2827 size_t fieldsSoFar
= totalFieldCount
;
2828 for (ClassDeclaration c
= cd
; c
; c
= c
.baseClass
)
2830 fieldsSoFar
-= c
.fields
.length
;
2831 foreach (i
, v
; c
.fields
)
2835 error(e
.loc
, "circular reference to `%s`", v
.toPrettyChars());
2836 result
= CTFEExp
.cantexp
;
2842 if (v
._init
.isVoidInitializer())
2843 m
= voidInitLiteral(v
.type
, v
).copy();
2845 m
= v
.getConstInitializer(true);
2847 else if (v
.type
.isTypeNoreturn())
2849 // Noreturn field with default initializer
2850 (*elems
)[fieldsSoFar
+ i
] = null;
2854 m
= v
.type
.defaultInitLiteral(e
.loc
);
2855 if (exceptionOrCant(m
))
2857 (*elems
)[fieldsSoFar
+ i
] = copyLiteral(m
).copy();
2860 // Hack: we store a ClassDeclaration instead of a StructDeclaration.
2861 // We probably won't get away with this.
2862 // auto se = new StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
2863 auto se
= ctfeEmplaceExp
!StructLiteralExp(e
.loc
, cast(StructDeclaration
)cd
, elems
, e
.newtype
);
2865 se
.ownedByCtfe
= OwnedBy
.ctfe
;
2866 Expression eref
= ctfeEmplaceExp
!ClassReferenceExp(e
.loc
, se
, e
.type
);
2870 if (!e
.member
.fbody
)
2872 Expression ctorfail
= evaluateIfBuiltin(pue
, istate
, e
.loc
, e
.member
, e
.arguments
, eref
);
2875 if (exceptionOrCant(ctorfail
))
2881 error(m
.loc
, "%s `%s` `%s` cannot be constructed at compile time, because the constructor has no available source code",
2882 m
.kind
, m
.toPrettyChars
, e
.newtype
.toChars());
2883 result
= CTFEExp
.cantexp
;
2887 Expression ctorfail
= interpretFunction(&ue
, e
.member
, istate
, e
.arguments
, eref
);
2888 if (exceptionOrCant(ctorfail
))
2891 /* https://issues.dlang.org/show_bug.cgi?id=14465
2892 * Repaint the loc, because a super() call
2893 * in the constructor modifies the loc of ClassReferenceExp
2894 * in CallExp::interpret().
2901 if (e
.newtype
.toBasetype().isscalar())
2904 if (e
.arguments
&& e
.arguments
.length
)
2905 newval
= (*e
.arguments
)[0];
2907 newval
= e
.newtype
.defaultInitLiteral(e
.loc
);
2908 newval
= interpretRegion(newval
, istate
);
2909 if (exceptionOrCant(newval
))
2912 // Create a CTFE pointer &[newval][0]
2913 auto elements
= new Expressions(1);
2914 (*elements
)[0] = newval
;
2915 auto ae
= ctfeEmplaceExp
!ArrayLiteralExp(e
.loc
, e
.newtype
.arrayOf(), elements
);
2916 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
2918 auto ei
= ctfeEmplaceExp
!IndexExp(e
.loc
, ae
, ctfeEmplaceExp
!IntegerExp(Loc
.initial
, 0, Type
.tsize_t
));
2919 ei
.type
= e
.newtype
;
2920 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ei
, e
.type
);
2924 error(e
.loc
, "cannot interpret `%s` at compile time", e
.toChars());
2925 result
= CTFEExp
.cantexp
;
2928 override void visit(UnaExp e
)
2932 printf("%s UnaExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2935 Expression e1
= interpret(&ue
, e
.e1
, istate
);
2936 if (exceptionOrCant(e1
))
2941 *pue
= Neg(e
.type
, e1
);
2945 *pue
= Com(e
.type
, e1
);
2949 *pue
= Not(e
.type
, e1
);
2955 result
= (*pue
).exp();
2958 override void visit(DotTypeExp e
)
2962 printf("%s DotTypeExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
2965 Expression e1
= interpret(&ue
, e
.e1
, istate
);
2966 if (exceptionOrCant(e1
))
2969 result
= e
; // optimize: reuse this CTFE reference
2972 auto edt
= e
.copy().isDotTypeExp();
2973 edt
.e1
= (e1
== ue
.exp()) ? e1
.copy() : e1
; // don't return pointer to ue
2978 extern (D
) private void interpretCommon(BinExp e
, fp_t fp
)
2982 printf("%s BinExp::interpretCommon() %s\n", e
.loc
.toChars(), e
.toChars());
2984 if (e
.e1
.type
.ty
== Tpointer
&& e
.e2
.type
.ty
== Tpointer
&& e
.op
== EXP
.min
)
2986 UnionExp ue1
= void;
2987 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
2988 if (exceptionOrCant(e1
))
2990 UnionExp ue2
= void;
2991 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
2992 if (exceptionOrCant(e2
))
2994 result
= pointerDifference(pue
, e
.loc
, e
.type
, e1
, e2
);
2997 if (e
.e1
.type
.ty
== Tpointer
&& e
.e2
.type
.isintegral())
2999 UnionExp ue1
= void;
3000 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
3001 if (exceptionOrCant(e1
))
3003 UnionExp ue2
= void;
3004 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
3005 if (exceptionOrCant(e2
))
3007 result
= pointerArithmetic(pue
, e
.loc
, e
.op
, e
.type
, e1
, e2
);
3010 if (e
.e2
.type
.ty
== Tpointer
&& e
.e1
.type
.isintegral() && e
.op
== EXP
.add)
3012 UnionExp ue1
= void;
3013 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
3014 if (exceptionOrCant(e1
))
3016 UnionExp ue2
= void;
3017 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
3018 if (exceptionOrCant(e2
))
3020 result
= pointerArithmetic(pue
, e
.loc
, e
.op
, e
.type
, e2
, e1
);
3023 if (e
.e1
.type
.ty
== Tpointer || e
.e2
.type
.ty
== Tpointer
)
3025 error(e
.loc
, "pointer expression `%s` cannot be interpreted at compile time", e
.toChars());
3026 result
= CTFEExp
.cantexp
;
3030 bool evalOperand(UnionExp
* pue
, Expression ex
, out Expression er
)
3032 er
= interpret(pue
, ex
, istate
);
3033 if (exceptionOrCant(er
))
3038 UnionExp ue1
= void;
3040 if (!evalOperand(&ue1
, e
.e1
, e1
))
3043 UnionExp ue2
= void;
3045 if (!evalOperand(&ue2
, e
.e2
, e2
))
3048 if (e
.op
== EXP
.rightShift || e
.op
== EXP
.leftShift || e
.op
== EXP
.unsignedRightShift
)
3050 const sinteger_t i2
= e2
.toInteger();
3051 const uinteger_t sz
= e1
.type
.size() * 8;
3052 if (i2
< 0 || i2
>= sz
)
3054 error(e
.loc
, "shift by %lld is outside the range 0..%llu", i2
, cast(ulong)sz
- 1);
3055 result
= CTFEExp
.cantexp
;
3060 /******************************************
3061 * Perform the operation fp on operands e1 and e2.
3063 UnionExp
evaluate(Loc loc
, Type type
, Expression e1
, Expression e2
)
3066 auto ae1
= e1
.isArrayLiteralExp();
3067 auto ae2
= e2
.isArrayLiteralExp();
3075 if (ae1
&& e2
.implicitConvTo(e1
.type
.toBasetype().nextOf())) // case 3
3077 else if (ae2
&& e1
.implicitConvTo(e2
.type
.toBasetype().nextOf())) // case 2
3081 auto aex
= ae1 ? ae1
: ae2
;
3084 emplaceExp
!ArrayLiteralExp(&ue
, loc
, type
, cast(Expressions
*) null);
3087 const length
= aex
.elements
.length
;
3088 Expressions
* elements
= new Expressions(length
);
3090 emplaceExp
!ArrayLiteralExp(&ue
, loc
, type
, elements
);
3091 foreach (i
; 0 .. length
)
3093 Expression e1x
= ae1 ? ae1
[i
] : e1
;
3094 Expression e2x
= ae2 ? ae2
[i
] : e2
;
3095 UnionExp uex
= evaluate(loc
, e1x
.type
, e1x
, e2x
);
3096 // This can be made more efficient by making use of ue.basis
3097 (*elements
)[i
] = uex
.copy();
3102 if (e1
.isConst() != 1)
3104 // The following should really be an assert()
3105 error(e1
.loc
, "CTFE internal error: non-constant value `%s`", e1
.toChars());
3106 emplaceExp
!CTFEExp(&ue
, EXP
.cantExpression
);
3109 if (e2
.isConst() != 1)
3111 error(e2
.loc
, "CTFE internal error: non-constant value `%s`", e2
.toChars());
3112 emplaceExp
!CTFEExp(&ue
, EXP
.cantExpression
);
3116 return (*fp
)(loc
, type
, e1
, e2
);
3119 *pue
= evaluate(e
.loc
, e
.type
, e1
, e2
);
3120 result
= (*pue
).exp();
3121 if (CTFEExp
.isCantExp(result
))
3122 error(e
.loc
, "`%s` cannot be interpreted at compile time", e
.toChars());
3125 extern (D
) private void interpretCompareCommon(BinExp e
, fp2_t fp
)
3129 printf("%s BinExp::interpretCompareCommon() %s\n", e
.loc
.toChars(), e
.toChars());
3131 UnionExp ue1
= void;
3132 UnionExp ue2
= void;
3133 if (e
.e1
.type
.ty
== Tpointer
&& e
.e2
.type
.ty
== Tpointer
)
3135 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
3136 if (exceptionOrCant(e1
))
3138 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
3139 if (exceptionOrCant(e2
))
3141 //printf("e1 = %s %s, e2 = %s %s\n", e1.type.toChars(), e1.toChars(), e2.type.toChars(), e2.toChars());
3142 dinteger_t ofs1
, ofs2
;
3143 Expression agg1
= getAggregateFromPointer(e1
, &ofs1
);
3144 Expression agg2
= getAggregateFromPointer(e2
, &ofs2
);
3145 //printf("agg1 = %p %s, agg2 = %p %s\n", agg1, agg1.toChars(), agg2, agg2.toChars());
3146 const cmp = comparePointers(e
.op
, agg1
, ofs1
, agg2
, ofs2
);
3149 char dir
= (e
.op
== EXP
.greaterThan || e
.op
== EXP
.greaterOrEqual
) ?
'<' : '>';
3150 error(e
.loc
, "the ordering of pointers to unrelated memory blocks is indeterminate in CTFE. To check if they point to the same memory block, use both `>` and `<` inside `&&` or `||`, eg `%s && %s %c= %s + 1`", e
.toChars(), e
.e1
.toChars(), dir
, e
.e2
.toChars());
3151 result
= CTFEExp
.cantexp
;
3154 if (e
.type
.equals(Type
.tbool
))
3155 result
= IntegerExp
.createBool(cmp != 0);
3158 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, cmp, e
.type
);
3159 result
= (*pue
).exp();
3163 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
3164 if (exceptionOrCant(e1
))
3166 if (!isCtfeComparable(e1
))
3168 error(e
.loc
, "cannot compare `%s` at compile time", e1
.toChars());
3169 result
= CTFEExp
.cantexp
;
3172 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
3173 if (exceptionOrCant(e2
))
3175 if (!isCtfeComparable(e2
))
3177 error(e
.loc
, "cannot compare `%s` at compile time", e2
.toChars());
3178 result
= CTFEExp
.cantexp
;
3181 const cmp = (*fp
)(e
.loc
, e
.op
, e1
, e2
);
3182 if (e
.type
.equals(Type
.tbool
))
3183 result
= IntegerExp
.createBool(cmp);
3186 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, cmp, e
.type
);
3187 result
= (*pue
).exp();
3191 override void visit(BinExp e
)
3196 interpretCommon(e
, &Add
);
3200 interpretCommon(e
, &Min
);
3204 interpretCommon(e
, &Mul
);
3208 interpretCommon(e
, &Div
);
3212 interpretCommon(e
, &Mod
);
3216 interpretCommon(e
, &Shl
);
3219 case EXP
.rightShift
:
3220 interpretCommon(e
, &Shr
);
3223 case EXP
.unsignedRightShift
:
3224 interpretCommon(e
, &Ushr
);
3228 interpretCommon(e
, &And
);
3232 interpretCommon(e
, &Or
);
3236 interpretCommon(e
, &Xor
);
3240 interpretCommon(e
, &Pow
);
3245 interpretCompareCommon(e
, &ctfeEqual
);
3249 case EXP
.notIdentity
:
3250 interpretCompareCommon(e
, &ctfeIdentity
);
3254 case EXP
.lessOrEqual
:
3255 case EXP
.greaterThan
:
3256 case EXP
.greaterOrEqual
:
3257 interpretCompareCommon(e
, &ctfeCmp
);
3261 printf("be = '%s' %s at [%s]\n", EXPtoString(e
.op
).ptr
, e
.toChars(), e
.loc
.toChars());
3266 /* Helper functions for BinExp::interpretAssignCommon
3268 // Returns the variable which is eventually modified, or NULL if an rvalue.
3269 // thisval is the current value of 'this'.
3270 static VarDeclaration
findParentVar(Expression e
) @safe
3274 if (auto ve
= e
.isVarExp())
3276 VarDeclaration v
= ve
.var
.isVarDeclaration();
3280 if (auto ie
= e
.isIndexExp())
3282 else if (auto dve
= e
.isDotVarExp())
3284 else if (auto dtie
= e
.isDotTemplateInstanceExp())
3286 else if (auto se
= e
.isSliceExp())
3293 extern (D
) private void interpretAssignCommon(BinExp e
, fp_t fp
, int post
= 0)
3297 printf("%s BinExp::interpretAssignCommon() %s\n", e
.loc
.toChars(), e
.toChars());
3299 result
= CTFEExp
.cantexp
;
3301 Expression e1
= e
.e1
;
3304 error(e
.loc
, "value of `%s` is not known at compile time", e1
.toChars());
3308 ++ctfeGlobals
.numAssignments
;
3310 /* Before we begin, we need to know if this is a reference assignment
3311 * (dynamic array, AA, or class) or a value assignment.
3312 * Determining this for slice assignments are tricky: we need to know
3313 * if it is a block assignment (a[] = e) rather than a direct slice
3314 * assignment (a[] = b[]). Note that initializers of multi-dimensional
3315 * static arrays can have 2D block assignments (eg, int[7][7] x = 6;).
3316 * So we need to recurse to determine if it is a block assignment.
3318 bool isBlockAssignment
= false;
3319 if (e1
.op
== EXP
.slice
)
3321 // a[] = e can have const e. So we compare the naked types.
3322 Type tdst
= e1
.type
.toBasetype();
3323 Type tsrc
= e
.e2
.type
.toBasetype();
3324 while (tdst
.ty
== Tsarray || tdst
.ty
== Tarray
)
3326 tdst
= (cast(TypeArray
)tdst
).next
.toBasetype();
3327 if (tsrc
.equivalent(tdst
))
3329 isBlockAssignment
= true;
3335 // ---------------------------------------
3336 // Deal with reference assignment
3337 // ---------------------------------------
3338 // If it is a construction of a ref variable, it is a ref assignment
3339 if ((e
.op
== EXP
.construct || e
.op
== EXP
.blit
) &&
3340 ((cast(AssignExp
)e
).memset
== MemorySet
.referenceInit
))
3344 Expression newval
= interpretRegion(e
.e2
, istate
, CTFEGoal
.LValue
);
3345 if (exceptionOrCant(newval
))
3348 VarDeclaration v
= e1
.isVarExp().var
.isVarDeclaration();
3349 setValue(v
, newval
);
3351 // Get the value to return. Note that 'newval' is an Lvalue,
3352 // so if we need an Rvalue, we have to interpret again.
3353 if (goal
== CTFEGoal
.RValue
)
3354 result
= interpretRegion(newval
, istate
);
3356 result
= e1
; // VarExp is a CTFE reference
3362 while (e1
.op
== EXP
.cast_
)
3364 CastExp ce
= e1
.isCastExp();
3369 // ---------------------------------------
3370 // Interpret left hand side
3371 // ---------------------------------------
3372 AssocArrayLiteralExp existingAA
= null;
3373 Expression lastIndex
= null;
3374 Expression oldval
= null;
3375 if (e1
.op
== EXP
.index
&& e1
.isIndexExp().e1
.type
.toBasetype().ty
== Taarray
)
3377 // ---------------------------------------
3378 // Deal with AA index assignment
3379 // ---------------------------------------
3380 /* This needs special treatment if the AA doesn't exist yet.
3381 * There are two special cases:
3382 * (1) If the AA is itself an index of another AA, we may need to create
3383 * multiple nested AA literals before we can insert the new value.
3384 * (2) If the ultimate AA is null, no insertion happens at all. Instead,
3385 * we create nested AA literals, and change it into a assignment.
3387 IndexExp ie
= e1
.isIndexExp();
3388 int depth
= 0; // how many nested AA indices are there?
3389 while (ie
.e1
.op
== EXP
.index
&& ie
.e1
.isIndexExp().e1
.type
.toBasetype().ty
== Taarray
)
3391 assert(ie
.modifiable
);
3392 ie
= ie
.e1
.isIndexExp();
3396 // Get the AA value to be modified.
3397 Expression aggregate
= interpretRegion(ie
.e1
, istate
);
3398 if (exceptionOrCant(aggregate
))
3400 if ((existingAA
= aggregate
.isAssocArrayLiteralExp()) !is null)
3402 // Normal case, ultimate parent AA already exists
3403 // We need to walk from the deepest index up, checking that an AA literal
3404 // already exists on each level.
3405 lastIndex
= interpretRegion(e1
.isIndexExp().e2
, istate
);
3406 lastIndex
= resolveSlice(lastIndex
); // only happens with AA assignment
3407 if (exceptionOrCant(lastIndex
))
3412 // Walk the syntax tree to find the indexExp at this depth
3413 IndexExp xe
= e1
.isIndexExp();
3414 foreach (d
; 0 .. depth
)
3415 xe
= xe
.e1
.isIndexExp();
3417 Expression ekey
= interpretRegion(xe
.e2
, istate
);
3418 if (exceptionOrCant(ekey
))
3420 UnionExp ekeyTmp
= void;
3421 ekey
= resolveSlice(ekey
, &ekeyTmp
); // only happens with AA assignment
3423 // Look up this index in it up in the existing AA, to get the next level of AA.
3424 AssocArrayLiteralExp newAA
= cast(AssocArrayLiteralExp
)findKeyInAA(e
.loc
, existingAA
, ekey
);
3425 if (exceptionOrCant(newAA
))
3429 // Doesn't exist yet, create an empty AA...
3430 auto keysx
= new Expressions();
3431 auto valuesx
= new Expressions();
3432 newAA
= ctfeEmplaceExp
!AssocArrayLiteralExp(e
.loc
, keysx
, valuesx
);
3433 newAA
.type
= xe
.type
;
3434 newAA
.ownedByCtfe
= OwnedBy
.ctfe
;
3435 //... and insert it into the existing AA.
3436 existingAA
.keys
.push(ekey
);
3437 existingAA
.values
.push(newAA
);
3445 oldval
= findKeyInAA(e
.loc
, existingAA
, lastIndex
);
3447 oldval
= copyLiteral(e
.e1
.type
.defaultInitLiteral(e
.loc
)).copy();
3452 /* The AA is currently null. 'aggregate' is actually a reference to
3453 * whatever contains it. It could be anything: var, dotvarexp, ...
3454 * We rewrite the assignment from:
3455 * aa[i][j] op= newval;
3457 * aa = [i:[j:T.init]];
3460 oldval
= copyLiteral(e
.e1
.type
.defaultInitLiteral(e
.loc
)).copy();
3462 Expression newaae
= oldval
;
3463 while (e1
.op
== EXP
.index
&& e1
.isIndexExp().e1
.type
.toBasetype().ty
== Taarray
)
3465 Expression ekey
= interpretRegion(e1
.isIndexExp().e2
, istate
);
3466 if (exceptionOrCant(ekey
))
3468 ekey
= resolveSlice(ekey
); // only happens with AA assignment
3470 auto keysx
= new Expressions();
3471 auto valuesx
= new Expressions();
3473 valuesx
.push(newaae
);
3475 auto aae
= ctfeEmplaceExp
!AssocArrayLiteralExp(e
.loc
, keysx
, valuesx
);
3476 aae
.type
= e1
.isIndexExp().e1
.type
;
3477 aae
.ownedByCtfe
= OwnedBy
.ctfe
;
3484 e1
= e1
.isIndexExp().e1
;
3487 // We must set to aggregate with newaae
3488 e1
= interpretRegion(e1
, istate
, CTFEGoal
.LValue
);
3489 if (exceptionOrCant(e1
))
3491 e1
= assignToLvalue(e
, e1
, newaae
);
3492 if (exceptionOrCant(e1
))
3495 assert(existingAA
&& lastIndex
);
3498 else if (e1
.op
== EXP
.arrayLength
)
3500 oldval
= interpretRegion(e1
, istate
);
3501 if (exceptionOrCant(oldval
))
3504 else if (e
.op
== EXP
.construct || e
.op
== EXP
.blit
)
3506 // Unless we have a simple var assignment, we're
3507 // only modifying part of the variable. So we need to make sure
3508 // that the parent variable exists.
3509 VarDeclaration ultimateVar
= findParentVar(e1
);
3510 if (auto ve
= e1
.isVarExp())
3512 VarDeclaration v
= ve
.var
.isVarDeclaration();
3514 if (v
.storage_class
& STC
.out_
)
3517 else if (ultimateVar
&& !getValue(ultimateVar
))
3519 Expression ex
= interpretRegion(ultimateVar
.type
.defaultInitLiteral(e
.loc
), istate
);
3520 if (exceptionOrCant(ex
))
3522 setValue(ultimateVar
, ex
);
3530 e1
= interpretRegion(e1
, istate
, CTFEGoal
.LValue
);
3531 if (exceptionOrCant(e1
))
3534 if (e1
.op
== EXP
.index
&& e1
.isIndexExp().e1
.type
.toBasetype().ty
== Taarray
)
3536 IndexExp ie
= e1
.isIndexExp();
3537 assert(ie
.e1
.op
== EXP
.assocArrayLiteral
);
3538 existingAA
= ie
.e1
.isAssocArrayLiteralExp();
3543 // ---------------------------------------
3544 // Interpret right hand side
3545 // ---------------------------------------
3546 Expression newval
= interpretRegion(e
.e2
, istate
);
3547 if (exceptionOrCant(newval
))
3549 if (e
.op
== EXP
.blit
&& newval
.op
== EXP
.int64
)
3551 Type tbn
= e
.type
.baseElemOf();
3552 if (tbn
.ty
== Tstruct
)
3554 /* Look for special case of struct being initialized with 0.
3556 newval
= e
.type
.defaultInitLiteral(e
.loc
);
3557 if (newval
.op
== EXP
.error
)
3559 result
= CTFEExp
.cantexp
;
3562 newval
= interpretRegion(newval
, istate
); // copy and set ownedByCtfe flag
3563 if (exceptionOrCant(newval
))
3568 // ----------------------------------------------------
3569 // Deal with read-modify-write assignments.
3570 // Set 'newval' to the final assignment value
3571 // Also determine the return value (except for slice
3572 // assignments, which are more complicated)
3573 // ----------------------------------------------------
3578 // Load the left hand side after interpreting the right hand side.
3579 oldval
= interpretRegion(e1
, istate
);
3580 if (exceptionOrCant(oldval
))
3584 if (e
.e1
.type
.ty
!= Tpointer
)
3586 // ~= can create new values (see bug 6052)
3587 if (e
.op
== EXP
.concatenateAssign || e
.op
== EXP
.concatenateElemAssign || e
.op
== EXP
.concatenateDcharAssign
)
3589 // We need to dup it and repaint the type. For a dynamic array
3590 // we can skip duplication, because it gets copied later anyway.
3591 if (newval
.type
.ty
!= Tarray
)
3593 newval
= copyLiteral(newval
).copy();
3594 newval
.type
= e
.e2
.type
; // repaint type
3598 newval
= paintTypeOntoLiteral(e
.e2
.type
, newval
);
3599 newval
= resolveSlice(newval
);
3602 oldval
= resolveSlice(oldval
);
3604 newval
= (*fp
)(e
.loc
, e
.type
, oldval
, newval
).copy();
3606 else if (e
.e2
.type
.isintegral() &&
3607 (e
.op
== EXP
.addAssign ||
3608 e
.op
== EXP
.minAssign ||
3609 e
.op
== EXP
.plusPlus ||
3610 e
.op
== EXP
.minusMinus
))
3612 newval
= pointerArithmetic(pue
, e
.loc
, e
.op
, e
.type
, oldval
, newval
).copy();
3613 if (newval
== pue
.exp())
3614 newval
= pue
.copy();
3618 error(e
.loc
, "pointer expression `%s` cannot be interpreted at compile time", e
.toChars());
3619 result
= CTFEExp
.cantexp
;
3622 if (exceptionOrCant(newval
))
3624 if (CTFEExp
.isCantExp(newval
))
3625 error(e
.loc
, "cannot interpret `%s` at compile time", e
.toChars());
3632 if (existingAA
.ownedByCtfe
!= OwnedBy
.ctfe
)
3634 error(e
.loc
, "cannot modify read-only constant `%s`", existingAA
.toChars());
3635 result
= CTFEExp
.cantexp
;
3639 //printf("\t+L%d existingAA = %s, lastIndex = %s, oldval = %s, newval = %s\n",
3640 // __LINE__, existingAA.toChars(), lastIndex.toChars(), oldval ? oldval.toChars() : NULL, newval.toChars());
3641 assignAssocArrayElement(e
.loc
, existingAA
, lastIndex
, newval
);
3643 // Determine the return value
3644 result
= ctfeCast(pue
, e
.loc
, e
.type
, e
.type
, fp
&& post ? oldval
: newval
);
3647 if (e1
.op
== EXP
.arrayLength
)
3649 /* Change the assignment from:
3652 * arr = new_length_array; (result is n)
3655 // Determine the return value
3656 result
= ctfeCast(pue
, e
.loc
, e
.type
, e
.type
, fp
&& post ? oldval
: newval
);
3657 if (exceptionOrCant(result
))
3660 if (result
== pue
.exp())
3661 result
= pue
.copy();
3663 size_t oldlen
= cast(size_t
)oldval
.toInteger();
3664 size_t newlen
= cast(size_t
)newval
.toInteger();
3665 if (oldlen
== newlen
) // no change required -- we're done!
3668 // We have changed it into a reference assignment
3669 // Note that returnValue is still the new length.
3670 e1
= e1
.isArrayLengthExp().e1
;
3671 Type t
= e1
.type
.toBasetype();
3674 error(e
.loc
, "`%s` is not yet supported at compile time", e
.toChars());
3675 result
= CTFEExp
.cantexp
;
3678 e1
= interpretRegion(e1
, istate
, CTFEGoal
.LValue
);
3679 if (exceptionOrCant(e1
))
3682 if (oldlen
!= 0) // Get the old array literal.
3683 oldval
= interpretRegion(e1
, istate
);
3684 UnionExp utmp
= void;
3685 oldval
= resolveSlice(oldval
, &utmp
);
3687 newval
= changeArrayLiteralLength(pue
, e
.loc
, cast(TypeArray
)t
, oldval
, oldlen
, newlen
);
3688 if (newval
== pue
.exp())
3689 newval
= pue
.copy();
3691 e1
= assignToLvalue(e
, e1
, newval
);
3692 if (exceptionOrCant(e1
))
3698 if (!isBlockAssignment
)
3700 newval
= ctfeCast(pue
, e
.loc
, e
.type
, e
.type
, newval
);
3701 if (exceptionOrCant(newval
))
3703 if (newval
== pue
.exp())
3704 newval
= pue
.copy();
3706 // Determine the return value
3707 if (goal
== CTFEGoal
.LValue
) // https://issues.dlang.org/show_bug.cgi?id=14371
3711 result
= ctfeCast(pue
, e
.loc
, e
.type
, e
.type
, fp
&& post ? oldval
: newval
);
3712 if (result
== pue
.exp())
3713 result
= pue
.copy();
3715 if (exceptionOrCant(result
))
3718 if (exceptionOrCant(newval
))
3723 printf("ASSIGN: %s=%s\n", e1
.toChars(), newval
.toChars());
3724 showCtfeExpr(newval
);
3727 /* Block assignment or element-wise assignment.
3729 if (e1
.op
== EXP
.slice ||
3730 e1
.op
== EXP
.vector ||
3731 e1
.op
== EXP
.arrayLiteral ||
3732 e1
.op
== EXP
.string_ ||
3733 e1
.op
== EXP
.null_
&& e1
.type
.toBasetype().ty
== Tarray
)
3735 // Note that slice assignments don't support things like ++, so
3736 // we don't need to remember 'returnValue'.
3737 result
= interpretAssignToSlice(pue
, e
, e1
, newval
, isBlockAssignment
);
3738 if (exceptionOrCant(result
))
3740 if (auto se
= e
.e1
.isSliceExp())
3742 Expression e1x
= interpretRegion(se
.e1
, istate
, CTFEGoal
.LValue
);
3743 if (auto dve
= e1x
.isDotVarExp())
3746 auto sle
= ex
.op
== EXP
.structLiteral ? ex
.isStructLiteralExp()
3747 : ex
.op
== EXP
.classReference ? ex
.isClassReferenceExp().value
3749 auto v
= dve
.var
.isVarDeclaration();
3752 error(e
.loc
, "CTFE internal error: dotvar slice assignment");
3753 result
= CTFEExp
.cantexp
;
3756 stompOverlappedFields(sle
, v
);
3763 /* Assignment to a CTFE reference.
3765 if (Expression ex
= assignToLvalue(e
, e1
, newval
))
3771 /* Set all sibling fields which overlap with v to VoidExp.
3773 private void stompOverlappedFields(StructLiteralExp sle
, VarDeclaration v
)
3777 foreach (size_t i
, v2
; sle
.sd
.fields
)
3779 if (v
is v2 ||
!v
.isOverlappedWith(v2
))
3781 auto e
= (*sle
.elements
)[i
];
3782 if (e
.op
!= EXP
.void_
)
3783 (*sle
.elements
)[i
] = voidInitLiteral(e
.type
, v
).copy();
3787 private Expression
assignToLvalue(BinExp e
, Expression e1
, Expression newval
)
3789 //printf("assignToLvalue() e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
3790 VarDeclaration vd
= null;
3791 Expression
* payload
= null; // dead-store to prevent spurious warning
3794 if (auto ve
= e1
.isVarExp())
3796 vd
= ve
.var
.isVarDeclaration();
3797 oldval
= getValue(vd
);
3799 else if (auto dve
= e1
.isDotVarExp())
3801 /* Assignment to member variable of the form:
3805 auto sle
= ex
.op
== EXP
.structLiteral ? ex
.isStructLiteralExp()
3806 : ex
.op
== EXP
.classReference ? ex
.isClassReferenceExp().value
3808 auto v
= e1
.isDotVarExp().var
.isVarDeclaration();
3811 error(e
.loc
, "CTFE internal error: dotvar assignment");
3812 return CTFEExp
.cantexp
;
3814 if (sle
.ownedByCtfe
!= OwnedBy
.ctfe
)
3816 error(e
.loc
, "cannot modify read-only constant `%s`", sle
.toChars());
3817 return CTFEExp
.cantexp
;
3820 int fieldi
= ex
.op
== EXP
.structLiteral ?
findFieldIndexByName(sle
.sd
, v
)
3821 : ex
.isClassReferenceExp().findFieldIndexByName(v
);
3824 error(e
.loc
, "CTFE internal error: cannot find field `%s` in `%s`", v
.toChars(), ex
.toChars());
3825 return CTFEExp
.cantexp
;
3827 assert(0 <= fieldi
&& fieldi
< sle
.elements
.length
);
3829 // If it's a union, set all other members of this union to void
3830 stompOverlappedFields(sle
, v
);
3832 payload
= &(*sle
.elements
)[fieldi
];
3834 if (auto ival
= newval
.isIntegerExp())
3836 if (auto bf
= v
.isBitFieldDeclaration())
3838 sinteger_t value
= ival
.toInteger();
3839 if (bf
.type
.isunsigned())
3840 value
&= (1L << bf
.fieldWidth
) - 1; // zero extra bits
3842 { // sign extend extra bits
3843 value
= value
<< (64 - bf
.fieldWidth
);
3844 value
= value
>> (64 - bf
.fieldWidth
);
3846 ival
.setInteger(value
);
3850 else if (auto ie
= e1
.isIndexExp())
3852 assert(ie
.e1
.type
.toBasetype().ty
!= Taarray
);
3854 Expression aggregate
;
3855 uinteger_t indexToModify
;
3856 if (!resolveIndexing(ie
, istate
, &aggregate
, &indexToModify
, true))
3858 return CTFEExp
.cantexp
;
3860 size_t index
= cast(size_t
)indexToModify
;
3862 if (auto existingSE
= aggregate
.isStringExp())
3864 if (existingSE
.ownedByCtfe
!= OwnedBy
.ctfe
)
3866 error(e
.loc
, "cannot modify read-only string literal `%s`", ie
.e1
.toChars());
3867 return CTFEExp
.cantexp
;
3869 existingSE
.setCodeUnit(index
, cast(dchar)newval
.toInteger());
3872 if (aggregate
.op
!= EXP
.arrayLiteral
)
3874 error(e
.loc
, "index assignment `%s` is not yet supported in CTFE ", e
.toChars());
3875 return CTFEExp
.cantexp
;
3878 ArrayLiteralExp existingAE
= aggregate
.isArrayLiteralExp();
3879 if (existingAE
.ownedByCtfe
!= OwnedBy
.ctfe
)
3881 error(e
.loc
, "cannot modify read-only constant `%s`", existingAE
.toChars());
3882 return CTFEExp
.cantexp
;
3885 payload
= &(*existingAE
.elements
)[index
];
3890 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
3891 return CTFEExp
.cantexp
;
3894 Type t1b
= e1
.type
.toBasetype();
3895 bool wantCopy
= t1b
.baseElemOf().ty
== Tstruct
;
3897 if (auto ve
= newval
.isVectorExp())
3899 // Ensure ve is an array literal, and not a broadcast
3900 if (ve
.e1
.op
== EXP
.int64 || ve
.e1
.op
== EXP
.float64
) // if broadcast
3903 Expression ex
= interpretVectorToArray(&ue
, ve
);
3904 ve
.e1
= (ex
== ue
.exp()) ? ue
.copy() : ex
;
3908 if (newval
.op
== EXP
.structLiteral
&& oldval
)
3910 assert(oldval
.op
== EXP
.structLiteral || oldval
.op
== EXP
.arrayLiteral || oldval
.op
== EXP
.string_
);
3911 newval
= copyLiteral(newval
).copy();
3912 assignInPlace(oldval
, newval
);
3914 else if (wantCopy
&& (e
.op
== EXP
.assign || e
.op
== EXP
.loweredAssignExp
))
3916 // Currently postblit/destructor calls on static array are done
3917 // in the druntime internal functions so they don't appear in AST.
3918 // Therefore interpreter should handle them specially.
3921 version (all
) // todo: instead we can directly access to each elements of the slice
3923 newval
= resolveSlice(newval
);
3924 if (CTFEExp
.isCantExp(newval
))
3926 error(e
.loc
, "CTFE internal error: assignment `%s`", e
.toChars());
3927 return CTFEExp
.cantexp
;
3930 assert(oldval
.op
== EXP
.arrayLiteral
);
3931 assert(newval
.op
== EXP
.arrayLiteral
);
3933 Expressions
* oldelems
= oldval
.isArrayLiteralExp().elements
;
3934 Expressions
* newelems
= newval
.isArrayLiteralExp().elements
;
3935 assert(oldelems
.length
== newelems
.length
);
3937 Type elemtype
= oldval
.type
.nextOf();
3938 foreach (i
, ref oldelem
; *oldelems
)
3940 Expression newelem
= paintTypeOntoLiteral(elemtype
, (*newelems
)[i
]);
3941 // https://issues.dlang.org/show_bug.cgi?id=9245
3942 if (e
.e2
.isLvalue())
3944 if (Expression ex
= evaluatePostblit(istate
, newelem
))
3947 // https://issues.dlang.org/show_bug.cgi?id=13661
3948 if (Expression ex
= evaluateDtor(istate
, oldelem
))
3955 // e1 has its own payload, so we have to create a new literal.
3957 newval
= copyLiteral(newval
).copy();
3959 if (t1b
.ty
== Tsarray
&& e
.op
== EXP
.construct
&& e
.e2
.isLvalue())
3961 // https://issues.dlang.org/show_bug.cgi?id=9245
3962 if (Expression ex
= evaluatePostblit(istate
, newval
))
3970 setValue(vd
, oldval
);
3974 // Blit assignment should return the newly created value.
3975 if (e
.op
== EXP
.blit
)
3982 * Deal with assignments of the form:
3984 * dest[low..upp] = newval
3985 * where newval has already been interpreted
3987 * This could be a slice assignment or a block assignment, and
3988 * dest could be either an array literal, or a string.
3990 * Returns EXP.cantExpression on failure. If there are no errors,
3991 * it returns aggregate[low..upp], except that as an optimisation,
3992 * if goal == CTFEGoal.Nothing, it will return NULL
3994 private Expression
interpretAssignToSlice(UnionExp
* pue
, BinExp e
, Expression e1
, Expression newval
, bool isBlockAssignment
)
3996 //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars());
3998 dinteger_t lowerbound
;
3999 dinteger_t upperbound
;
4000 dinteger_t firstIndex
;
4002 Expression aggregate
;
4004 if (auto se
= e1
.isSliceExp())
4006 // ------------------------------
4007 // aggregate[] = newval
4008 // aggregate[low..upp] = newval
4009 // ------------------------------
4010 aggregate
= interpretRegion(se
.e1
, istate
);
4011 lowerbound
= se
.lwr ? se
.lwr
.toInteger() : 0;
4012 upperbound
= se
.upr ? se
.upr
.toInteger() : resolveArrayLength(aggregate
);
4014 // Slice of a slice --> change the bounds
4015 if (auto oldse
= aggregate
.isSliceExp())
4017 aggregate
= oldse
.e1
;
4018 firstIndex
= lowerbound
+ oldse
.lwr
.toInteger();
4021 firstIndex
= lowerbound
;
4025 if (auto ale
= e1
.isArrayLiteralExp())
4028 upperbound
= ale
.elements
.length
;
4030 else if (auto se
= e1
.isStringExp())
4033 upperbound
= se
.len
;
4035 else if (e1
.op
== EXP
.null_
)
4040 else if (VectorExp ve
= e1
.isVectorExp())
4042 // ve is not handled but a proper error message is returned
4043 // this is to prevent https://issues.dlang.org/show_bug.cgi?id=20042
4045 upperbound
= ve
.dim
;
4051 firstIndex
= lowerbound
;
4053 if (upperbound
== lowerbound
)
4056 // For slice assignment, we check that the lengths match.
4057 if (!isBlockAssignment
&& e1
.type
.ty
!= Tpointer
)
4059 const srclen
= resolveArrayLength(newval
);
4060 if (srclen
!= (upperbound
- lowerbound
))
4062 error(e
.loc
, "array length mismatch assigning `[0..%llu]` to `[%llu..%llu]`",
4063 ulong(srclen
), ulong(lowerbound
), ulong(upperbound
));
4064 return CTFEExp
.cantexp
;
4068 if (auto existingSE
= aggregate
.isStringExp())
4070 if (existingSE
.ownedByCtfe
!= OwnedBy
.ctfe
)
4072 error(e
.loc
, "cannot modify read-only string literal `%s`", existingSE
.toChars());
4073 return CTFEExp
.cantexp
;
4076 if (auto se
= newval
.isSliceExp())
4079 const srclower
= se
.lwr
.toInteger();
4080 const srcupper
= se
.upr
.toInteger();
4082 if (aggregate
== aggr2
&&
4083 lowerbound
< srcupper
&& srclower
< upperbound
)
4085 error(e
.loc
, "overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`",
4086 ulong(lowerbound
), ulong(upperbound
), ulong(srclower
), ulong(srcupper
));
4087 return CTFEExp
.cantexp
;
4089 version (all
) // todo: instead we can directly access to each elements of the slice
4091 Expression orignewval
= newval
;
4092 newval
= resolveSlice(newval
);
4093 if (CTFEExp
.isCantExp(newval
))
4095 error(e
.loc
, "CTFE internal error: slice `%s`", orignewval
.toChars());
4096 return CTFEExp
.cantexp
;
4099 assert(newval
.op
!= EXP
.slice
);
4101 if (auto se
= newval
.isStringExp())
4103 sliceAssignStringFromString(existingSE
, se
, cast(size_t
)firstIndex
);
4106 if (auto ale
= newval
.isArrayLiteralExp())
4108 /* Mixed slice: it was initialized as a string literal.
4109 * Now a slice of it is being set with an array literal.
4111 sliceAssignStringFromArrayLiteral(existingSE
, ale
, cast(size_t
)firstIndex
);
4115 // String literal block slice assign
4116 const value
= cast(dchar)newval
.toInteger();
4117 foreach (i
; 0 .. upperbound
- lowerbound
)
4119 existingSE
.setCodeUnit(cast(size_t
)(i
+ firstIndex
), value
);
4121 if (goal
== CTFEGoal
.Nothing
)
4122 return null; // avoid creating an unused literal
4123 auto retslice
= ctfeEmplaceExp
!SliceExp(e
.loc
, existingSE
,
4124 ctfeEmplaceExp
!IntegerExp(e
.loc
, firstIndex
, Type
.tsize_t
),
4125 ctfeEmplaceExp
!IntegerExp(e
.loc
, firstIndex
+ upperbound
- lowerbound
, Type
.tsize_t
));
4126 retslice
.type
= e
.type
;
4127 return interpret(pue
, retslice
, istate
);
4129 if (auto existingAE
= aggregate
.isArrayLiteralExp())
4131 if (existingAE
.ownedByCtfe
!= OwnedBy
.ctfe
)
4133 error(e
.loc
, "cannot modify read-only constant `%s`", existingAE
.toChars());
4134 return CTFEExp
.cantexp
;
4137 if (newval
.op
== EXP
.slice
&& !isBlockAssignment
)
4139 auto se
= newval
.isSliceExp();
4141 const srclower
= se
.lwr
.toInteger();
4142 const srcupper
= se
.upr
.toInteger();
4143 const wantCopy
= (newval
.type
.toBasetype().nextOf().baseElemOf().ty
== Tstruct
);
4145 //printf("oldval = %p %s[%d..%u]\nnewval = %p %s[%llu..%llu] wantCopy = %d\n",
4146 // aggregate, aggregate.toChars(), lowerbound, upperbound,
4147 // aggr2, aggr2.toChars(), srclower, srcupper, wantCopy);
4150 // Currently overlapping for struct array is allowed.
4151 // The order of elements processing depends on the overlapping.
4152 // https://issues.dlang.org/show_bug.cgi?id=14024
4153 assert(aggr2
.op
== EXP
.arrayLiteral
);
4154 Expressions
* oldelems
= existingAE
.elements
;
4155 Expressions
* newelems
= aggr2
.isArrayLiteralExp().elements
;
4157 Type elemtype
= aggregate
.type
.nextOf();
4158 bool needsPostblit
= e
.e2
.isLvalue();
4160 if (aggregate
== aggr2
&& srclower
< lowerbound
&& lowerbound
< srcupper
)
4163 for (auto i
= upperbound
- lowerbound
; 0 < i
--;)
4165 Expression oldelem
= (*oldelems
)[cast(size_t
)(i
+ firstIndex
)];
4166 Expression newelem
= (*newelems
)[cast(size_t
)(i
+ srclower
)];
4167 newelem
= copyLiteral(newelem
).copy();
4168 newelem
.type
= elemtype
;
4171 if (Expression x
= evaluatePostblit(istate
, newelem
))
4174 if (Expression x
= evaluateDtor(istate
, oldelem
))
4176 (*oldelems
)[cast(size_t
)(lowerbound
+ i
)] = newelem
;
4182 for (auto i
= 0; i
< upperbound
- lowerbound
; i
++)
4184 Expression oldelem
= (*oldelems
)[cast(size_t
)(i
+ firstIndex
)];
4185 Expression newelem
= (*newelems
)[cast(size_t
)(i
+ srclower
)];
4186 newelem
= copyLiteral(newelem
).copy();
4187 newelem
.type
= elemtype
;
4190 if (Expression x
= evaluatePostblit(istate
, newelem
))
4193 if (Expression x
= evaluateDtor(istate
, oldelem
))
4195 (*oldelems
)[cast(size_t
)(lowerbound
+ i
)] = newelem
;
4200 return newval
; // oldval?
4202 if (aggregate
== aggr2
&&
4203 lowerbound
< srcupper
&& srclower
< upperbound
)
4205 error(e
.loc
, "overlapping slice assignment `[%llu..%llu] = [%llu..%llu]`",
4206 ulong(lowerbound
), ulong(upperbound
), ulong(srclower
), ulong(srcupper
));
4207 return CTFEExp
.cantexp
;
4209 version (all
) // todo: instead we can directly access to each elements of the slice
4211 Expression orignewval
= newval
;
4212 newval
= resolveSlice(newval
);
4213 if (CTFEExp
.isCantExp(newval
))
4215 error(e
.loc
, "CTFE internal error: slice `%s`", orignewval
.toChars());
4216 return CTFEExp
.cantexp
;
4221 assert(newval
.op
!= EXP
.slice
);
4223 if (newval
.op
== EXP
.string_
&& !isBlockAssignment
)
4225 /* Mixed slice: it was initialized as an array literal of chars/integers.
4226 * Now a slice of it is being set with a string.
4228 sliceAssignArrayLiteralFromString(existingAE
, newval
.isStringExp(), cast(size_t
)firstIndex
);
4231 if (newval
.op
== EXP
.arrayLiteral
&& !isBlockAssignment
)
4233 Expressions
* oldelems
= existingAE
.elements
;
4234 Expressions
* newelems
= newval
.isArrayLiteralExp().elements
;
4235 Type elemtype
= existingAE
.type
.nextOf();
4236 bool needsPostblit
= e
.op
!= EXP
.blit
&& e
.e2
.isLvalue();
4237 foreach (j
, newelem
; *newelems
)
4239 newelem
= paintTypeOntoLiteral(elemtype
, newelem
);
4242 Expression x
= evaluatePostblit(istate
, newelem
);
4243 if (exceptionOrCantInterpret(x
))
4246 (*oldelems
)[cast(size_t
)(j
+ firstIndex
)] = newelem
;
4251 /* Block assignment, initialization of static arrays
4253 * x may be a multidimensional static array. (Note that this
4254 * only happens with array literals, never with strings).
4256 struct RecursiveBlock
4264 Expression
assignTo(ArrayLiteralExp ae
)
4266 return assignTo(ae
, 0, ae
.elements
.length
);
4269 Expression
assignTo(ArrayLiteralExp ae
, size_t lwr
, size_t upr
)
4271 Expressions
* w
= ae
.elements
;
4272 assert(ae
.type
.ty
== Tsarray || ae
.type
.ty
== Tarray || ae
.type
.ty
== Tpointer
);
4273 bool directblk
= (cast(TypeNext
)ae
.type
).next
.equivalent(newval
.type
);
4274 for (size_t k
= lwr
; k
< upr
; k
++)
4276 if (!directblk
&& (*w
)[k
].op
== EXP
.arrayLiteral
)
4278 // Multidimensional array block assign
4279 if (Expression ex
= assignTo((*w
)[k
].isArrayLiteralExp()))
4286 else if (!needsPostblit
&& !needsDtor
)
4288 assignInPlace((*w
)[k
], newval
);
4292 Expression oldelem
= (*w
)[k
];
4293 Expression tmpelem
= needsDtor ?
copyLiteral(oldelem
).copy() : null;
4294 assignInPlace(oldelem
, newval
);
4297 if (Expression ex
= evaluatePostblit(istate
, oldelem
))
4302 // https://issues.dlang.org/show_bug.cgi?id=14860
4303 if (Expression ex
= evaluateDtor(istate
, tmpelem
))
4312 Type tn
= newval
.type
.toBasetype();
4313 bool wantRef
= (tn
.ty
== Tarray ||
isAssocArray(tn
) || tn
.ty
== Tclass
);
4314 bool cow
= newval
.op
!= EXP
.structLiteral
&& newval
.op
!= EXP
.arrayLiteral
&& newval
.op
!= EXP
.string_
;
4315 Type tb
= tn
.baseElemOf();
4316 StructDeclaration sd
= (tb
.ty
== Tstruct ?
(cast(TypeStruct
)tb
).sym
: null);
4321 rb
.refCopy
= wantRef || cow
;
4322 rb
.needsPostblit
= sd
&& sd
.postblit
&& e
.op
!= EXP
.blit
&& e
.e2
.isLvalue();
4323 rb
.needsDtor
= sd
&& sd
.dtor
&& (e
.op
== EXP
.assign || e
.op
== EXP
.loweredAssignExp
);
4324 if (Expression ex
= rb
.assignTo(existingAE
, cast(size_t
)lowerbound
, cast(size_t
)upperbound
))
4327 if (goal
== CTFEGoal
.Nothing
)
4328 return null; // avoid creating an unused literal
4329 auto retslice
= ctfeEmplaceExp
!SliceExp(e
.loc
, existingAE
,
4330 ctfeEmplaceExp
!IntegerExp(e
.loc
, firstIndex
, Type
.tsize_t
),
4331 ctfeEmplaceExp
!IntegerExp(e
.loc
, firstIndex
+ upperbound
- lowerbound
, Type
.tsize_t
));
4332 retslice
.type
= e
.type
;
4333 return interpret(pue
, retslice
, istate
);
4336 error(e
.loc
, "slice operation `%s = %s` cannot be evaluated at compile time", e1
.toChars(), newval
.toChars());
4337 return CTFEExp
.cantexp
;
4340 override void visit(AssignExp e
)
4342 interpretAssignCommon(e
, null);
4345 override void visit(BinAssignExp e
)
4350 interpretAssignCommon(e
, &Add
);
4354 interpretAssignCommon(e
, &Min
);
4357 case EXP
.concatenateAssign
:
4358 case EXP
.concatenateElemAssign
:
4359 case EXP
.concatenateDcharAssign
:
4360 interpretAssignCommon(e
, &ctfeCat
);
4364 interpretAssignCommon(e
, &Mul
);
4368 interpretAssignCommon(e
, &Div
);
4372 interpretAssignCommon(e
, &Mod
);
4375 case EXP
.leftShiftAssign
:
4376 interpretAssignCommon(e
, &Shl
);
4379 case EXP
.rightShiftAssign
:
4380 interpretAssignCommon(e
, &Shr
);
4383 case EXP
.unsignedRightShiftAssign
:
4384 interpretAssignCommon(e
, &Ushr
);
4388 interpretAssignCommon(e
, &And
);
4392 interpretAssignCommon(e
, &Or
);
4396 interpretAssignCommon(e
, &Xor
);
4400 interpretAssignCommon(e
, &Pow
);
4408 override void visit(PostExp e
)
4412 printf("%s PostExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
4414 if (e
.op
== EXP
.plusPlus
)
4415 interpretAssignCommon(e
, &Add
, 1);
4417 interpretAssignCommon(e
, &Min
, 1);
4420 if (CTFEExp
.isCantExp(result
))
4421 printf("PostExp::interpret() CANT\n");
4425 /* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison;
4426 * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison;
4429 static int isPointerCmpExp(Expression e
, Expression
* p1
, Expression
* p2
)
4432 while (e
.op
== EXP
.not)
4435 e
= e
.isNotExp().e1
;
4440 case EXP
.lessOrEqual
:
4442 goto case; /+ fall through +/
4443 case EXP
.greaterThan
:
4444 case EXP
.greaterOrEqual
:
4445 *p1
= e
.isBinExp().e1
;
4446 *p2
= e
.isBinExp().e2
;
4447 if (!(isPointer((*p1
).type
) && isPointer((*p2
).type
)))
4458 /** If this is a four pointer relation, evaluate it, else return NULL.
4460 * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2)
4461 * where p1, p2 are expressions yielding pointers to memory block p,
4462 * and q1, q2 are expressions yielding pointers to memory block q.
4463 * This expression is valid even if p and q are independent memory
4464 * blocks and are therefore not normally comparable; the && form returns true
4465 * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns
4466 * true if [p1..p2] lies outside [q1..q2], and false otherwise.
4468 * Within the expression, any ordering of p1, p2, q1, q2 is permissible;
4469 * the comparison operators can be any of >, <, <=, >=, provided that
4470 * both directions (p > q and p < q) are checked. Additionally the
4471 * relational sub-expressions can be negated, eg
4472 * (!(q1 < p1) && p2 <= q2) is valid.
4474 private void interpretFourPointerRelation(UnionExp
* pue
, BinExp e
)
4476 assert(e
.op
== EXP
.andAnd || e
.op
== EXP
.orOr
);
4478 /* It can only be an isInside expression, if both e1 and e2 are
4479 * directional pointer comparisons.
4480 * Note that this check can be made statically; it does not depends on
4481 * any runtime values. This allows a JIT implementation to compile a
4482 * special AndAndPossiblyInside, keeping the normal AndAnd case efficient.
4485 // Save the pointer expressions and the comparison directions,
4486 // so we can use them later.
4487 Expression p1
= null;
4488 Expression p2
= null;
4489 Expression p3
= null;
4490 Expression p4
= null;
4491 int dir1
= isPointerCmpExp(e
.e1
, &p1
, &p2
);
4492 int dir2
= isPointerCmpExp(e
.e2
, &p3
, &p4
);
4493 if (dir1
== 0 || dir2
== 0)
4499 //printf("FourPointerRelation %s\n", toChars());
4501 UnionExp ue1
= void;
4502 UnionExp ue2
= void;
4503 UnionExp ue3
= void;
4504 UnionExp ue4
= void;
4506 // Evaluate the first two pointers
4507 p1
= interpret(&ue1
, p1
, istate
);
4508 if (exceptionOrCant(p1
))
4510 p2
= interpret(&ue2
, p2
, istate
);
4511 if (exceptionOrCant(p2
))
4513 dinteger_t ofs1
, ofs2
;
4514 Expression agg1
= getAggregateFromPointer(p1
, &ofs1
);
4515 Expression agg2
= getAggregateFromPointer(p2
, &ofs2
);
4517 if (!pointToSameMemoryBlock(agg1
, agg2
) && agg1
.op
!= EXP
.null_
&& agg2
.op
!= EXP
.null_
)
4519 // Here it is either CANT_INTERPRET,
4520 // or an IsInside comparison returning false.
4521 p3
= interpret(&ue3
, p3
, istate
);
4522 if (CTFEExp
.isCantExp(p3
))
4524 // Note that it is NOT legal for it to throw an exception!
4525 Expression except
= null;
4526 if (exceptionOrCantInterpret(p3
))
4530 p4
= interpret(&ue4
, p4
, istate
);
4531 if (CTFEExp
.isCantExp(p4
))
4536 if (exceptionOrCantInterpret(p4
))
4541 error(e
.loc
, "comparison `%s` of pointers to unrelated memory blocks remains indeterminate at compile time because exception `%s` was thrown while evaluating `%s`", e
.e1
.toChars(), except
.toChars(), e
.e2
.toChars());
4542 result
= CTFEExp
.cantexp
;
4545 dinteger_t ofs3
, ofs4
;
4546 Expression agg3
= getAggregateFromPointer(p3
, &ofs3
);
4547 Expression agg4
= getAggregateFromPointer(p4
, &ofs4
);
4548 // The valid cases are:
4549 // p1 > p2 && p3 > p4 (same direction, also for < && <)
4550 // p1 > p2 && p3 < p4 (different direction, also < && >)
4551 // Changing any > into >= doesn't affect the result
4552 if ((dir1
== dir2
&& pointToSameMemoryBlock(agg1
, agg4
) && pointToSameMemoryBlock(agg2
, agg3
)) ||
4553 (dir1
!= dir2
&& pointToSameMemoryBlock(agg1
, agg3
) && pointToSameMemoryBlock(agg2
, agg4
)))
4555 // it's a legal two-sided comparison
4556 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, (e
.op
== EXP
.andAnd
) ?
0 : 1, e
.type
);
4560 // It's an invalid four-pointer comparison. Either the second
4561 // comparison is in the same direction as the first, or else
4562 // more than two memory blocks are involved (either two independent
4563 // invalid comparisons are present, or else agg3 == agg4).
4564 error(e
.loc
, "comparison `%s` of pointers to unrelated memory blocks is indeterminate at compile time, even when combined with `%s`.", e
.e1
.toChars(), e
.e2
.toChars());
4565 result
= CTFEExp
.cantexp
;
4568 // The first pointer expression didn't need special treatment, so we
4569 // we need to interpret the entire expression exactly as a normal && or ||.
4570 // This is easy because we haven't evaluated e2 at all yet, and we already
4571 // know it will return a bool.
4572 // But we mustn't evaluate the pointer expressions in e1 again, in case
4573 // they have side-effects.
4575 Expression ex
= e
.e1
;
4578 if (auto ne
= ex
.isNotExp())
4587 /** Negate relational operator, eg >= becomes <
4589 * op = comparison operator to negate
4593 static EXP
negateRelation(EXP op
) pure
4597 case EXP
.greaterOrEqual
: op
= EXP
.lessThan
; break;
4598 case EXP
.greaterThan
: op
= EXP
.lessOrEqual
; break;
4599 case EXP
.lessOrEqual
: op
= EXP
.greaterThan
; break;
4600 case EXP
.lessThan
: op
= EXP
.greaterOrEqual
; break;
4606 const EXP cmpop
= nott ?
negateRelation(ex
.op
) : ex
.op
;
4607 const cmp = comparePointers(cmpop
, agg1
, ofs1
, agg2
, ofs2
);
4608 // We already know this is a valid comparison.
4610 if (e
.op
== EXP
.andAnd
&& cmp == 1 || e
.op
== EXP
.orOr
&& cmp == 0)
4612 result
= interpret(pue
, e
.e2
, istate
);
4615 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, (e
.op
== EXP
.andAnd
) ?
0 : 1, e
.type
);
4619 override void visit(LogicalExp e
)
4623 printf("%s LogicalExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
4625 // Check for an insidePointer expression, evaluate it if so
4626 interpretFourPointerRelation(pue
, e
);
4630 UnionExp ue1
= void;
4631 result
= interpret(&ue1
, e
.e1
, istate
);
4632 if (exceptionOrCant(result
))
4636 const andand
= e
.op
== EXP
.andAnd
;
4637 if (andand ? result
.toBool().hasValue(false) : isTrueBool(result
))
4639 else if (andand ?
isTrueBool(result
) : result
.toBool().hasValue(false))
4641 UnionExp ue2
= void;
4642 result
= interpret(&ue2
, e
.e2
, istate
);
4643 if (exceptionOrCant(result
))
4645 if (result
.op
== EXP
.voidExpression
)
4647 assert(e
.type
.ty
== Tvoid
);
4651 if (result
.toBool().hasValue(false))
4653 else if (isTrueBool(result
))
4657 error(e
.loc
, "`%s` does not evaluate to a `bool`", result
.toChars());
4658 result
= CTFEExp
.cantexp
;
4664 error(e
.loc
, "`%s` cannot be interpreted as a `bool`", result
.toChars());
4665 result
= CTFEExp
.cantexp
;
4668 incUsageCtfe(istate
, e
.e2
.loc
);
4670 if (goal
!= CTFEGoal
.Nothing
)
4672 if (e
.type
.equals(Type
.tbool
))
4673 result
= IntegerExp
.createBool(res
);
4676 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, res
, e
.type
);
4683 // Print a stack trace, starting from callingExp which called fd.
4684 // To shorten the stack trace, try to detect recursion.
4685 private void showCtfeBackTrace(CallExp callingExp
, FuncDeclaration fd
)
4687 if (ctfeGlobals
.stackTraceCallsToSuppress
> 0)
4689 --ctfeGlobals
.stackTraceCallsToSuppress
;
4692 errorSupplemental(callingExp
.loc
, "called from here: `%s`", callingExp
.toChars());
4693 // Quit if it's not worth trying to compress the stack trace
4694 if (ctfeGlobals
.callDepth
< 6 || global
.params
.v
.verbose
)
4696 // Recursion happens if the current function already exists in the call stack.
4697 int numToSuppress
= 0;
4698 int recurseCount
= 0;
4700 InterState
* lastRecurse
= istate
;
4701 for (InterState
* cur
= istate
; cur
; cur
= cur
.caller
)
4706 numToSuppress
= depthSoFar
;
4711 // We need at least three calls to the same function, to make compression worthwhile
4712 if (recurseCount
< 2)
4714 // We found a useful recursion. Print all the calls involved in the recursion
4715 errorSupplemental(fd
.loc
, "%d recursive calls to function `%s`", recurseCount
, fd
.toChars());
4716 for (InterState
* cur
= istate
; cur
.fd
!= fd
; cur
= cur
.caller
)
4718 errorSupplemental(cur
.fd
.loc
, "recursively called from function `%s`", cur
.fd
.toChars());
4720 // We probably didn't enter the recursion in this function.
4721 // Go deeper to find the real beginning.
4722 InterState
* cur
= istate
;
4723 while (lastRecurse
.caller
&& cur
.fd
== lastRecurse
.caller
.fd
)
4726 lastRecurse
= lastRecurse
.caller
;
4729 ctfeGlobals
.stackTraceCallsToSuppress
= numToSuppress
;
4732 override void visit(CallExp e
)
4736 printf("%s CallExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
4738 Expression pthis
= null;
4739 FuncDeclaration fd
= null;
4741 Expression ecall
= interpretRegion(e
.e1
, istate
);
4742 if (exceptionOrCant(ecall
))
4745 if (auto dve
= ecall
.isDotVarExp())
4747 // Calling a member function
4749 fd
= dve
.var
.isFuncDeclaration();
4752 if (auto dte
= pthis
.isDotTypeExp())
4755 else if (auto ve
= ecall
.isVarExp())
4757 fd
= ve
.var
.isFuncDeclaration();
4760 // If `_d_HookTraceImpl` is found, resolve the underlying hook and replace `e` and `fd` with it.
4761 removeHookTraceImpl(e
, fd
);
4763 if (fd
.ident
== Id
.__ArrayPostblit || fd
.ident
== Id
.__ArrayDtor
)
4765 assert(e
.arguments
.length
== 1);
4766 Expression ea
= (*e
.arguments
)[0];
4767 // printf("1 ea = %s %s\n", ea.type.toChars(), ea.toChars());
4768 if (auto se
= ea
.isSliceExp())
4770 if (auto ce
= ea
.isCastExp())
4773 // printf("2 ea = %s, %s %s\n", ea.type.toChars(), EXPtoString(ea.op).ptr, ea.toChars());
4774 if (ea
.op
== EXP
.variable || ea
.op
== EXP
.symbolOffset
)
4775 result
= getVarExp(e
.loc
, istate
, (cast(SymbolExp
)ea
).var
, CTFEGoal
.RValue
);
4776 else if (auto ae
= ea
.isAddrExp())
4777 result
= interpretRegion(ae
.e1
, istate
);
4779 // https://issues.dlang.org/show_bug.cgi?id=18871
4780 // https://issues.dlang.org/show_bug.cgi?id=18819
4781 else if (auto ale
= ea
.isArrayLiteralExp())
4782 result
= interpretRegion(ale
, istate
);
4786 if (CTFEExp
.isCantExp(result
))
4789 if (fd
.ident
== Id
.__ArrayPostblit
)
4790 result
= evaluatePostblit(istate
, result
);
4792 result
= evaluateDtor(istate
, result
);
4794 result
= CTFEExp
.voidexp
;
4797 else if (isArrayConstruction(fd
.ident
))
4799 // In expressionsem.d, `T[x] ea = eb;` was lowered to:
4800 // `_d_array{,set}ctor(ea[], eb[]);`.
4801 // The following code will rewrite it back to `ea = eb` and
4802 // then interpret that expression.
4804 if (fd
.ident
== Id
._d_arrayctor
)
4805 assert(e
.arguments
.length
== 3);
4807 assert(e
.arguments
.length
== 2);
4809 Expression ea
= (*e
.arguments
)[0];
4811 ea
= ea
.isCastExp
.e1
;
4813 Expression eb
= (*e
.arguments
)[1];
4814 if (eb
.isCastExp() && fd
.ident
== Id
._d_arrayctor
)
4815 eb
= eb
.isCastExp
.e1
;
4817 ConstructExp ce
= new ConstructExp(e
.loc
, ea
, eb
);
4821 result
= interpret(ce
, istate
);
4825 else if (fd
.ident
== Id
._d_arrayappendT || fd
.ident
== Id
._d_arrayappendTTrace
)
4827 // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`.
4828 // The following code will rewrite it back to `ea ~= eb` and then interpret that expression.
4829 Expression lhs
, rhs
;
4831 if (fd
.ident
== Id
._d_arrayappendT
)
4833 assert(e
.arguments
.length
== 2);
4834 lhs
= (*e
.arguments
)[0];
4835 rhs
= (*e
.arguments
)[1];
4839 assert(e
.arguments
.length
== 5);
4840 lhs
= (*e
.arguments
)[3];
4841 rhs
= (*e
.arguments
)[4];
4844 auto cae
= new CatAssignExp(e
.loc
, lhs
, rhs
);
4847 result
= interpretRegion(cae
, istate
, CTFEGoal
.LValue
);
4850 else if (fd
.ident
== Id
._d_arrayappendcTX
)
4851 assert(0, "CTFE cannot interpret _d_arrayappendcTX!");
4853 else if (auto soe
= ecall
.isSymOffExp())
4855 fd
= soe
.var
.isFuncDeclaration();
4856 assert(fd
&& soe
.offset
== 0);
4858 else if (auto de = ecall
.isDelegateExp())
4860 // Calling a delegate
4864 // Special handling for: &nestedfunc --> DelegateExp(VarExp(nestedfunc), nestedfunc)
4865 if (auto ve
= pthis
.isVarExp())
4867 pthis
= null; // context is not necessary for CTFE
4869 else if (auto fe
= ecall
.isFuncExp())
4871 // Calling a delegate literal
4876 // delegate.funcptr()
4878 error(e
.loc
, "cannot call `%s` at compile time", e
.toChars());
4879 result
= CTFEExp
.cantexp
;
4884 error(e
.loc
, "CTFE internal error: cannot evaluate `%s` at compile time", e
.toChars());
4885 result
= CTFEExp
.cantexp
;
4890 // Member function call
4892 // Currently this is satisfied because closure is not yet supported.
4893 assert(!fd
.isNested() || fd
.needThis());
4895 if (pthis
.op
== EXP
.typeid_
)
4897 error(pthis
.loc
, "static variable `%s` cannot be read at compile time", pthis
.toChars());
4898 result
= CTFEExp
.cantexp
;
4903 if (pthis
.op
== EXP
.null_
)
4905 assert(pthis
.type
.toBasetype().ty
== Tclass
);
4906 error(e
.loc
, "function call through null class reference `%s`", pthis
.toChars());
4907 result
= CTFEExp
.cantexp
;
4911 assert(pthis
.op
== EXP
.structLiteral || pthis
.op
== EXP
.classReference || pthis
.op
== EXP
.type
);
4913 if (fd
.isVirtual() && !e
.directcall
)
4915 // Make a virtual function call.
4916 // Get the function from the vtable of the original class
4917 ClassDeclaration cd
= pthis
.isClassReferenceExp().originalClass();
4919 // We can't just use the vtable index to look it up, because
4920 // vtables for interfaces don't get populated until the glue layer.
4921 fd
= cd
.findFunc(fd
.ident
, fd
.type
.isTypeFunction());
4926 if (fd
&& fd
.semanticRun
>= PASS
.semantic3done
&& fd
.hasSemantic3Errors())
4928 error(e
.loc
, "CTFE failed because of previous errors in `%s`", fd
.toChars());
4929 result
= CTFEExp
.cantexp
;
4933 // Check for built-in functions
4934 result
= evaluateIfBuiltin(pue
, istate
, e
.loc
, fd
, e
.arguments
, pthis
);
4940 error(e
.loc
, "`%s` cannot be interpreted at compile time, because it has no available source code", fd
.toChars());
4941 result
= CTFEExp
.showcontext
;
4945 result
= interpretFunction(pue
, fd
, istate
, e
.arguments
, pthis
);
4946 if (result
.op
== EXP
.voidExpression
)
4948 if (!exceptionOrCantInterpret(result
))
4950 if (goal
!= CTFEGoal
.LValue
) // Peel off CTFE reference if it's unnecessary
4952 if (result
== pue
.exp())
4953 result
= pue
.copy();
4954 result
= interpret(pue
, result
, istate
);
4957 if (!exceptionOrCantInterpret(result
))
4959 result
= paintTypeOntoLiteral(pue
, e
.type
, result
);
4962 else if (CTFEExp
.isCantExp(result
) && !global
.gag
)
4963 showCtfeBackTrace(e
, fd
); // Print a stack trace.
4966 override void visit(CommaExp e
)
4970 printf("%s CommaExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
4973 bool isNewThrowableHook()
4975 auto de = e
.e1
.isDeclarationExp();
4979 auto vd
= de.declaration
.isVarDeclaration();
4983 auto ei
= vd
._init
.isExpInitializer();
4987 auto ce
= ei
.exp
.isConstructExp();
4991 return isRuntimeHook(ce
.e2
, Id
._d_newThrowable
) !is null;
4994 if (auto ce
= isRuntimeHook(e
.e1
, Id
._d_arrayappendcTX
))
4996 // In expressionsem.d `arr ~= elem` was lowered to
4997 // `_d_arrayappendcTX(arr, elem), arr[arr.length - 1] = elem, elem;`.
4998 // The following code will rewrite it back to `arr ~= elem`
4999 // and then interpret that expression.
5000 assert(ce
.arguments
.length
== 2);
5002 auto arr
= (*ce
.arguments
)[0];
5003 auto elem
= e
.e2
.isConstructExp().e2
;
5006 auto cae
= new CatAssignExp(e
.loc
, arr
, elem
);
5007 cae
.type
= arr
.type
;
5009 result
= interpret(cae
, istate
);
5012 else if (isNewThrowableHook())
5014 // In expressionsem.d `throw new Exception(args)` was lowered to
5015 // `throw (tmp = _d_newThrowable!Exception(), tmp.ctor(args), tmp)`.
5016 // The following code will rewrite it back to `throw new Exception(args)`
5017 // and then interpret this expression instead.
5018 auto ce
= e
.e2
.isCallExp();
5021 auto ne
= new NewExp(e
.loc
, null, e
.type
, ce
.arguments
);
5022 ne
.type
= e
.e1
.type
;
5024 result
= interpret(ne
, istate
);
5028 // If it creates a variable, and there's no context for
5029 // the variable to be created in, we need to create one now.
5030 InterState istateComma
;
5031 if (!istate
&& firstComma(e
.e1
).op
== EXP
.declaration
)
5033 ctfeGlobals
.stack
.startFrame(null);
5034 istate
= &istateComma
;
5037 void endTempStackFrame()
5039 // If we created a temporary stack frame, end it now.
5040 if (istate
== &istateComma
)
5041 ctfeGlobals
.stack
.endFrame();
5044 result
= CTFEExp
.cantexp
;
5046 // If the comma returns a temporary variable, it needs to be an lvalue
5047 // (this is particularly important for struct constructors)
5048 if (e
.e1
.op
== EXP
.declaration
&&
5049 e
.e2
.op
== EXP
.variable
&&
5050 e
.e1
.isDeclarationExp().declaration
== e
.e2
.isVarExp().var
&&
5051 e
.e2
.isVarExp().var
.storage_class
& STC
.ctfe
)
5053 VarExp ve
= e
.e2
.isVarExp();
5054 VarDeclaration v
= ve
.var
.isVarDeclaration();
5055 ctfeGlobals
.stack
.push(v
);
5056 if (!v
._init
&& !getValue(v
))
5058 setValue(v
, copyLiteral(v
.type
.defaultInitLiteral(e
.loc
)).copy());
5062 Expression newval
= v
._init
.initializerToExpression();
5063 // Bug 4027. Copy constructors are a weird case where the
5064 // initializer is a void function (the variable is modified
5065 // through a reference parameter instead).
5066 newval
= interpretRegion(newval
, istate
);
5067 if (exceptionOrCant(newval
))
5068 return endTempStackFrame();
5069 if (newval
.op
!= EXP
.voidExpression
)
5071 // v isn't necessarily null.
5072 setValueWithoutChecking(v
, copyLiteral(newval
).copy());
5079 auto e1
= interpret(&ue
, e
.e1
, istate
, CTFEGoal
.Nothing
);
5080 if (exceptionOrCant(e1
))
5081 return endTempStackFrame();
5083 result
= interpret(pue
, e
.e2
, istate
, goal
);
5084 return endTempStackFrame();
5087 override void visit(CondExp e
)
5091 printf("%s CondExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5093 UnionExp uecond
= void;
5095 econd
= interpret(&uecond
, e
.econd
, istate
);
5096 if (exceptionOrCant(econd
))
5099 if (isPointer(e
.econd
.type
))
5101 if (econd
.op
!= EXP
.null_
)
5103 econd
= IntegerExp
.createBool(true);
5107 if (isTrueBool(econd
))
5109 result
= interpret(pue
, e
.e1
, istate
, goal
);
5110 incUsageCtfe(istate
, e
.e1
.loc
);
5112 else if (econd
.toBool().hasValue(false))
5114 result
= interpret(pue
, e
.e2
, istate
, goal
);
5115 incUsageCtfe(istate
, e
.e2
.loc
);
5119 error(e
.loc
, "`%s` does not evaluate to boolean result at compile time", e
.econd
.toChars());
5120 result
= CTFEExp
.cantexp
;
5124 override void visit(ArrayLengthExp e
)
5128 printf("%s ArrayLengthExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5131 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
5133 if (exceptionOrCant(e1
))
5135 if (e1
.op
!= EXP
.string_
&& e1
.op
!= EXP
.arrayLiteral
&& e1
.op
!= EXP
.slice
&& e1
.op
!= EXP
.null_
)
5137 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
5138 result
= CTFEExp
.cantexp
;
5141 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, resolveArrayLength(e1
), e
.type
);
5146 * Interpret the vector expression as an array literal.
5148 * pue = non-null pointer to temporary storage that can be used to store the return value
5149 * e = Expression to interpret
5151 * resulting array literal or 'e' if unable to interpret
5153 static Expression
interpretVectorToArray(UnionExp
* pue
, VectorExp e
)
5155 if (auto ale
= e
.e1
.isArrayLiteralExp())
5156 return ale
; // it's already an array literal
5157 if (e
.e1
.op
== EXP
.int64 || e
.e1
.op
== EXP
.float64
)
5159 // Convert literal __vector(int) -> __vector([array])
5160 auto elements
= new Expressions(e
.dim
);
5161 foreach (ref element
; *elements
)
5162 element
= copyLiteral(e
.e1
).copy();
5163 auto type
= (e
.type
.ty
== Tvector
) ? e
.type
.isTypeVector().basetype
: e
.type
.isTypeSArray();
5165 emplaceExp
!(ArrayLiteralExp
)(pue
, e
.loc
, type
, elements
);
5166 auto ale
= pue
.exp().isArrayLiteralExp();
5167 ale
.ownedByCtfe
= OwnedBy
.ctfe
;
5173 override void visit(VectorExp e
)
5177 printf("%s VectorExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5179 if (e
.ownedByCtfe
>= OwnedBy
.ctfe
) // We've already interpreted all the elements
5184 Expression e1
= interpret(pue
, e
.e1
, istate
);
5186 if (exceptionOrCant(e1
))
5188 if (e1
.op
!= EXP
.arrayLiteral
&& e1
.op
!= EXP
.int64
&& e1
.op
!= EXP
.float64
)
5190 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
5191 result
= CTFEExp
.cantexp
;
5194 if (e1
== pue
.exp())
5196 emplaceExp
!(VectorExp
)(pue
, e
.loc
, e1
, e
.to
);
5197 auto ve
= pue
.exp().isVectorExp();
5200 ve
.ownedByCtfe
= OwnedBy
.ctfe
;
5204 override void visit(VectorArrayExp e
)
5208 printf("%s VectorArrayExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5210 Expression e1
= interpret(pue
, e
.e1
, istate
);
5212 if (exceptionOrCant(e1
))
5214 if (auto ve
= e1
.isVectorExp())
5216 result
= interpretVectorToArray(pue
, ve
);
5217 if (result
.op
!= EXP
.vector
)
5220 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
5221 result
= CTFEExp
.cantexp
;
5224 override void visit(DelegatePtrExp e
)
5228 printf("%s DelegatePtrExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5230 Expression e1
= interpret(pue
, e
.e1
, istate
);
5232 if (exceptionOrCant(e1
))
5234 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
5235 result
= CTFEExp
.cantexp
;
5238 override void visit(DelegateFuncptrExp e
)
5242 printf("%s DelegateFuncptrExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5244 Expression e1
= interpret(pue
, e
.e1
, istate
);
5246 if (exceptionOrCant(e1
))
5248 error(e
.loc
, "`%s` cannot be evaluated at compile time", e
.toChars());
5249 result
= CTFEExp
.cantexp
;
5252 static bool resolveIndexing(IndexExp e
, InterState
* istate
, Expression
* pagg
, uinteger_t
* pidx
, bool modify
)
5254 assert(e
.e1
.type
.toBasetype().ty
!= Taarray
);
5256 if (e
.e1
.type
.toBasetype().ty
== Tpointer
)
5258 // Indexing a pointer. Note that there is no $ in this case.
5259 Expression e1
= interpretRegion(e
.e1
, istate
);
5260 if (exceptionOrCantInterpret(e1
))
5263 Expression e2
= interpretRegion(e
.e2
, istate
);
5264 if (exceptionOrCantInterpret(e2
))
5266 sinteger_t indx
= e2
.toInteger();
5269 Expression agg
= getAggregateFromPointer(e1
, &ofs
);
5271 if (agg
.op
== EXP
.null_
)
5273 error(e
.loc
, "cannot index through null pointer `%s`", e
.e1
.toChars());
5276 if (agg
.op
== EXP
.int64
)
5278 error(e
.loc
, "cannot index through invalid pointer `%s` of value `%s`", e
.e1
.toChars(), e1
.toChars());
5281 // Pointer to a non-array variable
5282 if (agg
.op
== EXP
.symbolOffset
)
5284 error(e
.loc
, "mutable variable `%s` cannot be %s at compile time, even through a pointer", cast(char*)(modify ?
"modified" : "read"), agg
.isSymOffExp().var
.toChars());
5288 if (agg
.op
== EXP
.arrayLiteral || agg
.op
== EXP
.string_
)
5290 dinteger_t len
= resolveArrayLength(agg
);
5291 if (ofs
+ indx
>= len
)
5293 error(e
.loc
, "pointer index `[%lld]` exceeds allocated memory block `[0..%lld]`", ofs
+ indx
, len
);
5299 if (ofs
+ indx
!= 0)
5301 error(e
.loc
, "pointer index `[%lld]` lies outside memory block `[0..1]`", ofs
+ indx
);
5310 Expression e1
= interpretRegion(e
.e1
, istate
);
5311 if (exceptionOrCantInterpret(e1
))
5313 if (e1
.op
== EXP
.null_
)
5315 error(e
.loc
, "cannot index null array `%s`", e
.e1
.toChars());
5318 if (auto ve
= e1
.isVectorExp())
5321 e1
= interpretVectorToArray(&ue
, ve
);
5322 e1
= (e1
== ue
.exp()) ? ue
.copy() : e1
;
5325 // Set the $ variable, and find the array literal to modify
5327 if (e1
.op
== EXP
.variable
&& e1
.type
.toBasetype().ty
== Tsarray
)
5328 len
= e1
.type
.toBasetype().isTypeSArray().dim
.toInteger();
5331 if (e1
.op
!= EXP
.arrayLiteral
&& e1
.op
!= EXP
.string_
&& e1
.op
!= EXP
.slice
&& e1
.op
!= EXP
.vector
)
5333 error(e
.loc
, "cannot determine length of `%s` at compile time", e
.e1
.toChars());
5336 len
= resolveArrayLength(e1
);
5341 Expression dollarExp
= ctfeEmplaceExp
!IntegerExp(e
.loc
, len
, Type
.tsize_t
);
5342 ctfeGlobals
.stack
.push(e
.lengthVar
);
5343 setValue(e
.lengthVar
, dollarExp
);
5345 Expression e2
= interpretRegion(e
.e2
, istate
);
5347 ctfeGlobals
.stack
.pop(e
.lengthVar
); // $ is defined only inside []
5348 if (exceptionOrCantInterpret(e2
))
5350 if (e2
.op
!= EXP
.int64
)
5352 error(e
.loc
, "CTFE internal error: non-integral index `[%s]`", e
.e2
.toChars());
5356 if (auto se
= e1
.isSliceExp())
5358 // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx']
5359 uinteger_t index
= e2
.toInteger();
5360 uinteger_t ilwr
= se
.lwr
.toInteger();
5361 uinteger_t iupr
= se
.upr
.toInteger();
5363 if (index
> iupr
- ilwr
)
5365 error(e
.loc
, "index %llu exceeds array length %llu", index
, iupr
- ilwr
);
5368 *pagg
= e1
.isSliceExp().e1
;
5369 *pidx
= index
+ ilwr
;
5374 *pidx
= e2
.toInteger();
5377 error(e
.loc
, "array index %lld is out of bounds `[0..%lld]`", *pidx
, len
);
5384 override void visit(IndexExp e
)
5388 printf("%s IndexExp::interpret() %s, goal = %d\n", e
.loc
.toChars(), e
.toChars(), goal
);
5390 if (e
.e1
.type
.toBasetype().ty
== Tpointer
)
5393 uinteger_t indexToAccess
;
5394 if (!resolveIndexing(e
, istate
, &agg
, &indexToAccess
, false))
5396 result
= CTFEExp
.cantexp
;
5399 if (agg
.op
== EXP
.arrayLiteral || agg
.op
== EXP
.string_
)
5401 if (goal
== CTFEGoal
.LValue
)
5403 // if we need a reference, IndexExp shouldn't be interpreting
5404 // the expression to a value, it should stay as a reference
5405 emplaceExp
!(IndexExp
)(pue
, e
.loc
, agg
, ctfeEmplaceExp
!IntegerExp(e
.e2
.loc
, indexToAccess
, e
.e2
.type
));
5407 result
.type
= e
.type
;
5410 result
= ctfeIndex(pue
, e
.loc
, e
.type
, agg
, indexToAccess
);
5415 assert(indexToAccess
== 0);
5416 result
= interpretRegion(agg
, istate
, goal
);
5417 if (exceptionOrCant(result
))
5419 result
= paintTypeOntoLiteral(pue
, e
.type
, result
);
5424 if (e
.e1
.type
.toBasetype().ty
== Taarray
)
5426 Expression e1
= interpretRegion(e
.e1
, istate
);
5427 if (exceptionOrCant(e1
))
5429 if (e1
.op
== EXP
.null_
)
5431 if (goal
== CTFEGoal
.LValue
&& e1
.type
.ty
== Taarray
&& e
.modifiable
)
5433 assert(0); // does not reach here?
5435 error(e
.loc
, "cannot index null array `%s`", e
.e1
.toChars());
5436 result
= CTFEExp
.cantexp
;
5439 Expression e2
= interpretRegion(e
.e2
, istate
);
5440 if (exceptionOrCant(e2
))
5443 if (goal
== CTFEGoal
.LValue
)
5445 // Pointer or reference of a scalar type
5446 if (e1
== e
.e1
&& e2
== e
.e2
)
5450 emplaceExp
!(IndexExp
)(pue
, e
.loc
, e1
, e2
);
5452 result
.type
= e
.type
;
5457 assert(e1
.op
== EXP
.assocArrayLiteral
);
5458 UnionExp e2tmp
= void;
5459 e2
= resolveSlice(e2
, &e2tmp
);
5460 result
= findKeyInAA(e
.loc
, e1
.isAssocArrayLiteralExp(), e2
);
5463 error(e
.loc
, "key `%s` not found in associative array `%s`", e2
.toChars(), e
.e1
.toChars());
5464 result
= CTFEExp
.cantexp
;
5470 uinteger_t indexToAccess
;
5471 if (!resolveIndexing(e
, istate
, &agg
, &indexToAccess
, false))
5473 result
= CTFEExp
.cantexp
;
5477 if (goal
== CTFEGoal
.LValue
)
5479 Expression e2
= ctfeEmplaceExp
!IntegerExp(e
.e2
.loc
, indexToAccess
, Type
.tsize_t
);
5480 emplaceExp
!(IndexExp
)(pue
, e
.loc
, agg
, e2
);
5482 result
.type
= e
.type
;
5486 result
= ctfeIndex(pue
, e
.loc
, e
.type
, agg
, indexToAccess
);
5487 if (exceptionOrCant(result
))
5489 if (result
.op
== EXP
.void_
)
5491 error(e
.loc
, "`%s` is used before initialized", e
.toChars());
5492 errorSupplemental(result
.loc
, "originally uninitialized here");
5493 result
= CTFEExp
.cantexp
;
5496 if (result
== pue
.exp())
5497 result
= result
.copy();
5500 override void visit(SliceExp e
)
5504 printf("%s SliceExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5506 if (e
.e1
.type
.toBasetype().ty
== Tpointer
)
5508 // Slicing a pointer. Note that there is no $ in this case.
5509 Expression e1
= interpretRegion(e
.e1
, istate
);
5510 if (exceptionOrCant(e1
))
5512 if (e1
.op
== EXP
.int64
)
5514 error(e
.loc
, "cannot slice invalid pointer `%s` of value `%s`", e
.e1
.toChars(), e1
.toChars());
5515 result
= CTFEExp
.cantexp
;
5519 /* Evaluate lower and upper bounds of slice
5521 Expression lwr
= interpretRegion(e
.lwr
, istate
);
5522 if (exceptionOrCant(lwr
))
5524 Expression upr
= interpretRegion(e
.upr
, istate
);
5525 if (exceptionOrCant(upr
))
5527 uinteger_t ilwr
= lwr
.toInteger();
5528 uinteger_t iupr
= upr
.toInteger();
5531 Expression agg
= getAggregateFromPointer(e1
, &ofs
);
5534 if (agg
.op
== EXP
.null_
)
5538 result
= ctfeEmplaceExp
!NullExp(e
.loc
);
5539 result
.type
= e
.type
;
5542 error(e
.loc
, "cannot slice null pointer `%s`", e
.e1
.toChars());
5543 result
= CTFEExp
.cantexp
;
5546 if (agg
.op
== EXP
.symbolOffset
)
5548 error(e
.loc
, "slicing pointers to static variables is not supported in CTFE");
5549 result
= CTFEExp
.cantexp
;
5552 if (agg
.op
!= EXP
.arrayLiteral
&& agg
.op
!= EXP
.string_
)
5554 error(e
.loc
, "pointer `%s` cannot be sliced at compile time (it does not point to an array)", e
.e1
.toChars());
5555 result
= CTFEExp
.cantexp
;
5558 assert(agg
.op
== EXP
.arrayLiteral || agg
.op
== EXP
.string_
);
5559 dinteger_t len
= ArrayLength(Type
.tsize_t
, agg
).exp().toInteger();
5560 //Type *pointee = ((TypePointer *)agg.type)->next;
5561 if (sliceBoundsCheck(0, len
, ilwr
, iupr
))
5563 error(e
.loc
, "pointer slice `[%lld..%lld]` exceeds allocated memory block `[0..%lld]`", ilwr
, iupr
, len
);
5564 result
= CTFEExp
.cantexp
;
5569 lwr
= ctfeEmplaceExp
!IntegerExp(e
.loc
, ilwr
, lwr
.type
);
5570 upr
= ctfeEmplaceExp
!IntegerExp(e
.loc
, iupr
, upr
.type
);
5572 emplaceExp
!(SliceExp
)(pue
, e
.loc
, agg
, lwr
, upr
);
5574 result
.type
= e
.type
;
5578 CTFEGoal goal1
= CTFEGoal
.RValue
;
5579 if (goal
== CTFEGoal
.LValue
)
5581 if (e
.e1
.type
.toBasetype().ty
== Tsarray
)
5582 if (auto ve
= e
.e1
.isVarExp())
5583 if (auto vd
= ve
.var
.isVarDeclaration())
5584 if (vd
.storage_class
& STC
.ref_
)
5585 goal1
= CTFEGoal
.LValue
;
5587 Expression e1
= interpret(e
.e1
, istate
, goal1
);
5588 if (exceptionOrCant(e1
))
5593 result
= paintTypeOntoLiteral(pue
, e
.type
, e1
);
5596 if (auto ve
= e1
.isVectorExp())
5598 e1
= interpretVectorToArray(pue
, ve
);
5599 e1
= (e1
== pue
.exp()) ? pue
.copy() : e1
;
5602 /* Set dollar to the length of the array
5605 if ((e1
.op
== EXP
.variable || e1
.op
== EXP
.dotVariable
) && e1
.type
.toBasetype().ty
== Tsarray
)
5606 dollar
= e1
.type
.toBasetype().isTypeSArray().dim
.toInteger();
5609 if (e1
.op
!= EXP
.arrayLiteral
&& e1
.op
!= EXP
.string_
&& e1
.op
!= EXP
.null_
&& e1
.op
!= EXP
.slice
&& e1
.op
!= EXP
.vector
)
5611 error(e
.loc
, "cannot determine length of `%s` at compile time", e1
.toChars());
5612 result
= CTFEExp
.cantexp
;
5615 dollar
= resolveArrayLength(e1
);
5618 /* Set the $ variable
5622 auto dollarExp
= ctfeEmplaceExp
!IntegerExp(e
.loc
, dollar
, Type
.tsize_t
);
5623 ctfeGlobals
.stack
.push(e
.lengthVar
);
5624 setValue(e
.lengthVar
, dollarExp
);
5627 /* Evaluate lower and upper bounds of slice
5629 Expression lwr
= interpretRegion(e
.lwr
, istate
);
5630 if (exceptionOrCant(lwr
))
5633 ctfeGlobals
.stack
.pop(e
.lengthVar
);
5636 Expression upr
= interpretRegion(e
.upr
, istate
);
5637 if (exceptionOrCant(upr
))
5640 ctfeGlobals
.stack
.pop(e
.lengthVar
);
5644 ctfeGlobals
.stack
.pop(e
.lengthVar
); // $ is defined only inside [L..U]
5646 uinteger_t ilwr
= lwr
.toInteger();
5647 uinteger_t iupr
= upr
.toInteger();
5648 if (e1
.op
== EXP
.null_
)
5650 if (ilwr
== 0 && iupr
== 0)
5655 error(e1
.loc
, "slice `[%llu..%llu]` is out of bounds", ilwr
, iupr
);
5656 result
= CTFEExp
.cantexp
;
5659 if (auto se
= e1
.isSliceExp())
5661 // Simplify slice of slice:
5662 // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr']
5663 uinteger_t lo1
= se
.lwr
.toInteger();
5664 uinteger_t up1
= se
.upr
.toInteger();
5665 if (sliceBoundsCheck(0, up1
- lo1
, ilwr
, iupr
))
5667 error(e
.loc
, "slice `[%llu..%llu]` exceeds array bounds `[0..%llu]`", ilwr
, iupr
, up1
- lo1
);
5668 result
= CTFEExp
.cantexp
;
5673 emplaceExp
!(SliceExp
)(pue
, e
.loc
, se
.e1
,
5674 ctfeEmplaceExp
!IntegerExp(e
.loc
, ilwr
, lwr
.type
),
5675 ctfeEmplaceExp
!IntegerExp(e
.loc
, iupr
, upr
.type
));
5677 result
.type
= e
.type
;
5680 if (e1
.op
== EXP
.arrayLiteral || e1
.op
== EXP
.string_
)
5682 if (sliceBoundsCheck(0, dollar
, ilwr
, iupr
))
5684 error(e
.loc
, "slice `[%lld..%lld]` exceeds array bounds `[0..%lld]`", ilwr
, iupr
, dollar
);
5685 result
= CTFEExp
.cantexp
;
5689 emplaceExp
!(SliceExp
)(pue
, e
.loc
, e1
, lwr
, upr
);
5691 result
.type
= e
.type
;
5694 override void visit(InExp e
)
5698 printf("%s InExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5700 Expression e1
= interpretRegion(e
.e1
, istate
);
5701 if (exceptionOrCant(e1
))
5703 Expression e2
= interpretRegion(e
.e2
, istate
);
5704 if (exceptionOrCant(e2
))
5706 if (e2
.op
== EXP
.null_
)
5708 emplaceExp
!(NullExp
)(pue
, e
.loc
, e
.type
);
5712 if (e2
.op
!= EXP
.assocArrayLiteral
)
5714 error(e
.loc
, "`%s` cannot be interpreted at compile time", e
.toChars());
5715 result
= CTFEExp
.cantexp
;
5719 e1
= resolveSlice(e1
);
5720 result
= findKeyInAA(e
.loc
, e2
.isAssocArrayLiteralExp(), e1
);
5721 if (exceptionOrCant(result
))
5725 emplaceExp
!(NullExp
)(pue
, e
.loc
, e
.type
);
5730 // Create a CTFE pointer &aa[index]
5731 result
= ctfeEmplaceExp
!IndexExp(e
.loc
, e2
, e1
);
5732 result
.type
= e
.type
.nextOf();
5733 emplaceExp
!(AddrExp
)(pue
, e
.loc
, result
, e
.type
);
5738 override void visit(CatExp e
)
5742 printf("%s CatExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5745 UnionExp ue1
= void;
5746 Expression e1
= interpret(&ue1
, e
.e1
, istate
);
5747 if (exceptionOrCant(e1
))
5750 UnionExp ue2
= void;
5751 Expression e2
= interpret(&ue2
, e
.e2
, istate
);
5752 if (exceptionOrCant(e2
))
5755 UnionExp e1tmp
= void;
5756 e1
= resolveSlice(e1
, &e1tmp
);
5758 UnionExp e2tmp
= void;
5759 e2
= resolveSlice(e2
, &e2tmp
);
5761 /* e1 and e2 can't go on the stack because of x~[y] and [x]~y will
5762 * result in [x,y] and then x or y is on the stack.
5763 * But if they are both strings, we can, because it isn't the x~[y] case.
5765 if (!(e1
.op
== EXP
.string_
&& e2
.op
== EXP
.string_
))
5767 if (e1
== ue1
.exp())
5769 if (e2
== ue2
.exp())
5773 Expression
prepareCatOperand(Expression exp
)
5775 /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an
5776 * array. This is needed because interpreting the `CatExp` calls
5777 * `Cat()`, which cannot handle concatenations between different
5778 * types, except for strings and chars.
5780 auto tb
= e
.type
.toBasetype();
5781 auto tbNext
= tb
.nextOf();
5782 auto expTb
= exp
.type
.toBasetype();
5784 if (exp
.type
.implicitConvTo(tbNext
) >= MATCH
.convert
&&
5785 (tb
.ty
== Tarray || tb
.ty
== Tsarray
) &&
5786 (expTb
.ty
== Tarray || expTb
.ty
== Tsarray
))
5787 return new ArrayLiteralExp(exp
.loc
, e
.type
, exp
);
5791 *pue
= ctfeCat(e
.loc
, e
.type
, prepareCatOperand(e1
), prepareCatOperand(e2
));
5794 if (CTFEExp
.isCantExp(result
))
5796 error(e
.loc
, "`%s` cannot be interpreted at compile time", e
.toChars());
5799 // We know we still own it, because we interpreted both e1 and e2
5800 if (auto ale
= result
.isArrayLiteralExp())
5802 ale
.ownedByCtfe
= OwnedBy
.ctfe
;
5804 // https://issues.dlang.org/show_bug.cgi?id=14686
5805 foreach (elem
; *ale
.elements
)
5807 Expression ex
= evaluatePostblit(istate
, elem
);
5808 if (exceptionOrCant(ex
))
5812 else if (auto se
= result
.isStringExp())
5813 se
.ownedByCtfe
= OwnedBy
.ctfe
;
5816 override void visit(DeleteExp e
)
5820 printf("%s DeleteExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5822 result
= interpretRegion(e
.e1
, istate
);
5823 if (exceptionOrCant(result
))
5826 if (result
.op
== EXP
.null_
)
5828 result
= CTFEExp
.voidexp
;
5832 auto tb
= e
.e1
.type
.toBasetype();
5836 if (result
.op
!= EXP
.classReference
)
5838 error(e
.loc
, "`delete` on invalid class reference `%s`", result
.toChars());
5839 result
= CTFEExp
.cantexp
;
5843 auto cre
= result
.isClassReferenceExp();
5844 auto cd
= cre
.originalClass();
5846 // Find dtor(s) in inheritance chain
5851 result
= interpretFunction(pue
, cd
.dtor
, istate
, null, cre
);
5852 if (exceptionOrCant(result
))
5855 // Dtors of Non-extern(D) classes use implicit chaining (like structs)
5856 import dmd
.aggregate
: ClassKind
;
5857 if (cd
.classKind
!= ClassKind
.d
)
5861 // Emulate manual chaining as done in rt_finalize2
5864 } while (cd
); // Stop after Object
5871 result
= CTFEExp
.voidexp
;
5874 override void visit(CastExp e
)
5878 printf("%s CastExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
5880 Expression e1
= interpretRegion(e
.e1
, istate
, goal
);
5881 if (exceptionOrCant(e1
))
5883 // If the expression has been cast to void, do nothing.
5884 if (e
.to
.ty
== Tvoid
)
5886 result
= CTFEExp
.voidexp
;
5889 if (e
.to
.ty
== Tpointer
&& e1
.op
!= EXP
.null_
)
5891 Type pointee
= (cast(TypePointer
)e
.type
).next
;
5892 // Implement special cases of normally-unsafe casts
5893 if (e1
.op
== EXP
.int64
)
5895 // Happens with Windows HANDLEs, for example.
5896 result
= paintTypeOntoLiteral(pue
, e
.to
, e1
);
5900 bool castToSarrayPointer
= false;
5901 bool castBackFromVoid
= false;
5902 if (e1
.type
.ty
== Tarray || e1
.type
.ty
== Tsarray || e1
.type
.ty
== Tpointer
)
5904 // Check for unsupported type painting operations
5905 // For slices, we need the type being sliced,
5906 // since it may have already been type painted
5907 Type elemtype
= e1
.type
.nextOf();
5908 if (auto se
= e1
.isSliceExp())
5909 elemtype
= se
.e1
.type
.nextOf();
5911 // Allow casts from X* to void *, and X** to void** for any X.
5912 // But don't allow cast from X* to void**.
5913 // So, we strip all matching * from source and target to find X.
5914 // Allow casts to X* from void* only if the 'void' was originally an X;
5915 // we check this later on.
5916 Type ultimatePointee
= pointee
;
5917 Type ultimateSrc
= elemtype
;
5918 while (ultimatePointee
.ty
== Tpointer
&& ultimateSrc
.ty
== Tpointer
)
5920 ultimatePointee
= ultimatePointee
.nextOf();
5921 ultimateSrc
= ultimateSrc
.nextOf();
5923 if (ultimatePointee
.ty
== Tsarray
&& ultimatePointee
.nextOf().equivalent(ultimateSrc
))
5925 castToSarrayPointer
= true;
5927 else if (ultimatePointee
.ty
!= Tvoid
&& ultimateSrc
.ty
!= Tvoid
&& !isSafePointerCast(elemtype
, pointee
))
5929 error(e
.loc
, "reinterpreting cast from `%s*` to `%s*` is not supported in CTFE", elemtype
.toChars(), pointee
.toChars());
5930 result
= CTFEExp
.cantexp
;
5933 if (ultimateSrc
.ty
== Tvoid
)
5934 castBackFromVoid
= true;
5937 if (auto se
= e1
.isSliceExp())
5939 if (se
.e1
.op
== EXP
.null_
)
5941 result
= paintTypeOntoLiteral(pue
, e
.type
, se
.e1
);
5944 // Create a CTFE pointer &aggregate[1..2]
5945 auto ei
= ctfeEmplaceExp
!IndexExp(e
.loc
, se
.e1
, se
.lwr
);
5946 ei
.type
= e
.type
.nextOf();
5947 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ei
, e
.type
);
5951 if (e1
.op
== EXP
.arrayLiteral || e1
.op
== EXP
.string_
)
5953 // Create a CTFE pointer &[1,2,3][0] or &"abc"[0]
5954 auto ei
= ctfeEmplaceExp
!IndexExp(e
.loc
, e1
, ctfeEmplaceExp
!IntegerExp(e
.loc
, 0, Type
.tsize_t
));
5955 ei
.type
= e
.type
.nextOf();
5956 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ei
, e
.type
);
5960 if (e1
.op
== EXP
.index
&& !e1
.isIndexExp().e1
.type
.equals(e1
.type
))
5962 // type painting operation
5963 IndexExp ie
= e1
.isIndexExp();
5964 if (castBackFromVoid
)
5966 // get the original type. For strings, it's just the type...
5967 Type origType
= ie
.e1
.type
.nextOf();
5968 // ..but for arrays of type void*, it's the type of the element
5969 if (ie
.e1
.op
== EXP
.arrayLiteral
&& ie
.e2
.op
== EXP
.int64
)
5971 ArrayLiteralExp ale
= ie
.e1
.isArrayLiteralExp();
5972 const indx
= cast(size_t
)ie
.e2
.toInteger();
5973 if (indx
< ale
.elements
.length
)
5975 if (Expression xx
= (*ale
.elements
)[indx
])
5977 if (auto iex
= xx
.isIndexExp())
5978 origType
= iex
.e1
.type
.nextOf();
5979 else if (auto ae
= xx
.isAddrExp())
5980 origType
= ae
.e1
.type
;
5981 else if (auto ve
= xx
.isVarExp())
5982 origType
= ve
.var
.type
;
5986 if (!isSafePointerCast(origType
, pointee
))
5988 error(e
.loc
, "using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType
.toChars(), pointee
.toChars());
5989 result
= CTFEExp
.cantexp
;
5993 emplaceExp
!(IndexExp
)(pue
, e1
.loc
, ie
.e1
, ie
.e2
);
5995 result
.type
= e
.type
;
5999 if (auto ae
= e1
.isAddrExp())
6001 Type origType
= ae
.e1
.type
;
6002 if (isSafePointerCast(origType
, pointee
))
6004 emplaceExp
!(AddrExp
)(pue
, e
.loc
, ae
.e1
, e
.type
);
6009 if (castToSarrayPointer
&& pointee
.toBasetype().ty
== Tsarray
&& ae
.e1
.op
== EXP
.index
)
6012 dinteger_t dim
= (cast(TypeSArray
)pointee
.toBasetype()).dim
.toInteger();
6013 IndexExp ie
= ae
.e1
.isIndexExp();
6014 Expression lwr
= ie
.e2
;
6015 Expression upr
= ctfeEmplaceExp
!IntegerExp(ie
.e2
.loc
, ie
.e2
.toInteger() + dim
, Type
.tsize_t
);
6017 // Create a CTFE pointer &val[idx..idx+dim]
6018 auto er
= ctfeEmplaceExp
!SliceExp(e
.loc
, ie
.e1
, lwr
, upr
);
6020 emplaceExp
!(AddrExp
)(pue
, e
.loc
, er
, e
.type
);
6026 if (e1
.op
== EXP
.variable || e1
.op
== EXP
.symbolOffset
)
6028 // type painting operation
6029 Type origType
= (cast(SymbolExp
)e1
).var
.type
;
6030 if (castBackFromVoid
&& !isSafePointerCast(origType
, pointee
))
6032 error(e
.loc
, "using `void*` to reinterpret cast from `%s*` to `%s*` is not supported in CTFE", origType
.toChars(), pointee
.toChars());
6033 result
= CTFEExp
.cantexp
;
6036 if (auto ve
= e1
.isVarExp())
6037 emplaceExp
!(VarExp
)(pue
, e
.loc
, ve
.var
);
6039 emplaceExp
!(SymOffExp
)(pue
, e
.loc
, e1
.isSymOffExp().var
, e1
.isSymOffExp().offset
);
6045 // Check if we have a null pointer (eg, inside a struct)
6046 e1
= interpretRegion(e1
, istate
);
6047 if (e1
.op
!= EXP
.null_
)
6049 error(e
.loc
, "pointer cast from `%s` to `%s` is not supported at compile time", e1
.type
.toChars(), e
.to
.toChars());
6050 result
= CTFEExp
.cantexp
;
6054 if (e
.to
.ty
== Tsarray
&& e
.e1
.type
.ty
== Tvector
)
6056 // Special handling for: cast(float[4])__vector([w, x, y, z])
6057 e1
= interpretRegion(e
.e1
, istate
);
6058 if (exceptionOrCant(e1
))
6060 assert(e1
.op
== EXP
.vector
);
6061 e1
= interpretVectorToArray(pue
, e1
.isVectorExp());
6063 if (e
.to
.ty
== Tarray
&& e1
.op
== EXP
.slice
)
6065 // Note that the slice may be void[], so when checking for dangerous
6066 // casts, we need to use the original type, which is se.e1.
6067 SliceExp se
= e1
.isSliceExp();
6068 if (!isSafePointerCast(se
.e1
.type
.nextOf(), e
.to
.nextOf()))
6070 error(e
.loc
, "array cast from `%s` to `%s` is not supported at compile time", se
.e1
.type
.toChars(), e
.to
.toChars());
6071 result
= CTFEExp
.cantexp
;
6074 emplaceExp
!(SliceExp
)(pue
, e1
.loc
, se
.e1
, se
.lwr
, se
.upr
);
6079 // Disallow array type painting, except for conversions between built-in
6080 // types of identical size.
6081 if ((e
.to
.ty
== Tsarray || e
.to
.ty
== Tarray
) && (e1
.type
.ty
== Tsarray || e1
.type
.ty
== Tarray
) && !isSafePointerCast(e1
.type
.nextOf(), e
.to
.nextOf()))
6083 error(e
.loc
, "array cast from `%s` to `%s` is not supported at compile time", e1
.type
.toChars(), e
.to
.toChars());
6084 result
= CTFEExp
.cantexp
;
6087 if (e
.to
.ty
== Tsarray
)
6088 e1
= resolveSlice(e1
);
6090 auto tobt
= e
.to
.toBasetype();
6091 if (tobt
.ty
== Tbool
&& e1
.type
.ty
== Tpointer
)
6093 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, e1
.op
!= EXP
.null_
, e
.to
);
6097 else if (tobt
.isTypeBasic() && e1
.op
== EXP
.null_
)
6099 if (tobt
.isintegral())
6100 emplaceExp
!(IntegerExp
)(pue
, e
.loc
, 0, e
.to
);
6101 else if (tobt
.isreal())
6102 emplaceExp
!(RealExp
)(pue
, e
.loc
, CTFloat
.zero
, e
.to
);
6106 result
= ctfeCast(pue
, e
.loc
, e
.type
, e
.to
, e1
, true);
6109 override void visit(AssertExp e
)
6113 printf("%s AssertExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
6115 Expression e1
= interpret(pue
, e
.e1
, istate
);
6116 if (exceptionOrCant(e1
))
6121 else if (e1
.toBool().hasValue(false))
6126 result
= interpret(&ue
, e
.msg
, istate
);
6127 if (exceptionOrCant(result
))
6129 result
= scrubReturnValue(e
.loc
, result
);
6130 if (StringExp se
= result
.toStringExp())
6131 error(e
.loc
, "%s", se
.toStringz().ptr
);
6133 error(e
.loc
, "%s", result
.toChars());
6136 error(e
.loc
, "`%s` failed", e
.toChars());
6137 result
= CTFEExp
.cantexp
;
6142 error(e
.loc
, "`%s` is not a compile time boolean expression", e1
.toChars());
6143 result
= CTFEExp
.cantexp
;
6150 override void visit(ThrowExp te
)
6154 printf("%s ThrowExpression::interpret()\n", te
.loc
.toChars());
6156 interpretThrow(result
, te
.e1
, te
.loc
, istate
);
6159 override void visit(PtrExp e
)
6161 // Called for both lvalues and rvalues
6162 const lvalue
= goal
== CTFEGoal
.LValue
;
6165 printf("%s PtrExp::interpret(%d) %s, %s\n", e
.loc
.toChars(), lvalue
, e
.type
.toChars(), e
.toChars());
6168 // Check for int<->float and long<->double casts.
6169 if (auto soe1
= e
.e1
.isSymOffExp())
6170 if (soe1
.offset
== 0 && soe1
.var
.isVarDeclaration() && isFloatIntPaint(e
.type
, soe1
.var
.type
))
6172 // *(cast(int*)&v), where v is a float variable
6173 result
= paintFloatInt(pue
, getVarExp(e
.loc
, istate
, soe1
.var
, CTFEGoal
.RValue
), e
.type
);
6177 if (auto ce1
= e
.e1
.isCastExp())
6178 if (auto ae11
= ce1
.e1
.isAddrExp())
6180 // *(cast(int*)&x), where x is a float expression
6181 Expression x
= ae11
.e1
;
6182 if (isFloatIntPaint(e
.type
, x
.type
))
6184 result
= paintFloatInt(pue
, interpretRegion(x
, istate
), e
.type
);
6189 // Constant fold *(&structliteral + offset)
6190 if (auto ae
= e
.e1
.isAddExp())
6192 if (ae
.e1
.op
== EXP
.address
&& ae
.e2
.op
== EXP
.int64
)
6194 AddrExp ade
= ae
.e1
.isAddrExp();
6195 Expression ex
= interpretRegion(ade
.e1
, istate
);
6196 if (exceptionOrCant(ex
))
6198 if (auto se
= ex
.isStructLiteralExp())
6200 dinteger_t offset
= ae
.e2
.toInteger();
6201 result
= se
.getField(e
.type
, cast(uint)offset
);
6208 // It's possible we have an array bounds error. We need to make sure it
6209 // errors with this line number, not the one where the pointer was set.
6210 result
= interpretRegion(e
.e1
, istate
);
6211 if (exceptionOrCant(result
))
6214 if (result
.op
== EXP
.function_
)
6216 if (auto soe
= result
.isSymOffExp())
6218 if (soe
.offset
== 0 && soe
.var
.isFuncDeclaration())
6220 error(e
.loc
, "cannot dereference pointer to static variable `%s` at compile time", soe
.var
.toChars());
6221 result
= CTFEExp
.cantexp
;
6225 if (!lvalue
&& result
.isArrayLiteralExp() &&
6226 result
.type
.isTypePointer())
6228 /* A pointer variable can point to an array literal like `[3]`.
6229 * Dereferencing it means accessing the first element value.
6230 * Dereference it only if result should be an rvalue
6232 auto ae
= result
.isArrayLiteralExp();
6233 if (ae
.elements
.length
== 1)
6235 result
= (*ae
.elements
)[0];
6239 if (result
.isStringExp() || result
.isArrayLiteralExp())
6242 if (result
.op
!= EXP
.address
)
6244 if (result
.op
== EXP
.null_
)
6245 error(e
.loc
, "dereference of null pointer `%s`", e
.e1
.toChars());
6247 error(e
.loc
, "dereference of invalid pointer `%s`", result
.toChars());
6248 result
= CTFEExp
.cantexp
;
6253 result
= result
.isAddrExp().e1
;
6255 if (result
.op
== EXP
.slice
&& e
.type
.toBasetype().ty
== Tsarray
)
6258 * upr may exceed the upper boundary of aggr, but the check is deferred
6259 * until those out-of-bounds elements will be touched.
6263 result
= interpret(pue
, result
, istate
, goal
);
6264 if (exceptionOrCant(result
))
6269 if (CTFEExp
.isCantExp(result
))
6270 printf("PtrExp::interpret() %s = CTFEExp::cantexp\n", e
.toChars());
6274 override void visit(DotVarExp e
)
6276 void notImplementedYet()
6278 error(e
.loc
, "`%s.%s` is not yet implemented at compile time", e
.e1
.toChars(), e
.var
.toChars());
6279 result
= CTFEExp
.cantexp
;
6285 printf("%s DotVarExp::interpret() %s, goal = %d\n", e
.loc
.toChars(), e
.toChars(), goal
);
6287 Expression ex
= interpretRegion(e
.e1
, istate
);
6288 if (exceptionOrCant(ex
))
6291 if (FuncDeclaration f
= e
.var
.isFuncDeclaration())
6294 result
= e
; // optimize: reuse this CTFE reference
6297 emplaceExp
!(DotVarExp
)(pue
, e
.loc
, ex
, f
, false);
6299 result
.type
= e
.type
;
6304 VarDeclaration v
= e
.var
.isVarDeclaration();
6307 error(e
.loc
, "CTFE internal error: `%s`", e
.toChars());
6308 result
= CTFEExp
.cantexp
;
6312 if (ex
.op
== EXP
.null_
)
6314 if (ex
.type
.toBasetype().ty
== Tclass
)
6315 error(e
.loc
, "class `%s` is `null` and cannot be dereferenced", e
.e1
.toChars());
6317 error(e
.loc
, "CTFE internal error: null this `%s`", e
.e1
.toChars());
6318 result
= CTFEExp
.cantexp
;
6322 StructLiteralExp se
;
6325 if (ex
.op
!= EXP
.structLiteral
&& ex
.op
!= EXP
.classReference
&& ex
.op
!= EXP
.typeid_
)
6327 return notImplementedYet();
6330 // We can't use getField, because it makes a copy
6331 if (ex
.op
== EXP
.classReference
)
6333 se
= ex
.isClassReferenceExp().value
;
6334 i
= ex
.isClassReferenceExp().findFieldIndexByName(v
);
6336 else if (ex
.op
== EXP
.typeid_
)
6338 if (v
.ident
== Identifier
.idPool("name"))
6340 if (auto t
= isType(ex
.isTypeidExp().obj
))
6342 auto sym
= t
.toDsymbol(null);
6343 if (auto ident
= (sym ? sym
.ident
: null))
6345 result
= new StringExp(e
.loc
, ident
.toString());
6346 result
.expressionSemantic(null);
6351 return notImplementedYet();
6355 se
= ex
.isStructLiteralExp();
6356 i
= findFieldIndexByName(se
.sd
, v
);
6360 error(e
.loc
, "couldn't find field `%s` of type `%s` in `%s`", v
.toChars(), e
.type
.toChars(), se
.toChars());
6361 result
= CTFEExp
.cantexp
;
6365 // https://issues.dlang.org/show_bug.cgi?id=19897
6366 // https://issues.dlang.org/show_bug.cgi?id=20710
6367 // Zero-elements fields don't have an initializer. See: scrubArray function
6368 if ((*se
.elements
)[i
] is null)
6369 (*se
.elements
)[i
] = voidInitLiteral(e
.type
, v
).copy();
6371 if (goal
== CTFEGoal
.LValue
)
6373 // just return the (simplified) dotvar expression as a CTFE reference
6378 emplaceExp
!(DotVarExp
)(pue
, e
.loc
, ex
, v
);
6380 result
.type
= e
.type
;
6385 result
= (*se
.elements
)[i
];
6388 error(e
.loc
, "internal compiler error: null field `%s`", v
.toChars());
6389 result
= CTFEExp
.cantexp
;
6392 if (auto vie
= result
.isVoidInitExp())
6394 const s
= vie
.var
.toChars();
6397 error(e
.loc
, "reinterpretation through overlapped field `%s` is not allowed in CTFE", s
);
6398 result
= CTFEExp
.cantexp
;
6401 error(e
.loc
, "cannot read uninitialized variable `%s` in CTFE", s
);
6402 result
= CTFEExp
.cantexp
;
6406 if (v
.type
.ty
!= result
.type
.ty
&& v
.type
.ty
== Tsarray
)
6408 // Block assignment from inside struct literals
6409 auto tsa
= cast(TypeSArray
)v
.type
;
6410 auto len
= cast(size_t
)tsa
.dim
.toInteger();
6412 result
= createBlockDuplicatedArrayLiteral(&ue
, e
.loc
, v
.type
, result
, len
);
6413 if (result
== ue
.exp())
6415 (*se
.elements
)[i
] = result
;
6419 if (CTFEExp
.isCantExp(result
))
6420 printf("DotVarExp::interpret() %s = CTFEExp::cantexp\n", e
.toChars());
6424 override void visit(RemoveExp e
)
6428 printf("%s RemoveExp::interpret() %s\n", e
.loc
.toChars(), e
.toChars());
6430 Expression agg
= interpret(e
.e1
, istate
);
6431 if (exceptionOrCant(agg
))
6433 Expression index
= interpret(e
.e2
, istate
);
6434 if (exceptionOrCant(index
))
6436 if (agg
.op
== EXP
.null_
)
6438 result
= CTFEExp
.voidexp
;
6442 AssocArrayLiteralExp aae
= agg
.isAssocArrayLiteralExp();
6443 Expressions
* keysx
= aae
.keys
;
6444 Expressions
* valuesx
= aae
.values
;
6446 foreach (j
, evalue
; *valuesx
)
6448 Expression ekey
= (*keysx
)[j
];
6449 int eq
= ctfeEqual(e
.loc
, EXP
.equal
, ekey
, index
);
6452 else if (removed
!= 0)
6454 (*keysx
)[j
- removed
] = ekey
;
6455 (*valuesx
)[j
- removed
] = evalue
;
6458 valuesx
.length
= valuesx
.length
- removed
;
6459 keysx
.length
= keysx
.length
- removed
;
6460 result
= IntegerExp
.createBool(removed
!= 0);
6463 override void visit(ClassReferenceExp e
)
6465 //printf("ClassReferenceExp::interpret() %s\n", e.value.toChars());
6469 override void visit(VoidInitExp e
)
6471 error(e
.loc
, "CTFE internal error: trying to read uninitialized variable");
6475 override void visit(ThrownExceptionExp e
)
6477 assert(0); // This should never be interpreted
6481 /// Interpret `throw <exp>` found at the specified location `loc`
6483 void interpretThrow(ref Expression result
, Expression exp
, const ref Loc loc
, InterState
* istate
)
6485 incUsageCtfe(istate
, loc
);
6487 Expression e
= interpretRegion(exp
, istate
);
6488 if (exceptionOrCantInterpret(e
))
6490 // Make sure e is not pointing to a stack temporary
6491 result
= (e
.op
== EXP
.cantExpression
) ? CTFEExp
.cantexp
: e
;
6493 else if (e
.op
== EXP
.classReference
)
6495 result
= ctfeEmplaceExp
!ThrownExceptionExp(loc
, e
.isClassReferenceExp());
6499 error(exp
.loc
, "to be thrown `%s` must be non-null", exp
.toChars());
6500 result
= ErrorExp
.get();
6504 /*********************************************
6505 * Checks if the given expresion is a call to the runtime hook `id`.
6508 * e = the expression to check
6509 * id = the identifier of the runtime hook
6511 * `e` cast to `CallExp` if it's the hook, `null` otherwise
6513 public CallExp
isRuntimeHook(Expression e
, Identifier id
)
6515 if (auto ce
= e
.isCallExp())
6517 if (auto ve
= ce
.e1
.isVarExp())
6519 if (auto fd
= ve
.var
.isFuncDeclaration())
6521 // If `_d_HookTraceImpl` is found, resolve the underlying hook
6522 // and replace `e` and `fd` with it.
6523 removeHookTraceImpl(ce
, fd
);
6524 return fd
.ident
== id ? ce
: null;
6532 /********************************************
6533 * Interpret the expression.
6535 * pue = non-null pointer to temporary storage that can be used to store the return value
6536 * e = Expression to interpret
6538 * goal = what the result will be used for
6540 * resulting expression
6543 Expression
interpret(UnionExp
* pue
, Expression e
, InterState
* istate
, CTFEGoal goal
= CTFEGoal
.RValue
)
6547 //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars());
6548 scope Interpreter v
= new Interpreter(pue
, istate
, goal
);
6550 Expression ex
= v
.result
;
6551 assert(goal
== CTFEGoal
.Nothing || ex
!is null);
6552 //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n");
6557 Expression
interpret(Expression e
, InterState
* istate
, CTFEGoal goal
= CTFEGoal
.RValue
)
6560 auto result
= interpret(&ue
, e
, istate
, goal
);
6561 if (result
== ue
.exp())
6566 /*****************************
6567 * Same as interpret(), but return result allocated in Region.
6569 * e = Expression to interpret
6571 * goal = what the result will be used for
6573 * resulting expression
6575 Expression
interpretRegion(Expression e
, InterState
* istate
, CTFEGoal goal
= CTFEGoal
.RValue
)
6578 auto result
= interpret(&ue
, e
, istate
, goal
);
6579 auto uexp
= ue
.exp();
6582 if (mem
.isGCEnabled
)
6585 // mimicking UnionExp.copy, but with region allocation
6588 case EXP
.cantExpression
: return CTFEExp
.cantexp
;
6589 case EXP
.voidExpression
: return CTFEExp
.voidexp
;
6590 case EXP
.break_
: return CTFEExp
.breakexp
;
6591 case EXP
.continue_
: return CTFEExp
.continueexp
;
6592 case EXP
.goto_
: return CTFEExp
.gotoexp
;
6595 auto p
= ctfeGlobals
.region
.malloc(uexp
.size
);
6596 return cast(Expression
)memcpy(p
, cast(void*)uexp
, uexp
.size
);
6600 Expressions
* copyArrayOnWrite(Expressions
* exps
, Expressions
* original
)
6602 if (exps
is original
)
6605 exps
= new Expressions();
6607 exps
= original
.copy();
6608 ++ctfeGlobals
.numArrayAllocs
;
6614 Given an expression e which is about to be returned from the current
6615 function, generate an error if it contains pointers to local variables.
6617 Only checks expressions passed by value (pointers to local variables
6618 may already be stored in members of classes, arrays, or AAs which
6619 were passed as mutable function parameters).
6621 true if it is safe to return, false if an error was generated.
6624 bool stopPointersEscaping(const ref Loc loc
, Expression e
)
6626 if (!e
.type
.hasPointers())
6628 if (isPointer(e
.type
))
6631 if (auto eaddr
= e
.isAddrExp())
6634 while (x
.op
== EXP
.variable
&& (v
= x
.isVarExp().var
.isVarDeclaration()) !is null)
6636 if (v
.storage_class
& STC
.ref_
)
6639 if (auto eaddr
= e
.isAddrExp())
6643 if (ctfeGlobals
.stack
.isInCurrentFrame(v
))
6645 error(loc
, "returning a pointer to a local stack variable");
6651 // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not
6652 // pointing to a local struct or static array.
6654 if (auto se
= e
.isStructLiteralExp())
6656 return stopPointersEscapingFromArray(loc
, se
.elements
);
6658 if (auto ale
= e
.isArrayLiteralExp())
6660 return stopPointersEscapingFromArray(loc
, ale
.elements
);
6662 if (auto aae
= e
.isAssocArrayLiteralExp())
6664 if (!stopPointersEscapingFromArray(loc
, aae
.keys
))
6666 return stopPointersEscapingFromArray(loc
, aae
.values
);
6671 // Check all elements of an array for escaping local variables. Return false if error
6673 bool stopPointersEscapingFromArray(const ref Loc loc
, Expressions
* elems
)
6677 if (e
&& !stopPointersEscaping(loc
, e
))
6684 Statement
findGotoTarget(InterState
* istate
, Identifier ident
)
6686 Statement target
= null;
6689 LabelDsymbol label
= istate
.fd
.searchLabel(ident
);
6690 assert(label
&& label
.statement
);
6691 LabelStatement ls
= label
.statement
;
6692 target
= ls
.gotoTarget ? ls
.gotoTarget
: ls
.statement
;
6698 ThrownExceptionExp
chainExceptions(ThrownExceptionExp oldest
, ThrownExceptionExp newest
)
6702 printf("Collided exceptions %s %s\n", oldest
.thrown
.toChars(), newest
.thrown
.toChars());
6704 // Little sanity check to make sure it's really a Throwable
6705 ClassReferenceExp boss
= oldest
.thrown
;
6706 const next
= 5; // index of Throwable.next
6707 assert((*boss
.value
.elements
)[next
].type
.ty
== Tclass
); // Throwable.next
6708 ClassReferenceExp collateral
= newest
.thrown
;
6709 if (collateral
.originalClass().isErrorException() && !boss
.originalClass().isErrorException())
6711 /* Find the index of the Error.bypassException field
6713 auto bypass
= next
+ 1;
6714 if ((*collateral
.value
.elements
)[bypass
].type
.ty
== Tuns32
)
6715 bypass
+= 1; // skip over _refcount field
6716 assert((*collateral
.value
.elements
)[bypass
].type
.ty
== Tclass
);
6718 // The new exception bypass the existing chain
6719 (*collateral
.value
.elements
)[bypass
] = boss
;
6722 while ((*boss
.value
.elements
)[next
].op
== EXP
.classReference
)
6724 boss
= (*boss
.value
.elements
)[next
].isClassReferenceExp();
6726 (*boss
.value
.elements
)[next
] = collateral
;
6731 * All results destined for use outside of CTFE need to have their CTFE-specific
6734 * 1. all slices must be resolved.
6735 * 2. all .ownedByCtfe set to OwnedBy.code
6737 private Expression
scrubReturnValue(const ref Loc loc
, Expression e
)
6739 /* Returns: true if e is void,
6740 * or is an array literal or struct literal of void elements.
6742 static bool isVoid(const Expression e
, bool checkArrayType
= false) pure
6744 if (e
.op
== EXP
.void_
)
6747 static bool isEntirelyVoid(const Expressions
* elems
)
6751 // It can be NULL for performance reasons,
6752 // see StructLiteralExp::interpret().
6753 if (e
&& !isVoid(e
))
6759 if (auto sle
= e
.isStructLiteralExp())
6760 return isEntirelyVoid(sle
.elements
);
6762 if (checkArrayType
&& e
.type
.ty
!= Tsarray
)
6765 if (auto ale
= e
.isArrayLiteralExp())
6766 return isEntirelyVoid(ale
.elements
);
6772 /* Scrub all elements of elems[].
6773 * Returns: null for success, error Expression for failure
6775 Expression
scrubArray(Expressions
* elems
, bool structlit
= false)
6777 foreach (ref e
; *elems
)
6779 // It can be NULL for performance reasons,
6780 // see StructLiteralExp::interpret().
6784 // A struct .init may contain void members.
6785 // Static array members are a weird special case https://issues.dlang.org/show_bug.cgi?id=10994
6786 if (structlit
&& isVoid(e
, true))
6792 e
= scrubReturnValue(loc
, e
);
6793 if (CTFEExp
.isCantExp(e
) || e
.op
== EXP
.error
)
6800 Expression
scrubSE(StructLiteralExp sle
)
6802 sle
.ownedByCtfe
= OwnedBy
.code
;
6803 if (!(sle
.stageflags
& stageScrub
))
6805 const old
= sle
.stageflags
;
6806 sle
.stageflags |
= stageScrub
; // prevent infinite recursion
6807 if (auto ex
= scrubArray(sle
.elements
, true))
6809 sle
.stageflags
= old
;
6814 if (e
.op
== EXP
.classReference
)
6816 StructLiteralExp sle
= e
.isClassReferenceExp().value
;
6817 if (auto ex
= scrubSE(sle
))
6820 else if (auto vie
= e
.isVoidInitExp())
6822 error(loc
, "uninitialized variable `%s` cannot be returned from CTFE", vie
.var
.toChars());
6823 return ErrorExp
.get();
6826 e
= resolveSlice(e
);
6828 if (auto sle
= e
.isStructLiteralExp())
6830 if (auto ex
= scrubSE(sle
))
6833 else if (auto se
= e
.isStringExp())
6835 se
.ownedByCtfe
= OwnedBy
.code
;
6837 else if (auto ale
= e
.isArrayLiteralExp())
6839 ale
.ownedByCtfe
= OwnedBy
.code
;
6840 if (auto ex
= scrubArray(ale
.elements
))
6843 else if (auto aae
= e
.isAssocArrayLiteralExp())
6845 aae
.ownedByCtfe
= OwnedBy
.code
;
6846 if (auto ex
= scrubArray(aae
.keys
))
6848 if (auto ex
= scrubArray(aae
.values
))
6850 aae
.type
= toBuiltinAAType(aae
.type
);
6852 else if (auto ve
= e
.isVectorExp())
6854 ve
.ownedByCtfe
= OwnedBy
.code
;
6855 if (auto ale
= ve
.e1
.isArrayLiteralExp())
6857 ale
.ownedByCtfe
= OwnedBy
.code
;
6858 if (auto ex
= scrubArray(ale
.elements
))
6865 /**************************************
6866 * Transitively set all .ownedByCtfe to OwnedBy.cache
6868 private Expression
scrubCacheValue(Expression e
)
6873 Expression
scrubArrayCache(Expressions
* elems
)
6875 foreach (ref e
; *elems
)
6876 e
= scrubCacheValue(e
);
6880 Expression
scrubSE(StructLiteralExp sle
)
6882 sle
.ownedByCtfe
= OwnedBy
.cache
;
6883 if (!(sle
.stageflags
& stageScrub
))
6885 const old
= sle
.stageflags
;
6886 sle
.stageflags |
= stageScrub
; // prevent infinite recursion
6887 if (auto ex
= scrubArrayCache(sle
.elements
))
6889 sle
.stageflags
= old
;
6894 if (e
.op
== EXP
.classReference
)
6896 if (auto ex
= scrubSE(e
.isClassReferenceExp().value
))
6899 else if (auto sle
= e
.isStructLiteralExp())
6901 if (auto ex
= scrubSE(sle
))
6904 else if (auto se
= e
.isStringExp())
6906 se
.ownedByCtfe
= OwnedBy
.cache
;
6908 else if (auto ale
= e
.isArrayLiteralExp())
6910 ale
.ownedByCtfe
= OwnedBy
.cache
;
6911 if (Expression ex
= scrubArrayCache(ale
.elements
))
6914 else if (auto aae
= e
.isAssocArrayLiteralExp())
6916 aae
.ownedByCtfe
= OwnedBy
.cache
;
6917 if (auto ex
= scrubArrayCache(aae
.keys
))
6919 if (auto ex
= scrubArrayCache(aae
.values
))
6922 else if (auto ve
= e
.isVectorExp())
6924 ve
.ownedByCtfe
= OwnedBy
.cache
;
6925 if (auto ale
= ve
.e1
.isArrayLiteralExp())
6927 ale
.ownedByCtfe
= OwnedBy
.cache
;
6928 if (auto ex
= scrubArrayCache(ale
.elements
))
6935 /********************************************
6936 * Transitively replace all Expressions allocated in ctfeGlobals.region
6937 * with Mem owned copies.
6939 * e = possible ctfeGlobals.region owned expression
6941 * Mem owned expression
6943 private Expression
copyRegionExp(Expression e
)
6948 static void copyArray(Expressions
* elems
)
6950 foreach (ref e
; *elems
)
6954 e
= copyRegionExp(ex
);
6958 static void copySE(StructLiteralExp sle
)
6960 if (1 ||
!(sle
.stageflags
& stageScrub
))
6962 const old
= sle
.stageflags
;
6963 sle
.stageflags |
= stageScrub
; // prevent infinite recursion
6964 copyArray(sle
.elements
);
6965 sle
.stageflags
= old
;
6971 case EXP
.classReference
:
6973 auto cre
= e
.isClassReferenceExp();
6974 cre
.value
= copyRegionExp(cre
.value
).isStructLiteralExp();
6978 case EXP
.structLiteral
:
6980 auto sle
= e
.isStructLiteralExp();
6982 /* The following is to take care of updating sle.origin correctly,
6983 * which may have multiple objects pointing to it.
6985 if (sle
.isOriginal
&& !ctfeGlobals
.region
.contains(cast(void*)sle
.origin
))
6987 /* This means sle has already been moved out of the region,
6988 * and sle.origin is the new location.
6993 sle
.isOriginal
= sle
is sle
.origin
;
6995 auto slec
= ctfeGlobals
.region
.contains(cast(void*)e
)
6996 ? e
.copy().isStructLiteralExp() // move sle out of region to slec
6999 if (ctfeGlobals
.region
.contains(cast(void*)sle
.origin
))
7001 auto sleo
= sle
.origin
== sle ? slec
: sle
.origin
.copy().isStructLiteralExp();
7008 case EXP
.arrayLiteral
:
7010 auto ale
= e
.isArrayLiteralExp();
7011 ale
.basis
= copyRegionExp(ale
.basis
);
7012 copyArray(ale
.elements
);
7016 case EXP
.assocArrayLiteral
:
7017 copyArray(e
.isAssocArrayLiteralExp().keys
);
7018 copyArray(e
.isAssocArrayLiteralExp().values
);
7023 auto se
= e
.isSliceExp();
7024 se
.e1
= copyRegionExp(se
.e1
);
7025 se
.upr
= copyRegionExp(se
.upr
);
7026 se
.lwr
= copyRegionExp(se
.lwr
);
7032 auto te
= e
.isTupleExp();
7033 te
.e0
= copyRegionExp(te
.e0
);
7041 case EXP
.dotVariable
:
7043 UnaExp ue
= e
.isUnaExp();
7044 ue
.e1
= copyRegionExp(ue
.e1
);
7050 BinExp be
= e
.isBinExp();
7051 be
.e1
= copyRegionExp(be
.e1
);
7052 be
.e2
= copyRegionExp(be
.e2
);
7069 case EXP
.symbolOffset
:
7072 case EXP
.cantExpression
:
7073 case EXP
.voidExpression
:
7074 case EXP
.showCtfeContext
:
7078 printf("e: %s, %s\n", EXPtoString(e
.op
).ptr
, e
.toChars());
7082 if (ctfeGlobals
.region
.contains(cast(void*)e
))
7089 /******************************* Special Functions ***************************/
7091 private Expression
interpret_length(UnionExp
* pue
, InterState
* istate
, Expression earg
)
7093 //printf("interpret_length()\n");
7094 earg
= interpret(pue
, earg
, istate
);
7095 if (exceptionOrCantInterpret(earg
))
7098 if (auto aae
= earg
.isAssocArrayLiteralExp())
7099 len
= aae
.keys
.length
;
7101 assert(earg
.op
== EXP
.null_
);
7102 emplaceExp
!(IntegerExp
)(pue
, earg
.loc
, len
, Type
.tsize_t
);
7106 private Expression
interpret_keys(UnionExp
* pue
, InterState
* istate
, Expression earg
, Type returnType
)
7110 printf("interpret_keys()\n");
7112 earg
= interpret(pue
, earg
, istate
);
7113 if (exceptionOrCantInterpret(earg
))
7115 if (earg
.op
== EXP
.null_
)
7117 emplaceExp
!(NullExp
)(pue
, earg
.loc
, earg
.type
);
7120 if (earg
.op
!= EXP
.assocArrayLiteral
&& earg
.type
.toBasetype().ty
!= Taarray
)
7122 AssocArrayLiteralExp aae
= earg
.isAssocArrayLiteralExp();
7123 auto ae
= ctfeEmplaceExp
!ArrayLiteralExp(aae
.loc
, returnType
, aae
.keys
);
7124 ae
.ownedByCtfe
= aae
.ownedByCtfe
;
7125 *pue
= copyLiteral(ae
);
7129 private Expression
interpret_values(UnionExp
* pue
, InterState
* istate
, Expression earg
, Type returnType
)
7133 printf("interpret_values()\n");
7135 earg
= interpret(pue
, earg
, istate
);
7136 if (exceptionOrCantInterpret(earg
))
7138 if (earg
.op
== EXP
.null_
)
7140 emplaceExp
!(NullExp
)(pue
, earg
.loc
, earg
.type
);
7143 if (earg
.op
!= EXP
.assocArrayLiteral
&& earg
.type
.toBasetype().ty
!= Taarray
)
7145 auto aae
= earg
.isAssocArrayLiteralExp();
7146 auto ae
= ctfeEmplaceExp
!ArrayLiteralExp(aae
.loc
, returnType
, aae
.values
);
7147 ae
.ownedByCtfe
= aae
.ownedByCtfe
;
7148 //printf("result is %s\n", e.toChars());
7149 *pue
= copyLiteral(ae
);
7153 private Expression
interpret_dup(UnionExp
* pue
, InterState
* istate
, Expression earg
)
7157 printf("interpret_dup()\n");
7159 earg
= interpret(pue
, earg
, istate
);
7160 if (exceptionOrCantInterpret(earg
))
7162 if (earg
.op
== EXP
.null_
)
7164 emplaceExp
!(NullExp
)(pue
, earg
.loc
, earg
.type
);
7167 if (earg
.op
!= EXP
.assocArrayLiteral
&& earg
.type
.toBasetype().ty
!= Taarray
)
7169 auto aae
= copyLiteral(earg
).copy().isAssocArrayLiteralExp();
7170 for (size_t i
= 0; i
< aae
.keys
.length
; i
++)
7172 if (Expression e
= evaluatePostblit(istate
, (*aae
.keys
)[i
]))
7174 if (Expression e
= evaluatePostblit(istate
, (*aae
.values
)[i
]))
7177 aae
.type
= earg
.type
.mutableOf(); // repaint type from const(int[int]) to const(int)[int]
7178 //printf("result is %s\n", aae.toChars());
7182 // signature is int delegate(ref Value) OR int delegate(ref Key, ref Value)
7183 private Expression
interpret_aaApply(UnionExp
* pue
, InterState
* istate
, Expression aa
, Expression deleg
)
7185 aa
= interpret(aa
, istate
);
7186 if (exceptionOrCantInterpret(aa
))
7188 if (aa
.op
!= EXP
.assocArrayLiteral
)
7190 emplaceExp
!(IntegerExp
)(pue
, deleg
.loc
, 0, Type
.tsize_t
);
7194 FuncDeclaration fd
= null;
7195 Expression pthis
= null;
7196 if (auto de = deleg
.isDelegateExp())
7201 else if (auto fe
= deleg
.isFuncExp())
7204 assert(fd
&& fd
.fbody
);
7205 assert(fd
.parameters
);
7206 size_t numParams
= fd
.parameters
.length
;
7207 assert(numParams
== 1 || numParams
== 2);
7209 Parameter fparam
= fd
.type
.isTypeFunction().parameterList
[numParams
- 1];
7210 const wantRefValue
= fparam
.isReference();
7212 Expressions args
= Expressions(numParams
);
7214 AssocArrayLiteralExp ae
= aa
.isAssocArrayLiteralExp();
7215 if (!ae
.keys || ae
.keys
.length
== 0)
7216 return ctfeEmplaceExp
!IntegerExp(deleg
.loc
, 0, Type
.tsize_t
);
7219 for (size_t i
= 0; i
< ae
.keys
.length
; ++i
)
7221 Expression ekey
= (*ae
.keys
)[i
];
7222 Expression evalue
= (*ae
.values
)[i
];
7225 Type t
= evalue
.type
;
7226 evalue
= ctfeEmplaceExp
!IndexExp(deleg
.loc
, ae
, ekey
);
7229 args
[numParams
- 1] = evalue
;
7234 eresult
= interpretFunction(&ue
, fd
, istate
, &args
, pthis
);
7235 if (eresult
== ue
.exp())
7236 eresult
= ue
.copy();
7237 if (exceptionOrCantInterpret(eresult
))
7240 if (eresult
.isIntegerExp().getInteger() != 0)
7246 /* Decoding UTF strings for foreach loops. Duplicates the functionality of
7247 * the twelve _aApplyXXn functions in aApply.d in the runtime.
7249 private Expression
foreachApplyUtf(UnionExp
* pue
, InterState
* istate
, Expression
str, Expression deleg
, bool rvs
)
7253 printf("foreachApplyUtf(%s, %s)\n", str.toChars(), deleg
.toChars());
7255 FuncDeclaration fd
= null;
7256 Expression pthis
= null;
7257 if (auto de = deleg
.isDelegateExp())
7262 else if (auto fe
= deleg
.isFuncExp())
7265 assert(fd
&& fd
.fbody
);
7266 assert(fd
.parameters
);
7267 size_t numParams
= fd
.parameters
.length
;
7268 assert(numParams
== 1 || numParams
== 2);
7269 Type charType
= (*fd
.parameters
)[numParams
- 1].type
;
7270 Type indexType
= numParams
== 2 ?
(*fd
.parameters
)[0].type
: Type
.tsize_t
;
7271 size_t len
= cast(size_t
)resolveArrayLength(str);
7274 emplaceExp
!(IntegerExp
)(pue
, deleg
.loc
, 0, indexType
);
7278 UnionExp strTmp
= void;
7279 str = resolveSlice(str, &strTmp
);
7281 auto se
= str.isStringExp();
7282 auto ale
= str.isArrayLiteralExp();
7285 error(str.loc
, "CTFE internal error: cannot foreach `%s`", str.toChars());
7286 return CTFEExp
.cantexp
;
7288 Expressions args
= Expressions(numParams
);
7290 Expression eresult
= null; // ded-store to prevent spurious warning
7292 // Buffers for encoding; also used for decoding array literals
7293 char[4] utf8buf
= void;
7294 wchar[2] utf16buf
= void;
7296 size_t start
= rvs ? len
: 0;
7297 size_t end
= rvs ?
0 : len
;
7298 for (size_t indx
= start
; indx
!= end
;)
7300 // Step 1: Decode the next dchar from the string.
7302 string errmsg
= null; // Used for reporting decoding errors
7303 dchar rawvalue
; // Holds the decoded dchar
7304 size_t currentIndex
= indx
; // The index of the decoded character
7308 // If it is an array literal, copy the code points into the buffer
7309 size_t buflen
= 1; // #code points in the buffer
7310 size_t n
= 1; // #code points in this char
7311 size_t sz
= cast(size_t
)ale
.type
.nextOf().size();
7318 // find the start of the string
7321 while (indx
> 0 && buflen
< 4)
7323 Expression r
= (*ale
.elements
)[indx
];
7324 char x
= cast(char)r
.isIntegerExp().getInteger();
7325 if ((x
& 0xC0) != 0x80)
7332 buflen
= (indx
+ 4 > len
) ? len
- indx
: 4;
7333 for (size_t i
= 0; i
< buflen
; ++i
)
7335 Expression r
= (*ale
.elements
)[indx
+ i
];
7336 utf8buf
[i
] = cast(char)r
.isIntegerExp().getInteger();
7339 errmsg
= utf_decodeChar(utf8buf
[0 .. buflen
], n
, rawvalue
);
7345 // find the start of the string
7348 Expression r
= (*ale
.elements
)[indx
];
7349 ushort x
= cast(ushort)r
.isIntegerExp().getInteger();
7350 if (indx
> 0 && x
>= 0xDC00 && x
<= 0xDFFF)
7357 buflen
= (indx
+ 2 > len
) ? len
- indx
: 2;
7358 for (size_t i
= 0; i
< buflen
; ++i
)
7360 Expression r
= (*ale
.elements
)[indx
+ i
];
7361 utf16buf
[i
] = cast(ushort)r
.isIntegerExp().getInteger();
7364 errmsg
= utf_decodeWchar(utf16buf
[0 .. buflen
], n
, rawvalue
);
7371 Expression r
= (*ale
.elements
)[indx
];
7372 rawvalue
= cast(dchar)r
.isIntegerExp().getInteger();
7386 size_t saveindx
; // used for reverse iteration
7394 // find the start of the string
7396 while (indx
> 0 && ((se
.getCodeUnit(indx
) & 0xC0) == 0x80))
7400 auto slice
= se
.peekString();
7401 errmsg
= utf_decodeChar(slice
, indx
, rawvalue
);
7412 auto wc
= se
.getCodeUnit(indx
);
7413 if (wc
>= 0xDC00 && wc
<= 0xDFFF)
7417 const slice
= se
.peekWstring();
7418 errmsg
= utf_decodeWchar(slice
, indx
, rawvalue
);
7426 rawvalue
= se
.getCodeUnit(indx
);
7437 error(deleg
.loc
, "`%.*s`", cast(int)errmsg
.length
, errmsg
.ptr
);
7438 return CTFEExp
.cantexp
;
7441 // Step 2: encode the dchar in the target encoding
7443 int charlen
= 1; // How many codepoints are involved?
7444 switch (charType
.size())
7447 charlen
= utf_codeLengthChar(rawvalue
);
7448 utf_encodeChar(&utf8buf
[0], rawvalue
);
7451 charlen
= utf_codeLengthWchar(rawvalue
);
7452 utf_encodeWchar(&utf16buf
[0], rawvalue
);
7460 currentIndex
= indx
;
7462 // Step 3: call the delegate once for each code point
7464 // The index only needs to be set once
7466 args
[0] = ctfeEmplaceExp
!IntegerExp(deleg
.loc
, currentIndex
, indexType
);
7468 Expression val
= null;
7470 foreach (k
; 0 .. charlen
)
7473 switch (charType
.size())
7476 codepoint
= utf8buf
[k
];
7479 codepoint
= utf16buf
[k
];
7482 codepoint
= rawvalue
;
7487 val
= ctfeEmplaceExp
!IntegerExp(str.loc
, codepoint
, charType
);
7489 args
[numParams
- 1] = val
;
7492 eresult
= interpretFunction(&ue
, fd
, istate
, &args
, pthis
);
7493 if (eresult
== ue
.exp())
7494 eresult
= ue
.copy();
7495 if (exceptionOrCantInterpret(eresult
))
7497 if (eresult
.isIntegerExp().getInteger() != 0)
7504 /* If this is a built-in function, return the interpreted result,
7505 * Otherwise, return NULL.
7507 private Expression
evaluateIfBuiltin(UnionExp
* pue
, InterState
* istate
, const ref Loc loc
, FuncDeclaration fd
, Expressions
* arguments
, Expression pthis
)
7509 Expression e
= null;
7510 size_t nargs
= arguments ? arguments
.length
: 0;
7513 if (isBuiltin(fd
) != BUILTIN
.unimp
)
7515 Expressions args
= Expressions(nargs
);
7516 foreach (i
, ref arg
; args
)
7518 Expression earg
= (*arguments
)[i
];
7519 earg
= interpret(earg
, istate
);
7520 if (exceptionOrCantInterpret(earg
))
7524 e
= eval_builtin(loc
, fd
, &args
);
7527 error(loc
, "cannot evaluate unimplemented builtin `%s` at compile time", fd
.toChars());
7528 e
= CTFEExp
.cantexp
;
7534 if (nargs
== 1 || nargs
== 3)
7536 Expression firstarg
= (*arguments
)[0];
7537 if (auto firstAAtype
= firstarg
.type
.toBasetype().isTypeAArray())
7539 const id
= fd
.ident
;
7543 return interpret_length(pue
, istate
, firstarg
);
7545 if (fd
.toParent2().ident
== Id
.object
)
7548 return interpret_keys(pue
, istate
, firstarg
, firstAAtype
.index
.arrayOf());
7549 if (id
== Id
.values
)
7550 return interpret_values(pue
, istate
, firstarg
, firstAAtype
.nextOf().arrayOf());
7551 if (id
== Id
.rehash
)
7552 return interpret(pue
, firstarg
, istate
);
7554 return interpret_dup(pue
, istate
, firstarg
);
7557 else // (nargs == 3)
7559 if (id
== Id
._aaApply
)
7560 return interpret_aaApply(pue
, istate
, firstarg
, (*arguments
)[2]);
7561 if (id
== Id
._aaApply2
)
7562 return interpret_aaApply(pue
, istate
, firstarg
, (*arguments
)[2]);
7567 if (pthis
&& !fd
.fbody
&& fd
.isCtorDeclaration() && fd
.parent
&& fd
.parent
.parent
&& fd
.parent
.parent
.ident
== Id
.object
)
7569 if (pthis
.op
== EXP
.classReference
&& fd
.parent
.ident
== Id
.Throwable
)
7571 // At present, the constructors just copy their arguments into the struct.
7572 // But we might need some magic if stack tracing gets added to druntime.
7573 StructLiteralExp se
= pthis
.isClassReferenceExp().value
;
7574 assert(arguments
.length
<= se
.elements
.length
);
7575 foreach (i
, arg
; *arguments
)
7577 auto elem
= interpret(arg
, istate
);
7578 if (exceptionOrCantInterpret(elem
))
7580 (*se
.elements
)[i
] = elem
;
7582 return CTFEExp
.voidexp
;
7585 if (nargs
== 1 && !pthis
&& (fd
.ident
== Id
.criticalenter || fd
.ident
== Id
.criticalexit
))
7587 // Support synchronized{} as a no-op
7588 return CTFEExp
.voidexp
;
7592 const idlen
= fd
.ident
.toString().length
;
7593 const id
= fd
.ident
.toChars();
7594 if (nargs
== 2 && (idlen
== 10 || idlen
== 11) && !strncmp(id
, "_aApply", 7))
7596 // Functions from aApply.d and aApplyR.d in the runtime
7597 bool rvs
= (idlen
== 11); // true if foreach_reverse
7598 char c
= id
[idlen
- 3]; // char width: 'c', 'w', or 'd'
7599 char s
= id
[idlen
- 2]; // string width: 'c', 'w', or 'd'
7600 char n
= id
[idlen
- 1]; // numParams: 1 or 2.
7601 // There are 12 combinations
7602 if ((n
== '1' || n
== '2') &&
7603 (c
== 'c' || c
== 'w' || c
== 'd') &&
7604 (s
== 'c' || s
== 'w' || s
== 'd') &&
7607 Expression
str = (*arguments
)[0];
7608 str = interpret(str, istate
);
7609 if (exceptionOrCantInterpret(str))
7611 return foreachApplyUtf(pue
, istate
, str, (*arguments
)[1], rvs
);
7618 private Expression
evaluatePostblit(InterState
* istate
, Expression e
)
7620 auto ts
= e
.type
.baseElemOf().isTypeStruct();
7623 StructDeclaration sd
= ts
.sym
;
7627 if (auto ale
= e
.isArrayLiteralExp())
7629 foreach (elem
; *ale
.elements
)
7631 if (auto ex
= evaluatePostblit(istate
, elem
))
7636 if (e
.op
== EXP
.structLiteral
)
7640 e
= interpretFunction(&ue
, sd
.postblit
, istate
, null, e
);
7643 if (exceptionOrCantInterpret(e
))
7650 private Expression
evaluateDtor(InterState
* istate
, Expression e
)
7652 auto ts
= e
.type
.baseElemOf().isTypeStruct();
7655 StructDeclaration sd
= ts
.sym
;
7660 if (auto ale
= e
.isArrayLiteralExp())
7662 foreach_reverse (elem
; *ale
.elements
)
7663 e
= evaluateDtor(istate
, elem
);
7665 else if (e
.op
== EXP
.structLiteral
)
7668 e
= interpretFunction(&ue
, sd
.dtor
, istate
, null, e
);
7672 if (exceptionOrCantInterpret(e
))
7681 /*************************** CTFE Sanity Checks ***************************/
7682 /* Setter functions for CTFE variable values.
7683 * These functions exist to check for compiler CTFE bugs.
7685 private bool hasValue(VarDeclaration vd
)
7687 return vd
.ctfeAdrOnStack
!= VarDeclaration
.AdrOnStackNone
&&
7688 getValue(vd
) !is null;
7691 // Don't check for validity
7692 private void setValueWithoutChecking(VarDeclaration vd
, Expression newval
)
7694 ctfeGlobals
.stack
.setValue(vd
, newval
);
7697 private void setValue(VarDeclaration vd
, Expression newval
)
7699 //printf("setValue() vd: %s newval: %s\n", vd.toChars(), newval.toChars());
7702 if (!((vd
.storage_class
& (STC
.out_ | STC
.ref_
)) ?
isCtfeReferenceValid(newval
) : isCtfeValueValid(newval
)))
7704 printf("[%s] vd = %s %s, newval = %s\n", vd
.loc
.toChars(), vd
.type
.toChars(), vd
.toChars(), newval
.toChars());
7707 assert((vd
.storage_class
& (STC
.out_ | STC
.ref_
)) ?
isCtfeReferenceValid(newval
) : isCtfeValueValid(newval
));
7708 ctfeGlobals
.stack
.setValue(vd
, newval
);
7712 * Removes `_d_HookTraceImpl` if found from `ce` and `fd`.
7713 * This is needed for the CTFE interception code to be able to find hooks that are called though the hook's `*Trace`
7716 * This is done by replacing `_d_HookTraceImpl!(T, Hook, errMsg)(..., parameters)` with `Hook(parameters)`.
7718 * ce = The CallExp that possible will be be replaced
7719 * fd = Fully resolve function declaration that `ce` would call
7721 private void removeHookTraceImpl(ref CallExp ce
, ref FuncDeclaration fd
)
7723 if (fd
.ident
!= Id
._d_HookTraceImpl
)
7728 // Get the Hook from the second template parameter
7729 TemplateInstance templateInstance
= fd
.parent
.isTemplateInstance
;
7730 RootObject hook
= (*templateInstance
.tiargs
)[1];
7731 assert(hook
.isDsymbol(), "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!");
7732 fd
= (cast(Dsymbol
)hook
).isFuncDeclaration
;
7734 // Remove the first three trace parameters
7735 auto arguments
= new Expressions();
7736 arguments
.reserve(ce
.arguments
.length
- 3);
7737 arguments
.pushSlice((*ce
.arguments
)[3 .. $]);
7739 ce
= ctfeEmplaceExp
!CallExp(ce
.loc
, ctfeEmplaceExp
!VarExp(ce
.loc
, fd
, false), arguments
);
7741 if (global
.params
.v
.verbose
)
7742 message("strip %s =>\n %s", oldCE
.toChars(), ce
.toChars());