1 /** 2 * CTFE for expressions involving pointers, slices, array concatenation etc. 3 * 4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://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 10 */ 11 12 module dmd.ctfeexpr; 13 14 import core.stdc.stdio; 15 import core.stdc.stdlib; 16 import core.stdc.string; 17 import dmd.arraytypes; 18 import dmd.complex; 19 import dmd.constfold; 20 import dmd.compiler; 21 import dmd.dclass; 22 import dmd.declaration; 23 import dmd.dinterpret; 24 import dmd.dstruct; 25 import dmd.dtemplate; 26 import dmd.errors; 27 import dmd.expression; 28 import dmd.func; 29 import dmd.globals; 30 import dmd.mtype; 31 import dmd.root.ctfloat; 32 import dmd.root.port; 33 import dmd.root.rmem; 34 import dmd.tokens; 35 import dmd.visitor; 36 37 38 /*********************************************************** 39 * A reference to a class, or an interface. We need this when we 40 * point to a base class (we must record what the type is). 41 */ 42 extern (C++) final class ClassReferenceExp : Expression 43 { 44 StructLiteralExp value; 45 46 extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) 47 { 48 super(loc, TOK.classReference, __traits(classInstanceSize, ClassReferenceExp)); 49 assert(lit && lit.sd && lit.sd.isClassDeclaration()); 50 this.value = lit; 51 this.type = type; 52 } 53 54 ClassDeclaration originalClass() 55 { 56 return value.sd.isClassDeclaration(); 57 } 58 59 // Return index of the field, or -1 if not found 60 private int getFieldIndex(Type fieldtype, uint fieldoffset) 61 { 62 ClassDeclaration cd = originalClass(); 63 uint fieldsSoFar = 0; 64 for (size_t j = 0; j < value.elements.dim; j++) 65 { 66 while (j - fieldsSoFar >= cd.fields.dim) 67 { 68 fieldsSoFar += cd.fields.dim; 69 cd = cd.baseClass; 70 } 71 VarDeclaration v2 = cd.fields[j - fieldsSoFar]; 72 if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size()) 73 { 74 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar)); 75 } 76 } 77 return -1; 78 } 79 80 // Return index of the field, or -1 if not found 81 // Same as getFieldIndex, but checks for a direct match with the VarDeclaration 82 int findFieldIndexByName(VarDeclaration v) 83 { 84 ClassDeclaration cd = originalClass(); 85 size_t fieldsSoFar = 0; 86 for (size_t j = 0; j < value.elements.dim; j++) 87 { 88 while (j - fieldsSoFar >= cd.fields.dim) 89 { 90 fieldsSoFar += cd.fields.dim; 91 cd = cd.baseClass; 92 } 93 VarDeclaration v2 = cd.fields[j - fieldsSoFar]; 94 if (v == v2) 95 { 96 return cast(int)(value.elements.dim - fieldsSoFar - cd.fields.dim + (j - fieldsSoFar)); 97 } 98 } 99 return -1; 100 } 101 102 override void accept(Visitor v) 103 { 104 v.visit(this); 105 } 106 } 107 108 /************************* 109 * Same as getFieldIndex, but checks for a direct match with the VarDeclaration 110 * Returns: 111 * index of the field, or -1 if not found 112 */ 113 int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pure 114 { 115 foreach (i, field; sd.fields) 116 { 117 if (field == v) 118 return cast(int)i; 119 } 120 return -1; 121 } 122 123 /*********************************************************** 124 * Fake class which holds the thrown exception. 125 * Used for implementing exception handling. 126 */ 127 extern (C++) final class ThrownExceptionExp : Expression 128 { 129 ClassReferenceExp thrown; // the thing being tossed 130 131 extern (D) this(const ref Loc loc, ClassReferenceExp victim) 132 { 133 super(loc, TOK.thrownException, __traits(classInstanceSize, ThrownExceptionExp)); 134 this.thrown = victim; 135 this.type = victim.type; 136 } 137 138 override const(char)* toChars() const 139 { 140 return "CTFE ThrownException"; 141 } 142 143 // Generate an error message when this exception is not caught 144 extern (D) void generateUncaughtError() 145 { 146 UnionExp ue = void; 147 Expression e = resolveSlice((*thrown.value.elements)[0], &ue); 148 StringExp se = e.toStringExp(); 149 thrown.error("uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars()); 150 /* Also give the line where the throw statement was. We won't have it 151 * in the case where the ThrowStatement is generated internally 152 * (eg, in ScopeStatement) 153 */ 154 if (loc.isValid() && !loc.equals(thrown.loc)) 155 .errorSupplemental(loc, "thrown from here"); 156 } 157 158 override void accept(Visitor v) 159 { 160 v.visit(this); 161 } 162 } 163 164 /*********************************************************** 165 * This type is only used by the interpreter. 166 */ 167 extern (C++) final class CTFEExp : Expression 168 { 169 extern (D) this(TOK tok) 170 { 171 super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp)); 172 type = Type.tvoid; 173 } 174 175 override const(char)* toChars() const 176 { 177 switch (op) 178 { 179 case TOK.cantExpression: 180 return "<cant>"; 181 case TOK.voidExpression: 182 return "cast(void)0"; 183 case TOK.showCtfeContext: 184 return "<error>"; 185 case TOK.break_: 186 return "<break>"; 187 case TOK.continue_: 188 return "<continue>"; 189 case TOK.goto_: 190 return "<goto>"; 191 default: 192 assert(0); 193 } 194 } 195 196 extern (D) __gshared CTFEExp cantexp; 197 extern (D) __gshared CTFEExp voidexp; 198 extern (D) __gshared CTFEExp breakexp; 199 extern (D) __gshared CTFEExp continueexp; 200 extern (D) __gshared CTFEExp gotoexp; 201 /* Used when additional information is needed regarding 202 * a ctfe error. 203 */ 204 extern (D) __gshared CTFEExp showcontext; 205 206 extern (D) static bool isCantExp(const Expression e) 207 { 208 return e && e.op == TOK.cantExpression; 209 } 210 211 extern (D) static bool isGotoExp(const Expression e) 212 { 213 return e && e.op == TOK.goto_; 214 } 215 } 216 217 // True if 'e' is CTFEExp::cantexp, or an exception 218 bool exceptionOrCantInterpret(const Expression e) 219 { 220 return e && (e.op == TOK.cantExpression || e.op == TOK.thrownException || e.op == TOK.showCtfeContext); 221 } 222 223 /************** Aggregate literals (AA/string/array/struct) ******************/ 224 // Given expr, which evaluates to an array/AA/string literal, 225 // return true if it needs to be copied 226 bool needToCopyLiteral(const Expression expr) 227 { 228 Expression e = cast()expr; 229 for (;;) 230 { 231 switch (e.op) 232 { 233 case TOK.arrayLiteral: 234 return (cast(ArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; 235 case TOK.assocArrayLiteral: 236 return (cast(AssocArrayLiteralExp)e).ownedByCtfe == OwnedBy.code; 237 case TOK.structLiteral: 238 return (cast(StructLiteralExp)e).ownedByCtfe == OwnedBy.code; 239 case TOK.string_: 240 case TOK.this_: 241 case TOK.variable: 242 return false; 243 case TOK.assign: 244 return false; 245 case TOK.index: 246 case TOK.dotVariable: 247 case TOK.slice: 248 case TOK.cast_: 249 e = (cast(UnaExp)e).e1; 250 continue; 251 case TOK.concatenate: 252 return needToCopyLiteral((cast(BinExp)e).e1) || needToCopyLiteral((cast(BinExp)e).e2); 253 case TOK.concatenateAssign: 254 case TOK.concatenateElemAssign: 255 case TOK.concatenateDcharAssign: 256 e = (cast(BinExp)e).e2; 257 continue; 258 default: 259 return false; 260 } 261 } 262 } 263 264 private Expressions* copyLiteralArray(Expressions* oldelems, Expression basis = null) 265 { 266 if (!oldelems) 267 return oldelems; 268 incArrayAllocs(); 269 auto newelems = new Expressions(oldelems.dim); 270 foreach (i, el; *oldelems) 271 { 272 (*newelems)[i] = copyLiteral(el ? el : basis).copy(); 273 } 274 return newelems; 275 } 276 277 // Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. 278 // This value will be used for in-place modification. 279 UnionExp copyLiteral(Expression e) 280 { 281 UnionExp ue = void; 282 if (auto se = e.isStringExp()) // syntaxCopy doesn't make a copy for StringExp! 283 { 284 char* s = cast(char*)mem.xcalloc(se.len + 1, se.sz); 285 const slice = se.peekData(); 286 memcpy(s, slice.ptr, slice.length); 287 emplaceExp!(StringExp)(&ue, se.loc, s[0 .. se.len * se.sz], se.len, se.sz); 288 StringExp se2 = cast(StringExp)ue.exp(); 289 se2.committed = se.committed; 290 se2.postfix = se.postfix; 291 se2.type = se.type; 292 se2.ownedByCtfe = OwnedBy.ctfe; 293 return ue; 294 } 295 if (auto ale = e.isArrayLiteralExp()) 296 { 297 auto elements = copyLiteralArray(ale.elements, ale.basis); 298 299 emplaceExp!(ArrayLiteralExp)(&ue, e.loc, e.type, elements); 300 301 ArrayLiteralExp r = cast(ArrayLiteralExp)ue.exp(); 302 r.ownedByCtfe = OwnedBy.ctfe; 303 return ue; 304 } 305 if (auto aae = e.isAssocArrayLiteralExp()) 306 { 307 emplaceExp!(AssocArrayLiteralExp)(&ue, e.loc, copyLiteralArray(aae.keys), copyLiteralArray(aae.values)); 308 AssocArrayLiteralExp r = cast(AssocArrayLiteralExp)ue.exp(); 309 r.type = e.type; 310 r.ownedByCtfe = OwnedBy.ctfe; 311 return ue; 312 } 313 if (auto sle = e.isStructLiteralExp()) 314 { 315 /* syntaxCopy doesn't work for struct literals, because of a nasty special 316 * case: block assignment is permitted inside struct literals, eg, 317 * an int[4] array can be initialized with a single int. 318 */ 319 auto oldelems = sle.elements; 320 auto newelems = new Expressions(oldelems.dim); 321 foreach (i, ref el; *newelems) 322 { 323 // We need the struct definition to detect block assignment 324 auto v = sle.sd.fields[i]; 325 auto m = (*oldelems)[i]; 326 327 // If it is a void assignment, use the default initializer 328 if (!m) 329 m = voidInitLiteral(v.type, v).copy(); 330 331 if (v.type.ty == Tarray || v.type.ty == Taarray) 332 { 333 // Don't have to copy array references 334 } 335 else 336 { 337 // Buzilla 15681: Copy the source element always. 338 m = copyLiteral(m).copy(); 339 340 // Block assignment from inside struct literals 341 if (v.type.ty != m.type.ty && v.type.ty == Tsarray) 342 { 343 auto tsa = v.type.isTypeSArray(); 344 auto len = cast(size_t)tsa.dim.toInteger(); 345 UnionExp uex = void; 346 m = createBlockDuplicatedArrayLiteral(&uex, e.loc, v.type, m, len); 347 if (m == uex.exp()) 348 m = uex.copy(); 349 } 350 } 351 el = m; 352 } 353 emplaceExp!(StructLiteralExp)(&ue, e.loc, sle.sd, newelems, sle.stype); 354 auto r = ue.exp().isStructLiteralExp(); 355 r.type = e.type; 356 r.ownedByCtfe = OwnedBy.ctfe; 357 r.origin = sle.origin; 358 return ue; 359 } 360 if (e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.symbolOffset || e.op == TOK.null_ || e.op == TOK.variable || e.op == TOK.dotVariable || e.op == TOK.int64 || e.op == TOK.float64 || e.op == TOK.char_ || e.op == TOK.complex80 || e.op == TOK.void_ || e.op == TOK.vector || e.op == TOK.typeid_) 361 { 362 // Simple value types 363 // Keep e1 for DelegateExp and DotVarExp 364 emplaceExp!(UnionExp)(&ue, e); 365 Expression r = ue.exp(); 366 r.type = e.type; 367 return ue; 368 } 369 if (auto se = e.isSliceExp()) 370 { 371 if (se.type.toBasetype().ty == Tsarray) 372 { 373 // same with resolveSlice() 374 if (se.e1.op == TOK.null_) 375 { 376 emplaceExp!(NullExp)(&ue, se.loc, se.type); 377 return ue; 378 } 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; 383 return ue; 384 } 385 else 386 { 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(); 390 r.type = e.type; 391 return ue; 392 } 393 } 394 if (isPointer(e.type)) 395 { 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()) 402 { 403 emplaceExp!(DotVarExp)(&ue, e.loc, dve.e1, dve.var, dve.hasOverloads); 404 } 405 else 406 assert(0); 407 408 Expression r = ue.exp(); 409 r.type = e.type; 410 return ue; 411 } 412 if (auto cre = e.isClassReferenceExp()) 413 { 414 emplaceExp!(ClassReferenceExp)(&ue, e.loc, cre.value, e.type); 415 return ue; 416 } 417 if (e.op == TOK.error) 418 { 419 emplaceExp!(UnionExp)(&ue, e); 420 return ue; 421 } 422 e.error("CTFE internal error: literal `%s`", e.toChars()); 423 assert(0); 424 } 425 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. 431 */ 432 Expression paintTypeOntoLiteral(Type type, Expression lit) 433 { 434 if (lit.type.equals(type)) 435 return lit; 436 return paintTypeOntoLiteralCopy(type, lit).copy(); 437 } 438 439 Expression paintTypeOntoLiteral(UnionExp* pue, Type type, Expression lit) 440 { 441 if (lit.type.equals(type)) 442 return lit; 443 *pue = paintTypeOntoLiteralCopy(type, lit); 444 return pue.exp(); 445 } 446 447 private UnionExp paintTypeOntoLiteralCopy(Type type, Expression lit) 448 { 449 UnionExp ue; 450 if (lit.type.equals(type)) 451 { 452 emplaceExp!(UnionExp)(&ue, lit); 453 return ue; 454 } 455 // If it is a cast to inout, retain the original type of the referenced part. 456 if (type.hasWild() && type.hasPointers()) 457 { 458 emplaceExp!(UnionExp)(&ue, lit); 459 ue.exp().type = type; 460 return ue; 461 } 462 if (auto se = lit.isSliceExp()) 463 { 464 emplaceExp!(SliceExp)(&ue, lit.loc, se.e1, se.lwr, se.upr); 465 } 466 else if (auto ie = lit.isIndexExp()) 467 { 468 emplaceExp!(IndexExp)(&ue, lit.loc, ie.e1, ie.e2); 469 } 470 else if (lit.op == TOK.arrayLiteral) 471 { 472 emplaceExp!(SliceExp)(&ue, lit.loc, lit, ctfeEmplaceExp!IntegerExp(Loc.initial, 0, Type.tsize_t), ArrayLength(Type.tsize_t, lit).copy()); 473 } 474 else if (lit.op == TOK.string_) 475 { 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()); 478 } 479 else if (auto aae = lit.isAssocArrayLiteralExp()) 480 { 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 = cast(AssocArrayLiteralExp)ue.exp(); 486 aae.ownedByCtfe = wasOwned; 487 } 488 else 489 { 490 // Can't type paint from struct to struct*; this needs another 491 // level of indirection 492 if (lit.op == TOK.structLiteral && isPointer(type)) 493 lit.error("CTFE internal error: painting `%s`", type.toChars()); 494 ue = copyLiteral(lit); 495 } 496 ue.exp().type = type; 497 return ue; 498 } 499 500 /************************************* 501 * If e is a SliceExp, constant fold it. 502 * Params: 503 * e = expression to resolve 504 * pue = if not null, store resulting expression here 505 * Returns: 506 * resulting expression 507 */ 508 Expression resolveSlice(Expression e, UnionExp* pue = null) 509 { 510 SliceExp se = e.isSliceExp(); 511 if (!se) 512 return e; 513 if (se.e1.op == TOK.null_) 514 return se.e1; 515 if (pue) 516 { 517 *pue = Slice(e.type, se.e1, se.lwr, se.upr); 518 return pue.exp(); 519 } 520 else 521 return Slice(e.type, se.e1, se.lwr, se.upr).copy(); 522 } 523 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 527 * need the length. 528 */ 529 uinteger_t resolveArrayLength(const Expression e) 530 { 531 switch (e.op) 532 { 533 case TOK.vector: 534 return e.isVectorExp().dim; 535 536 case TOK.null_: 537 return 0; 538 539 case TOK.slice: 540 { 541 auto se = cast(SliceExp)e; 542 const ilo = se.lwr.toInteger(); 543 const iup = se.upr.toInteger(); 544 return iup - ilo; 545 } 546 547 case TOK.string_: 548 return e.isStringExp().len; 549 550 case TOK.arrayLiteral: 551 { 552 const ale = e.isArrayLiteralExp(); 553 return ale.elements ? ale.elements.dim : 0; 554 } 555 556 case TOK.assocArrayLiteral: 557 { 558 return e.isAssocArrayLiteralExp().keys.dim; 559 } 560 561 default: 562 assert(0); 563 } 564 } 565 566 /****************************** 567 * Helper for NewExp 568 * Create an array literal consisting of 'elem' duplicated 'dim' times. 569 * Params: 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 575 * Returns: 576 * Constructed ArrayLiteralExp 577 */ 578 ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim) 579 { 580 if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray) 581 { 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 UnionExp ue = void; 586 elem = createBlockDuplicatedArrayLiteral(&ue, loc, type.nextOf(), elem, len); 587 if (elem == ue.exp()) 588 elem = ue.copy(); 589 } 590 591 // Buzilla 15681 592 const tb = elem.type.toBasetype(); 593 const mustCopy = tb.ty == Tstruct || tb.ty == Tsarray; 594 595 auto elements = new Expressions(dim); 596 foreach (i, ref el; *elements) 597 { 598 el = mustCopy && i ? copyLiteral(elem).copy() : elem; 599 } 600 emplaceExp!(ArrayLiteralExp)(pue, loc, type, elements); 601 auto ale = pue.exp().isArrayLiteralExp(); 602 ale.ownedByCtfe = OwnedBy.ctfe; 603 return ale; 604 } 605 606 /****************************** 607 * Helper for NewExp 608 * Create a string literal consisting of 'value' duplicated 'dim' times. 609 */ 610 StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz) 611 { 612 auto s = cast(char*)mem.xcalloc(dim, sz); 613 foreach (elemi; 0 .. dim) 614 { 615 switch (sz) 616 { 617 case 1: 618 s[elemi] = cast(char)value; 619 break; 620 case 2: 621 (cast(wchar*)s)[elemi] = cast(wchar)value; 622 break; 623 case 4: 624 (cast(dchar*)s)[elemi] = value; 625 break; 626 default: 627 assert(0); 628 } 629 } 630 emplaceExp!(StringExp)(pue, loc, s[0 .. dim * sz], dim, sz); 631 auto se = pue.exp().isStringExp(); 632 se.type = type; 633 se.committed = true; 634 se.ownedByCtfe = OwnedBy.ctfe; 635 return se; 636 } 637 638 // Return true if t is an AA 639 bool isAssocArray(Type t) 640 { 641 return t.toBasetype().isTypeAArray() !is null; 642 } 643 644 // Given a template AA type, extract the corresponding built-in AA type 645 TypeAArray toBuiltinAAType(Type t) 646 { 647 return t.toBasetype().isTypeAArray(); 648 } 649 650 /************** TypeInfo operations ************************************/ 651 // Return true if type is TypeInfo_Class 652 bool isTypeInfo_Class(const Type type) 653 { 654 auto tc = cast()type.isTypeClass(); 655 return tc && (Type.dtypeinfo == tc.sym || Type.dtypeinfo.isBaseOf(tc.sym, null)); 656 } 657 658 /************** Pointer operations ************************************/ 659 // Return true if t is a pointer (not a function pointer) 660 bool isPointer(Type t) 661 { 662 Type tb = t.toBasetype(); 663 return tb.ty == Tpointer && tb.nextOf().ty != Tfunction; 664 } 665 666 // For CTFE only. Returns true if 'e' is true or a non-null pointer. 667 bool isTrueBool(Expression e) 668 { 669 return e.isBool(true) || ((e.type.ty == Tpointer || e.type.ty == Tclass) && e.op != TOK.null_); 670 } 671 672 /* Is it safe to convert from srcPointee* to destPointee* ? 673 * srcPointee is the genuine type (never void). 674 * destPointee may be void. 675 */ 676 bool isSafePointerCast(Type srcPointee, Type destPointee) 677 { 678 // It's safe to cast S** to D** if it's OK to cast S* to D* 679 while (srcPointee.ty == Tpointer && destPointee.ty == Tpointer) 680 { 681 srcPointee = srcPointee.nextOf(); 682 destPointee = destPointee.nextOf(); 683 } 684 // It's OK if both are the same (modulo const) 685 if (srcPointee.constConv(destPointee)) 686 return true; 687 // It's OK if function pointers differ only in safe/pure/nothrow 688 if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction) 689 return srcPointee.covariant(destPointee) == 1; 690 // it's OK to cast to void* 691 if (destPointee.ty == Tvoid) 692 return true; 693 // It's OK to cast from V[K] to void* 694 if (srcPointee.ty == Taarray && destPointee == Type.tvoidptr) 695 return true; 696 // It's OK if they are the same size (static array of) integers, eg: 697 // int* --> uint* 698 // int[5][] --> uint[5][] 699 if (srcPointee.ty == Tsarray && destPointee.ty == Tsarray) 700 { 701 if (srcPointee.size() != destPointee.size()) 702 return false; 703 srcPointee = srcPointee.baseElemOf(); 704 destPointee = destPointee.baseElemOf(); 705 } 706 return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size(); 707 } 708 709 Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) 710 { 711 *ofs = 0; 712 if (auto ae = e.isAddrExp()) 713 e = ae.e1; 714 if (auto soe = e.isSymOffExp()) 715 *ofs = soe.offset; 716 if (auto dve = e.isDotVarExp()) 717 { 718 const ex = dve.e1; 719 const v = dve.var.isVarDeclaration(); 720 assert(v); 721 StructLiteralExp se = (ex.op == TOK.classReference) 722 ? (cast(ClassReferenceExp)ex).value 723 : cast(StructLiteralExp)ex; 724 725 // We can't use getField, because it makes a copy 726 const i = (ex.op == TOK.classReference) 727 ? (cast(ClassReferenceExp)ex).getFieldIndex(e.type, v.offset) 728 : se.getFieldIndex(e.type, v.offset); 729 e = (*se.elements)[i]; 730 } 731 if (auto ie = e.isIndexExp()) 732 { 733 // Note that each AA element is part of its own memory block 734 if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == TOK.string_ || ie.e1.op == TOK.arrayLiteral) && ie.e2.op == TOK.int64) 735 { 736 *ofs = ie.e2.toInteger(); 737 return ie.e1; 738 } 739 } 740 if (auto se = e.isSliceExp()) 741 { 742 if (se && e.type.toBasetype().ty == Tsarray && 743 (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral) && se.lwr.op == TOK.int64) 744 { 745 *ofs = se.lwr.toInteger(); 746 return se.e1; 747 } 748 } 749 750 // It can be a `null` disguised as a cast, e.g. `cast(void*)0`. 751 if (auto ie = e.isIntegerExp()) 752 if (ie.type.ty == Tpointer && ie.getInteger() == 0) 753 return new NullExp(ie.loc, e.type.nextOf()); 754 // Those casts are invalid, but let the rest of the code handle it, 755 // as it could be something like `x !is null`, which doesn't need 756 // to dereference the pointer, even if the pointer is `cast(void*)420`. 757 758 return e; 759 } 760 761 /** Return true if agg1 and agg2 are pointers to the same memory block 762 */ 763 bool pointToSameMemoryBlock(Expression agg1, Expression agg2) 764 { 765 if (agg1 == agg2) 766 return true; 767 // For integers cast to pointers, we regard them as non-comparable 768 // unless they are identical. (This may be overly strict). 769 if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger()) 770 { 771 return true; 772 } 773 // Note that type painting can occur with VarExp, so we 774 // must compare the variables being pointed to. 775 if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var) 776 { 777 return true; 778 } 779 if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) 780 { 781 return true; 782 } 783 return false; 784 } 785 786 // return e1 - e2 as an integer, or error if not possible 787 UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2) 788 { 789 UnionExp ue = void; 790 dinteger_t ofs1, ofs2; 791 Expression agg1 = getAggregateFromPointer(e1, &ofs1); 792 Expression agg2 = getAggregateFromPointer(e2, &ofs2); 793 if (agg1 == agg2) 794 { 795 Type pointee = (cast(TypePointer)agg1.type).next; 796 const sz = pointee.size(); 797 emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); 798 } 799 else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ && 800 (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr) 801 { 802 Type pointee = (cast(TypePointer)agg1.type).next; 803 const sz = pointee.size(); 804 emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); 805 } 806 else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && 807 (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) 808 { 809 emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type); 810 } 811 else 812 { 813 error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars()); 814 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 815 } 816 return ue; 817 } 818 819 // Return eptr op e2, where eptr is a pointer, e2 is an integer, 820 // and op is TOK.add or TOK.min 821 UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2) 822 { 823 UnionExp ue; 824 if (eptr.type.nextOf().ty == Tvoid) 825 { 826 error(loc, "cannot perform arithmetic on `void*` pointers at compile time"); 827 Lcant: 828 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 829 return ue; 830 } 831 if (eptr.op == TOK.address) 832 eptr = (cast(AddrExp)eptr).e1; 833 dinteger_t ofs1; 834 Expression agg1 = getAggregateFromPointer(eptr, &ofs1); 835 if (agg1.op == TOK.symbolOffset) 836 { 837 if ((cast(SymOffExp)agg1).var.type.ty != Tsarray) 838 { 839 error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time"); 840 goto Lcant; 841 } 842 } 843 else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral) 844 { 845 error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); 846 goto Lcant; 847 } 848 dinteger_t ofs2 = e2.toInteger(); 849 Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next; 850 dinteger_t sz = pointee.size(); 851 sinteger_t indx; 852 dinteger_t len; 853 if (agg1.op == TOK.symbolOffset) 854 { 855 indx = ofs1 / sz; 856 len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger(); 857 } 858 else 859 { 860 Expression dollar = ArrayLength(Type.tsize_t, agg1).copy(); 861 assert(!CTFEExp.isCantExp(dollar)); 862 indx = ofs1; 863 len = dollar.toInteger(); 864 } 865 if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus) 866 indx += ofs2 / sz; 867 else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus) 868 indx -= ofs2 / sz; 869 else 870 { 871 error(loc, "CTFE internal error: bad pointer operation"); 872 goto Lcant; 873 } 874 if (indx < 0 || len < indx) 875 { 876 error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len); 877 goto Lcant; 878 } 879 if (agg1.op == TOK.symbolOffset) 880 { 881 emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz); 882 SymOffExp se = cast(SymOffExp)ue.exp(); 883 se.type = type; 884 return ue; 885 } 886 if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_) 887 { 888 error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars()); 889 goto Lcant; 890 } 891 if (eptr.type.toBasetype().ty == Tsarray) 892 { 893 dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger(); 894 // Create a CTFE pointer &agg1[indx .. indx+dim] 895 auto se = ctfeEmplaceExp!SliceExp(loc, agg1, 896 ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t), 897 ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t)); 898 se.type = type.toBasetype().nextOf(); 899 emplaceExp!(AddrExp)(&ue, loc, se); 900 ue.exp().type = type; 901 return ue; 902 } 903 // Create a CTFE pointer &agg1[indx] 904 auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t); 905 Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs); 906 ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992 907 emplaceExp!(AddrExp)(&ue, loc, ie); 908 ue.exp().type = type; 909 return ue; 910 } 911 912 // Return 1 if true, 0 if false 913 // -1 if comparison is illegal because they point to non-comparable memory blocks 914 int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2) 915 { 916 if (pointToSameMemoryBlock(agg1, agg2)) 917 { 918 int n; 919 switch (op) 920 { 921 case TOK.lessThan: 922 n = (ofs1 < ofs2); 923 break; 924 case TOK.lessOrEqual: 925 n = (ofs1 <= ofs2); 926 break; 927 case TOK.greaterThan: 928 n = (ofs1 > ofs2); 929 break; 930 case TOK.greaterOrEqual: 931 n = (ofs1 >= ofs2); 932 break; 933 case TOK.identity: 934 case TOK.equal: 935 n = (ofs1 == ofs2); 936 break; 937 case TOK.notIdentity: 938 case TOK.notEqual: 939 n = (ofs1 != ofs2); 940 break; 941 default: 942 assert(0); 943 } 944 return n; 945 } 946 const null1 = (agg1.op == TOK.null_); 947 const null2 = (agg2.op == TOK.null_); 948 int cmp; 949 if (null1 || null2) 950 { 951 switch (op) 952 { 953 case TOK.lessThan: 954 cmp = null1 && !null2; 955 break; 956 case TOK.greaterThan: 957 cmp = !null1 && null2; 958 break; 959 case TOK.lessOrEqual: 960 cmp = null1; 961 break; 962 case TOK.greaterOrEqual: 963 cmp = null2; 964 break; 965 case TOK.identity: 966 case TOK.equal: 967 case TOK.notIdentity: // 'cmp' gets inverted below 968 case TOK.notEqual: 969 cmp = (null1 == null2); 970 break; 971 default: 972 assert(0); 973 } 974 } 975 else 976 { 977 switch (op) 978 { 979 case TOK.identity: 980 case TOK.equal: 981 case TOK.notIdentity: // 'cmp' gets inverted below 982 case TOK.notEqual: 983 cmp = 0; 984 break; 985 default: 986 return -1; // memory blocks are different 987 } 988 } 989 if (op == TOK.notIdentity || op == TOK.notEqual) 990 cmp ^= 1; 991 return cmp; 992 } 993 994 // True if conversion from type 'from' to 'to' involves a reinterpret_cast 995 // floating point -> integer or integer -> floating point 996 bool isFloatIntPaint(Type to, Type from) 997 { 998 return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral()); 999 } 1000 1001 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. 1002 Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to) 1003 { 1004 if (exceptionOrCantInterpret(fromVal)) 1005 return fromVal; 1006 assert(to.size() == 4 || to.size() == 8); 1007 return Compiler.paintAsType(pue, fromVal, to); 1008 } 1009 1010 /******** Constant folding, with support for CTFE ***************************/ 1011 /// Return true if non-pointer expression e can be compared 1012 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity 1013 bool isCtfeComparable(Expression e) 1014 { 1015 if (e.op == TOK.slice) 1016 e = (cast(SliceExp)e).e1; 1017 if (e.isConst() != 1) 1018 { 1019 if (e.op == TOK.null_ || e.op == TOK.string_ || e.op == TOK.function_ || e.op == TOK.delegate_ || e.op == TOK.arrayLiteral || e.op == TOK.structLiteral || e.op == TOK.assocArrayLiteral || e.op == TOK.classReference) 1020 { 1021 return true; 1022 } 1023 // https://issues.dlang.org/show_bug.cgi?id=14123 1024 // TypeInfo object is comparable in CTFE 1025 if (e.op == TOK.typeid_) 1026 return true; 1027 return false; 1028 } 1029 return true; 1030 } 1031 1032 /// Map TOK comparison ops 1033 private bool numCmp(N)(TOK op, N n1, N n2) 1034 { 1035 switch (op) 1036 { 1037 case TOK.lessThan: 1038 return n1 < n2; 1039 case TOK.lessOrEqual: 1040 return n1 <= n2; 1041 case TOK.greaterThan: 1042 return n1 > n2; 1043 case TOK.greaterOrEqual: 1044 return n1 >= n2; 1045 1046 default: 1047 assert(0); 1048 } 1049 } 1050 1051 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1052 bool specificCmp(TOK op, int rawCmp) 1053 { 1054 return numCmp!int(op, rawCmp, 0); 1055 } 1056 1057 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1058 bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2) 1059 { 1060 return numCmp!dinteger_t(op, n1, n2); 1061 } 1062 1063 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1064 bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2) 1065 { 1066 return numCmp!sinteger_t(op, n1, n2); 1067 } 1068 1069 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1070 bool realCmp(TOK op, real_t r1, real_t r2) 1071 { 1072 // Don't rely on compiler, handle NAN arguments separately 1073 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered 1074 { 1075 switch (op) 1076 { 1077 case TOK.lessThan: 1078 case TOK.lessOrEqual: 1079 case TOK.greaterThan: 1080 case TOK.greaterOrEqual: 1081 return false; 1082 1083 default: 1084 assert(0); 1085 } 1086 } 1087 else 1088 { 1089 return numCmp!real_t(op, r1, r2); 1090 } 1091 } 1092 1093 /* Conceptually the same as memcmp(e1, e2). 1094 * e1 and e2 may be strings, arrayliterals, or slices. 1095 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. 1096 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. 1097 * Returns: 1098 * -1,0,1 1099 */ 1100 private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len) 1101 { 1102 // Resolve slices, if necessary 1103 uinteger_t lo1 = 0; 1104 uinteger_t lo2 = 0; 1105 1106 Expression x1 = e1; 1107 if (auto sle1 = x1.isSliceExp()) 1108 { 1109 lo1 = sle1.lwr.toInteger(); 1110 x1 = sle1.e1; 1111 } 1112 auto se1 = x1.isStringExp(); 1113 auto ae1 = x1.isArrayLiteralExp(); 1114 1115 Expression x2 = e2; 1116 if (auto sle2 = x2.isSliceExp()) 1117 { 1118 lo2 = sle2.lwr.toInteger(); 1119 x2 = sle2.e1; 1120 } 1121 auto se2 = x2.isStringExp(); 1122 auto ae2 = x2.isArrayLiteralExp(); 1123 1124 // Now both must be either TOK.arrayLiteral or TOK.string_ 1125 if (se1 && se2) 1126 return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); 1127 if (se1 && ae2) 1128 return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); 1129 if (se2 && ae1) 1130 return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len); 1131 assert(ae1 && ae2); 1132 // Comparing two array literals. This case is potentially recursive. 1133 // If they aren't strings, we just need an equality check rather than 1134 // a full cmp. 1135 const bool needCmp = ae1.type.nextOf().isintegral(); 1136 foreach (size_t i; 0 .. cast(size_t)len) 1137 { 1138 Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; 1139 Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)]; 1140 if (needCmp) 1141 { 1142 const sinteger_t c = ee1.toInteger() - ee2.toInteger(); 1143 if (c > 0) 1144 return 1; 1145 if (c < 0) 1146 return -1; 1147 } 1148 else 1149 { 1150 if (ctfeRawCmp(loc, ee1, ee2)) 1151 return 1; 1152 } 1153 } 1154 return 0; 1155 } 1156 1157 /* Given a delegate expression e, return .funcptr. 1158 * If e is NullExp, return NULL. 1159 */ 1160 private FuncDeclaration funcptrOf(Expression e) 1161 { 1162 assert(e.type.ty == Tdelegate); 1163 if (auto de = e.isDelegateExp()) 1164 return de.func; 1165 if (auto fe = e.isFuncExp()) 1166 return fe.fd; 1167 assert(e.op == TOK.null_); 1168 return null; 1169 } 1170 1171 private bool isArray(const Expression e) 1172 { 1173 return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_; 1174 } 1175 1176 /***** 1177 * Params: 1178 * loc = source file location 1179 * e1 = left operand 1180 * e2 = right operand 1181 * identity = true for `is` identity comparisons 1182 * Returns: 1183 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. 1184 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. 1185 */ 1186 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false) 1187 { 1188 if (e1.op == TOK.classReference || e2.op == TOK.classReference) 1189 { 1190 if (e1.op == TOK.classReference && e2.op == TOK.classReference && 1191 (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value) 1192 return 0; 1193 return 1; 1194 } 1195 if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_) 1196 { 1197 // printf("e1: %s\n", e1.toChars()); 1198 // printf("e2: %s\n", e2.toChars()); 1199 Type t1 = isType((cast(TypeidExp)e1).obj); 1200 Type t2 = isType((cast(TypeidExp)e2).obj); 1201 assert(t1); 1202 assert(t2); 1203 return t1 != t2; 1204 } 1205 // null == null, regardless of type 1206 if (e1.op == TOK.null_ && e2.op == TOK.null_) 1207 return 0; 1208 if (e1.type.ty == Tpointer && e2.type.ty == Tpointer) 1209 { 1210 // Can only be an equality test. 1211 dinteger_t ofs1, ofs2; 1212 Expression agg1 = getAggregateFromPointer(e1, &ofs1); 1213 Expression agg2 = getAggregateFromPointer(e2, &ofs2); 1214 if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) 1215 { 1216 if (ofs1 == ofs2) 1217 return 0; 1218 } 1219 return 1; 1220 } 1221 if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate) 1222 { 1223 // If .funcptr isn't the same, they are not equal 1224 if (funcptrOf(e1) != funcptrOf(e2)) 1225 return 1; 1226 // If both are delegate literals, assume they have the 1227 // same closure pointer. TODO: We don't support closures yet! 1228 if (e1.op == TOK.function_ && e2.op == TOK.function_) 1229 return 0; 1230 assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_); 1231 // Same .funcptr. Do they have the same .ptr? 1232 Expression ptr1 = (cast(DelegateExp)e1).e1; 1233 Expression ptr2 = (cast(DelegateExp)e2).e1; 1234 dinteger_t ofs1, ofs2; 1235 Expression agg1 = getAggregateFromPointer(ptr1, &ofs1); 1236 Expression agg2 = getAggregateFromPointer(ptr2, &ofs2); 1237 // If they are TOK.variable, it means they are FuncDeclarations 1238 if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) 1239 { 1240 return 0; 1241 } 1242 return 1; 1243 } 1244 if (isArray(e1) && isArray(e2)) 1245 { 1246 const uinteger_t len1 = resolveArrayLength(e1); 1247 const uinteger_t len2 = resolveArrayLength(e2); 1248 // workaround for dmc optimizer bug calculating wrong len for 1249 // uinteger_t len = (len1 < len2 ? len1 : len2); 1250 // if (len == 0) ... 1251 if (len1 > 0 && len2 > 0) 1252 { 1253 const uinteger_t len = (len1 < len2 ? len1 : len2); 1254 const int res = ctfeCmpArrays(loc, e1, e2, len); 1255 if (res != 0) 1256 return res; 1257 } 1258 return cast(int)(len1 - len2); 1259 } 1260 if (e1.type.isintegral()) 1261 { 1262 return e1.toInteger() != e2.toInteger(); 1263 } 1264 if (e1.type.isreal() || e1.type.isimaginary()) 1265 { 1266 real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); 1267 real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); 1268 if (identity) 1269 return !RealIdentical(r1, r2); 1270 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered 1271 { 1272 return 1; // they are not equal 1273 } 1274 else 1275 { 1276 return (r1 != r2); 1277 } 1278 } 1279 else if (e1.type.iscomplex()) 1280 { 1281 auto c1 = e1.toComplex(); 1282 auto c2 = e2.toComplex(); 1283 if (identity) 1284 { 1285 return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im); 1286 } 1287 return c1 != c2; 1288 } 1289 if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral) 1290 { 1291 StructLiteralExp es1 = cast(StructLiteralExp)e1; 1292 StructLiteralExp es2 = cast(StructLiteralExp)e2; 1293 // For structs, we only need to return 0 or 1 (< and > aren't legal). 1294 if (es1.sd != es2.sd) 1295 return 1; 1296 else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim)) 1297 return 0; // both arrays are empty 1298 else if (!es1.elements || !es2.elements) 1299 return 1; 1300 else if (es1.elements.dim != es2.elements.dim) 1301 return 1; 1302 else 1303 { 1304 foreach (size_t i; 0 .. es1.elements.dim) 1305 { 1306 Expression ee1 = (*es1.elements)[i]; 1307 Expression ee2 = (*es2.elements)[i]; 1308 1309 // https://issues.dlang.org/show_bug.cgi?id=16284 1310 if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp 1311 continue; 1312 1313 if (ee1 == ee2) 1314 continue; 1315 if (!ee1 || !ee2) 1316 return 1; 1317 const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); 1318 if (cmp) 1319 return 1; 1320 } 1321 return 0; // All elements are equal 1322 } 1323 } 1324 if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral) 1325 { 1326 AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1; 1327 AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2; 1328 size_t dim = es1.keys.dim; 1329 if (es2.keys.dim != dim) 1330 return 1; 1331 bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim); 1332 memset(used, 0, bool.sizeof * dim); 1333 foreach (size_t i; 0 .. dim) 1334 { 1335 Expression k1 = (*es1.keys)[i]; 1336 Expression v1 = (*es1.values)[i]; 1337 Expression v2 = null; 1338 foreach (size_t j; 0 .. dim) 1339 { 1340 if (used[j]) 1341 continue; 1342 Expression k2 = (*es2.keys)[j]; 1343 if (ctfeRawCmp(loc, k1, k2, identity)) 1344 continue; 1345 used[j] = true; 1346 v2 = (*es2.values)[j]; 1347 break; 1348 } 1349 if (!v2 || ctfeRawCmp(loc, v1, v2, identity)) 1350 { 1351 mem.xfree(used); 1352 return 1; 1353 } 1354 } 1355 mem.xfree(used); 1356 return 0; 1357 } 1358 error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars()); 1359 assert(0); 1360 } 1361 1362 /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 1363 bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2) 1364 { 1365 return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual); 1366 } 1367 1368 /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 1369 bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2) 1370 { 1371 //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars()); 1372 //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op), 1373 // Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars()); 1374 bool cmp; 1375 if (e1.op == TOK.null_) 1376 { 1377 cmp = (e2.op == TOK.null_); 1378 } 1379 else if (e2.op == TOK.null_) 1380 { 1381 cmp = false; 1382 } 1383 else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset) 1384 { 1385 SymOffExp es1 = cast(SymOffExp)e1; 1386 SymOffExp es2 = cast(SymOffExp)e2; 1387 cmp = (es1.var == es2.var && es1.offset == es2.offset); 1388 } 1389 else if (e1.type.isreal()) 1390 cmp = RealIdentical(e1.toReal(), e2.toReal()); 1391 else if (e1.type.isimaginary()) 1392 cmp = RealIdentical(e1.toImaginary(), e2.toImaginary()); 1393 else if (e1.type.iscomplex()) 1394 { 1395 complex_t v1 = e1.toComplex(); 1396 complex_t v2 = e2.toComplex(); 1397 cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1)); 1398 } 1399 else 1400 { 1401 cmp = !ctfeRawCmp(loc, e1, e2, true); 1402 } 1403 if (op == TOK.notIdentity || op == TOK.notEqual) 1404 cmp ^= true; 1405 return cmp; 1406 } 1407 1408 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 1409 bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2) 1410 { 1411 Type t1 = e1.type.toBasetype(); 1412 Type t2 = e2.type.toBasetype(); 1413 1414 if (t1.isString() && t2.isString()) 1415 return specificCmp(op, ctfeRawCmp(loc, e1, e2)); 1416 else if (t1.isreal()) 1417 return realCmp(op, e1.toReal(), e2.toReal()); 1418 else if (t1.isimaginary()) 1419 return realCmp(op, e1.toImaginary(), e2.toImaginary()); 1420 else if (t1.isunsigned() || t2.isunsigned()) 1421 return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); 1422 else 1423 return intSignedCmp(op, e1.toInteger(), e2.toInteger()); 1424 } 1425 1426 UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) 1427 { 1428 Type t1 = e1.type.toBasetype(); 1429 Type t2 = e2.type.toBasetype(); 1430 UnionExp ue; 1431 if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral()) 1432 { 1433 // [chars] ~ string => string (only valid for CTFE) 1434 StringExp es1 = cast(StringExp)e2; 1435 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1; 1436 const len = es1.len + es2.elements.dim; 1437 const sz = es1.sz; 1438 void* s = mem.xmalloc((len + 1) * sz); 1439 const data1 = es1.peekData(); 1440 memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length); 1441 foreach (size_t i; 0 .. es2.elements.dim) 1442 { 1443 Expression es2e = (*es2.elements)[i]; 1444 if (es2e.op != TOK.int64) 1445 { 1446 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 1447 return ue; 1448 } 1449 dinteger_t v = es2e.toInteger(); 1450 Port.valcpy(cast(char*)s + i * sz, v, sz); 1451 } 1452 // Add terminating 0 1453 memset(cast(char*)s + len * sz, 0, sz); 1454 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); 1455 StringExp es = cast(StringExp)ue.exp(); 1456 es.committed = 0; 1457 es.type = type; 1458 return ue; 1459 } 1460 if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral()) 1461 { 1462 // string ~ [chars] => string (only valid for CTFE) 1463 // Concatenate the strings 1464 StringExp es1 = cast(StringExp)e1; 1465 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; 1466 const len = es1.len + es2.elements.dim; 1467 const sz = es1.sz; 1468 void* s = mem.xmalloc((len + 1) * sz); 1469 auto slice = es1.peekData(); 1470 memcpy(s, slice.ptr, slice.length); 1471 foreach (size_t i; 0 .. es2.elements.dim) 1472 { 1473 Expression es2e = (*es2.elements)[i]; 1474 if (es2e.op != TOK.int64) 1475 { 1476 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 1477 return ue; 1478 } 1479 const v = es2e.toInteger(); 1480 Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz); 1481 } 1482 // Add terminating 0 1483 memset(cast(char*)s + len * sz, 0, sz); 1484 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); 1485 StringExp es = cast(StringExp)ue.exp(); 1486 es.sz = sz; 1487 es.committed = 0; //es1.committed; 1488 es.type = type; 1489 return ue; 1490 } 1491 if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) 1492 { 1493 // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ] 1494 ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; 1495 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; 1496 emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements)); 1497 es1 = cast(ArrayLiteralExp)ue.exp(); 1498 es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements)); 1499 return ue; 1500 } 1501 if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf())) 1502 { 1503 // [ e1 ] ~ null ----> [ e1 ].dup 1504 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy()); 1505 return ue; 1506 } 1507 if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) 1508 { 1509 // null ~ [ e2 ] ----> [ e2 ].dup 1510 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy()); 1511 return ue; 1512 } 1513 ue = Cat(type, e1, e2); 1514 return ue; 1515 } 1516 1517 /* Given an AA literal 'ae', and a key 'e2': 1518 * Return ae[e2] if present, or NULL if not found. 1519 */ 1520 Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2) 1521 { 1522 /* Search the keys backwards, in case there are duplicate keys 1523 */ 1524 for (size_t i = ae.keys.dim; i;) 1525 { 1526 --i; 1527 Expression ekey = (*ae.keys)[i]; 1528 const int eq = ctfeEqual(loc, TOK.equal, ekey, e2); 1529 if (eq) 1530 { 1531 return (*ae.values)[i]; 1532 } 1533 } 1534 return null; 1535 } 1536 1537 /* Same as for constfold.Index, except that it only works for static arrays, 1538 * dynamic arrays, and strings. We know that e1 is an 1539 * interpreted CTFE expression, so it cannot have side-effects. 1540 */ 1541 Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx) 1542 { 1543 //printf("ctfeIndex(e1 = %s)\n", e1.toChars()); 1544 assert(e1.type); 1545 if (auto es1 = e1.isStringExp()) 1546 { 1547 if (indx >= es1.len) 1548 { 1549 error(loc, "string index %llu is out of bounds `[0 .. %zu]`", indx, es1.len); 1550 return CTFEExp.cantexp; 1551 } 1552 emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type); 1553 return pue.exp(); 1554 } 1555 1556 if (auto ale = e1.isArrayLiteralExp()) 1557 { 1558 if (indx >= ale.elements.dim) 1559 { 1560 error(loc, "array index %llu is out of bounds `%s[0 .. %zu]`", indx, e1.toChars(), ale.elements.dim); 1561 return CTFEExp.cantexp; 1562 } 1563 Expression e = (*ale.elements)[cast(size_t)indx]; 1564 return paintTypeOntoLiteral(pue, type, e); 1565 } 1566 1567 assert(0); 1568 } 1569 1570 Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e) 1571 { 1572 Expression paint() 1573 { 1574 return paintTypeOntoLiteral(pue, to, e); 1575 } 1576 1577 if (e.op == TOK.null_) 1578 return paint(); 1579 1580 if (e.op == TOK.classReference) 1581 { 1582 // Disallow reinterpreting class casts. Do this by ensuring that 1583 // the original class can implicitly convert to the target class 1584 ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass(); 1585 if (originalClass.type.implicitConvTo(to.mutableOf())) 1586 return paint(); 1587 else 1588 { 1589 emplaceExp!(NullExp)(pue, loc, to); 1590 return pue.exp(); 1591 } 1592 } 1593 1594 // Allow TypeInfo type painting 1595 if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to)) 1596 return paint(); 1597 1598 // Allow casting away const for struct literals 1599 if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0)) 1600 return paint(); 1601 1602 Expression r; 1603 if (e.type.equals(type) && type.equals(to)) 1604 { 1605 // necessary not to change e's address for pointer comparisons 1606 r = e; 1607 } 1608 else if (to.toBasetype().ty == Tarray && 1609 type.toBasetype().ty == Tarray && 1610 to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size()) 1611 { 1612 // https://issues.dlang.org/show_bug.cgi?id=12495 1613 // Array reinterpret casts: eg. string to immutable(ubyte)[] 1614 return paint(); 1615 } 1616 else 1617 { 1618 *pue = Cast(loc, type, to, e); 1619 r = pue.exp(); 1620 } 1621 1622 if (CTFEExp.isCantExp(r)) 1623 error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars()); 1624 1625 if (auto ae = e.isArrayLiteralExp()) 1626 ae.ownedByCtfe = OwnedBy.ctfe; 1627 1628 if (auto se = e.isStringExp()) 1629 se.ownedByCtfe = OwnedBy.ctfe; 1630 1631 return r; 1632 } 1633 1634 /******** Assignment helper functions ***************************/ 1635 /* Set dest = src, where both dest and src are container value literals 1636 * (ie, struct literals, or static arrays (can be an array literal or a string)) 1637 * Assignment is recursively in-place. 1638 * Purpose: any reference to a member of 'dest' will remain valid after the 1639 * assignment. 1640 */ 1641 void assignInPlace(Expression dest, Expression src) 1642 { 1643 if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_)) 1644 { 1645 printf("invalid op %d %d\n", src.op, dest.op); 1646 assert(0); 1647 } 1648 Expressions* oldelems; 1649 Expressions* newelems; 1650 if (dest.op == TOK.structLiteral) 1651 { 1652 assert(dest.op == src.op); 1653 oldelems = (cast(StructLiteralExp)dest).elements; 1654 newelems = (cast(StructLiteralExp)src).elements; 1655 auto sd = (cast(StructLiteralExp)dest).sd; 1656 const nfields = sd.nonHiddenFields(); 1657 const nvthis = sd.fields.dim - nfields; 1658 if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim) 1659 foreach (_; 0 .. newelems.dim - oldelems.dim) 1660 oldelems.push(null); 1661 } 1662 else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral) 1663 { 1664 oldelems = (cast(ArrayLiteralExp)dest).elements; 1665 newelems = (cast(ArrayLiteralExp)src).elements; 1666 } 1667 else if (dest.op == TOK.string_ && src.op == TOK.string_) 1668 { 1669 sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0); 1670 return; 1671 } 1672 else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_) 1673 { 1674 sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0); 1675 return; 1676 } 1677 else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_) 1678 { 1679 sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0); 1680 return; 1681 } 1682 else 1683 { 1684 printf("invalid op %d %d\n", src.op, dest.op); 1685 assert(0); 1686 } 1687 assert(oldelems.dim == newelems.dim); 1688 foreach (size_t i; 0 .. oldelems.dim) 1689 { 1690 Expression e = (*newelems)[i]; 1691 Expression o = (*oldelems)[i]; 1692 if (e.op == TOK.structLiteral) 1693 { 1694 assert(o.op == e.op); 1695 assignInPlace(o, e); 1696 } 1697 else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray) 1698 { 1699 assignInPlace(o, e); 1700 } 1701 else 1702 { 1703 (*oldelems)[i] = (*newelems)[i]; 1704 } 1705 } 1706 } 1707 1708 // Given an AA literal aae, set aae[index] = newval and return newval. 1709 Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) 1710 { 1711 /* Create new associative array literal reflecting updated key/value 1712 */ 1713 Expressions* keysx = aae.keys; 1714 Expressions* valuesx = aae.values; 1715 int updated = 0; 1716 for (size_t j = valuesx.dim; j;) 1717 { 1718 j--; 1719 Expression ekey = (*aae.keys)[j]; 1720 int eq = ctfeEqual(loc, TOK.equal, ekey, index); 1721 if (eq) 1722 { 1723 (*valuesx)[j] = newval; 1724 updated = 1; 1725 } 1726 } 1727 if (!updated) 1728 { 1729 // Append index/newval to keysx[]/valuesx[] 1730 valuesx.push(newval); 1731 keysx.push(index); 1732 } 1733 return newval; 1734 } 1735 1736 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length 1737 /// oldlen, change its length to newlen. If the newlen is longer than oldlen, 1738 /// all new elements will be set to the default initializer for the element type. 1739 UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) 1740 { 1741 UnionExp ue; 1742 Type elemType = arrayType.next; 1743 assert(elemType); 1744 Expression defaultElem = elemType.defaultInitLiteral(loc); 1745 auto elements = new Expressions(newlen); 1746 // Resolve slices 1747 size_t indxlo = 0; 1748 if (oldval.op == TOK.slice) 1749 { 1750 indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger(); 1751 oldval = (cast(SliceExp)oldval).e1; 1752 } 1753 size_t copylen = oldlen < newlen ? oldlen : newlen; 1754 if (oldval.op == TOK.string_) 1755 { 1756 StringExp oldse = cast(StringExp)oldval; 1757 void* s = mem.xcalloc(newlen + 1, oldse.sz); 1758 const data = oldse.peekData(); 1759 memcpy(s, data.ptr, copylen * oldse.sz); 1760 const defaultValue = cast(uint)defaultElem.toInteger(); 1761 foreach (size_t elemi; copylen .. newlen) 1762 { 1763 switch (oldse.sz) 1764 { 1765 case 1: 1766 (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue; 1767 break; 1768 case 2: 1769 (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue; 1770 break; 1771 case 4: 1772 (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue; 1773 break; 1774 default: 1775 assert(0); 1776 } 1777 } 1778 emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz); 1779 StringExp se = cast(StringExp)ue.exp(); 1780 se.type = arrayType; 1781 se.sz = oldse.sz; 1782 se.committed = oldse.committed; 1783 se.ownedByCtfe = OwnedBy.ctfe; 1784 } 1785 else 1786 { 1787 if (oldlen != 0) 1788 { 1789 assert(oldval.op == TOK.arrayLiteral); 1790 ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval; 1791 foreach (size_t i; 0 .. copylen) 1792 (*elements)[i] = (*ae.elements)[indxlo + i]; 1793 } 1794 if (elemType.ty == Tstruct || elemType.ty == Tsarray) 1795 { 1796 /* If it is an aggregate literal representing a value type, 1797 * we need to create a unique copy for each element 1798 */ 1799 foreach (size_t i; copylen .. newlen) 1800 (*elements)[i] = copyLiteral(defaultElem).copy(); 1801 } 1802 else 1803 { 1804 foreach (size_t i; copylen .. newlen) 1805 (*elements)[i] = defaultElem; 1806 } 1807 emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements); 1808 ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp(); 1809 aae.ownedByCtfe = OwnedBy.ctfe; 1810 } 1811 return ue; 1812 } 1813 1814 /*************************** CTFE Sanity Checks ***************************/ 1815 1816 bool isCtfeValueValid(Expression newval) 1817 { 1818 Type tb = newval.type.toBasetype(); 1819 switch (newval.op) 1820 { 1821 case TOK.int64: 1822 case TOK.float64: 1823 case TOK.char_: 1824 case TOK.complex80: 1825 return tb.isscalar(); 1826 1827 case TOK.null_: 1828 return tb.ty == Tnull || 1829 tb.ty == Tpointer || 1830 tb.ty == Tarray || 1831 tb.ty == Taarray || 1832 tb.ty == Tclass || 1833 tb.ty == Tdelegate; 1834 1835 case TOK.string_: 1836 return true; // CTFE would directly use the StringExp in AST. 1837 1838 case TOK.arrayLiteral: 1839 return true; //((ArrayLiteralExp *)newval)->ownedByCtfe; 1840 1841 case TOK.assocArrayLiteral: 1842 return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe; 1843 1844 case TOK.structLiteral: 1845 return true; //((StructLiteralExp *)newval)->ownedByCtfe; 1846 1847 case TOK.classReference: 1848 return true; 1849 1850 case TOK.type: 1851 return true; 1852 1853 case TOK.vector: 1854 return true; // vector literal 1855 1856 case TOK.function_: 1857 return true; // function literal or delegate literal 1858 1859 case TOK.delegate_: 1860 { 1861 // &struct.func or &clasinst.func 1862 // &nestedfunc 1863 Expression ethis = (cast(DelegateExp)newval).e1; 1864 return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func); 1865 } 1866 1867 case TOK.symbolOffset: 1868 { 1869 // function pointer, or pointer to static variable 1870 Declaration d = (cast(SymOffExp)newval).var; 1871 return d.isFuncDeclaration() || d.isDataseg(); 1872 } 1873 1874 case TOK.typeid_: 1875 { 1876 // always valid 1877 return true; 1878 } 1879 1880 case TOK.address: 1881 { 1882 // e1 should be a CTFE reference 1883 Expression e1 = (cast(AddrExp)newval).e1; 1884 return tb.ty == Tpointer && 1885 ( 1886 (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) || 1887 e1.op == TOK.variable || 1888 e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) || 1889 e1.op == TOK.index && isCtfeReferenceValid(e1) || 1890 e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray 1891 ); 1892 } 1893 1894 case TOK.slice: 1895 { 1896 // e1 should be an array aggregate 1897 const SliceExp se = cast(SliceExp)newval; 1898 assert(se.lwr && se.lwr.op == TOK.int64); 1899 assert(se.upr && se.upr.op == TOK.int64); 1900 return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral); 1901 } 1902 1903 case TOK.void_: 1904 return true; // uninitialized value 1905 1906 default: 1907 newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars()); 1908 return false; 1909 } 1910 } 1911 1912 bool isCtfeReferenceValid(Expression newval) 1913 { 1914 switch (newval.op) 1915 { 1916 case TOK.this_: 1917 return true; 1918 1919 case TOK.variable: 1920 { 1921 const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration(); 1922 assert(v); 1923 // Must not be a reference to a reference 1924 return true; 1925 } 1926 1927 case TOK.index: 1928 { 1929 const Expression eagg = (cast(IndexExp)newval).e1; 1930 return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral; 1931 } 1932 1933 case TOK.dotVariable: 1934 { 1935 Expression eagg = (cast(DotVarExp)newval).e1; 1936 return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg); 1937 } 1938 1939 default: 1940 // Internally a ref variable may directly point a stack memory. 1941 // e.g. ref int v = 1; 1942 return isCtfeValueValid(newval); 1943 } 1944 } 1945 1946 // Used for debugging only 1947 void showCtfeExpr(Expression e, int level = 0) 1948 { 1949 for (int i = level; i > 0; --i) 1950 printf(" "); 1951 Expressions* elements = null; 1952 // We need the struct definition to detect block assignment 1953 StructDeclaration sd = null; 1954 ClassDeclaration cd = null; 1955 if (e.op == TOK.structLiteral) 1956 { 1957 elements = (cast(StructLiteralExp)e).elements; 1958 sd = (cast(StructLiteralExp)e).sd; 1959 printf("STRUCT type = %s %p:\n", e.type.toChars(), e); 1960 } 1961 else if (e.op == TOK.classReference) 1962 { 1963 elements = (cast(ClassReferenceExp)e).value.elements; 1964 cd = (cast(ClassReferenceExp)e).originalClass(); 1965 printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value); 1966 } 1967 else if (e.op == TOK.arrayLiteral) 1968 { 1969 elements = (cast(ArrayLiteralExp)e).elements; 1970 printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e); 1971 } 1972 else if (e.op == TOK.assocArrayLiteral) 1973 { 1974 printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e); 1975 } 1976 else if (e.op == TOK.string_) 1977 { 1978 printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr); 1979 } 1980 else if (e.op == TOK.slice) 1981 { 1982 printf("SLICE %p: %s\n", e, e.toChars()); 1983 showCtfeExpr((cast(SliceExp)e).e1, level + 1); 1984 } 1985 else if (e.op == TOK.variable) 1986 { 1987 printf("VAR %p %s\n", e, e.toChars()); 1988 VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); 1989 if (v && getValue(v)) 1990 showCtfeExpr(getValue(v), level + 1); 1991 } 1992 else if (e.op == TOK.address) 1993 { 1994 // This is potentially recursive. We mustn't try to print the thing we're pointing to. 1995 printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars()); 1996 } 1997 else 1998 printf("VALUE %p: %s\n", e, e.toChars()); 1999 if (elements) 2000 { 2001 size_t fieldsSoFar = 0; 2002 for (size_t i = 0; i < elements.dim; i++) 2003 { 2004 Expression z = null; 2005 VarDeclaration v = null; 2006 if (i > 15) 2007 { 2008 printf("...(total %d elements)\n", cast(int)elements.dim); 2009 return; 2010 } 2011 if (sd) 2012 { 2013 v = sd.fields[i]; 2014 z = (*elements)[i]; 2015 } 2016 else if (cd) 2017 { 2018 while (i - fieldsSoFar >= cd.fields.dim) 2019 { 2020 fieldsSoFar += cd.fields.dim; 2021 cd = cd.baseClass; 2022 for (int j = level; j > 0; --j) 2023 printf(" "); 2024 printf(" BASE CLASS: %s\n", cd.toChars()); 2025 } 2026 v = cd.fields[i - fieldsSoFar]; 2027 assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim)); 2028 size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i; 2029 assert(indx < elements.dim); 2030 z = (*elements)[indx]; 2031 } 2032 if (!z) 2033 { 2034 for (int j = level; j > 0; --j) 2035 printf(" "); 2036 printf(" void\n"); 2037 continue; 2038 } 2039 if (v) 2040 { 2041 // If it is a void assignment, use the default initializer 2042 if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray) 2043 { 2044 for (int j = level; --j;) 2045 printf(" "); 2046 printf(" field: block initialized static array\n"); 2047 continue; 2048 } 2049 } 2050 showCtfeExpr(z, level + 1); 2051 } 2052 } 2053 } 2054 2055 /*************************** Void initialization ***************************/ 2056 UnionExp voidInitLiteral(Type t, VarDeclaration var) 2057 { 2058 UnionExp ue; 2059 if (t.ty == Tsarray) 2060 { 2061 TypeSArray tsa = cast(TypeSArray)t; 2062 Expression elem = voidInitLiteral(tsa.next, var).copy(); 2063 // For aggregate value types (structs, static arrays) we must 2064 // create an a separate copy for each element. 2065 const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); 2066 const d = cast(size_t)tsa.dim.toInteger(); 2067 auto elements = new Expressions(d); 2068 foreach (i; 0 .. d) 2069 { 2070 if (mustCopy && i > 0) 2071 elem = copyLiteral(elem).copy(); 2072 (*elements)[i] = elem; 2073 } 2074 emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements); 2075 ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp(); 2076 ae.ownedByCtfe = OwnedBy.ctfe; 2077 } 2078 else if (t.ty == Tstruct) 2079 { 2080 TypeStruct ts = cast(TypeStruct)t; 2081 auto exps = new Expressions(ts.sym.fields.dim); 2082 foreach (size_t i; 0 .. ts.sym.fields.dim) 2083 { 2084 (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy(); 2085 } 2086 emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps); 2087 StructLiteralExp se = cast(StructLiteralExp)ue.exp(); 2088 se.type = ts; 2089 se.ownedByCtfe = OwnedBy.ctfe; 2090 } 2091 else 2092 emplaceExp!(VoidInitExp)(&ue, var); 2093 return ue; 2094 }