2 * CTFE for expressions involving pointers, slices, array concatenation etc.
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d)
8 * Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d
14 import core
.stdc
.stdio
;
15 import core
.stdc
.string
;
16 import dmd
.arraytypes
;
21 import dmd
.declaration
;
22 import dmd
.dinterpret
;
26 import dmd
.expression
;
31 import dmd
.root
.bitarray
;
32 import dmd
.root
.complex
;
33 import dmd
.root
.ctfloat
;
39 /****************************************************************/
40 /* A type meant as a union of all the Expression types,
41 * to serve essentially as a Variant that will sit on the stack
42 * during CTFE to reduce memory consumption.
44 extern (D
) struct UnionExp
46 // yes, default constructor does nothing
47 extern (D
) this(Expression e
) nothrow
49 memcpy(&this, cast(void*)e
, e
.size
);
52 /* Extract pointer to Expression
54 extern (D
) Expression
exp() return nothrow
56 return cast(Expression
)&u
;
59 /* Convert to an allocated Expression
61 extern (D
) Expression
copy()
64 //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr);
65 assert(e
.size
<= u
.sizeof
);
68 case EXP
.cantExpression
: return CTFEExp
.cantexp
;
69 case EXP
.voidExpression
: return CTFEExp
.voidexp
;
70 case EXP
.break_
: return CTFEExp
.breakexp
;
71 case EXP
.continue_
: return CTFEExp
.continueexp
;
72 case EXP
.goto_
: return CTFEExp
.gotoexp
;
73 default: return e
.copy();
78 // Ensure that the union is suitably aligned.
79 align(8) union _AnonStruct_u
81 char[__traits(classInstanceSize
, Expression
)] exp
;
82 char[__traits(classInstanceSize
, IntegerExp
)] integerexp
;
83 char[__traits(classInstanceSize
, ErrorExp
)] errorexp
;
84 char[__traits(classInstanceSize
, RealExp
)] realexp
;
85 char[__traits(classInstanceSize
, ComplexExp
)] complexexp
;
86 char[__traits(classInstanceSize
, SymOffExp
)] symoffexp
;
87 char[__traits(classInstanceSize
, StringExp
)] stringexp
;
88 char[__traits(classInstanceSize
, ArrayLiteralExp
)] arrayliteralexp
;
89 char[__traits(classInstanceSize
, AssocArrayLiteralExp
)] assocarrayliteralexp
;
90 char[__traits(classInstanceSize
, StructLiteralExp
)] structliteralexp
;
91 char[__traits(classInstanceSize
, CompoundLiteralExp
)] compoundliteralexp
;
92 char[__traits(classInstanceSize
, NullExp
)] nullexp
;
93 char[__traits(classInstanceSize
, DotVarExp
)] dotvarexp
;
94 char[__traits(classInstanceSize
, AddrExp
)] addrexp
;
95 char[__traits(classInstanceSize
, IndexExp
)] indexexp
;
96 char[__traits(classInstanceSize
, SliceExp
)] sliceexp
;
97 char[__traits(classInstanceSize
, VectorExp
)] vectorexp
;
103 void emplaceExp(T
: Expression
, Args
...)(void* p
, Args args
)
105 static if (__VERSION__
< 2099)
106 const init
= typeid(T
).initializer
;
108 const init
= __traits(initSymbol
, T
);
109 p
[0 .. __traits(classInstanceSize
, T
)] = init
[];
110 (cast(T
)p
).__ctor(args
);
113 void emplaceExp(T
: UnionExp
)(T
* p
, Expression e
) nothrow
115 memcpy(p
, cast(void*)e
, e
.size
);
118 // Generate an error message when this exception is not caught
119 void generateUncaughtError(ThrownExceptionExp tee
)
122 Expression e
= resolveSlice((*tee
.thrown
.value
.elements
)[0], &ue
);
123 StringExp se
= e
.toStringExp();
124 error(tee
.thrown
.loc
, "uncaught CTFE exception `%s(%s)`", tee
.thrown
.type
.toChars(), se ? se
.toChars() : e
.toChars());
125 /* Also give the line where the throw statement was. We won't have it
126 * in the case where the ThrowStatement is generated internally
127 * (eg, in ScopeStatement)
129 if (tee
.loc
.isValid() && !tee
.loc
.equals(tee
.thrown
.loc
))
130 .errorSupplemental(tee
.loc
, "thrown from here");
133 /*************************
134 * Same as getFieldIndex, but checks for a direct match with the VarDeclaration
136 * index of the field, or -1 if not found
138 int findFieldIndexByName(const StructDeclaration sd
, const VarDeclaration v
) pure @safe nothrow
140 foreach (i
, field
; sd
.fields
)
148 // True if 'e' is CTFEExp::cantexp, or an exception
149 bool exceptionOrCantInterpret(const Expression e
) @safe nothrow
151 return e
&& (e
.op
== EXP
.cantExpression || e
.op
== EXP
.thrownException || e
.op
== EXP
.showCtfeContext
);
154 /************** Aggregate literals (AA/string/array/struct) ******************/
155 // Given expr, which evaluates to an array/AA/string literal,
156 // return true if it needs to be copied
157 bool needToCopyLiteral(const Expression expr
) nothrow
159 Expression e
= cast()expr
;
164 case EXP
.arrayLiteral
:
165 return e
.isArrayLiteralExp().ownedByCtfe
== OwnedBy
.code
;
166 case EXP
.assocArrayLiteral
:
167 return e
.isAssocArrayLiteralExp().ownedByCtfe
== OwnedBy
.code
;
168 case EXP
.structLiteral
:
169 return e
.isStructLiteralExp().ownedByCtfe
== OwnedBy
.code
;
177 case EXP
.dotVariable
:
182 case EXP
.concatenate
:
183 return needToCopyLiteral(e
.isBinExp().e1
) ||
needToCopyLiteral(e
.isBinExp().e2
);
184 case EXP
.concatenateAssign
:
185 case EXP
.concatenateElemAssign
:
186 case EXP
.concatenateDcharAssign
:
195 private Expressions
* copyLiteralArray(Expressions
* oldelems
, Expression basis
= null)
200 auto newelems
= new Expressions(oldelems
.length
);
201 foreach (i
, el
; *oldelems
)
203 (*newelems
)[i
] = copyLiteral(el ? el
: basis
).copy();
208 // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
209 // This value will be used for in-place modification.
210 UnionExp
copyLiteral(Expression e
)
213 if (auto se
= e
.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
215 char* s
= cast(char*)mem
.xcalloc(se
.len
+ 1, se
.sz
);
216 const slice
= se
.peekData();
217 memcpy(s
, slice
.ptr
, slice
.length
);
218 emplaceExp
!(StringExp
)(&ue
, se
.loc
, s
[0 .. se
.len
* se
.sz
], se
.len
, se
.sz
);
219 StringExp se2
= ue
.exp().isStringExp();
220 se2
.committed
= se
.committed
;
221 se2
.postfix
= se
.postfix
;
223 se2
.ownedByCtfe
= OwnedBy
.ctfe
;
226 if (auto ale
= e
.isArrayLiteralExp())
228 auto elements
= copyLiteralArray(ale
.elements
, ale
.basis
);
230 emplaceExp
!(ArrayLiteralExp
)(&ue
, e
.loc
, e
.type
, elements
);
232 ArrayLiteralExp r
= ue
.exp().isArrayLiteralExp();
233 r
.ownedByCtfe
= OwnedBy
.ctfe
;
236 if (auto aae
= e
.isAssocArrayLiteralExp())
238 emplaceExp
!(AssocArrayLiteralExp
)(&ue
, aae
.loc
, copyLiteralArray(aae
.keys
), copyLiteralArray(aae
.values
));
239 AssocArrayLiteralExp r
= ue
.exp().isAssocArrayLiteralExp();
241 r
.lowering
= aae
.lowering
;
242 r
.ownedByCtfe
= OwnedBy
.ctfe
;
245 if (auto sle
= e
.isStructLiteralExp())
247 /* syntaxCopy doesn't work for struct literals, because of a nasty special
248 * case: block assignment is permitted inside struct literals, eg,
249 * an int[4] array can be initialized with a single int.
251 auto oldelems
= sle
.elements
;
252 auto newelems
= new Expressions(oldelems
.length
);
253 foreach (i
, ref el
; *newelems
)
255 // We need the struct definition to detect block assignment
256 auto v
= sle
.sd
.fields
[i
];
257 auto m
= (*oldelems
)[i
];
259 // If it is a void assignment, use the default initializer
261 m
= voidInitLiteral(v
.type
, v
).copy();
263 if (v
.type
.ty
== Tarray || v
.type
.ty
== Taarray
)
265 // Don't have to copy array references
269 // Buzilla 15681: Copy the source element always.
270 m
= copyLiteral(m
).copy();
272 // Block assignment from inside struct literals
273 if (v
.type
.ty
!= m
.type
.ty
&& v
.type
.ty
== Tsarray
)
275 auto tsa
= v
.type
.isTypeSArray();
276 auto len
= cast(size_t
)tsa
.dim
.toInteger();
277 m
= createBlockDuplicatedArrayLiteral(&ue
, e
.loc
, v
.type
, m
, len
);
284 emplaceExp
!(StructLiteralExp
)(&ue
, e
.loc
, sle
.sd
, newelems
, sle
.stype
);
285 auto r
= ue
.exp().isStructLiteralExp();
287 r
.ownedByCtfe
= OwnedBy
.ctfe
;
288 r
.origin
= sle
.origin
;
296 case EXP
.symbolOffset
:
299 case EXP
.dotVariable
:
306 // Simple value types
307 // Keep e1 for DelegateExp and DotVarExp
308 emplaceExp
!(UnionExp
)(&ue
, e
);
309 Expression r
= ue
.exp();
315 if (auto se
= e
.isSliceExp())
317 if (se
.type
.toBasetype().ty
== Tsarray
)
319 // same with resolveSlice()
320 if (se
.e1
.op
== EXP
.null_
)
322 emplaceExp
!(NullExp
)(&ue
, se
.loc
, se
.type
);
325 ue
= Slice(se
.type
, se
.e1
, se
.lwr
, se
.upr
);
326 auto r
= ue
.exp().isArrayLiteralExp();
327 r
.elements
= copyLiteralArray(r
.elements
);
328 r
.ownedByCtfe
= OwnedBy
.ctfe
;
333 // Array slices only do a shallow copy
334 emplaceExp
!(SliceExp
)(&ue
, e
.loc
, se
.e1
, se
.lwr
, se
.upr
);
335 Expression r
= ue
.exp();
340 if (isPointer(e
.type
))
342 // For pointers, we only do a shallow copy.
343 if (auto ae
= e
.isAddrExp())
344 emplaceExp
!(AddrExp
)(&ue
, e
.loc
, ae
.e1
);
345 else if (auto ie
= e
.isIndexExp())
346 emplaceExp
!(IndexExp
)(&ue
, e
.loc
, ie
.e1
, ie
.e2
);
347 else if (auto dve
= e
.isDotVarExp())
349 emplaceExp
!(DotVarExp
)(&ue
, e
.loc
, dve
.e1
, dve
.var
, dve
.hasOverloads
);
354 Expression r
= ue
.exp();
358 if (auto cre
= e
.isClassReferenceExp())
360 emplaceExp
!(ClassReferenceExp
)(&ue
, e
.loc
, cre
.value
, e
.type
);
363 if (e
.op
== EXP
.error
)
365 emplaceExp
!(UnionExp
)(&ue
, e
);
368 error(e
.loc
, "CTFE internal error: literal `%s`", e
.toChars());
372 /* Deal with type painting.
373 * Type painting is a major nuisance: we can't just set
374 * e.type = type, because that would change the original literal.
375 * But, we can't simply copy the literal either, because that would change
376 * the values of any pointers.
378 Expression
paintTypeOntoLiteral(Type type
, Expression lit
)
380 if (lit
.type
.equals(type
))
382 return paintTypeOntoLiteralCopy(type
, lit
).copy();
385 Expression
paintTypeOntoLiteral(UnionExp
* pue
, Type type
, Expression lit
)
387 if (lit
.type
.equals(type
))
389 *pue
= paintTypeOntoLiteralCopy(type
, lit
);
393 private UnionExp
paintTypeOntoLiteralCopy(Type type
, Expression lit
)
396 if (lit
.type
.equals(type
))
398 emplaceExp
!(UnionExp
)(&ue
, lit
);
401 // If it is a cast to inout, retain the original type of the referenced part.
404 emplaceExp
!(UnionExp
)(&ue
, lit
);
405 ue
.exp().type
= type
;
408 if (auto se
= lit
.isSliceExp())
410 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, se
.e1
, se
.lwr
, se
.upr
);
412 else if (auto ie
= lit
.isIndexExp())
414 emplaceExp
!(IndexExp
)(&ue
, lit
.loc
, ie
.e1
, ie
.e2
);
416 else if (lit
.op
== EXP
.arrayLiteral
)
418 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, lit
, ctfeEmplaceExp
!IntegerExp(Loc
.initial
, 0, Type
.tsize_t
), ArrayLength(Type
.tsize_t
, lit
).copy());
420 else if (lit
.op
== EXP
.string_
)
422 // For strings, we need to introduce another level of indirection
423 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, lit
, ctfeEmplaceExp
!IntegerExp(Loc
.initial
, 0, Type
.tsize_t
), ArrayLength(Type
.tsize_t
, lit
).copy());
425 else if (auto aae
= lit
.isAssocArrayLiteralExp())
427 // TODO: we should be creating a reference to this AAExp, not
428 // just a ref to the keys and values.
429 OwnedBy wasOwned
= aae
.ownedByCtfe
;
430 emplaceExp
!(AssocArrayLiteralExp
)(&ue
, lit
.loc
, aae
.keys
, aae
.values
);
431 aae
= ue
.exp().isAssocArrayLiteralExp();
432 aae
.ownedByCtfe
= wasOwned
;
436 // Can't type paint from struct to struct*; this needs another
437 // level of indirection
438 if (lit
.op
== EXP
.structLiteral
&& isPointer(type
))
439 error(lit
.loc
, "CTFE internal error: painting `%s`", type
.toChars());
440 ue
= copyLiteral(lit
);
442 ue
.exp().type
= type
;
446 /*************************************
447 * If e is a SliceExp, constant fold it.
449 * e = expression to resolve
450 * pue = if not null, store resulting expression here
452 * resulting expression
454 Expression
resolveSlice(Expression e
, UnionExp
* pue
= null)
456 SliceExp se
= e
.isSliceExp();
459 if (se
.e1
.op
== EXP
.null_
)
463 *pue
= Slice(e
.type
, se
.e1
, se
.lwr
, se
.upr
);
467 return Slice(e
.type
, se
.e1
, se
.lwr
, se
.upr
).copy();
470 /* Determine the array length, without interpreting it.
471 * e must be an array literal, or a slice
472 * It's very wasteful to resolve the slice when we only
475 uinteger_t
resolveArrayLength(Expression e
)
480 return e
.isVectorExp().dim
;
487 auto se
= e
.isSliceExp();
488 const ilo
= se
.lwr
.toInteger();
489 const iup
= se
.upr
.toInteger();
494 return e
.isStringExp().len
;
496 case EXP
.arrayLiteral
:
498 const ale
= e
.isArrayLiteralExp();
499 return ale
.elements ? ale
.elements
.length
: 0;
502 case EXP
.assocArrayLiteral
:
504 return e
.isAssocArrayLiteralExp().keys
.length
;
512 /******************************
514 * Create an array literal consisting of 'elem' duplicated 'dim' times.
516 * pue = where to store result
517 * loc = source location where the interpretation occurs
518 * type = target type of the result
519 * elem = the source of array element, it will be owned by the result
520 * dim = element number of the result
522 * Constructed ArrayLiteralExp
524 ArrayLiteralExp
createBlockDuplicatedArrayLiteral(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression elem
, size_t dim
)
526 if (type
.ty
== Tsarray
&& type
.nextOf().ty
== Tsarray
&& elem
.type
.ty
!= Tsarray
)
528 // If it is a multidimensional array literal, do it recursively
529 auto tsa
= type
.nextOf().isTypeSArray();
530 const len
= cast(size_t
)tsa
.dim
.toInteger();
531 elem
= createBlockDuplicatedArrayLiteral(pue
, loc
, type
.nextOf(), elem
, len
);
532 if (elem
== pue
.exp())
537 const tb
= elem
.type
.toBasetype();
538 const mustCopy
= tb
.ty
== Tstruct || tb
.ty
== Tsarray
;
540 auto elements
= new Expressions(dim
);
541 foreach (i
, ref el
; *elements
)
543 el
= mustCopy
&& i ?
copyLiteral(elem
).copy() : elem
;
545 emplaceExp
!(ArrayLiteralExp
)(pue
, loc
, type
, elements
);
546 auto ale
= pue
.exp().isArrayLiteralExp();
547 ale
.ownedByCtfe
= OwnedBy
.ctfe
;
551 /******************************
553 * Create a string literal consisting of 'value' duplicated 'dim' times.
555 StringExp
createBlockDuplicatedStringLiteral(UnionExp
* pue
, const ref Loc loc
, Type type
, dchar value
, size_t dim
, ubyte sz
)
557 auto s
= cast(char*)mem
.xcalloc(dim
, sz
);
558 foreach (elemi
; 0 .. dim
)
563 s
[elemi
] = cast(char)value
;
566 (cast(wchar*)s
)[elemi
] = cast(wchar)value
;
569 (cast(dchar*)s
)[elemi
] = value
;
572 (cast(ulong*)s
)[elemi
] = value
;
578 emplaceExp
!(StringExp
)(pue
, loc
, s
[0 .. dim
* sz
], dim
, sz
);
579 auto se
= pue
.exp().isStringExp();
582 se
.ownedByCtfe
= OwnedBy
.ctfe
;
586 // Return true if t is an AA
587 bool isAssocArray(Type t
)
589 return t
.toBasetype().isTypeAArray() !is null;
592 // Given a template AA type, extract the corresponding built-in AA type
593 TypeAArray
toBuiltinAAType(Type t
)
595 return t
.toBasetype().isTypeAArray();
598 /************** TypeInfo operations ************************************/
599 // Return true if type is TypeInfo_Class
600 bool isTypeInfo_Class(const Type type
) nothrow
602 auto tc
= cast()type
.isTypeClass();
603 return tc
&& (Type
.dtypeinfo
== tc
.sym || Type
.dtypeinfo
.isBaseOf(tc
.sym
, null));
606 /************** Pointer operations ************************************/
607 // Return true if t is a pointer (not a function pointer)
608 bool isPointer(Type t
)
610 Type tb
= t
.toBasetype();
611 return tb
.ty
== Tpointer
&& tb
.nextOf().ty
!= Tfunction
;
614 // For CTFE only. Returns true if 'e' is true or a non-null pointer.
615 bool isTrueBool(Expression e
)
617 return e
.toBool().hasValue(true) ||
((e
.type
.ty
== Tpointer || e
.type
.ty
== Tclass
) && e
.op
!= EXP
.null_
);
620 /* Is it safe to convert from srcPointee* to destPointee* ?
621 * srcPointee is the genuine type (never void).
622 * destPointee may be void.
624 bool isSafePointerCast(Type srcPointee
, Type destPointee
)
626 // It's safe to cast S** to D** if it's OK to cast S* to D*
627 while (srcPointee
.ty
== Tpointer
&& destPointee
.ty
== Tpointer
)
629 srcPointee
= srcPointee
.nextOf();
630 destPointee
= destPointee
.nextOf();
632 // It's OK if both are the same (modulo const)
633 if (srcPointee
.constConv(destPointee
))
636 // It's ok to cast from/to shared because CTFE is single threaded anyways
637 if (srcPointee
.unSharedOf() == destPointee
.unSharedOf())
640 // It's OK if function pointers differ only in safe/pure/nothrow
641 if (srcPointee
.ty
== Tfunction
&& destPointee
.ty
== Tfunction
)
643 import dmd
.typesem
: covariant
;
644 return srcPointee
.covariant(destPointee
) == Covariant
.yes ||
645 destPointee
.covariant(srcPointee
) == Covariant
.yes
;
647 // it's OK to cast to void*
648 if (destPointee
.ty
== Tvoid
)
650 // It's OK to cast from V[K] to void*
651 if (srcPointee
.ty
== Taarray
&& destPointee
== Type
.tvoidptr
)
653 // It's OK if they are the same size (static array of) integers, eg:
655 // int[5][] --> uint[5][]
656 if (srcPointee
.ty
== Tsarray
&& destPointee
.ty
== Tsarray
)
658 if (srcPointee
.size() != destPointee
.size())
660 srcPointee
= srcPointee
.baseElemOf();
661 destPointee
= destPointee
.baseElemOf();
663 return srcPointee
.isintegral() && destPointee
.isintegral() && srcPointee
.size() == destPointee
.size();
666 Expression
getAggregateFromPointer(Expression e
, dinteger_t
* ofs
)
669 if (auto ae
= e
.isAddrExp())
671 if (auto soe
= e
.isSymOffExp())
673 if (auto dve
= e
.isDotVarExp())
676 const v
= dve
.var
.isVarDeclaration();
678 StructLiteralExp se
= (ex
.op
== EXP
.classReference
)
679 ? ex
.isClassReferenceExp().value
680 : ex
.isStructLiteralExp();
682 // We can't use getField, because it makes a copy
683 const i
= (ex
.op
== EXP
.classReference
)
684 ? ex
.isClassReferenceExp().getFieldIndex(e
.type
, v
.offset
)
685 : se
.getFieldIndex(e
.type
, v
.offset
);
686 e
= (*se
.elements
)[i
];
688 if (auto ie
= e
.isIndexExp())
690 // Note that each AA element is part of its own memory block
691 if ((ie
.e1
.type
.ty
== Tarray || ie
.e1
.type
.ty
== Tsarray || ie
.e1
.op
== EXP
.string_ || ie
.e1
.op
== EXP
.arrayLiteral
) && ie
.e2
.op
== EXP
.int64
)
693 *ofs
= ie
.e2
.toInteger();
697 if (auto se
= e
.isSliceExp())
699 if (se
&& e
.type
.toBasetype().ty
== Tsarray
&&
700 (se
.e1
.type
.ty
== Tarray || se
.e1
.type
.ty
== Tsarray || se
.e1
.op
== EXP
.string_ || se
.e1
.op
== EXP
.arrayLiteral
) && se
.lwr
.op
== EXP
.int64
)
702 *ofs
= se
.lwr
.toInteger();
707 // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
708 if (auto ie
= e
.isIntegerExp())
709 if (ie
.type
.ty
== Tpointer
&& ie
.getInteger() == 0)
710 return new NullExp(ie
.loc
, e
.type
.nextOf());
711 // Those casts are invalid, but let the rest of the code handle it,
712 // as it could be something like `x !is null`, which doesn't need
713 // to dereference the pointer, even if the pointer is `cast(void*)420`.
718 /** Return true if agg1 and agg2 are pointers to the same memory block
720 bool pointToSameMemoryBlock(Expression agg1
, Expression agg2
)
724 // For integers cast to pointers, we regard them as non-comparable
725 // unless they are identical. (This may be overly strict).
726 if (agg1
.op
== EXP
.int64
&& agg2
.op
== EXP
.int64
&& agg1
.toInteger() == agg2
.toInteger())
730 // Note that type painting can occur with VarExp, so we
731 // must compare the variables being pointed to.
732 if (agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
)
736 if (agg1
.op
== EXP
.symbolOffset
&& agg2
.op
== EXP
.symbolOffset
&& agg1
.isSymOffExp().var
== agg2
.isSymOffExp().var
)
743 // return e1 - e2 as an integer, or error if not possible
744 Expression
pointerDifference(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression e1
, Expression e2
)
746 dinteger_t ofs1
, ofs2
;
747 Expression agg1
= getAggregateFromPointer(e1
, &ofs1
);
748 Expression agg2
= getAggregateFromPointer(e2
, &ofs2
);
751 Type pointee
= agg1
.type
.nextOf();
752 const sz
= pointee
.size();
753 emplaceExp
!(IntegerExp
)(pue
, loc
, (ofs1
- ofs2
) * sz
, type
);
755 else if (agg1
.op
== EXP
.string_
&& agg2
.op
== EXP
.string_
&&
756 agg1
.isStringExp().peekString().ptr
== agg2
.isStringExp().peekString().ptr
)
758 Type pointee
= agg1
.type
.nextOf();
759 const sz
= pointee
.size();
760 emplaceExp
!(IntegerExp
)(pue
, loc
, (ofs1
- ofs2
) * sz
, type
);
762 else if (agg1
.op
== EXP
.symbolOffset
&& agg2
.op
== EXP
.symbolOffset
&&
763 agg1
.isSymOffExp().var
== agg2
.isSymOffExp().var
)
765 emplaceExp
!(IntegerExp
)(pue
, loc
, ofs1
- ofs2
, type
);
769 error(loc
, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1
.toChars(), e2
.toChars());
770 emplaceExp
!(CTFEExp
)(pue
, EXP
.cantExpression
);
775 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
776 // and op is EXP.add or EXP.min
777 Expression
pointerArithmetic(UnionExp
* pue
, const ref Loc loc
, EXP op
, Type type
, Expression eptr
, Expression e2
)
779 if (eptr
.type
.nextOf().ty
== Tvoid
)
781 error(loc
, "cannot perform arithmetic on `void*` pointers at compile time");
783 emplaceExp
!(CTFEExp
)(pue
, EXP
.cantExpression
);
786 if (eptr
.op
== EXP
.address
)
787 eptr
= eptr
.isAddrExp().e1
;
789 Expression agg1
= getAggregateFromPointer(eptr
, &ofs1
);
790 if (agg1
.op
== EXP
.symbolOffset
)
792 if (agg1
.isSymOffExp().var
.type
.ty
!= Tsarray
)
794 error(loc
, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
798 else if (agg1
.op
!= EXP
.string_
&& agg1
.op
!= EXP
.arrayLiteral
)
800 error(loc
, "cannot perform pointer arithmetic on non-arrays at compile time");
803 dinteger_t ofs2
= e2
.toInteger();
804 Type pointee
= agg1
.type
.toBasetype().nextOf();
805 dinteger_t sz
= pointee
.size();
808 if (auto soe
= agg1
.isSymOffExp())
811 len
= soe
.var
.type
.isTypeSArray().dim
.toInteger();
815 Expression dollar
= ArrayLength(Type
.tsize_t
, agg1
).copy();
816 assert(!CTFEExp
.isCantExp(dollar
));
818 len
= dollar
.toInteger();
820 if (op
== EXP
.add || op
== EXP
.addAssign || op
== EXP
.plusPlus
)
822 else if (op
== EXP
.min || op
== EXP
.minAssign || op
== EXP
.minusMinus
)
826 error(loc
, "CTFE internal error: bad pointer operation");
829 if (indx
< 0 || len
< indx
)
831 error(loc
, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx
, len
);
834 if (agg1
.op
== EXP
.symbolOffset
)
836 emplaceExp
!(SymOffExp
)(pue
, loc
, agg1
.isSymOffExp().var
, indx
* sz
);
837 SymOffExp se
= pue
.exp().isSymOffExp();
841 if (agg1
.op
!= EXP
.arrayLiteral
&& agg1
.op
!= EXP
.string_
)
843 error(loc
, "CTFE internal error: pointer arithmetic `%s`", agg1
.toChars());
846 if (auto tsa
= eptr
.type
.toBasetype().isTypeSArray())
848 dinteger_t dim
= tsa
.dim
.toInteger();
849 // Create a CTFE pointer &agg1[indx .. indx+dim]
850 auto se
= ctfeEmplaceExp
!SliceExp(loc
, agg1
,
851 ctfeEmplaceExp
!IntegerExp(loc
, indx
, Type
.tsize_t
),
852 ctfeEmplaceExp
!IntegerExp(loc
, indx
+ dim
, Type
.tsize_t
));
853 se
.type
= type
.toBasetype().nextOf();
854 emplaceExp
!(AddrExp
)(pue
, loc
, se
);
855 pue
.exp().type
= type
;
858 // Create a CTFE pointer &agg1[indx]
859 auto ofs
= ctfeEmplaceExp
!IntegerExp(loc
, indx
, Type
.tsize_t
);
860 Expression ie
= ctfeEmplaceExp
!IndexExp(loc
, agg1
, ofs
);
861 ie
.type
= type
.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
862 emplaceExp
!(AddrExp
)(pue
, loc
, ie
);
863 pue
.exp().type
= type
;
867 // Return 1 if true, 0 if false
868 // -1 if comparison is illegal because they point to non-comparable memory blocks
869 int comparePointers(EXP op
, Expression agg1
, dinteger_t ofs1
, Expression agg2
, dinteger_t ofs2
)
871 if (pointToSameMemoryBlock(agg1
, agg2
))
879 case EXP
.lessOrEqual
:
882 case EXP
.greaterThan
:
885 case EXP
.greaterOrEqual
:
892 case EXP
.notIdentity
:
901 const null1
= (agg1
.op
== EXP
.null_
);
902 const null2
= (agg2
.op
== EXP
.null_
);
909 cmp = null1
&& !null2
;
911 case EXP
.greaterThan
:
912 cmp = !null1
&& null2
;
914 case EXP
.lessOrEqual
:
917 case EXP
.greaterOrEqual
:
922 case EXP
.notIdentity
: // 'cmp' gets inverted below
924 cmp = (null1
== null2
);
936 case EXP
.notIdentity
: // 'cmp' gets inverted below
941 return -1; // memory blocks are different
944 if (op
== EXP
.notIdentity || op
== EXP
.notEqual
)
949 // True if conversion from type 'from' to 'to' involves a reinterpret_cast
950 // floating point -> integer or integer -> floating point
951 bool isFloatIntPaint(Type to
, Type from
)
953 return from
.size() == to
.size() && (from
.isintegral() && to
.isfloating() || from
.isfloating() && to
.isintegral());
956 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
957 Expression
paintFloatInt(UnionExp
* pue
, Expression fromVal
, Type to
)
959 if (exceptionOrCantInterpret(fromVal
))
961 assert(to
.size() == 4 || to
.size() == 8);
962 return Compiler
.paintAsType(pue
, fromVal
, to
);
965 /******** Constant folding, with support for CTFE ***************************/
966 /// Return true if non-pointer expression e can be compared
967 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
968 bool isCtfeComparable(Expression e
)
970 if (e
.op
== EXP
.slice
)
971 e
= e
.isSliceExp().e1
;
972 if (e
.isConst() != 1)
974 if (e
.op
== EXP
.null_ || e
.op
== EXP
.string_ || e
.op
== EXP
.function_ || e
.op
== EXP
.delegate_ || e
.op
== EXP
.arrayLiteral || e
.op
== EXP
.structLiteral || e
.op
== EXP
.assocArrayLiteral || e
.op
== EXP
.classReference
)
978 // https://issues.dlang.org/show_bug.cgi?id=14123
979 // TypeInfo object is comparable in CTFE
980 if (e
.op
== EXP
.typeid_
)
987 /// Map EXP comparison ops
988 private bool numCmp(N
)(EXP op
, N n1
, N n2
) nothrow
994 case EXP
.lessOrEqual
:
996 case EXP
.greaterThan
:
998 case EXP
.greaterOrEqual
:
1006 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1007 bool specificCmp(EXP op
, int rawCmp
) @safe nothrow
1009 return numCmp
!int(op
, rawCmp
, 0);
1012 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1013 bool intUnsignedCmp(EXP op
, dinteger_t n1
, dinteger_t n2
) @safe nothrow
1015 return numCmp
!dinteger_t(op
, n1
, n2
);
1018 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1019 bool intSignedCmp(EXP op
, sinteger_t n1
, sinteger_t n2
) @safe nothrow
1021 return numCmp
!sinteger_t(op
, n1
, n2
);
1024 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1025 bool realCmp(EXP op
, real_t r1
, real_t r2
) @safe nothrow
1027 // Don't rely on compiler, handle NAN arguments separately
1028 if (CTFloat
.isNaN(r1
) || CTFloat
.isNaN(r2
)) // if unordered
1033 case EXP
.lessOrEqual
:
1034 case EXP
.greaterThan
:
1035 case EXP
.greaterOrEqual
:
1044 return numCmp
!real_t(op
, r1
, r2
);
1048 /* Conceptually the same as memcmp(e1, e2).
1049 * e1 and e2 may be strings, arrayliterals, or slices.
1050 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1051 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1055 private int ctfeCmpArrays(const ref Loc loc
, Expression e1
, Expression e2
, uinteger_t len
)
1057 // Resolve slices, if necessary
1062 if (auto sle1
= x1
.isSliceExp())
1064 lo1
= sle1
.lwr
.toInteger();
1067 auto se1
= x1
.isStringExp();
1068 auto ae1
= x1
.isArrayLiteralExp();
1071 if (auto sle2
= x2
.isSliceExp())
1073 lo2
= sle2
.lwr
.toInteger();
1076 auto se2
= x2
.isStringExp();
1077 auto ae2
= x2
.isArrayLiteralExp();
1079 // Now both must be either EXP.arrayLiteral or EXP.string_
1081 return sliceCmpStringWithString(se1
, se2
, cast(size_t
)lo1
, cast(size_t
)lo2
, cast(size_t
)len
);
1083 return sliceCmpStringWithArray(se1
, ae2
, cast(size_t
)lo1
, cast(size_t
)lo2
, cast(size_t
)len
);
1085 return -sliceCmpStringWithArray(se2
, ae1
, cast(size_t
)lo2
, cast(size_t
)lo1
, cast(size_t
)len
);
1087 // Comparing two array literals. This case is potentially recursive.
1088 // If they aren't strings, we just need an equality check rather than
1090 const bool needCmp
= ae1
.type
.nextOf().isintegral();
1091 foreach (size_t i
; 0 .. cast(size_t
)len
)
1093 Expression ee1
= (*ae1
.elements
)[cast(size_t
)(lo1
+ i
)];
1094 Expression ee2
= (*ae2
.elements
)[cast(size_t
)(lo2
+ i
)];
1097 const sinteger_t c
= ee1
.toInteger() - ee2
.toInteger();
1105 if (ctfeRawCmp(loc
, ee1
, ee2
))
1112 /* Given a delegate expression e, return .funcptr.
1113 * If e is NullExp, return NULL.
1115 private FuncDeclaration
funcptrOf(Expression e
) @safe nothrow
1117 assert(e
.type
.ty
== Tdelegate
);
1118 if (auto de = e
.isDelegateExp())
1120 if (auto fe
= e
.isFuncExp())
1122 assert(e
.op
== EXP
.null_
);
1126 private bool isArray(const Expression e
) @safe nothrow
1128 return e
.op
== EXP
.arrayLiteral || e
.op
== EXP
.string_ || e
.op
== EXP
.slice || e
.op
== EXP
.null_
;
1133 * loc = source file location
1135 * e2 = right operand
1136 * identity = true for `is` identity comparisons
1138 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1139 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1141 private int ctfeRawCmp(const ref Loc loc
, Expression e1
, Expression e2
, bool identity
= false)
1143 if (e1
.op
== EXP
.classReference || e2
.op
== EXP
.classReference
)
1145 if (e1
.op
== EXP
.classReference
&& e2
.op
== EXP
.classReference
&&
1146 e1
.isClassReferenceExp().value
== e2
.isClassReferenceExp().value
)
1150 if (e1
.op
== EXP
.typeid_
&& e2
.op
== EXP
.typeid_
)
1152 // printf("e1: %s\n", e1.toChars());
1153 // printf("e2: %s\n", e2.toChars());
1154 Type t1
= isType(e1
.isTypeidExp().obj
);
1155 Type t2
= isType(e2
.isTypeidExp().obj
);
1160 // null == null, regardless of type
1161 if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.null_
)
1163 if (e1
.type
.ty
== Tpointer
&& e2
.type
.ty
== Tpointer
)
1165 // Can only be an equality test.
1166 dinteger_t ofs1
, ofs2
;
1167 Expression agg1
= getAggregateFromPointer(e1
, &ofs1
);
1168 Expression agg2
= getAggregateFromPointer(e2
, &ofs2
);
1169 if ((agg1
== agg2
) ||
(agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
))
1176 if (e1
.type
.ty
== Tdelegate
&& e2
.type
.ty
== Tdelegate
)
1178 // If .funcptr isn't the same, they are not equal
1179 if (funcptrOf(e1
) != funcptrOf(e2
))
1181 // If both are delegate literals, assume they have the
1182 // same closure pointer. TODO: We don't support closures yet!
1183 if (e1
.op
== EXP
.function_
&& e2
.op
== EXP
.function_
)
1185 assert(e1
.op
== EXP
.delegate_
&& e2
.op
== EXP
.delegate_
);
1186 // Same .funcptr. Do they have the same .ptr?
1187 Expression ptr1
= e1
.isDelegateExp().e1
;
1188 Expression ptr2
= e2
.isDelegateExp().e1
;
1189 dinteger_t ofs1
, ofs2
;
1190 Expression agg1
= getAggregateFromPointer(ptr1
, &ofs1
);
1191 Expression agg2
= getAggregateFromPointer(ptr2
, &ofs2
);
1192 // If they are EXP.variable, it means they are FuncDeclarations
1193 if ((agg1
== agg2
&& ofs1
== ofs2
) ||
(agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
))
1199 if (isArray(e1
) && isArray(e2
))
1201 const uinteger_t len1
= resolveArrayLength(e1
);
1202 const uinteger_t len2
= resolveArrayLength(e2
);
1203 // workaround for dmc optimizer bug calculating wrong len for
1204 // uinteger_t len = (len1 < len2 ? len1 : len2);
1205 // if (len == 0) ...
1206 if (len1
> 0 && len2
> 0)
1208 const uinteger_t len
= (len1
< len2 ? len1
: len2
);
1209 const int res
= ctfeCmpArrays(loc
, e1
, e2
, len
);
1213 return cast(int)(len1
- len2
);
1215 if (e1
.type
.isintegral())
1217 return e1
.toInteger() != e2
.toInteger();
1219 if (identity
&& e1
.type
.isfloating())
1220 return !e1
.isIdentical(e2
);
1221 if (e1
.type
.isreal() || e1
.type
.isimaginary())
1223 real_t r1
= e1
.type
.isreal() ? e1
.toReal() : e1
.toImaginary();
1224 real_t r2
= e1
.type
.isreal() ? e2
.toReal() : e2
.toImaginary();
1225 if (CTFloat
.isNaN(r1
) || CTFloat
.isNaN(r2
)) // if unordered
1227 return 1; // they are not equal
1234 else if (e1
.type
.iscomplex())
1236 return e1
.toComplex() != e2
.toComplex();
1238 if (e1
.op
== EXP
.structLiteral
&& e2
.op
== EXP
.structLiteral
)
1240 StructLiteralExp es1
= e1
.isStructLiteralExp();
1241 StructLiteralExp es2
= e2
.isStructLiteralExp();
1242 // For structs, we only need to return 0 or 1 (< and > aren't legal).
1243 if (es1
.sd
!= es2
.sd
)
1245 else if ((!es1
.elements ||
!es1
.elements
.length
) && (!es2
.elements ||
!es2
.elements
.length
))
1246 return 0; // both arrays are empty
1247 else if (!es1
.elements ||
!es2
.elements
)
1249 else if (es1
.elements
.length
!= es2
.elements
.length
)
1253 foreach (size_t i
; 0 .. es1
.elements
.length
)
1255 Expression ee1
= (*es1
.elements
)[i
];
1256 Expression ee2
= (*es2
.elements
)[i
];
1258 // https://issues.dlang.org/show_bug.cgi?id=16284
1259 if (ee1
.op
== EXP
.void_
&& ee2
.op
== EXP
.void_
) // if both are VoidInitExp
1266 const int cmp = ctfeRawCmp(loc
, ee1
, ee2
, identity
);
1270 return 0; // All elements are equal
1273 if (e1
.op
== EXP
.assocArrayLiteral
&& e2
.op
== EXP
.assocArrayLiteral
)
1275 AssocArrayLiteralExp es1
= e1
.isAssocArrayLiteralExp();
1276 AssocArrayLiteralExp es2
= e2
.isAssocArrayLiteralExp();
1277 size_t dim
= es1
.keys
.length
;
1278 if (es2
.keys
.length
!= dim
)
1282 foreach (size_t i
; 0 .. dim
)
1284 Expression k1
= (*es1
.keys
)[i
];
1285 Expression v1
= (*es1
.values
)[i
];
1286 Expression v2
= null;
1287 foreach (size_t j
; 0 .. dim
)
1291 Expression k2
= (*es2
.keys
)[j
];
1292 if (ctfeRawCmp(loc
, k1
, k2
, identity
))
1295 v2
= (*es2
.values
)[j
];
1298 if (!v2 ||
ctfeRawCmp(loc
, v1
, v2
, identity
))
1305 else if (e1
.op
== EXP
.assocArrayLiteral
&& e2
.op
== EXP
.null_
)
1307 return e1
.isAssocArrayLiteralExp
.keys
.length
!= 0;
1309 else if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.assocArrayLiteral
)
1311 return e2
.isAssocArrayLiteralExp
.keys
.length
!= 0;
1314 error(loc
, "CTFE internal error: bad compare of `%s` and `%s`", e1
.toChars(), e2
.toChars());
1318 /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
1319 bool ctfeEqual(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1321 return !ctfeRawCmp(loc
, e1
, e2
) ^
(op
== EXP
.notEqual
);
1324 /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
1325 bool ctfeIdentity(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1327 //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
1328 //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr,
1329 // EXPtoString(e1.op).ptr, e1.toChars(), EXPtoString(e2.op).ptr, e1.toChars());
1331 if (e1
.op
== EXP
.null_
)
1333 cmp = (e2
.op
== EXP
.null_
);
1335 else if (e2
.op
== EXP
.null_
)
1339 else if (e1
.op
== EXP
.symbolOffset
&& e2
.op
== EXP
.symbolOffset
)
1341 SymOffExp es1
= e1
.isSymOffExp();
1342 SymOffExp es2
= e2
.isSymOffExp();
1343 cmp = (es1
.var
== es2
.var
&& es1
.offset
== es2
.offset
);
1345 else if (e1
.type
.isfloating())
1346 cmp = e1
.isIdentical(e2
);
1349 cmp = !ctfeRawCmp(loc
, e1
, e2
, true);
1351 if (op
== EXP
.notIdentity || op
== EXP
.notEqual
)
1356 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
1357 bool ctfeCmp(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1359 Type t1
= e1
.type
.toBasetype();
1360 Type t2
= e2
.type
.toBasetype();
1362 if (t1
.isString() && t2
.isString())
1363 return specificCmp(op
, ctfeRawCmp(loc
, e1
, e2
));
1364 else if (t1
.isreal())
1365 return realCmp(op
, e1
.toReal(), e2
.toReal());
1366 else if (t1
.isimaginary())
1367 return realCmp(op
, e1
.toImaginary(), e2
.toImaginary());
1368 else if (t1
.isunsigned() || t2
.isunsigned())
1369 return intUnsignedCmp(op
, e1
.toInteger(), e2
.toInteger());
1371 return intSignedCmp(op
, e1
.toInteger(), e2
.toInteger());
1374 UnionExp
ctfeCat(const ref Loc loc
, Type type
, Expression e1
, Expression e2
)
1376 Type t1
= e1
.type
.toBasetype();
1377 Type t2
= e2
.type
.toBasetype();
1379 if (e2
.op
== EXP
.string_
&& e1
.op
== EXP
.arrayLiteral
&& t1
.nextOf().isintegral())
1381 // [chars] ~ string => string (only valid for CTFE)
1382 StringExp es1
= e2
.isStringExp();
1383 ArrayLiteralExp es2
= e1
.isArrayLiteralExp();
1384 const len
= es1
.len
+ es2
.elements
.length
;
1386 void* s
= mem
.xmalloc((len
+ 1) * sz
);
1387 const data1
= es1
.peekData();
1388 memcpy(cast(char*)s
+ sz
* es2
.elements
.length
, data1
.ptr
, data1
.length
);
1389 foreach (size_t i
; 0 .. es2
.elements
.length
)
1391 Expression es2e
= (*es2
.elements
)[i
];
1392 if (es2e
.op
!= EXP
.int64
)
1394 emplaceExp
!(CTFEExp
)(&ue
, EXP
.cantExpression
);
1397 dinteger_t v
= es2e
.toInteger();
1398 Port
.valcpy(cast(char*)s
+ i
* sz
, v
, sz
);
1400 // Add terminating 0
1401 memset(cast(char*)s
+ len
* sz
, 0, sz
);
1402 emplaceExp
!(StringExp
)(&ue
, loc
, s
[0 .. len
* sz
], len
, sz
);
1403 StringExp es
= ue
.exp().isStringExp();
1404 es
.committed
= false;
1408 if (e1
.op
== EXP
.string_
&& e2
.op
== EXP
.arrayLiteral
&& t2
.nextOf().isintegral())
1410 // string ~ [chars] => string (only valid for CTFE)
1411 // Concatenate the strings
1412 StringExp es1
= e1
.isStringExp();
1413 ArrayLiteralExp es2
= e2
.isArrayLiteralExp();
1414 const len
= es1
.len
+ es2
.elements
.length
;
1416 void* s
= mem
.xmalloc((len
+ 1) * sz
);
1417 auto slice
= es1
.peekData();
1418 memcpy(s
, slice
.ptr
, slice
.length
);
1419 foreach (size_t i
; 0 .. es2
.elements
.length
)
1421 Expression es2e
= (*es2
.elements
)[i
];
1422 if (es2e
.op
!= EXP
.int64
)
1424 emplaceExp
!(CTFEExp
)(&ue
, EXP
.cantExpression
);
1427 const v
= es2e
.toInteger();
1428 Port
.valcpy(cast(char*)s
+ (es1
.len
+ i
) * sz
, v
, sz
);
1430 // Add terminating 0
1431 memset(cast(char*)s
+ len
* sz
, 0, sz
);
1432 emplaceExp
!(StringExp
)(&ue
, loc
, s
[0 .. len
* sz
], len
, sz
);
1433 StringExp es
= ue
.exp().isStringExp();
1435 es
.committed
= false; //es1.committed;
1439 if (e1
.op
== EXP
.arrayLiteral
&& e2
.op
== EXP
.arrayLiteral
&& t1
.nextOf().equals(t2
.nextOf()))
1441 // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
1442 ArrayLiteralExp es1
= e1
.isArrayLiteralExp();
1443 ArrayLiteralExp es2
= e2
.isArrayLiteralExp();
1444 emplaceExp
!(ArrayLiteralExp
)(&ue
, es1
.loc
, type
, copyLiteralArray(es1
.elements
));
1445 es1
= ue
.exp().isArrayLiteralExp();
1446 es1
.elements
.insert(es1
.elements
.length
, copyLiteralArray(es2
.elements
));
1449 if (e1
.op
== EXP
.arrayLiteral
&& e2
.op
== EXP
.null_
&& t1
.nextOf().equals(t2
.nextOf()))
1451 // [ e1 ] ~ null ----> [ e1 ].dup
1452 ue
= paintTypeOntoLiteralCopy(type
, copyLiteral(e1
).copy());
1455 if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.arrayLiteral
&& t1
.nextOf().equals(t2
.nextOf()))
1457 // null ~ [ e2 ] ----> [ e2 ].dup
1458 ue
= paintTypeOntoLiteralCopy(type
, copyLiteral(e2
).copy());
1461 ue
= Cat(loc
, type
, e1
, e2
);
1465 /* Given an AA literal 'ae', and a key 'e2':
1466 * Return ae[e2] if present, or NULL if not found.
1468 Expression
findKeyInAA(const ref Loc loc
, AssocArrayLiteralExp ae
, Expression e2
)
1470 /* Search the keys backwards, in case there are duplicate keys
1472 for (size_t i
= ae
.keys
.length
; i
;)
1475 Expression ekey
= (*ae
.keys
)[i
];
1476 const int eq
= ctfeEqual(loc
, EXP
.equal
, ekey
, e2
);
1479 return (*ae
.values
)[i
];
1485 /* Same as for constfold.Index, except that it only works for static arrays,
1486 * dynamic arrays, and strings. We know that e1 is an
1487 * interpreted CTFE expression, so it cannot have side-effects.
1489 Expression
ctfeIndex(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression e1
, uinteger_t indx
)
1491 //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
1493 if (auto es1
= e1
.isStringExp())
1495 if (indx
>= es1
.len
)
1497 error(loc
, "string index %llu is out of bounds `[0 .. %llu]`", indx
, cast(ulong)es1
.len
);
1498 return CTFEExp
.cantexp
;
1500 emplaceExp
!IntegerExp(pue
, loc
, es1
.getIndex(cast(size_t
) indx
), type
);
1504 if (auto ale
= e1
.isArrayLiteralExp())
1506 if (indx
>= ale
.elements
.length
)
1508 error(loc
, "array index %llu is out of bounds `%s[0 .. %llu]`", indx
, e1
.toChars(), cast(ulong)ale
.elements
.length
);
1509 return CTFEExp
.cantexp
;
1511 Expression e
= (*ale
.elements
)[cast(size_t
)indx
];
1512 return paintTypeOntoLiteral(pue
, type
, e
);
1518 Expression
ctfeCast(UnionExp
* pue
, const ref Loc loc
, Type type
, Type to
, Expression e
, bool explicitCast
= false)
1522 return paintTypeOntoLiteral(pue
, to
, e
);
1525 if (e
.op
== EXP
.null_
)
1528 if (e
.op
== EXP
.classReference
)
1530 // Disallow reinterpreting class casts. Do this by ensuring that
1531 // the original class can implicitly convert to the target class.
1532 // Also do not check 'alias this' for explicit cast expressions.
1533 auto tclass
= e
.isClassReferenceExp().originalClass().type
.isTypeClass();
1534 auto match
= explicitCast ? tclass
.implicitConvToWithoutAliasThis(to
.mutableOf())
1535 : tclass
.implicitConvTo(to
.mutableOf());
1540 emplaceExp
!(NullExp
)(pue
, loc
, to
);
1545 // Allow TypeInfo type painting
1546 if (isTypeInfo_Class(e
.type
) && e
.type
.implicitConvTo(to
))
1549 // Allow casting away const for struct literals
1550 if (e
.op
== EXP
.structLiteral
&& e
.type
.toBasetype().castMod(0) == to
.toBasetype().castMod(0))
1554 if (e
.type
.equals(type
) && type
.equals(to
))
1556 // necessary not to change e's address for pointer comparisons
1559 else if (to
.toBasetype().ty
== Tarray
&&
1560 type
.toBasetype().ty
== Tarray
&&
1561 to
.toBasetype().nextOf().size() == type
.toBasetype().nextOf().size())
1563 // https://issues.dlang.org/show_bug.cgi?id=12495
1564 // Array reinterpret casts: eg. string to immutable(ubyte)[]
1569 *pue
= Cast(loc
, type
, to
, e
);
1573 if (CTFEExp
.isCantExp(r
))
1574 error(loc
, "cannot cast `%s` to `%s` at compile time", e
.toChars(), to
.toChars());
1576 if (auto ae
= e
.isArrayLiteralExp())
1577 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
1579 if (auto se
= e
.isStringExp())
1580 se
.ownedByCtfe
= OwnedBy
.ctfe
;
1585 /******** Assignment helper functions ***************************/
1586 /* Set dest = src, where both dest and src are container value literals
1587 * (ie, struct literals, or static arrays (can be an array literal or a string))
1588 * Assignment is recursively in-place.
1589 * Purpose: any reference to a member of 'dest' will remain valid after the
1592 void assignInPlace(Expression dest
, Expression src
)
1594 if (!(dest
.op
== EXP
.structLiteral || dest
.op
== EXP
.arrayLiteral || dest
.op
== EXP
.string_
))
1596 printf("invalid op %d %d\n", src
.op
, dest
.op
);
1599 Expressions
* oldelems
;
1600 Expressions
* newelems
;
1601 if (dest
.op
== EXP
.structLiteral
)
1603 assert(dest
.op
== src
.op
);
1604 oldelems
= dest
.isStructLiteralExp().elements
;
1605 newelems
= src
.isStructLiteralExp().elements
;
1606 auto sd
= dest
.isStructLiteralExp().sd
;
1607 const nfields
= sd
.nonHiddenFields();
1608 const nvthis
= sd
.fields
.length
- nfields
;
1609 if (nvthis
&& oldelems
.length
>= nfields
&& oldelems
.length
< newelems
.length
)
1610 foreach (_
; 0 .. newelems
.length
- oldelems
.length
)
1611 oldelems
.push(null);
1613 else if (dest
.op
== EXP
.arrayLiteral
&& src
.op
== EXP
.arrayLiteral
)
1615 oldelems
= dest
.isArrayLiteralExp().elements
;
1616 newelems
= src
.isArrayLiteralExp().elements
;
1618 else if (dest
.op
== EXP
.string_
&& src
.op
== EXP
.string_
)
1620 sliceAssignStringFromString(dest
.isStringExp(), src
.isStringExp(), 0);
1623 else if (dest
.op
== EXP
.arrayLiteral
&& src
.op
== EXP
.string_
)
1625 sliceAssignArrayLiteralFromString(dest
.isArrayLiteralExp(), src
.isStringExp(), 0);
1628 else if (src
.op
== EXP
.arrayLiteral
&& dest
.op
== EXP
.string_
)
1630 sliceAssignStringFromArrayLiteral(dest
.isStringExp(), src
.isArrayLiteralExp(), 0);
1635 printf("invalid op %d %d\n", src
.op
, dest
.op
);
1638 assert(oldelems
.length
== newelems
.length
);
1639 foreach (size_t i
; 0 .. oldelems
.length
)
1641 Expression e
= (*newelems
)[i
];
1642 Expression o
= (*oldelems
)[i
];
1643 if (e
.op
== EXP
.structLiteral
)
1645 assert(o
.op
== e
.op
);
1646 assignInPlace(o
, e
);
1648 else if (e
.type
.ty
== Tsarray
&& e
.op
!= EXP
.void_
&& o
.type
.ty
== Tsarray
)
1650 assignInPlace(o
, e
);
1654 (*oldelems
)[i
] = (*newelems
)[i
];
1659 // Given an AA literal aae, set aae[index] = newval and return newval.
1660 Expression
assignAssocArrayElement(const ref Loc loc
, AssocArrayLiteralExp aae
, Expression index
, Expression newval
)
1662 /* Create new associative array literal reflecting updated key/value
1664 Expressions
* keysx
= aae
.keys
;
1665 Expressions
* valuesx
= aae
.values
;
1667 for (size_t j
= valuesx
.length
; j
;)
1670 Expression ekey
= (*aae
.keys
)[j
];
1671 int eq
= ctfeEqual(loc
, EXP
.equal
, ekey
, index
);
1674 (*valuesx
)[j
] = newval
;
1680 // Append index/newval to keysx[]/valuesx[]
1681 valuesx
.push(newval
);
1687 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
1688 /// oldlen, change its length to newlen. If the newlen is longer than oldlen,
1689 /// all new elements will be set to the default initializer for the element type.
1690 Expression
changeArrayLiteralLength(UnionExp
* pue
, const ref Loc loc
, TypeArray arrayType
, Expression oldval
, size_t oldlen
, size_t newlen
)
1692 Type elemType
= arrayType
.next
;
1694 Expression defaultElem
= elemType
.defaultInitLiteral(loc
);
1695 auto elements
= new Expressions(newlen
);
1698 if (oldval
.op
== EXP
.slice
)
1700 indxlo
= cast(size_t
)oldval
.isSliceExp().lwr
.toInteger();
1701 oldval
= oldval
.isSliceExp().e1
;
1703 size_t copylen
= oldlen
< newlen ? oldlen
: newlen
;
1704 if (oldval
.op
== EXP
.string_
)
1706 StringExp oldse
= oldval
.isStringExp();
1707 void* s
= mem
.xcalloc(newlen
+ 1, oldse
.sz
);
1708 const data
= oldse
.peekData();
1709 memcpy(s
, data
.ptr
, copylen
* oldse
.sz
);
1710 const defaultValue
= cast(ulong)defaultElem
.toInteger();
1711 foreach (size_t elemi
; copylen
.. newlen
)
1716 (cast(char*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(char)defaultValue
;
1719 (cast(wchar*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(wchar)defaultValue
;
1722 (cast(dchar*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(dchar)defaultValue
;
1725 (cast(ulong*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(ulong)defaultValue
;
1731 emplaceExp
!(StringExp
)(pue
, loc
, s
[0 .. newlen
* oldse
.sz
], newlen
, oldse
.sz
);
1732 StringExp se
= pue
.exp().isStringExp();
1733 se
.type
= arrayType
;
1735 se
.committed
= oldse
.committed
;
1736 se
.ownedByCtfe
= OwnedBy
.ctfe
;
1742 assert(oldval
.op
== EXP
.arrayLiteral
);
1743 ArrayLiteralExp ae
= oldval
.isArrayLiteralExp();
1744 foreach (size_t i
; 0 .. copylen
)
1745 (*elements
)[i
] = (*ae
.elements
)[indxlo
+ i
];
1747 if (elemType
.ty
== Tstruct || elemType
.ty
== Tsarray
)
1749 /* If it is an aggregate literal representing a value type,
1750 * we need to create a unique copy for each element
1752 foreach (size_t i
; copylen
.. newlen
)
1753 (*elements
)[i
] = copyLiteral(defaultElem
).copy();
1757 foreach (size_t i
; copylen
.. newlen
)
1758 (*elements
)[i
] = defaultElem
;
1760 emplaceExp
!(ArrayLiteralExp
)(pue
, loc
, arrayType
, elements
);
1761 ArrayLiteralExp aae
= pue
.exp().isArrayLiteralExp();
1762 aae
.ownedByCtfe
= OwnedBy
.ctfe
;
1767 /*************************** CTFE Sanity Checks ***************************/
1769 bool isCtfeValueValid(Expression newval
)
1771 Type tb
= newval
.type
.toBasetype();
1777 return tb
.isscalar();
1780 return tb
.ty
== Tnull ||
1781 tb
.ty
== Tpointer ||
1788 return true; // CTFE would directly use the StringExp in AST.
1790 case EXP
.arrayLiteral
:
1791 return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
1793 case EXP
.assocArrayLiteral
:
1794 return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
1796 case EXP
.structLiteral
:
1797 return true; //((StructLiteralExp *)newval)->ownedByCtfe;
1799 case EXP
.classReference
:
1806 return true; // vector literal
1809 return true; // function literal or delegate literal
1813 // &struct.func or &clasinst.func
1815 Expression ethis
= newval
.isDelegateExp().e1
;
1816 return (ethis
.op
== EXP
.structLiteral || ethis
.op
== EXP
.classReference || ethis
.op
== EXP
.variable
&& ethis
.isVarExp().var
== newval
.isDelegateExp().func
);
1819 case EXP
.symbolOffset
:
1821 // function pointer, or pointer to static variable
1822 Declaration d
= newval
.isSymOffExp().var
;
1823 return d
.isFuncDeclaration() || d
.isDataseg();
1834 // e1 should be a CTFE reference
1835 Expression e1
= newval
.isAddrExp().e1
;
1836 return tb
.ty
== Tpointer
&&
1838 (e1
.op
== EXP
.structLiteral || e1
.op
== EXP
.arrayLiteral
) && isCtfeValueValid(e1
) ||
1839 e1
.op
== EXP
.variable ||
1840 e1
.op
== EXP
.dotVariable
&& isCtfeReferenceValid(e1
) ||
1841 e1
.op
== EXP
.index
&& isCtfeReferenceValid(e1
) ||
1842 e1
.op
== EXP
.slice
&& e1
.type
.toBasetype().ty
== Tsarray
1848 // e1 should be an array aggregate
1849 const SliceExp se
= newval
.isSliceExp();
1850 assert(se
.lwr
&& se
.lwr
.op
== EXP
.int64
);
1851 assert(se
.upr
&& se
.upr
.op
== EXP
.int64
);
1852 return (tb
.ty
== Tarray || tb
.ty
== Tsarray
) && (se
.e1
.op
== EXP
.string_ || se
.e1
.op
== EXP
.arrayLiteral
);
1856 return true; // uninitialized value
1859 error(newval
.loc
, "CTFE internal error: illegal CTFE value `%s`", newval
.toChars());
1864 bool isCtfeReferenceValid(Expression newval
)
1873 const VarDeclaration v
= newval
.isVarExp().var
.isVarDeclaration();
1875 // Must not be a reference to a reference
1881 const Expression eagg
= newval
.isIndexExp().e1
;
1882 return eagg
.op
== EXP
.string_ || eagg
.op
== EXP
.arrayLiteral || eagg
.op
== EXP
.assocArrayLiteral
;
1885 case EXP
.dotVariable
:
1887 Expression eagg
= newval
.isDotVarExp().e1
;
1888 return (eagg
.op
== EXP
.structLiteral || eagg
.op
== EXP
.classReference
) && isCtfeValueValid(eagg
);
1892 // Internally a ref variable may directly point a stack memory.
1893 // e.g. ref int v = 1;
1894 return isCtfeValueValid(newval
);
1898 // Used for debugging only
1899 void showCtfeExpr(Expression e
, int level
= 0)
1901 for (int i
= level
; i
> 0; --i
)
1903 Expressions
* elements
= null;
1904 // We need the struct definition to detect block assignment
1905 StructDeclaration sd
= null;
1906 ClassDeclaration cd
= null;
1907 if (e
.op
== EXP
.structLiteral
)
1909 elements
= e
.isStructLiteralExp().elements
;
1910 sd
= e
.isStructLiteralExp().sd
;
1911 printf("STRUCT type = %s %p:\n", e
.type
.toChars(), e
);
1913 else if (e
.op
== EXP
.classReference
)
1915 elements
= e
.isClassReferenceExp().value
.elements
;
1916 cd
= e
.isClassReferenceExp().originalClass();
1917 printf("CLASS type = %s %p:\n", e
.type
.toChars(), e
.isClassReferenceExp().value
);
1919 else if (e
.op
== EXP
.arrayLiteral
)
1921 elements
= e
.isArrayLiteralExp().elements
;
1922 printf("ARRAY LITERAL type=%s %p:\n", e
.type
.toChars(), e
);
1924 else if (e
.op
== EXP
.assocArrayLiteral
)
1926 printf("AA LITERAL type=%s %p:\n", e
.type
.toChars(), e
);
1928 else if (e
.op
== EXP
.string_
)
1930 printf("STRING %s %p\n", e
.toChars(), e
.isStringExp
.peekString
.ptr
);
1932 else if (e
.op
== EXP
.slice
)
1934 printf("SLICE %p: %s\n", e
, e
.toChars());
1935 showCtfeExpr(e
.isSliceExp().e1
, level
+ 1);
1937 else if (e
.op
== EXP
.variable
)
1939 printf("VAR %p %s\n", e
, e
.toChars());
1940 VarDeclaration v
= e
.isVarExp().var
.isVarDeclaration();
1941 if (v
&& getValue(v
))
1942 showCtfeExpr(getValue(v
), level
+ 1);
1944 else if (e
.op
== EXP
.address
)
1946 // This is potentially recursive. We mustn't try to print the thing we're pointing to.
1947 printf("POINTER %p to %p: %s\n", e
, e
.isAddrExp().e1
, e
.toChars());
1950 printf("VALUE %p: %s\n", e
, e
.toChars());
1953 size_t fieldsSoFar
= 0;
1954 for (size_t i
= 0; i
< elements
.length
; i
++)
1956 Expression z
= null;
1957 VarDeclaration v
= null;
1960 printf("...(total %d elements)\n", cast(int)elements
.length
);
1970 while (i
- fieldsSoFar
>= cd
.fields
.length
)
1972 fieldsSoFar
+= cd
.fields
.length
;
1974 for (int j
= level
; j
> 0; --j
)
1976 printf(" BASE CLASS: %s\n", cd
.toChars());
1978 v
= cd
.fields
[i
- fieldsSoFar
];
1979 assert((elements
.length
+ i
) >= (fieldsSoFar
+ cd
.fields
.length
));
1980 size_t indx
= (elements
.length
- fieldsSoFar
) - cd
.fields
.length
+ i
;
1981 assert(indx
< elements
.length
);
1982 z
= (*elements
)[indx
];
1986 for (int j
= level
; j
> 0; --j
)
1993 // If it is a void assignment, use the default initializer
1994 if ((v
.type
.ty
!= z
.type
.ty
) && v
.type
.ty
== Tsarray
)
1996 for (int j
= level
; --j
;)
1998 printf(" field: block initialized static array\n");
2002 showCtfeExpr(z
, level
+ 1);
2007 /*************************** Void initialization ***************************/
2008 UnionExp
voidInitLiteral(Type t
, VarDeclaration var
)
2011 if (auto tsa
= t
.isTypeSArray())
2013 Expression elem
= voidInitLiteral(tsa
.next
, var
).copy();
2014 // For aggregate value types (structs, static arrays) we must
2015 // create an a separate copy for each element.
2016 const mustCopy
= (elem
.op
== EXP
.arrayLiteral || elem
.op
== EXP
.structLiteral
);
2017 const d
= cast(size_t
)tsa
.dim
.toInteger();
2018 auto elements
= new Expressions(d
);
2021 if (mustCopy
&& i
> 0)
2022 elem
= copyLiteral(elem
).copy();
2023 (*elements
)[i
] = elem
;
2025 emplaceExp
!(ArrayLiteralExp
)(&ue
, var
.loc
, tsa
, elements
);
2026 ArrayLiteralExp ae
= ue
.exp().isArrayLiteralExp();
2027 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
2029 else if (auto ts
= t
.isTypeStruct())
2031 auto exps
= new Expressions(ts
.sym
.fields
.length
);
2032 foreach (size_t i
; 0 .. ts
.sym
.fields
.length
)
2034 (*exps
)[i
] = voidInitLiteral(ts
.sym
.fields
[i
].type
, ts
.sym
.fields
[i
]).copy();
2036 emplaceExp
!(StructLiteralExp
)(&ue
, var
.loc
, ts
.sym
, exps
);
2037 StructLiteralExp se
= ue
.exp().isStructLiteralExp();
2039 se
.ownedByCtfe
= OwnedBy
.ctfe
;
2042 emplaceExp
!(VoidInitExp
)(&ue
, var
);