2 * CTFE for expressions involving pointers, slices, array concatenation etc.
4 * Copyright: Copyright (C) 1999-2022 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
.stdlib
;
16 import core
.stdc
.string
;
17 import dmd
.arraytypes
;
22 import dmd
.declaration
;
23 import dmd
.dinterpret
;
27 import dmd
.expression
;
31 import dmd
.root
.complex
;
32 import dmd
.root
.ctfloat
;
39 /***********************************************************
40 * A reference to a class, or an interface. We need this when we
41 * point to a base class (we must record what the type is).
43 extern (C
++) final class ClassReferenceExp
: Expression
45 StructLiteralExp value
;
47 extern (D
) this(const ref Loc loc
, StructLiteralExp lit
, Type type
)
49 super(loc
, EXP
.classReference
, __traits(classInstanceSize
, ClassReferenceExp
));
50 assert(lit
&& lit
.sd
&& lit
.sd
.isClassDeclaration());
55 ClassDeclaration
originalClass()
57 return value
.sd
.isClassDeclaration();
60 // Return index of the field, or -1 if not found
61 private int getFieldIndex(Type fieldtype
, uint fieldoffset
)
63 ClassDeclaration cd
= originalClass();
65 for (size_t j
= 0; j
< value
.elements
.dim
; j
++)
67 while (j
- fieldsSoFar
>= cd
.fields
.dim
)
69 fieldsSoFar
+= cd
.fields
.dim
;
72 VarDeclaration v2
= cd
.fields
[j
- fieldsSoFar
];
73 if (fieldoffset
== v2
.offset
&& fieldtype
.size() == v2
.type
.size())
75 return cast(int)(value
.elements
.dim
- fieldsSoFar
- cd
.fields
.dim
+ (j
- fieldsSoFar
));
81 // Return index of the field, or -1 if not found
82 // Same as getFieldIndex, but checks for a direct match with the VarDeclaration
83 int findFieldIndexByName(VarDeclaration v
)
85 ClassDeclaration cd
= originalClass();
86 size_t fieldsSoFar
= 0;
87 for (size_t j
= 0; j
< value
.elements
.dim
; j
++)
89 while (j
- fieldsSoFar
>= cd
.fields
.dim
)
91 fieldsSoFar
+= cd
.fields
.dim
;
94 VarDeclaration v2
= cd
.fields
[j
- fieldsSoFar
];
97 return cast(int)(value
.elements
.dim
- fieldsSoFar
- cd
.fields
.dim
+ (j
- fieldsSoFar
));
103 override void accept(Visitor v
)
109 /*************************
110 * Same as getFieldIndex, but checks for a direct match with the VarDeclaration
112 * index of the field, or -1 if not found
114 int findFieldIndexByName(const StructDeclaration sd
, const VarDeclaration v
) pure
116 foreach (i
, field
; sd
.fields
)
124 /***********************************************************
125 * Fake class which holds the thrown exception.
126 * Used for implementing exception handling.
128 extern (C
++) final class ThrownExceptionExp
: Expression
130 ClassReferenceExp thrown
; // the thing being tossed
132 extern (D
) this(const ref Loc loc
, ClassReferenceExp victim
)
134 super(loc
, EXP
.thrownException
, __traits(classInstanceSize
, ThrownExceptionExp
));
135 this.thrown
= victim
;
136 this.type
= victim
.type
;
139 override const(char)* toChars() const
141 return "CTFE ThrownException";
144 // Generate an error message when this exception is not caught
145 extern (D
) void generateUncaughtError()
148 Expression e
= resolveSlice((*thrown
.value
.elements
)[0], &ue
);
149 StringExp se
= e
.toStringExp();
150 thrown
.error("uncaught CTFE exception `%s(%s)`", thrown
.type
.toChars(), se ? se
.toChars() : e
.toChars());
151 /* Also give the line where the throw statement was. We won't have it
152 * in the case where the ThrowStatement is generated internally
153 * (eg, in ScopeStatement)
155 if (loc
.isValid() && !loc
.equals(thrown
.loc
))
156 .errorSupplemental(loc
, "thrown from here");
159 override void accept(Visitor v
)
165 /***********************************************************
166 * This type is only used by the interpreter.
168 extern (C
++) final class CTFEExp
: Expression
170 extern (D
) this(EXP tok
)
172 super(Loc
.initial
, tok
, __traits(classInstanceSize
, CTFEExp
));
176 override const(char)* toChars() const
180 case EXP
.cantExpression
:
182 case EXP
.voidExpression
:
183 return "cast(void)0";
184 case EXP
.showCtfeContext
:
197 extern (D
) __gshared CTFEExp cantexp
;
198 extern (D
) __gshared CTFEExp voidexp
;
199 extern (D
) __gshared CTFEExp breakexp
;
200 extern (D
) __gshared CTFEExp continueexp
;
201 extern (D
) __gshared CTFEExp gotoexp
;
202 /* Used when additional information is needed regarding
205 extern (D
) __gshared CTFEExp showcontext
;
207 extern (D
) static bool isCantExp(const Expression e
)
209 return e
&& e
.op
== EXP
.cantExpression
;
212 extern (D
) static bool isGotoExp(const Expression e
)
214 return e
&& e
.op
== EXP
.goto_
;
218 // True if 'e' is CTFEExp::cantexp, or an exception
219 bool exceptionOrCantInterpret(const Expression e
)
221 return e
&& (e
.op
== EXP
.cantExpression || e
.op
== EXP
.thrownException || e
.op
== EXP
.showCtfeContext
);
224 /************** Aggregate literals (AA/string/array/struct) ******************/
225 // Given expr, which evaluates to an array/AA/string literal,
226 // return true if it needs to be copied
227 bool needToCopyLiteral(const Expression expr
)
229 Expression e
= cast()expr
;
234 case EXP
.arrayLiteral
:
235 return e
.isArrayLiteralExp().ownedByCtfe
== OwnedBy
.code
;
236 case EXP
.assocArrayLiteral
:
237 return e
.isAssocArrayLiteralExp().ownedByCtfe
== OwnedBy
.code
;
238 case EXP
.structLiteral
:
239 return e
.isStructLiteralExp().ownedByCtfe
== OwnedBy
.code
;
247 case EXP
.dotVariable
:
252 case EXP
.concatenate
:
253 return needToCopyLiteral(e
.isBinExp().e1
) ||
needToCopyLiteral(e
.isBinExp().e2
);
254 case EXP
.concatenateAssign
:
255 case EXP
.concatenateElemAssign
:
256 case EXP
.concatenateDcharAssign
:
265 private Expressions
* copyLiteralArray(Expressions
* oldelems
, Expression basis
= null)
270 auto newelems
= new Expressions(oldelems
.dim
);
271 foreach (i
, el
; *oldelems
)
273 (*newelems
)[i
] = copyLiteral(el ? el
: basis
).copy();
278 // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
279 // This value will be used for in-place modification.
280 UnionExp
copyLiteral(Expression e
)
283 if (auto se
= e
.isStringExp()) // syntaxCopy doesn't make a copy for StringExp!
285 char* s
= cast(char*)mem
.xcalloc(se
.len
+ 1, se
.sz
);
286 const slice
= se
.peekData();
287 memcpy(s
, slice
.ptr
, slice
.length
);
288 emplaceExp
!(StringExp
)(&ue
, se
.loc
, s
[0 .. se
.len
* se
.sz
], se
.len
, se
.sz
);
289 StringExp se2
= ue
.exp().isStringExp();
290 se2
.committed
= se
.committed
;
291 se2
.postfix
= se
.postfix
;
293 se2
.ownedByCtfe
= OwnedBy
.ctfe
;
296 if (auto ale
= e
.isArrayLiteralExp())
298 auto elements
= copyLiteralArray(ale
.elements
, ale
.basis
);
300 emplaceExp
!(ArrayLiteralExp
)(&ue
, e
.loc
, e
.type
, elements
);
302 ArrayLiteralExp r
= ue
.exp().isArrayLiteralExp();
303 r
.ownedByCtfe
= OwnedBy
.ctfe
;
306 if (auto aae
= e
.isAssocArrayLiteralExp())
308 emplaceExp
!(AssocArrayLiteralExp
)(&ue
, e
.loc
, copyLiteralArray(aae
.keys
), copyLiteralArray(aae
.values
));
309 AssocArrayLiteralExp r
= ue
.exp().isAssocArrayLiteralExp();
311 r
.ownedByCtfe
= OwnedBy
.ctfe
;
314 if (auto sle
= e
.isStructLiteralExp())
316 /* syntaxCopy doesn't work for struct literals, because of a nasty special
317 * case: block assignment is permitted inside struct literals, eg,
318 * an int[4] array can be initialized with a single int.
320 auto oldelems
= sle
.elements
;
321 auto newelems
= new Expressions(oldelems
.dim
);
322 foreach (i
, ref el
; *newelems
)
324 // We need the struct definition to detect block assignment
325 auto v
= sle
.sd
.fields
[i
];
326 auto m
= (*oldelems
)[i
];
328 // If it is a void assignment, use the default initializer
330 m
= voidInitLiteral(v
.type
, v
).copy();
332 if (v
.type
.ty
== Tarray || v
.type
.ty
== Taarray
)
334 // Don't have to copy array references
338 // Buzilla 15681: Copy the source element always.
339 m
= copyLiteral(m
).copy();
341 // Block assignment from inside struct literals
342 if (v
.type
.ty
!= m
.type
.ty
&& v
.type
.ty
== Tsarray
)
344 auto tsa
= v
.type
.isTypeSArray();
345 auto len
= cast(size_t
)tsa
.dim
.toInteger();
346 m
= createBlockDuplicatedArrayLiteral(&ue
, e
.loc
, v
.type
, m
, len
);
353 emplaceExp
!(StructLiteralExp
)(&ue
, e
.loc
, sle
.sd
, newelems
, sle
.stype
);
354 auto r
= ue
.exp().isStructLiteralExp();
356 r
.ownedByCtfe
= OwnedBy
.ctfe
;
357 r
.origin
= sle
.origin
;
360 if (e
.op
== EXP
.function_ || e
.op
== EXP
.delegate_ || e
.op
== EXP
.symbolOffset || e
.op
== EXP
.null_ || e
.op
== EXP
.variable || e
.op
== EXP
.dotVariable || e
.op
== EXP
.int64 || e
.op
== EXP
.float64 || e
.op
== EXP
.char_ || e
.op
== EXP
.complex80 || e
.op
== EXP
.void_ || e
.op
== EXP
.vector || e
.op
== EXP
.typeid_
)
362 // Simple value types
363 // Keep e1 for DelegateExp and DotVarExp
364 emplaceExp
!(UnionExp
)(&ue
, e
);
365 Expression r
= ue
.exp();
369 if (auto se
= e
.isSliceExp())
371 if (se
.type
.toBasetype().ty
== Tsarray
)
373 // same with resolveSlice()
374 if (se
.e1
.op
== EXP
.null_
)
376 emplaceExp
!(NullExp
)(&ue
, se
.loc
, se
.type
);
379 ue
= Slice(se
.type
, se
.e1
, se
.lwr
, se
.upr
);
380 auto r
= ue
.exp().isArrayLiteralExp();
381 r
.elements
= copyLiteralArray(r
.elements
);
382 r
.ownedByCtfe
= OwnedBy
.ctfe
;
387 // Array slices only do a shallow copy
388 emplaceExp
!(SliceExp
)(&ue
, e
.loc
, se
.e1
, se
.lwr
, se
.upr
);
389 Expression r
= ue
.exp();
394 if (isPointer(e
.type
))
396 // For pointers, we only do a shallow copy.
397 if (auto ae
= e
.isAddrExp())
398 emplaceExp
!(AddrExp
)(&ue
, e
.loc
, ae
.e1
);
399 else if (auto ie
= e
.isIndexExp())
400 emplaceExp
!(IndexExp
)(&ue
, e
.loc
, ie
.e1
, ie
.e2
);
401 else if (auto dve
= e
.isDotVarExp())
403 emplaceExp
!(DotVarExp
)(&ue
, e
.loc
, dve
.e1
, dve
.var
, dve
.hasOverloads
);
408 Expression r
= ue
.exp();
412 if (auto cre
= e
.isClassReferenceExp())
414 emplaceExp
!(ClassReferenceExp
)(&ue
, e
.loc
, cre
.value
, e
.type
);
417 if (e
.op
== EXP
.error
)
419 emplaceExp
!(UnionExp
)(&ue
, e
);
422 e
.error("CTFE internal error: literal `%s`", e
.toChars());
426 /* Deal with type painting.
427 * Type painting is a major nuisance: we can't just set
428 * e.type = type, because that would change the original literal.
429 * But, we can't simply copy the literal either, because that would change
430 * the values of any pointers.
432 Expression
paintTypeOntoLiteral(Type type
, Expression lit
)
434 if (lit
.type
.equals(type
))
436 return paintTypeOntoLiteralCopy(type
, lit
).copy();
439 Expression
paintTypeOntoLiteral(UnionExp
* pue
, Type type
, Expression lit
)
441 if (lit
.type
.equals(type
))
443 *pue
= paintTypeOntoLiteralCopy(type
, lit
);
447 private UnionExp
paintTypeOntoLiteralCopy(Type type
, Expression lit
)
450 if (lit
.type
.equals(type
))
452 emplaceExp
!(UnionExp
)(&ue
, lit
);
455 // If it is a cast to inout, retain the original type of the referenced part.
458 emplaceExp
!(UnionExp
)(&ue
, lit
);
459 ue
.exp().type
= type
;
462 if (auto se
= lit
.isSliceExp())
464 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, se
.e1
, se
.lwr
, se
.upr
);
466 else if (auto ie
= lit
.isIndexExp())
468 emplaceExp
!(IndexExp
)(&ue
, lit
.loc
, ie
.e1
, ie
.e2
);
470 else if (lit
.op
== EXP
.arrayLiteral
)
472 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, lit
, ctfeEmplaceExp
!IntegerExp(Loc
.initial
, 0, Type
.tsize_t
), ArrayLength(Type
.tsize_t
, lit
).copy());
474 else if (lit
.op
== EXP
.string_
)
476 // For strings, we need to introduce another level of indirection
477 emplaceExp
!(SliceExp
)(&ue
, lit
.loc
, lit
, ctfeEmplaceExp
!IntegerExp(Loc
.initial
, 0, Type
.tsize_t
), ArrayLength(Type
.tsize_t
, lit
).copy());
479 else if (auto aae
= lit
.isAssocArrayLiteralExp())
481 // TODO: we should be creating a reference to this AAExp, not
482 // just a ref to the keys and values.
483 OwnedBy wasOwned
= aae
.ownedByCtfe
;
484 emplaceExp
!(AssocArrayLiteralExp
)(&ue
, lit
.loc
, aae
.keys
, aae
.values
);
485 aae
= ue
.exp().isAssocArrayLiteralExp();
486 aae
.ownedByCtfe
= wasOwned
;
490 // Can't type paint from struct to struct*; this needs another
491 // level of indirection
492 if (lit
.op
== EXP
.structLiteral
&& isPointer(type
))
493 lit
.error("CTFE internal error: painting `%s`", type
.toChars());
494 ue
= copyLiteral(lit
);
496 ue
.exp().type
= type
;
500 /*************************************
501 * If e is a SliceExp, constant fold it.
503 * e = expression to resolve
504 * pue = if not null, store resulting expression here
506 * resulting expression
508 Expression
resolveSlice(Expression e
, UnionExp
* pue
= null)
510 SliceExp se
= e
.isSliceExp();
513 if (se
.e1
.op
== EXP
.null_
)
517 *pue
= Slice(e
.type
, se
.e1
, se
.lwr
, se
.upr
);
521 return Slice(e
.type
, se
.e1
, se
.lwr
, se
.upr
).copy();
524 /* Determine the array length, without interpreting it.
525 * e must be an array literal, or a slice
526 * It's very wasteful to resolve the slice when we only
529 uinteger_t
resolveArrayLength(Expression e
)
534 return e
.isVectorExp().dim
;
541 auto se
= e
.isSliceExp();
542 const ilo
= se
.lwr
.toInteger();
543 const iup
= se
.upr
.toInteger();
548 return e
.isStringExp().len
;
550 case EXP
.arrayLiteral
:
552 const ale
= e
.isArrayLiteralExp();
553 return ale
.elements ? ale
.elements
.dim
: 0;
556 case EXP
.assocArrayLiteral
:
558 return e
.isAssocArrayLiteralExp().keys
.dim
;
566 /******************************
568 * Create an array literal consisting of 'elem' duplicated 'dim' times.
570 * pue = where to store result
571 * loc = source location where the interpretation occurs
572 * type = target type of the result
573 * elem = the source of array element, it will be owned by the result
574 * dim = element number of the result
576 * Constructed ArrayLiteralExp
578 ArrayLiteralExp
createBlockDuplicatedArrayLiteral(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression elem
, size_t dim
)
580 if (type
.ty
== Tsarray
&& type
.nextOf().ty
== Tsarray
&& elem
.type
.ty
!= Tsarray
)
582 // If it is a multidimensional array literal, do it recursively
583 auto tsa
= type
.nextOf().isTypeSArray();
584 const len
= cast(size_t
)tsa
.dim
.toInteger();
585 elem
= createBlockDuplicatedArrayLiteral(pue
, loc
, type
.nextOf(), elem
, len
);
586 if (elem
== pue
.exp())
591 const tb
= elem
.type
.toBasetype();
592 const mustCopy
= tb
.ty
== Tstruct || tb
.ty
== Tsarray
;
594 auto elements
= new Expressions(dim
);
595 foreach (i
, ref el
; *elements
)
597 el
= mustCopy
&& i ?
copyLiteral(elem
).copy() : elem
;
599 emplaceExp
!(ArrayLiteralExp
)(pue
, loc
, type
, elements
);
600 auto ale
= pue
.exp().isArrayLiteralExp();
601 ale
.ownedByCtfe
= OwnedBy
.ctfe
;
605 /******************************
607 * Create a string literal consisting of 'value' duplicated 'dim' times.
609 StringExp
createBlockDuplicatedStringLiteral(UnionExp
* pue
, const ref Loc loc
, Type type
, dchar value
, size_t dim
, ubyte sz
)
611 auto s
= cast(char*)mem
.xcalloc(dim
, sz
);
612 foreach (elemi
; 0 .. dim
)
617 s
[elemi
] = cast(char)value
;
620 (cast(wchar*)s
)[elemi
] = cast(wchar)value
;
623 (cast(dchar*)s
)[elemi
] = value
;
629 emplaceExp
!(StringExp
)(pue
, loc
, s
[0 .. dim
* sz
], dim
, sz
);
630 auto se
= pue
.exp().isStringExp();
633 se
.ownedByCtfe
= OwnedBy
.ctfe
;
637 // Return true if t is an AA
638 bool isAssocArray(Type t
)
640 return t
.toBasetype().isTypeAArray() !is null;
643 // Given a template AA type, extract the corresponding built-in AA type
644 TypeAArray
toBuiltinAAType(Type t
)
646 return t
.toBasetype().isTypeAArray();
649 /************** TypeInfo operations ************************************/
650 // Return true if type is TypeInfo_Class
651 bool isTypeInfo_Class(const Type type
)
653 auto tc
= cast()type
.isTypeClass();
654 return tc
&& (Type
.dtypeinfo
== tc
.sym || Type
.dtypeinfo
.isBaseOf(tc
.sym
, null));
657 /************** Pointer operations ************************************/
658 // Return true if t is a pointer (not a function pointer)
659 bool isPointer(Type t
)
661 Type tb
= t
.toBasetype();
662 return tb
.ty
== Tpointer
&& tb
.nextOf().ty
!= Tfunction
;
665 // For CTFE only. Returns true if 'e' is true or a non-null pointer.
666 bool isTrueBool(Expression e
)
668 return e
.toBool().hasValue(true) ||
((e
.type
.ty
== Tpointer || e
.type
.ty
== Tclass
) && e
.op
!= EXP
.null_
);
671 /* Is it safe to convert from srcPointee* to destPointee* ?
672 * srcPointee is the genuine type (never void).
673 * destPointee may be void.
675 bool isSafePointerCast(Type srcPointee
, Type destPointee
)
677 // It's safe to cast S** to D** if it's OK to cast S* to D*
678 while (srcPointee
.ty
== Tpointer
&& destPointee
.ty
== Tpointer
)
680 srcPointee
= srcPointee
.nextOf();
681 destPointee
= destPointee
.nextOf();
683 // It's OK if both are the same (modulo const)
684 if (srcPointee
.constConv(destPointee
))
687 // It's ok to cast from/to shared because CTFE is single threaded anyways
688 if (srcPointee
.unSharedOf() == destPointee
.unSharedOf())
691 // It's OK if function pointers differ only in safe/pure/nothrow
692 if (srcPointee
.ty
== Tfunction
&& destPointee
.ty
== Tfunction
)
693 return srcPointee
.covariant(destPointee
) == Covariant
.yes ||
694 destPointee
.covariant(srcPointee
) == Covariant
.yes
;
695 // it's OK to cast to void*
696 if (destPointee
.ty
== Tvoid
)
698 // It's OK to cast from V[K] to void*
699 if (srcPointee
.ty
== Taarray
&& destPointee
== Type
.tvoidptr
)
701 // It's OK if they are the same size (static array of) integers, eg:
703 // int[5][] --> uint[5][]
704 if (srcPointee
.ty
== Tsarray
&& destPointee
.ty
== Tsarray
)
706 if (srcPointee
.size() != destPointee
.size())
708 srcPointee
= srcPointee
.baseElemOf();
709 destPointee
= destPointee
.baseElemOf();
711 return srcPointee
.isintegral() && destPointee
.isintegral() && srcPointee
.size() == destPointee
.size();
714 Expression
getAggregateFromPointer(Expression e
, dinteger_t
* ofs
)
717 if (auto ae
= e
.isAddrExp())
719 if (auto soe
= e
.isSymOffExp())
721 if (auto dve
= e
.isDotVarExp())
724 const v
= dve
.var
.isVarDeclaration();
726 StructLiteralExp se
= (ex
.op
== EXP
.classReference
)
727 ? ex
.isClassReferenceExp().value
728 : ex
.isStructLiteralExp();
730 // We can't use getField, because it makes a copy
731 const i
= (ex
.op
== EXP
.classReference
)
732 ? ex
.isClassReferenceExp().getFieldIndex(e
.type
, v
.offset
)
733 : se
.getFieldIndex(e
.type
, v
.offset
);
734 e
= (*se
.elements
)[i
];
736 if (auto ie
= e
.isIndexExp())
738 // Note that each AA element is part of its own memory block
739 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
)
741 *ofs
= ie
.e2
.toInteger();
745 if (auto se
= e
.isSliceExp())
747 if (se
&& e
.type
.toBasetype().ty
== Tsarray
&&
748 (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
)
750 *ofs
= se
.lwr
.toInteger();
755 // It can be a `null` disguised as a cast, e.g. `cast(void*)0`.
756 if (auto ie
= e
.isIntegerExp())
757 if (ie
.type
.ty
== Tpointer
&& ie
.getInteger() == 0)
758 return new NullExp(ie
.loc
, e
.type
.nextOf());
759 // Those casts are invalid, but let the rest of the code handle it,
760 // as it could be something like `x !is null`, which doesn't need
761 // to dereference the pointer, even if the pointer is `cast(void*)420`.
766 /** Return true if agg1 and agg2 are pointers to the same memory block
768 bool pointToSameMemoryBlock(Expression agg1
, Expression agg2
)
772 // For integers cast to pointers, we regard them as non-comparable
773 // unless they are identical. (This may be overly strict).
774 if (agg1
.op
== EXP
.int64
&& agg2
.op
== EXP
.int64
&& agg1
.toInteger() == agg2
.toInteger())
778 // Note that type painting can occur with VarExp, so we
779 // must compare the variables being pointed to.
780 if (agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
)
784 if (agg1
.op
== EXP
.symbolOffset
&& agg2
.op
== EXP
.symbolOffset
&& agg1
.isSymOffExp().var
== agg2
.isSymOffExp().var
)
791 // return e1 - e2 as an integer, or error if not possible
792 Expression
pointerDifference(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression e1
, Expression e2
)
794 dinteger_t ofs1
, ofs2
;
795 Expression agg1
= getAggregateFromPointer(e1
, &ofs1
);
796 Expression agg2
= getAggregateFromPointer(e2
, &ofs2
);
799 Type pointee
= (cast(TypePointer
)agg1
.type
).next
;
800 const sz
= pointee
.size();
801 emplaceExp
!(IntegerExp
)(pue
, loc
, (ofs1
- ofs2
) * sz
, type
);
803 else if (agg1
.op
== EXP
.string_
&& agg2
.op
== EXP
.string_
&&
804 agg1
.isStringExp().peekString().ptr
== agg2
.isStringExp().peekString().ptr
)
806 Type pointee
= (cast(TypePointer
)agg1
.type
).next
;
807 const sz
= pointee
.size();
808 emplaceExp
!(IntegerExp
)(pue
, loc
, (ofs1
- ofs2
) * sz
, type
);
810 else if (agg1
.op
== EXP
.symbolOffset
&& agg2
.op
== EXP
.symbolOffset
&&
811 agg1
.isSymOffExp().var
== agg2
.isSymOffExp().var
)
813 emplaceExp
!(IntegerExp
)(pue
, loc
, ofs1
- ofs2
, type
);
817 error(loc
, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1
.toChars(), e2
.toChars());
818 emplaceExp
!(CTFEExp
)(pue
, EXP
.cantExpression
);
823 // Return eptr op e2, where eptr is a pointer, e2 is an integer,
824 // and op is EXP.add or EXP.min
825 Expression
pointerArithmetic(UnionExp
* pue
, const ref Loc loc
, EXP op
, Type type
, Expression eptr
, Expression e2
)
827 if (eptr
.type
.nextOf().ty
== Tvoid
)
829 error(loc
, "cannot perform arithmetic on `void*` pointers at compile time");
831 emplaceExp
!(CTFEExp
)(pue
, EXP
.cantExpression
);
834 if (eptr
.op
== EXP
.address
)
835 eptr
= eptr
.isAddrExp().e1
;
837 Expression agg1
= getAggregateFromPointer(eptr
, &ofs1
);
838 if (agg1
.op
== EXP
.symbolOffset
)
840 if (agg1
.isSymOffExp().var
.type
.ty
!= Tsarray
)
842 error(loc
, "cannot perform pointer arithmetic on arrays of unknown length at compile time");
846 else if (agg1
.op
!= EXP
.string_
&& agg1
.op
!= EXP
.arrayLiteral
)
848 error(loc
, "cannot perform pointer arithmetic on non-arrays at compile time");
851 dinteger_t ofs2
= e2
.toInteger();
852 Type pointee
= (cast(TypeNext
)agg1
.type
.toBasetype()).next
;
853 dinteger_t sz
= pointee
.size();
856 if (agg1
.op
== EXP
.symbolOffset
)
859 len
= (cast(TypeSArray
)agg1
.isSymOffExp().var
.type
).dim
.toInteger();
863 Expression dollar
= ArrayLength(Type
.tsize_t
, agg1
).copy();
864 assert(!CTFEExp
.isCantExp(dollar
));
866 len
= dollar
.toInteger();
868 if (op
== EXP
.add || op
== EXP
.addAssign || op
== EXP
.plusPlus
)
870 else if (op
== EXP
.min || op
== EXP
.minAssign || op
== EXP
.minusMinus
)
874 error(loc
, "CTFE internal error: bad pointer operation");
877 if (indx
< 0 || len
< indx
)
879 error(loc
, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx
, len
);
882 if (agg1
.op
== EXP
.symbolOffset
)
884 emplaceExp
!(SymOffExp
)(pue
, loc
, agg1
.isSymOffExp().var
, indx
* sz
);
885 SymOffExp se
= pue
.exp().isSymOffExp();
889 if (agg1
.op
!= EXP
.arrayLiteral
&& agg1
.op
!= EXP
.string_
)
891 error(loc
, "CTFE internal error: pointer arithmetic `%s`", agg1
.toChars());
894 if (eptr
.type
.toBasetype().ty
== Tsarray
)
896 dinteger_t dim
= (cast(TypeSArray
)eptr
.type
.toBasetype()).dim
.toInteger();
897 // Create a CTFE pointer &agg1[indx .. indx+dim]
898 auto se
= ctfeEmplaceExp
!SliceExp(loc
, agg1
,
899 ctfeEmplaceExp
!IntegerExp(loc
, indx
, Type
.tsize_t
),
900 ctfeEmplaceExp
!IntegerExp(loc
, indx
+ dim
, Type
.tsize_t
));
901 se
.type
= type
.toBasetype().nextOf();
902 emplaceExp
!(AddrExp
)(pue
, loc
, se
);
903 pue
.exp().type
= type
;
906 // Create a CTFE pointer &agg1[indx]
907 auto ofs
= ctfeEmplaceExp
!IntegerExp(loc
, indx
, Type
.tsize_t
);
908 Expression ie
= ctfeEmplaceExp
!IndexExp(loc
, agg1
, ofs
);
909 ie
.type
= type
.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992
910 emplaceExp
!(AddrExp
)(pue
, loc
, ie
);
911 pue
.exp().type
= type
;
915 // Return 1 if true, 0 if false
916 // -1 if comparison is illegal because they point to non-comparable memory blocks
917 int comparePointers(EXP op
, Expression agg1
, dinteger_t ofs1
, Expression agg2
, dinteger_t ofs2
)
919 if (pointToSameMemoryBlock(agg1
, agg2
))
927 case EXP
.lessOrEqual
:
930 case EXP
.greaterThan
:
933 case EXP
.greaterOrEqual
:
940 case EXP
.notIdentity
:
949 const null1
= (agg1
.op
== EXP
.null_
);
950 const null2
= (agg2
.op
== EXP
.null_
);
957 cmp = null1
&& !null2
;
959 case EXP
.greaterThan
:
960 cmp = !null1
&& null2
;
962 case EXP
.lessOrEqual
:
965 case EXP
.greaterOrEqual
:
970 case EXP
.notIdentity
: // 'cmp' gets inverted below
972 cmp = (null1
== null2
);
984 case EXP
.notIdentity
: // 'cmp' gets inverted below
989 return -1; // memory blocks are different
992 if (op
== EXP
.notIdentity || op
== EXP
.notEqual
)
997 // True if conversion from type 'from' to 'to' involves a reinterpret_cast
998 // floating point -> integer or integer -> floating point
999 bool isFloatIntPaint(Type to
, Type from
)
1001 return from
.size() == to
.size() && (from
.isintegral() && to
.isfloating() || from
.isfloating() && to
.isintegral());
1004 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
1005 Expression
paintFloatInt(UnionExp
* pue
, Expression fromVal
, Type to
)
1007 if (exceptionOrCantInterpret(fromVal
))
1009 assert(to
.size() == 4 || to
.size() == 8);
1010 return Compiler
.paintAsType(pue
, fromVal
, to
);
1013 /******** Constant folding, with support for CTFE ***************************/
1014 /// Return true if non-pointer expression e can be compared
1015 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity
1016 bool isCtfeComparable(Expression e
)
1018 if (e
.op
== EXP
.slice
)
1019 e
= e
.isSliceExp().e1
;
1020 if (e
.isConst() != 1)
1022 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
)
1026 // https://issues.dlang.org/show_bug.cgi?id=14123
1027 // TypeInfo object is comparable in CTFE
1028 if (e
.op
== EXP
.typeid_
)
1035 /// Map EXP comparison ops
1036 private bool numCmp(N
)(EXP op
, N n1
, N n2
)
1042 case EXP
.lessOrEqual
:
1044 case EXP
.greaterThan
:
1046 case EXP
.greaterOrEqual
:
1054 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1055 bool specificCmp(EXP op
, int rawCmp
)
1057 return numCmp
!int(op
, rawCmp
, 0);
1060 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1061 bool intUnsignedCmp(EXP op
, dinteger_t n1
, dinteger_t n2
)
1063 return numCmp
!dinteger_t(op
, n1
, n2
);
1066 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1067 bool intSignedCmp(EXP op
, sinteger_t n1
, sinteger_t n2
)
1069 return numCmp
!sinteger_t(op
, n1
, n2
);
1072 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
1073 bool realCmp(EXP op
, real_t r1
, real_t r2
)
1075 // Don't rely on compiler, handle NAN arguments separately
1076 if (CTFloat
.isNaN(r1
) || CTFloat
.isNaN(r2
)) // if unordered
1081 case EXP
.lessOrEqual
:
1082 case EXP
.greaterThan
:
1083 case EXP
.greaterOrEqual
:
1092 return numCmp
!real_t(op
, r1
, r2
);
1096 /* Conceptually the same as memcmp(e1, e2).
1097 * e1 and e2 may be strings, arrayliterals, or slices.
1098 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1099 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1103 private int ctfeCmpArrays(const ref Loc loc
, Expression e1
, Expression e2
, uinteger_t len
)
1105 // Resolve slices, if necessary
1110 if (auto sle1
= x1
.isSliceExp())
1112 lo1
= sle1
.lwr
.toInteger();
1115 auto se1
= x1
.isStringExp();
1116 auto ae1
= x1
.isArrayLiteralExp();
1119 if (auto sle2
= x2
.isSliceExp())
1121 lo2
= sle2
.lwr
.toInteger();
1124 auto se2
= x2
.isStringExp();
1125 auto ae2
= x2
.isArrayLiteralExp();
1127 // Now both must be either EXP.arrayLiteral or EXP.string_
1129 return sliceCmpStringWithString(se1
, se2
, cast(size_t
)lo1
, cast(size_t
)lo2
, cast(size_t
)len
);
1131 return sliceCmpStringWithArray(se1
, ae2
, cast(size_t
)lo1
, cast(size_t
)lo2
, cast(size_t
)len
);
1133 return -sliceCmpStringWithArray(se2
, ae1
, cast(size_t
)lo2
, cast(size_t
)lo1
, cast(size_t
)len
);
1135 // Comparing two array literals. This case is potentially recursive.
1136 // If they aren't strings, we just need an equality check rather than
1138 const bool needCmp
= ae1
.type
.nextOf().isintegral();
1139 foreach (size_t i
; 0 .. cast(size_t
)len
)
1141 Expression ee1
= (*ae1
.elements
)[cast(size_t
)(lo1
+ i
)];
1142 Expression ee2
= (*ae2
.elements
)[cast(size_t
)(lo2
+ i
)];
1145 const sinteger_t c
= ee1
.toInteger() - ee2
.toInteger();
1153 if (ctfeRawCmp(loc
, ee1
, ee2
))
1160 /* Given a delegate expression e, return .funcptr.
1161 * If e is NullExp, return NULL.
1163 private FuncDeclaration
funcptrOf(Expression e
)
1165 assert(e
.type
.ty
== Tdelegate
);
1166 if (auto de = e
.isDelegateExp())
1168 if (auto fe
= e
.isFuncExp())
1170 assert(e
.op
== EXP
.null_
);
1174 private bool isArray(const Expression e
)
1176 return e
.op
== EXP
.arrayLiteral || e
.op
== EXP
.string_ || e
.op
== EXP
.slice || e
.op
== EXP
.null_
;
1181 * loc = source file location
1183 * e2 = right operand
1184 * identity = true for `is` identity comparisons
1186 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2.
1187 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2.
1189 private int ctfeRawCmp(const ref Loc loc
, Expression e1
, Expression e2
, bool identity
= false)
1191 if (e1
.op
== EXP
.classReference || e2
.op
== EXP
.classReference
)
1193 if (e1
.op
== EXP
.classReference
&& e2
.op
== EXP
.classReference
&&
1194 e1
.isClassReferenceExp().value
== e2
.isClassReferenceExp().value
)
1198 if (e1
.op
== EXP
.typeid_
&& e2
.op
== EXP
.typeid_
)
1200 // printf("e1: %s\n", e1.toChars());
1201 // printf("e2: %s\n", e2.toChars());
1202 Type t1
= isType(e1
.isTypeidExp().obj
);
1203 Type t2
= isType(e2
.isTypeidExp().obj
);
1208 // null == null, regardless of type
1209 if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.null_
)
1211 if (e1
.type
.ty
== Tpointer
&& e2
.type
.ty
== Tpointer
)
1213 // Can only be an equality test.
1214 dinteger_t ofs1
, ofs2
;
1215 Expression agg1
= getAggregateFromPointer(e1
, &ofs1
);
1216 Expression agg2
= getAggregateFromPointer(e2
, &ofs2
);
1217 if ((agg1
== agg2
) ||
(agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
))
1224 if (e1
.type
.ty
== Tdelegate
&& e2
.type
.ty
== Tdelegate
)
1226 // If .funcptr isn't the same, they are not equal
1227 if (funcptrOf(e1
) != funcptrOf(e2
))
1229 // If both are delegate literals, assume they have the
1230 // same closure pointer. TODO: We don't support closures yet!
1231 if (e1
.op
== EXP
.function_
&& e2
.op
== EXP
.function_
)
1233 assert(e1
.op
== EXP
.delegate_
&& e2
.op
== EXP
.delegate_
);
1234 // Same .funcptr. Do they have the same .ptr?
1235 Expression ptr1
= e1
.isDelegateExp().e1
;
1236 Expression ptr2
= e2
.isDelegateExp().e1
;
1237 dinteger_t ofs1
, ofs2
;
1238 Expression agg1
= getAggregateFromPointer(ptr1
, &ofs1
);
1239 Expression agg2
= getAggregateFromPointer(ptr2
, &ofs2
);
1240 // If they are EXP.variable, it means they are FuncDeclarations
1241 if ((agg1
== agg2
&& ofs1
== ofs2
) ||
(agg1
.op
== EXP
.variable
&& agg2
.op
== EXP
.variable
&& agg1
.isVarExp().var
== agg2
.isVarExp().var
))
1247 if (isArray(e1
) && isArray(e2
))
1249 const uinteger_t len1
= resolveArrayLength(e1
);
1250 const uinteger_t len2
= resolveArrayLength(e2
);
1251 // workaround for dmc optimizer bug calculating wrong len for
1252 // uinteger_t len = (len1 < len2 ? len1 : len2);
1253 // if (len == 0) ...
1254 if (len1
> 0 && len2
> 0)
1256 const uinteger_t len
= (len1
< len2 ? len1
: len2
);
1257 const int res
= ctfeCmpArrays(loc
, e1
, e2
, len
);
1261 return cast(int)(len1
- len2
);
1263 if (e1
.type
.isintegral())
1265 return e1
.toInteger() != e2
.toInteger();
1267 if (e1
.type
.isreal() || e1
.type
.isimaginary())
1269 real_t r1
= e1
.type
.isreal() ? e1
.toReal() : e1
.toImaginary();
1270 real_t r2
= e1
.type
.isreal() ? e2
.toReal() : e2
.toImaginary();
1272 return !CTFloat
.isIdentical(r1
, r2
);
1273 if (CTFloat
.isNaN(r1
) || CTFloat
.isNaN(r2
)) // if unordered
1275 return 1; // they are not equal
1282 else if (e1
.type
.iscomplex())
1284 auto c1
= e1
.toComplex();
1285 auto c2
= e2
.toComplex();
1288 return !RealIdentical(c1
.re
, c2
.re
) && !RealIdentical(c1
.im
, c2
.im
);
1292 if (e1
.op
== EXP
.structLiteral
&& e2
.op
== EXP
.structLiteral
)
1294 StructLiteralExp es1
= e1
.isStructLiteralExp();
1295 StructLiteralExp es2
= e2
.isStructLiteralExp();
1296 // For structs, we only need to return 0 or 1 (< and > aren't legal).
1297 if (es1
.sd
!= es2
.sd
)
1299 else if ((!es1
.elements ||
!es1
.elements
.dim
) && (!es2
.elements ||
!es2
.elements
.dim
))
1300 return 0; // both arrays are empty
1301 else if (!es1
.elements ||
!es2
.elements
)
1303 else if (es1
.elements
.dim
!= es2
.elements
.dim
)
1307 foreach (size_t i
; 0 .. es1
.elements
.dim
)
1309 Expression ee1
= (*es1
.elements
)[i
];
1310 Expression ee2
= (*es2
.elements
)[i
];
1312 // https://issues.dlang.org/show_bug.cgi?id=16284
1313 if (ee1
.op
== EXP
.void_
&& ee2
.op
== EXP
.void_
) // if both are VoidInitExp
1320 const int cmp = ctfeRawCmp(loc
, ee1
, ee2
, identity
);
1324 return 0; // All elements are equal
1327 if (e1
.op
== EXP
.assocArrayLiteral
&& e2
.op
== EXP
.assocArrayLiteral
)
1329 AssocArrayLiteralExp es1
= e1
.isAssocArrayLiteralExp();
1330 AssocArrayLiteralExp es2
= e2
.isAssocArrayLiteralExp();
1331 size_t dim
= es1
.keys
.dim
;
1332 if (es2
.keys
.dim
!= dim
)
1334 bool* used
= cast(bool*)mem
.xmalloc(bool.sizeof
* dim
);
1335 memset(used
, 0, bool.sizeof
* dim
);
1336 foreach (size_t i
; 0 .. dim
)
1338 Expression k1
= (*es1
.keys
)[i
];
1339 Expression v1
= (*es1
.values
)[i
];
1340 Expression v2
= null;
1341 foreach (size_t j
; 0 .. dim
)
1345 Expression k2
= (*es2
.keys
)[j
];
1346 if (ctfeRawCmp(loc
, k1
, k2
, identity
))
1349 v2
= (*es2
.values
)[j
];
1352 if (!v2 ||
ctfeRawCmp(loc
, v1
, v2
, identity
))
1361 else if (e1
.op
== EXP
.assocArrayLiteral
&& e2
.op
== EXP
.null_
)
1363 return e1
.isAssocArrayLiteralExp
.keys
.dim
!= 0;
1365 else if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.assocArrayLiteral
)
1367 return e2
.isAssocArrayLiteralExp
.keys
.dim
!= 0;
1370 error(loc
, "CTFE internal error: bad compare of `%s` and `%s`", e1
.toChars(), e2
.toChars());
1374 /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
1375 bool ctfeEqual(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1377 return !ctfeRawCmp(loc
, e1
, e2
) ^
(op
== EXP
.notEqual
);
1380 /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
1381 bool ctfeIdentity(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1383 //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars());
1384 //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr,
1385 // EXPtoString(e1.op).ptr, e1.toChars(), EXPtoString(e2.op).ptr, e1.toChars());
1387 if (e1
.op
== EXP
.null_
)
1389 cmp = (e2
.op
== EXP
.null_
);
1391 else if (e2
.op
== EXP
.null_
)
1395 else if (e1
.op
== EXP
.symbolOffset
&& e2
.op
== EXP
.symbolOffset
)
1397 SymOffExp es1
= e1
.isSymOffExp();
1398 SymOffExp es2
= e2
.isSymOffExp();
1399 cmp = (es1
.var
== es2
.var
&& es1
.offset
== es2
.offset
);
1401 else if (e1
.type
.isreal())
1402 cmp = CTFloat
.isIdentical(e1
.toReal(), e2
.toReal());
1403 else if (e1
.type
.isimaginary())
1404 cmp = RealIdentical(e1
.toImaginary(), e2
.toImaginary());
1405 else if (e1
.type
.iscomplex())
1407 complex_t v1
= e1
.toComplex();
1408 complex_t v2
= e2
.toComplex();
1409 cmp = RealIdentical(creall(v1
), creall(v2
)) && RealIdentical(cimagl(v1
), cimagl(v1
));
1413 cmp = !ctfeRawCmp(loc
, e1
, e2
, true);
1415 if (op
== EXP
.notIdentity || op
== EXP
.notEqual
)
1420 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
1421 bool ctfeCmp(const ref Loc loc
, EXP op
, Expression e1
, Expression e2
)
1423 Type t1
= e1
.type
.toBasetype();
1424 Type t2
= e2
.type
.toBasetype();
1426 if (t1
.isString() && t2
.isString())
1427 return specificCmp(op
, ctfeRawCmp(loc
, e1
, e2
));
1428 else if (t1
.isreal())
1429 return realCmp(op
, e1
.toReal(), e2
.toReal());
1430 else if (t1
.isimaginary())
1431 return realCmp(op
, e1
.toImaginary(), e2
.toImaginary());
1432 else if (t1
.isunsigned() || t2
.isunsigned())
1433 return intUnsignedCmp(op
, e1
.toInteger(), e2
.toInteger());
1435 return intSignedCmp(op
, e1
.toInteger(), e2
.toInteger());
1438 UnionExp
ctfeCat(const ref Loc loc
, Type type
, Expression e1
, Expression e2
)
1440 Type t1
= e1
.type
.toBasetype();
1441 Type t2
= e2
.type
.toBasetype();
1443 if (e2
.op
== EXP
.string_
&& e1
.op
== EXP
.arrayLiteral
&& t1
.nextOf().isintegral())
1445 // [chars] ~ string => string (only valid for CTFE)
1446 StringExp es1
= e2
.isStringExp();
1447 ArrayLiteralExp es2
= e1
.isArrayLiteralExp();
1448 const len
= es1
.len
+ es2
.elements
.dim
;
1450 void* s
= mem
.xmalloc((len
+ 1) * sz
);
1451 const data1
= es1
.peekData();
1452 memcpy(cast(char*)s
+ sz
* es2
.elements
.dim
, data1
.ptr
, data1
.length
);
1453 foreach (size_t i
; 0 .. es2
.elements
.dim
)
1455 Expression es2e
= (*es2
.elements
)[i
];
1456 if (es2e
.op
!= EXP
.int64
)
1458 emplaceExp
!(CTFEExp
)(&ue
, EXP
.cantExpression
);
1461 dinteger_t v
= es2e
.toInteger();
1462 Port
.valcpy(cast(char*)s
+ i
* sz
, v
, sz
);
1464 // Add terminating 0
1465 memset(cast(char*)s
+ len
* sz
, 0, sz
);
1466 emplaceExp
!(StringExp
)(&ue
, loc
, s
[0 .. len
* sz
], len
, sz
);
1467 StringExp es
= ue
.exp().isStringExp();
1472 if (e1
.op
== EXP
.string_
&& e2
.op
== EXP
.arrayLiteral
&& t2
.nextOf().isintegral())
1474 // string ~ [chars] => string (only valid for CTFE)
1475 // Concatenate the strings
1476 StringExp es1
= e1
.isStringExp();
1477 ArrayLiteralExp es2
= e2
.isArrayLiteralExp();
1478 const len
= es1
.len
+ es2
.elements
.dim
;
1480 void* s
= mem
.xmalloc((len
+ 1) * sz
);
1481 auto slice
= es1
.peekData();
1482 memcpy(s
, slice
.ptr
, slice
.length
);
1483 foreach (size_t i
; 0 .. es2
.elements
.dim
)
1485 Expression es2e
= (*es2
.elements
)[i
];
1486 if (es2e
.op
!= EXP
.int64
)
1488 emplaceExp
!(CTFEExp
)(&ue
, EXP
.cantExpression
);
1491 const v
= es2e
.toInteger();
1492 Port
.valcpy(cast(char*)s
+ (es1
.len
+ i
) * sz
, v
, sz
);
1494 // Add terminating 0
1495 memset(cast(char*)s
+ len
* sz
, 0, sz
);
1496 emplaceExp
!(StringExp
)(&ue
, loc
, s
[0 .. len
* sz
], len
, sz
);
1497 StringExp es
= ue
.exp().isStringExp();
1499 es
.committed
= 0; //es1.committed;
1503 if (e1
.op
== EXP
.arrayLiteral
&& e2
.op
== EXP
.arrayLiteral
&& t1
.nextOf().equals(t2
.nextOf()))
1505 // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ]
1506 ArrayLiteralExp es1
= e1
.isArrayLiteralExp();
1507 ArrayLiteralExp es2
= e2
.isArrayLiteralExp();
1508 emplaceExp
!(ArrayLiteralExp
)(&ue
, es1
.loc
, type
, copyLiteralArray(es1
.elements
));
1509 es1
= ue
.exp().isArrayLiteralExp();
1510 es1
.elements
.insert(es1
.elements
.dim
, copyLiteralArray(es2
.elements
));
1513 if (e1
.op
== EXP
.arrayLiteral
&& e2
.op
== EXP
.null_
&& t1
.nextOf().equals(t2
.nextOf()))
1515 // [ e1 ] ~ null ----> [ e1 ].dup
1516 ue
= paintTypeOntoLiteralCopy(type
, copyLiteral(e1
).copy());
1519 if (e1
.op
== EXP
.null_
&& e2
.op
== EXP
.arrayLiteral
&& t1
.nextOf().equals(t2
.nextOf()))
1521 // null ~ [ e2 ] ----> [ e2 ].dup
1522 ue
= paintTypeOntoLiteralCopy(type
, copyLiteral(e2
).copy());
1525 ue
= Cat(loc
, type
, e1
, e2
);
1529 /* Given an AA literal 'ae', and a key 'e2':
1530 * Return ae[e2] if present, or NULL if not found.
1532 Expression
findKeyInAA(const ref Loc loc
, AssocArrayLiteralExp ae
, Expression e2
)
1534 /* Search the keys backwards, in case there are duplicate keys
1536 for (size_t i
= ae
.keys
.dim
; i
;)
1539 Expression ekey
= (*ae
.keys
)[i
];
1540 const int eq
= ctfeEqual(loc
, EXP
.equal
, ekey
, e2
);
1543 return (*ae
.values
)[i
];
1549 /* Same as for constfold.Index, except that it only works for static arrays,
1550 * dynamic arrays, and strings. We know that e1 is an
1551 * interpreted CTFE expression, so it cannot have side-effects.
1553 Expression
ctfeIndex(UnionExp
* pue
, const ref Loc loc
, Type type
, Expression e1
, uinteger_t indx
)
1555 //printf("ctfeIndex(e1 = %s)\n", e1.toChars());
1557 if (auto es1
= e1
.isStringExp())
1559 if (indx
>= es1
.len
)
1561 error(loc
, "string index %llu is out of bounds `[0 .. %llu]`", indx
, cast(ulong)es1
.len
);
1562 return CTFEExp
.cantexp
;
1564 emplaceExp
!IntegerExp(pue
, loc
, es1
.getCodeUnit(cast(size_t
) indx
), type
);
1568 if (auto ale
= e1
.isArrayLiteralExp())
1570 if (indx
>= ale
.elements
.dim
)
1572 error(loc
, "array index %llu is out of bounds `%s[0 .. %llu]`", indx
, e1
.toChars(), cast(ulong)ale
.elements
.dim
);
1573 return CTFEExp
.cantexp
;
1575 Expression e
= (*ale
.elements
)[cast(size_t
)indx
];
1576 return paintTypeOntoLiteral(pue
, type
, e
);
1582 Expression
ctfeCast(UnionExp
* pue
, const ref Loc loc
, Type type
, Type to
, Expression e
, bool explicitCast
= false)
1586 return paintTypeOntoLiteral(pue
, to
, e
);
1589 if (e
.op
== EXP
.null_
)
1592 if (e
.op
== EXP
.classReference
)
1594 // Disallow reinterpreting class casts. Do this by ensuring that
1595 // the original class can implicitly convert to the target class.
1596 // Also do not check 'alias this' for explicit cast expressions.
1597 auto tclass
= e
.isClassReferenceExp().originalClass().type
.isTypeClass();
1598 auto match
= explicitCast ? tclass
.implicitConvToWithoutAliasThis(to
.mutableOf())
1599 : tclass
.implicitConvTo(to
.mutableOf());
1604 emplaceExp
!(NullExp
)(pue
, loc
, to
);
1609 // Allow TypeInfo type painting
1610 if (isTypeInfo_Class(e
.type
) && e
.type
.implicitConvTo(to
))
1613 // Allow casting away const for struct literals
1614 if (e
.op
== EXP
.structLiteral
&& e
.type
.toBasetype().castMod(0) == to
.toBasetype().castMod(0))
1618 if (e
.type
.equals(type
) && type
.equals(to
))
1620 // necessary not to change e's address for pointer comparisons
1623 else if (to
.toBasetype().ty
== Tarray
&&
1624 type
.toBasetype().ty
== Tarray
&&
1625 to
.toBasetype().nextOf().size() == type
.toBasetype().nextOf().size())
1627 // https://issues.dlang.org/show_bug.cgi?id=12495
1628 // Array reinterpret casts: eg. string to immutable(ubyte)[]
1633 *pue
= Cast(loc
, type
, to
, e
);
1637 if (CTFEExp
.isCantExp(r
))
1638 error(loc
, "cannot cast `%s` to `%s` at compile time", e
.toChars(), to
.toChars());
1640 if (auto ae
= e
.isArrayLiteralExp())
1641 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
1643 if (auto se
= e
.isStringExp())
1644 se
.ownedByCtfe
= OwnedBy
.ctfe
;
1649 /******** Assignment helper functions ***************************/
1650 /* Set dest = src, where both dest and src are container value literals
1651 * (ie, struct literals, or static arrays (can be an array literal or a string))
1652 * Assignment is recursively in-place.
1653 * Purpose: any reference to a member of 'dest' will remain valid after the
1656 void assignInPlace(Expression dest
, Expression src
)
1658 if (!(dest
.op
== EXP
.structLiteral || dest
.op
== EXP
.arrayLiteral || dest
.op
== EXP
.string_
))
1660 printf("invalid op %d %d\n", src
.op
, dest
.op
);
1663 Expressions
* oldelems
;
1664 Expressions
* newelems
;
1665 if (dest
.op
== EXP
.structLiteral
)
1667 assert(dest
.op
== src
.op
);
1668 oldelems
= dest
.isStructLiteralExp().elements
;
1669 newelems
= src
.isStructLiteralExp().elements
;
1670 auto sd
= dest
.isStructLiteralExp().sd
;
1671 const nfields
= sd
.nonHiddenFields();
1672 const nvthis
= sd
.fields
.dim
- nfields
;
1673 if (nvthis
&& oldelems
.dim
>= nfields
&& oldelems
.dim
< newelems
.dim
)
1674 foreach (_
; 0 .. newelems
.dim
- oldelems
.dim
)
1675 oldelems
.push(null);
1677 else if (dest
.op
== EXP
.arrayLiteral
&& src
.op
== EXP
.arrayLiteral
)
1679 oldelems
= dest
.isArrayLiteralExp().elements
;
1680 newelems
= src
.isArrayLiteralExp().elements
;
1682 else if (dest
.op
== EXP
.string_
&& src
.op
== EXP
.string_
)
1684 sliceAssignStringFromString(dest
.isStringExp(), src
.isStringExp(), 0);
1687 else if (dest
.op
== EXP
.arrayLiteral
&& src
.op
== EXP
.string_
)
1689 sliceAssignArrayLiteralFromString(dest
.isArrayLiteralExp(), src
.isStringExp(), 0);
1692 else if (src
.op
== EXP
.arrayLiteral
&& dest
.op
== EXP
.string_
)
1694 sliceAssignStringFromArrayLiteral(dest
.isStringExp(), src
.isArrayLiteralExp(), 0);
1699 printf("invalid op %d %d\n", src
.op
, dest
.op
);
1702 assert(oldelems
.dim
== newelems
.dim
);
1703 foreach (size_t i
; 0 .. oldelems
.dim
)
1705 Expression e
= (*newelems
)[i
];
1706 Expression o
= (*oldelems
)[i
];
1707 if (e
.op
== EXP
.structLiteral
)
1709 assert(o
.op
== e
.op
);
1710 assignInPlace(o
, e
);
1712 else if (e
.type
.ty
== Tsarray
&& e
.op
!= EXP
.void_
&& o
.type
.ty
== Tsarray
)
1714 assignInPlace(o
, e
);
1718 (*oldelems
)[i
] = (*newelems
)[i
];
1723 // Given an AA literal aae, set aae[index] = newval and return newval.
1724 Expression
assignAssocArrayElement(const ref Loc loc
, AssocArrayLiteralExp aae
, Expression index
, Expression newval
)
1726 /* Create new associative array literal reflecting updated key/value
1728 Expressions
* keysx
= aae
.keys
;
1729 Expressions
* valuesx
= aae
.values
;
1731 for (size_t j
= valuesx
.dim
; j
;)
1734 Expression ekey
= (*aae
.keys
)[j
];
1735 int eq
= ctfeEqual(loc
, EXP
.equal
, ekey
, index
);
1738 (*valuesx
)[j
] = newval
;
1744 // Append index/newval to keysx[]/valuesx[]
1745 valuesx
.push(newval
);
1751 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length
1752 /// oldlen, change its length to newlen. If the newlen is longer than oldlen,
1753 /// all new elements will be set to the default initializer for the element type.
1754 Expression
changeArrayLiteralLength(UnionExp
* pue
, const ref Loc loc
, TypeArray arrayType
, Expression oldval
, size_t oldlen
, size_t newlen
)
1756 Type elemType
= arrayType
.next
;
1758 Expression defaultElem
= elemType
.defaultInitLiteral(loc
);
1759 auto elements
= new Expressions(newlen
);
1762 if (oldval
.op
== EXP
.slice
)
1764 indxlo
= cast(size_t
)oldval
.isSliceExp().lwr
.toInteger();
1765 oldval
= oldval
.isSliceExp().e1
;
1767 size_t copylen
= oldlen
< newlen ? oldlen
: newlen
;
1768 if (oldval
.op
== EXP
.string_
)
1770 StringExp oldse
= oldval
.isStringExp();
1771 void* s
= mem
.xcalloc(newlen
+ 1, oldse
.sz
);
1772 const data
= oldse
.peekData();
1773 memcpy(s
, data
.ptr
, copylen
* oldse
.sz
);
1774 const defaultValue
= cast(uint)defaultElem
.toInteger();
1775 foreach (size_t elemi
; copylen
.. newlen
)
1780 (cast(char*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(char)defaultValue
;
1783 (cast(wchar*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(wchar)defaultValue
;
1786 (cast(dchar*)s
)[cast(size_t
)(indxlo
+ elemi
)] = cast(dchar)defaultValue
;
1792 emplaceExp
!(StringExp
)(pue
, loc
, s
[0 .. newlen
* oldse
.sz
], newlen
, oldse
.sz
);
1793 StringExp se
= pue
.exp().isStringExp();
1794 se
.type
= arrayType
;
1796 se
.committed
= oldse
.committed
;
1797 se
.ownedByCtfe
= OwnedBy
.ctfe
;
1803 assert(oldval
.op
== EXP
.arrayLiteral
);
1804 ArrayLiteralExp ae
= oldval
.isArrayLiteralExp();
1805 foreach (size_t i
; 0 .. copylen
)
1806 (*elements
)[i
] = (*ae
.elements
)[indxlo
+ i
];
1808 if (elemType
.ty
== Tstruct || elemType
.ty
== Tsarray
)
1810 /* If it is an aggregate literal representing a value type,
1811 * we need to create a unique copy for each element
1813 foreach (size_t i
; copylen
.. newlen
)
1814 (*elements
)[i
] = copyLiteral(defaultElem
).copy();
1818 foreach (size_t i
; copylen
.. newlen
)
1819 (*elements
)[i
] = defaultElem
;
1821 emplaceExp
!(ArrayLiteralExp
)(pue
, loc
, arrayType
, elements
);
1822 ArrayLiteralExp aae
= pue
.exp().isArrayLiteralExp();
1823 aae
.ownedByCtfe
= OwnedBy
.ctfe
;
1828 /*************************** CTFE Sanity Checks ***************************/
1830 bool isCtfeValueValid(Expression newval
)
1832 Type tb
= newval
.type
.toBasetype();
1839 return tb
.isscalar();
1842 return tb
.ty
== Tnull ||
1843 tb
.ty
== Tpointer ||
1850 return true; // CTFE would directly use the StringExp in AST.
1852 case EXP
.arrayLiteral
:
1853 return true; //((ArrayLiteralExp *)newval)->ownedByCtfe;
1855 case EXP
.assocArrayLiteral
:
1856 return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe;
1858 case EXP
.structLiteral
:
1859 return true; //((StructLiteralExp *)newval)->ownedByCtfe;
1861 case EXP
.classReference
:
1868 return true; // vector literal
1871 return true; // function literal or delegate literal
1875 // &struct.func or &clasinst.func
1877 Expression ethis
= newval
.isDelegateExp().e1
;
1878 return (ethis
.op
== EXP
.structLiteral || ethis
.op
== EXP
.classReference || ethis
.op
== EXP
.variable
&& ethis
.isVarExp().var
== newval
.isDelegateExp().func
);
1881 case EXP
.symbolOffset
:
1883 // function pointer, or pointer to static variable
1884 Declaration d
= newval
.isSymOffExp().var
;
1885 return d
.isFuncDeclaration() || d
.isDataseg();
1896 // e1 should be a CTFE reference
1897 Expression e1
= newval
.isAddrExp().e1
;
1898 return tb
.ty
== Tpointer
&&
1900 (e1
.op
== EXP
.structLiteral || e1
.op
== EXP
.arrayLiteral
) && isCtfeValueValid(e1
) ||
1901 e1
.op
== EXP
.variable ||
1902 e1
.op
== EXP
.dotVariable
&& isCtfeReferenceValid(e1
) ||
1903 e1
.op
== EXP
.index
&& isCtfeReferenceValid(e1
) ||
1904 e1
.op
== EXP
.slice
&& e1
.type
.toBasetype().ty
== Tsarray
1910 // e1 should be an array aggregate
1911 const SliceExp se
= newval
.isSliceExp();
1912 assert(se
.lwr
&& se
.lwr
.op
== EXP
.int64
);
1913 assert(se
.upr
&& se
.upr
.op
== EXP
.int64
);
1914 return (tb
.ty
== Tarray || tb
.ty
== Tsarray
) && (se
.e1
.op
== EXP
.string_ || se
.e1
.op
== EXP
.arrayLiteral
);
1918 return true; // uninitialized value
1921 newval
.error("CTFE internal error: illegal CTFE value `%s`", newval
.toChars());
1926 bool isCtfeReferenceValid(Expression newval
)
1935 const VarDeclaration v
= newval
.isVarExp().var
.isVarDeclaration();
1937 // Must not be a reference to a reference
1943 const Expression eagg
= newval
.isIndexExp().e1
;
1944 return eagg
.op
== EXP
.string_ || eagg
.op
== EXP
.arrayLiteral || eagg
.op
== EXP
.assocArrayLiteral
;
1947 case EXP
.dotVariable
:
1949 Expression eagg
= newval
.isDotVarExp().e1
;
1950 return (eagg
.op
== EXP
.structLiteral || eagg
.op
== EXP
.classReference
) && isCtfeValueValid(eagg
);
1954 // Internally a ref variable may directly point a stack memory.
1955 // e.g. ref int v = 1;
1956 return isCtfeValueValid(newval
);
1960 // Used for debugging only
1961 void showCtfeExpr(Expression e
, int level
= 0)
1963 for (int i
= level
; i
> 0; --i
)
1965 Expressions
* elements
= null;
1966 // We need the struct definition to detect block assignment
1967 StructDeclaration sd
= null;
1968 ClassDeclaration cd
= null;
1969 if (e
.op
== EXP
.structLiteral
)
1971 elements
= e
.isStructLiteralExp().elements
;
1972 sd
= e
.isStructLiteralExp().sd
;
1973 printf("STRUCT type = %s %p:\n", e
.type
.toChars(), e
);
1975 else if (e
.op
== EXP
.classReference
)
1977 elements
= e
.isClassReferenceExp().value
.elements
;
1978 cd
= e
.isClassReferenceExp().originalClass();
1979 printf("CLASS type = %s %p:\n", e
.type
.toChars(), e
.isClassReferenceExp().value
);
1981 else if (e
.op
== EXP
.arrayLiteral
)
1983 elements
= e
.isArrayLiteralExp().elements
;
1984 printf("ARRAY LITERAL type=%s %p:\n", e
.type
.toChars(), e
);
1986 else if (e
.op
== EXP
.assocArrayLiteral
)
1988 printf("AA LITERAL type=%s %p:\n", e
.type
.toChars(), e
);
1990 else if (e
.op
== EXP
.string_
)
1992 printf("STRING %s %p\n", e
.toChars(), e
.isStringExp
.peekString
.ptr
);
1994 else if (e
.op
== EXP
.slice
)
1996 printf("SLICE %p: %s\n", e
, e
.toChars());
1997 showCtfeExpr(e
.isSliceExp().e1
, level
+ 1);
1999 else if (e
.op
== EXP
.variable
)
2001 printf("VAR %p %s\n", e
, e
.toChars());
2002 VarDeclaration v
= e
.isVarExp().var
.isVarDeclaration();
2003 if (v
&& getValue(v
))
2004 showCtfeExpr(getValue(v
), level
+ 1);
2006 else if (e
.op
== EXP
.address
)
2008 // This is potentially recursive. We mustn't try to print the thing we're pointing to.
2009 printf("POINTER %p to %p: %s\n", e
, e
.isAddrExp().e1
, e
.toChars());
2012 printf("VALUE %p: %s\n", e
, e
.toChars());
2015 size_t fieldsSoFar
= 0;
2016 for (size_t i
= 0; i
< elements
.dim
; i
++)
2018 Expression z
= null;
2019 VarDeclaration v
= null;
2022 printf("...(total %d elements)\n", cast(int)elements
.dim
);
2032 while (i
- fieldsSoFar
>= cd
.fields
.dim
)
2034 fieldsSoFar
+= cd
.fields
.dim
;
2036 for (int j
= level
; j
> 0; --j
)
2038 printf(" BASE CLASS: %s\n", cd
.toChars());
2040 v
= cd
.fields
[i
- fieldsSoFar
];
2041 assert((elements
.dim
+ i
) >= (fieldsSoFar
+ cd
.fields
.dim
));
2042 size_t indx
= (elements
.dim
- fieldsSoFar
) - cd
.fields
.dim
+ i
;
2043 assert(indx
< elements
.dim
);
2044 z
= (*elements
)[indx
];
2048 for (int j
= level
; j
> 0; --j
)
2055 // If it is a void assignment, use the default initializer
2056 if ((v
.type
.ty
!= z
.type
.ty
) && v
.type
.ty
== Tsarray
)
2058 for (int j
= level
; --j
;)
2060 printf(" field: block initialized static array\n");
2064 showCtfeExpr(z
, level
+ 1);
2069 /*************************** Void initialization ***************************/
2070 UnionExp
voidInitLiteral(Type t
, VarDeclaration var
)
2073 if (t
.ty
== Tsarray
)
2075 TypeSArray tsa
= cast(TypeSArray
)t
;
2076 Expression elem
= voidInitLiteral(tsa
.next
, var
).copy();
2077 // For aggregate value types (structs, static arrays) we must
2078 // create an a separate copy for each element.
2079 const mustCopy
= (elem
.op
== EXP
.arrayLiteral || elem
.op
== EXP
.structLiteral
);
2080 const d
= cast(size_t
)tsa
.dim
.toInteger();
2081 auto elements
= new Expressions(d
);
2084 if (mustCopy
&& i
> 0)
2085 elem
= copyLiteral(elem
).copy();
2086 (*elements
)[i
] = elem
;
2088 emplaceExp
!(ArrayLiteralExp
)(&ue
, var
.loc
, tsa
, elements
);
2089 ArrayLiteralExp ae
= ue
.exp().isArrayLiteralExp();
2090 ae
.ownedByCtfe
= OwnedBy
.ctfe
;
2092 else if (t
.ty
== Tstruct
)
2094 TypeStruct ts
= cast(TypeStruct
)t
;
2095 auto exps
= new Expressions(ts
.sym
.fields
.dim
);
2096 foreach (size_t i
; 0 .. ts
.sym
.fields
.dim
)
2098 (*exps
)[i
] = voidInitLiteral(ts
.sym
.fields
[i
].type
, ts
.sym
.fields
[i
]).copy();
2100 emplaceExp
!(StructLiteralExp
)(&ue
, var
.loc
, ts
.sym
, exps
);
2101 StructLiteralExp se
= ue
.exp().isStructLiteralExp();
2103 se
.ownedByCtfe
= OwnedBy
.ctfe
;
2106 emplaceExp
!(VoidInitExp
)(&ue
, var
);