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