1 /** 2 * CTFE for expressions involving pointers, slices, array concatenation etc. 3 * 4 * Copyright: Copyright (C) 1999-2020 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 "<void>"; 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 return e; 750 } 751 752 /** Return true if agg1 and agg2 are pointers to the same memory block 753 */ 754 bool pointToSameMemoryBlock(Expression agg1, Expression agg2) 755 { 756 if (agg1 == agg2) 757 return true; 758 // For integers cast to pointers, we regard them as non-comparable 759 // unless they are identical. (This may be overly strict). 760 if (agg1.op == TOK.int64 && agg2.op == TOK.int64 && agg1.toInteger() == agg2.toInteger()) 761 { 762 return true; 763 } 764 // Note that type painting can occur with VarExp, so we 765 // must compare the variables being pointed to. 766 if (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var) 767 { 768 return true; 769 } 770 if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) 771 { 772 return true; 773 } 774 return false; 775 } 776 777 // return e1 - e2 as an integer, or error if not possible 778 UnionExp pointerDifference(const ref Loc loc, Type type, Expression e1, Expression e2) 779 { 780 UnionExp ue = void; 781 dinteger_t ofs1, ofs2; 782 Expression agg1 = getAggregateFromPointer(e1, &ofs1); 783 Expression agg2 = getAggregateFromPointer(e2, &ofs2); 784 if (agg1 == agg2) 785 { 786 Type pointee = (cast(TypePointer)agg1.type).next; 787 const sz = pointee.size(); 788 emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); 789 } 790 else if (agg1.op == TOK.string_ && agg2.op == TOK.string_ && 791 (cast(StringExp)agg1).peekString().ptr == (cast(StringExp)agg2).peekString().ptr) 792 { 793 Type pointee = (cast(TypePointer)agg1.type).next; 794 const sz = pointee.size(); 795 emplaceExp!(IntegerExp)(&ue, loc, (ofs1 - ofs2) * sz, type); 796 } 797 else if (agg1.op == TOK.symbolOffset && agg2.op == TOK.symbolOffset && 798 (cast(SymOffExp)agg1).var == (cast(SymOffExp)agg2).var) 799 { 800 emplaceExp!(IntegerExp)(&ue, loc, ofs1 - ofs2, type); 801 } 802 else 803 { 804 error(loc, "`%s - %s` cannot be interpreted at compile time: cannot subtract pointers to two different memory blocks", e1.toChars(), e2.toChars()); 805 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 806 } 807 return ue; 808 } 809 810 // Return eptr op e2, where eptr is a pointer, e2 is an integer, 811 // and op is TOK.add or TOK.min 812 UnionExp pointerArithmetic(const ref Loc loc, TOK op, Type type, Expression eptr, Expression e2) 813 { 814 UnionExp ue; 815 if (eptr.type.nextOf().ty == Tvoid) 816 { 817 error(loc, "cannot perform arithmetic on `void*` pointers at compile time"); 818 Lcant: 819 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 820 return ue; 821 } 822 if (eptr.op == TOK.address) 823 eptr = (cast(AddrExp)eptr).e1; 824 dinteger_t ofs1; 825 Expression agg1 = getAggregateFromPointer(eptr, &ofs1); 826 if (agg1.op == TOK.symbolOffset) 827 { 828 if ((cast(SymOffExp)agg1).var.type.ty != Tsarray) 829 { 830 error(loc, "cannot perform pointer arithmetic on arrays of unknown length at compile time"); 831 goto Lcant; 832 } 833 } 834 else if (agg1.op != TOK.string_ && agg1.op != TOK.arrayLiteral) 835 { 836 error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); 837 goto Lcant; 838 } 839 dinteger_t ofs2 = e2.toInteger(); 840 Type pointee = (cast(TypeNext)agg1.type.toBasetype()).next; 841 dinteger_t sz = pointee.size(); 842 sinteger_t indx; 843 dinteger_t len; 844 if (agg1.op == TOK.symbolOffset) 845 { 846 indx = ofs1 / sz; 847 len = (cast(TypeSArray)(cast(SymOffExp)agg1).var.type).dim.toInteger(); 848 } 849 else 850 { 851 Expression dollar = ArrayLength(Type.tsize_t, agg1).copy(); 852 assert(!CTFEExp.isCantExp(dollar)); 853 indx = ofs1; 854 len = dollar.toInteger(); 855 } 856 if (op == TOK.add || op == TOK.addAssign || op == TOK.plusPlus) 857 indx += ofs2 / sz; 858 else if (op == TOK.min || op == TOK.minAssign || op == TOK.minusMinus) 859 indx -= ofs2 / sz; 860 else 861 { 862 error(loc, "CTFE internal error: bad pointer operation"); 863 goto Lcant; 864 } 865 if (indx < 0 || len < indx) 866 { 867 error(loc, "cannot assign pointer to index %lld inside memory block `[0..%lld]`", indx, len); 868 goto Lcant; 869 } 870 if (agg1.op == TOK.symbolOffset) 871 { 872 emplaceExp!(SymOffExp)(&ue, loc, (cast(SymOffExp)agg1).var, indx * sz); 873 SymOffExp se = cast(SymOffExp)ue.exp(); 874 se.type = type; 875 return ue; 876 } 877 if (agg1.op != TOK.arrayLiteral && agg1.op != TOK.string_) 878 { 879 error(loc, "CTFE internal error: pointer arithmetic `%s`", agg1.toChars()); 880 goto Lcant; 881 } 882 if (eptr.type.toBasetype().ty == Tsarray) 883 { 884 dinteger_t dim = (cast(TypeSArray)eptr.type.toBasetype()).dim.toInteger(); 885 // Create a CTFE pointer &agg1[indx .. indx+dim] 886 auto se = ctfeEmplaceExp!SliceExp(loc, agg1, 887 ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t), 888 ctfeEmplaceExp!IntegerExp(loc, indx + dim, Type.tsize_t)); 889 se.type = type.toBasetype().nextOf(); 890 emplaceExp!(AddrExp)(&ue, loc, se); 891 ue.exp().type = type; 892 return ue; 893 } 894 // Create a CTFE pointer &agg1[indx] 895 auto ofs = ctfeEmplaceExp!IntegerExp(loc, indx, Type.tsize_t); 896 Expression ie = ctfeEmplaceExp!IndexExp(loc, agg1, ofs); 897 ie.type = type.toBasetype().nextOf(); // https://issues.dlang.org/show_bug.cgi?id=13992 898 emplaceExp!(AddrExp)(&ue, loc, ie); 899 ue.exp().type = type; 900 return ue; 901 } 902 903 // Return 1 if true, 0 if false 904 // -1 if comparison is illegal because they point to non-comparable memory blocks 905 int comparePointers(TOK op, Expression agg1, dinteger_t ofs1, Expression agg2, dinteger_t ofs2) 906 { 907 if (pointToSameMemoryBlock(agg1, agg2)) 908 { 909 int n; 910 switch (op) 911 { 912 case TOK.lessThan: 913 n = (ofs1 < ofs2); 914 break; 915 case TOK.lessOrEqual: 916 n = (ofs1 <= ofs2); 917 break; 918 case TOK.greaterThan: 919 n = (ofs1 > ofs2); 920 break; 921 case TOK.greaterOrEqual: 922 n = (ofs1 >= ofs2); 923 break; 924 case TOK.identity: 925 case TOK.equal: 926 n = (ofs1 == ofs2); 927 break; 928 case TOK.notIdentity: 929 case TOK.notEqual: 930 n = (ofs1 != ofs2); 931 break; 932 default: 933 assert(0); 934 } 935 return n; 936 } 937 const null1 = (agg1.op == TOK.null_); 938 const null2 = (agg2.op == TOK.null_); 939 int cmp; 940 if (null1 || null2) 941 { 942 switch (op) 943 { 944 case TOK.lessThan: 945 cmp = null1 && !null2; 946 break; 947 case TOK.greaterThan: 948 cmp = !null1 && null2; 949 break; 950 case TOK.lessOrEqual: 951 cmp = null1; 952 break; 953 case TOK.greaterOrEqual: 954 cmp = null2; 955 break; 956 case TOK.identity: 957 case TOK.equal: 958 case TOK.notIdentity: // 'cmp' gets inverted below 959 case TOK.notEqual: 960 cmp = (null1 == null2); 961 break; 962 default: 963 assert(0); 964 } 965 } 966 else 967 { 968 switch (op) 969 { 970 case TOK.identity: 971 case TOK.equal: 972 case TOK.notIdentity: // 'cmp' gets inverted below 973 case TOK.notEqual: 974 cmp = 0; 975 break; 976 default: 977 return -1; // memory blocks are different 978 } 979 } 980 if (op == TOK.notIdentity || op == TOK.notEqual) 981 cmp ^= 1; 982 return cmp; 983 } 984 985 // True if conversion from type 'from' to 'to' involves a reinterpret_cast 986 // floating point -> integer or integer -> floating point 987 bool isFloatIntPaint(Type to, Type from) 988 { 989 return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral()); 990 } 991 992 // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. 993 Expression paintFloatInt(UnionExp* pue, Expression fromVal, Type to) 994 { 995 if (exceptionOrCantInterpret(fromVal)) 996 return fromVal; 997 assert(to.size() == 4 || to.size() == 8); 998 return Compiler.paintAsType(pue, fromVal, to); 999 } 1000 1001 /******** Constant folding, with support for CTFE ***************************/ 1002 /// Return true if non-pointer expression e can be compared 1003 /// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity 1004 bool isCtfeComparable(Expression e) 1005 { 1006 if (e.op == TOK.slice) 1007 e = (cast(SliceExp)e).e1; 1008 if (e.isConst() != 1) 1009 { 1010 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) 1011 { 1012 return true; 1013 } 1014 // https://issues.dlang.org/show_bug.cgi?id=14123 1015 // TypeInfo object is comparable in CTFE 1016 if (e.op == TOK.typeid_) 1017 return true; 1018 return false; 1019 } 1020 return true; 1021 } 1022 1023 /// Map TOK comparison ops 1024 private bool numCmp(N)(TOK op, N n1, N n2) 1025 { 1026 switch (op) 1027 { 1028 case TOK.lessThan: 1029 return n1 < n2; 1030 case TOK.lessOrEqual: 1031 return n1 <= n2; 1032 case TOK.greaterThan: 1033 return n1 > n2; 1034 case TOK.greaterOrEqual: 1035 return n1 >= n2; 1036 1037 default: 1038 assert(0); 1039 } 1040 } 1041 1042 /// Returns cmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1043 bool specificCmp(TOK op, int rawCmp) 1044 { 1045 return numCmp!int(op, rawCmp, 0); 1046 } 1047 1048 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1049 bool intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2) 1050 { 1051 return numCmp!dinteger_t(op, n1, n2); 1052 } 1053 1054 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1055 bool intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2) 1056 { 1057 return numCmp!sinteger_t(op, n1, n2); 1058 } 1059 1060 /// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 1061 bool realCmp(TOK op, real_t r1, real_t r2) 1062 { 1063 // Don't rely on compiler, handle NAN arguments separately 1064 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered 1065 { 1066 switch (op) 1067 { 1068 case TOK.lessThan: 1069 case TOK.lessOrEqual: 1070 case TOK.greaterThan: 1071 case TOK.greaterOrEqual: 1072 return false; 1073 1074 default: 1075 assert(0); 1076 } 1077 } 1078 else 1079 { 1080 return numCmp!real_t(op, r1, r2); 1081 } 1082 } 1083 1084 /* Conceptually the same as memcmp(e1, e2). 1085 * e1 and e2 may be strings, arrayliterals, or slices. 1086 * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. 1087 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. 1088 * Returns: 1089 * -1,0,1 1090 */ 1091 private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len) 1092 { 1093 // Resolve slices, if necessary 1094 uinteger_t lo1 = 0; 1095 uinteger_t lo2 = 0; 1096 1097 Expression x1 = e1; 1098 if (auto sle1 = x1.isSliceExp()) 1099 { 1100 lo1 = sle1.lwr.toInteger(); 1101 x1 = sle1.e1; 1102 } 1103 auto se1 = x1.isStringExp(); 1104 auto ae1 = x1.isArrayLiteralExp(); 1105 1106 Expression x2 = e2; 1107 if (auto sle2 = x2.isSliceExp()) 1108 { 1109 lo2 = sle2.lwr.toInteger(); 1110 x2 = sle2.e1; 1111 } 1112 auto se2 = x2.isStringExp(); 1113 auto ae2 = x2.isArrayLiteralExp(); 1114 1115 // Now both must be either TOK.arrayLiteral or TOK.string_ 1116 if (se1 && se2) 1117 return sliceCmpStringWithString(se1, se2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); 1118 if (se1 && ae2) 1119 return sliceCmpStringWithArray(se1, ae2, cast(size_t)lo1, cast(size_t)lo2, cast(size_t)len); 1120 if (se2 && ae1) 1121 return -sliceCmpStringWithArray(se2, ae1, cast(size_t)lo2, cast(size_t)lo1, cast(size_t)len); 1122 assert(ae1 && ae2); 1123 // Comparing two array literals. This case is potentially recursive. 1124 // If they aren't strings, we just need an equality check rather than 1125 // a full cmp. 1126 const bool needCmp = ae1.type.nextOf().isintegral(); 1127 foreach (size_t i; 0 .. cast(size_t)len) 1128 { 1129 Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; 1130 Expression ee2 = (*ae2.elements)[cast(size_t)(lo2 + i)]; 1131 if (needCmp) 1132 { 1133 const sinteger_t c = ee1.toInteger() - ee2.toInteger(); 1134 if (c > 0) 1135 return 1; 1136 if (c < 0) 1137 return -1; 1138 } 1139 else 1140 { 1141 if (ctfeRawCmp(loc, ee1, ee2)) 1142 return 1; 1143 } 1144 } 1145 return 0; 1146 } 1147 1148 /* Given a delegate expression e, return .funcptr. 1149 * If e is NullExp, return NULL. 1150 */ 1151 private FuncDeclaration funcptrOf(Expression e) 1152 { 1153 assert(e.type.ty == Tdelegate); 1154 if (auto de = e.isDelegateExp()) 1155 return de.func; 1156 if (auto fe = e.isFuncExp()) 1157 return fe.fd; 1158 assert(e.op == TOK.null_); 1159 return null; 1160 } 1161 1162 private bool isArray(const Expression e) 1163 { 1164 return e.op == TOK.arrayLiteral || e.op == TOK.string_ || e.op == TOK.slice || e.op == TOK.null_; 1165 } 1166 1167 /***** 1168 * Params: 1169 * loc = source file location 1170 * e1 = left operand 1171 * e2 = right operand 1172 * identity = true for `is` identity comparisons 1173 * Returns: 1174 * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. 1175 * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. 1176 */ 1177 private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false) 1178 { 1179 if (e1.op == TOK.classReference || e2.op == TOK.classReference) 1180 { 1181 if (e1.op == TOK.classReference && e2.op == TOK.classReference && 1182 (cast(ClassReferenceExp)e1).value == (cast(ClassReferenceExp)e2).value) 1183 return 0; 1184 return 1; 1185 } 1186 if (e1.op == TOK.typeid_ && e2.op == TOK.typeid_) 1187 { 1188 // printf("e1: %s\n", e1.toChars()); 1189 // printf("e2: %s\n", e2.toChars()); 1190 Type t1 = isType((cast(TypeidExp)e1).obj); 1191 Type t2 = isType((cast(TypeidExp)e2).obj); 1192 assert(t1); 1193 assert(t2); 1194 return t1 != t2; 1195 } 1196 // null == null, regardless of type 1197 if (e1.op == TOK.null_ && e2.op == TOK.null_) 1198 return 0; 1199 if (e1.type.ty == Tpointer && e2.type.ty == Tpointer) 1200 { 1201 // Can only be an equality test. 1202 dinteger_t ofs1, ofs2; 1203 Expression agg1 = getAggregateFromPointer(e1, &ofs1); 1204 Expression agg2 = getAggregateFromPointer(e2, &ofs2); 1205 if ((agg1 == agg2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) 1206 { 1207 if (ofs1 == ofs2) 1208 return 0; 1209 } 1210 return 1; 1211 } 1212 if (e1.type.ty == Tdelegate && e2.type.ty == Tdelegate) 1213 { 1214 // If .funcptr isn't the same, they are not equal 1215 if (funcptrOf(e1) != funcptrOf(e2)) 1216 return 1; 1217 // If both are delegate literals, assume they have the 1218 // same closure pointer. TODO: We don't support closures yet! 1219 if (e1.op == TOK.function_ && e2.op == TOK.function_) 1220 return 0; 1221 assert(e1.op == TOK.delegate_ && e2.op == TOK.delegate_); 1222 // Same .funcptr. Do they have the same .ptr? 1223 Expression ptr1 = (cast(DelegateExp)e1).e1; 1224 Expression ptr2 = (cast(DelegateExp)e2).e1; 1225 dinteger_t ofs1, ofs2; 1226 Expression agg1 = getAggregateFromPointer(ptr1, &ofs1); 1227 Expression agg2 = getAggregateFromPointer(ptr2, &ofs2); 1228 // If they are TOK.variable, it means they are FuncDeclarations 1229 if ((agg1 == agg2 && ofs1 == ofs2) || (agg1.op == TOK.variable && agg2.op == TOK.variable && (cast(VarExp)agg1).var == (cast(VarExp)agg2).var)) 1230 { 1231 return 0; 1232 } 1233 return 1; 1234 } 1235 if (isArray(e1) && isArray(e2)) 1236 { 1237 const uinteger_t len1 = resolveArrayLength(e1); 1238 const uinteger_t len2 = resolveArrayLength(e2); 1239 // workaround for dmc optimizer bug calculating wrong len for 1240 // uinteger_t len = (len1 < len2 ? len1 : len2); 1241 // if (len == 0) ... 1242 if (len1 > 0 && len2 > 0) 1243 { 1244 const uinteger_t len = (len1 < len2 ? len1 : len2); 1245 const int res = ctfeCmpArrays(loc, e1, e2, len); 1246 if (res != 0) 1247 return res; 1248 } 1249 return cast(int)(len1 - len2); 1250 } 1251 if (e1.type.isintegral()) 1252 { 1253 return e1.toInteger() != e2.toInteger(); 1254 } 1255 if (e1.type.isreal() || e1.type.isimaginary()) 1256 { 1257 real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); 1258 real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); 1259 if (identity) 1260 return !RealIdentical(r1, r2); 1261 if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered 1262 { 1263 return 1; // they are not equal 1264 } 1265 else 1266 { 1267 return (r1 != r2); 1268 } 1269 } 1270 else if (e1.type.iscomplex()) 1271 { 1272 auto c1 = e1.toComplex(); 1273 auto c2 = e2.toComplex(); 1274 if (identity) 1275 { 1276 return !RealIdentical(c1.re, c2.re) && !RealIdentical(c1.im, c2.im); 1277 } 1278 return c1 != c2; 1279 } 1280 if (e1.op == TOK.structLiteral && e2.op == TOK.structLiteral) 1281 { 1282 StructLiteralExp es1 = cast(StructLiteralExp)e1; 1283 StructLiteralExp es2 = cast(StructLiteralExp)e2; 1284 // For structs, we only need to return 0 or 1 (< and > aren't legal). 1285 if (es1.sd != es2.sd) 1286 return 1; 1287 else if ((!es1.elements || !es1.elements.dim) && (!es2.elements || !es2.elements.dim)) 1288 return 0; // both arrays are empty 1289 else if (!es1.elements || !es2.elements) 1290 return 1; 1291 else if (es1.elements.dim != es2.elements.dim) 1292 return 1; 1293 else 1294 { 1295 foreach (size_t i; 0 .. es1.elements.dim) 1296 { 1297 Expression ee1 = (*es1.elements)[i]; 1298 Expression ee2 = (*es2.elements)[i]; 1299 1300 // https://issues.dlang.org/show_bug.cgi?id=16284 1301 if (ee1.op == TOK.void_ && ee2.op == TOK.void_) // if both are VoidInitExp 1302 continue; 1303 1304 if (ee1 == ee2) 1305 continue; 1306 if (!ee1 || !ee2) 1307 return 1; 1308 const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); 1309 if (cmp) 1310 return 1; 1311 } 1312 return 0; // All elements are equal 1313 } 1314 } 1315 if (e1.op == TOK.assocArrayLiteral && e2.op == TOK.assocArrayLiteral) 1316 { 1317 AssocArrayLiteralExp es1 = cast(AssocArrayLiteralExp)e1; 1318 AssocArrayLiteralExp es2 = cast(AssocArrayLiteralExp)e2; 1319 size_t dim = es1.keys.dim; 1320 if (es2.keys.dim != dim) 1321 return 1; 1322 bool* used = cast(bool*)mem.xmalloc(bool.sizeof * dim); 1323 memset(used, 0, bool.sizeof * dim); 1324 foreach (size_t i; 0 .. dim) 1325 { 1326 Expression k1 = (*es1.keys)[i]; 1327 Expression v1 = (*es1.values)[i]; 1328 Expression v2 = null; 1329 foreach (size_t j; 0 .. dim) 1330 { 1331 if (used[j]) 1332 continue; 1333 Expression k2 = (*es2.keys)[j]; 1334 if (ctfeRawCmp(loc, k1, k2, identity)) 1335 continue; 1336 used[j] = true; 1337 v2 = (*es2.values)[j]; 1338 break; 1339 } 1340 if (!v2 || ctfeRawCmp(loc, v1, v2, identity)) 1341 { 1342 mem.xfree(used); 1343 return 1; 1344 } 1345 } 1346 mem.xfree(used); 1347 return 0; 1348 } 1349 error(loc, "CTFE internal error: bad compare of `%s` and `%s`", e1.toChars(), e2.toChars()); 1350 assert(0); 1351 } 1352 1353 /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 1354 bool ctfeEqual(const ref Loc loc, TOK op, Expression e1, Expression e2) 1355 { 1356 return !ctfeRawCmp(loc, e1, e2) ^ (op == TOK.notEqual); 1357 } 1358 1359 /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 1360 bool ctfeIdentity(const ref Loc loc, TOK op, Expression e1, Expression e2) 1361 { 1362 //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars()); 1363 //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", Token::toChars(op), 1364 // Token::toChars(e1.op), e1.toChars(), Token::toChars(e2.op), e1.toChars()); 1365 bool cmp; 1366 if (e1.op == TOK.null_) 1367 { 1368 cmp = (e2.op == TOK.null_); 1369 } 1370 else if (e2.op == TOK.null_) 1371 { 1372 cmp = false; 1373 } 1374 else if (e1.op == TOK.symbolOffset && e2.op == TOK.symbolOffset) 1375 { 1376 SymOffExp es1 = cast(SymOffExp)e1; 1377 SymOffExp es2 = cast(SymOffExp)e2; 1378 cmp = (es1.var == es2.var && es1.offset == es2.offset); 1379 } 1380 else if (e1.type.isreal()) 1381 cmp = RealIdentical(e1.toReal(), e2.toReal()); 1382 else if (e1.type.isimaginary()) 1383 cmp = RealIdentical(e1.toImaginary(), e2.toImaginary()); 1384 else if (e1.type.iscomplex()) 1385 { 1386 complex_t v1 = e1.toComplex(); 1387 complex_t v2 = e2.toComplex(); 1388 cmp = RealIdentical(creall(v1), creall(v2)) && RealIdentical(cimagl(v1), cimagl(v1)); 1389 } 1390 else 1391 { 1392 cmp = !ctfeRawCmp(loc, e1, e2, true); 1393 } 1394 if (op == TOK.notIdentity || op == TOK.notEqual) 1395 cmp ^= true; 1396 return cmp; 1397 } 1398 1399 /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 1400 bool ctfeCmp(const ref Loc loc, TOK op, Expression e1, Expression e2) 1401 { 1402 Type t1 = e1.type.toBasetype(); 1403 Type t2 = e2.type.toBasetype(); 1404 1405 if (t1.isString() && t2.isString()) 1406 return specificCmp(op, ctfeRawCmp(loc, e1, e2)); 1407 else if (t1.isreal()) 1408 return realCmp(op, e1.toReal(), e2.toReal()); 1409 else if (t1.isimaginary()) 1410 return realCmp(op, e1.toImaginary(), e2.toImaginary()); 1411 else if (t1.isunsigned() || t2.isunsigned()) 1412 return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); 1413 else 1414 return intSignedCmp(op, e1.toInteger(), e2.toInteger()); 1415 } 1416 1417 UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) 1418 { 1419 Type t1 = e1.type.toBasetype(); 1420 Type t2 = e2.type.toBasetype(); 1421 UnionExp ue; 1422 if (e2.op == TOK.string_ && e1.op == TOK.arrayLiteral && t1.nextOf().isintegral()) 1423 { 1424 // [chars] ~ string => string (only valid for CTFE) 1425 StringExp es1 = cast(StringExp)e2; 1426 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e1; 1427 const len = es1.len + es2.elements.dim; 1428 const sz = es1.sz; 1429 void* s = mem.xmalloc((len + 1) * sz); 1430 const data1 = es1.peekData(); 1431 memcpy(cast(char*)s + sz * es2.elements.dim, data1.ptr, data1.length); 1432 foreach (size_t i; 0 .. es2.elements.dim) 1433 { 1434 Expression es2e = (*es2.elements)[i]; 1435 if (es2e.op != TOK.int64) 1436 { 1437 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 1438 return ue; 1439 } 1440 dinteger_t v = es2e.toInteger(); 1441 Port.valcpy(cast(char*)s + i * sz, v, sz); 1442 } 1443 // Add terminating 0 1444 memset(cast(char*)s + len * sz, 0, sz); 1445 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); 1446 StringExp es = cast(StringExp)ue.exp(); 1447 es.committed = 0; 1448 es.type = type; 1449 return ue; 1450 } 1451 if (e1.op == TOK.string_ && e2.op == TOK.arrayLiteral && t2.nextOf().isintegral()) 1452 { 1453 // string ~ [chars] => string (only valid for CTFE) 1454 // Concatenate the strings 1455 StringExp es1 = cast(StringExp)e1; 1456 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; 1457 const len = es1.len + es2.elements.dim; 1458 const sz = es1.sz; 1459 void* s = mem.xmalloc((len + 1) * sz); 1460 auto slice = es1.peekData(); 1461 memcpy(s, slice.ptr, slice.length); 1462 foreach (size_t i; 0 .. es2.elements.dim) 1463 { 1464 Expression es2e = (*es2.elements)[i]; 1465 if (es2e.op != TOK.int64) 1466 { 1467 emplaceExp!(CTFEExp)(&ue, TOK.cantExpression); 1468 return ue; 1469 } 1470 const v = es2e.toInteger(); 1471 Port.valcpy(cast(char*)s + (es1.len + i) * sz, v, sz); 1472 } 1473 // Add terminating 0 1474 memset(cast(char*)s + len * sz, 0, sz); 1475 emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); 1476 StringExp es = cast(StringExp)ue.exp(); 1477 es.sz = sz; 1478 es.committed = 0; //es1.committed; 1479 es.type = type; 1480 return ue; 1481 } 1482 if (e1.op == TOK.arrayLiteral && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) 1483 { 1484 // [ e1 ] ~ [ e2 ] ---> [ e1, e2 ] 1485 ArrayLiteralExp es1 = cast(ArrayLiteralExp)e1; 1486 ArrayLiteralExp es2 = cast(ArrayLiteralExp)e2; 1487 emplaceExp!(ArrayLiteralExp)(&ue, es1.loc, type, copyLiteralArray(es1.elements)); 1488 es1 = cast(ArrayLiteralExp)ue.exp(); 1489 es1.elements.insert(es1.elements.dim, copyLiteralArray(es2.elements)); 1490 return ue; 1491 } 1492 if (e1.op == TOK.arrayLiteral && e2.op == TOK.null_ && t1.nextOf().equals(t2.nextOf())) 1493 { 1494 // [ e1 ] ~ null ----> [ e1 ].dup 1495 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e1).copy()); 1496 return ue; 1497 } 1498 if (e1.op == TOK.null_ && e2.op == TOK.arrayLiteral && t1.nextOf().equals(t2.nextOf())) 1499 { 1500 // null ~ [ e2 ] ----> [ e2 ].dup 1501 ue = paintTypeOntoLiteralCopy(type, copyLiteral(e2).copy()); 1502 return ue; 1503 } 1504 ue = Cat(type, e1, e2); 1505 return ue; 1506 } 1507 1508 /* Given an AA literal 'ae', and a key 'e2': 1509 * Return ae[e2] if present, or NULL if not found. 1510 */ 1511 Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2) 1512 { 1513 /* Search the keys backwards, in case there are duplicate keys 1514 */ 1515 for (size_t i = ae.keys.dim; i;) 1516 { 1517 --i; 1518 Expression ekey = (*ae.keys)[i]; 1519 const int eq = ctfeEqual(loc, TOK.equal, ekey, e2); 1520 if (eq) 1521 { 1522 return (*ae.values)[i]; 1523 } 1524 } 1525 return null; 1526 } 1527 1528 /* Same as for constfold.Index, except that it only works for static arrays, 1529 * dynamic arrays, and strings. We know that e1 is an 1530 * interpreted CTFE expression, so it cannot have side-effects. 1531 */ 1532 Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx) 1533 { 1534 //printf("ctfeIndex(e1 = %s)\n", e1.toChars()); 1535 assert(e1.type); 1536 if (auto es1 = e1.isStringExp()) 1537 { 1538 if (indx >= es1.len) 1539 { 1540 error(loc, "string index %llu is out of bounds `[0 .. %llu]`", indx, cast(ulong)es1.len); 1541 return CTFEExp.cantexp; 1542 } 1543 emplaceExp!IntegerExp(pue, loc, es1.charAt(indx), type); 1544 return pue.exp(); 1545 } 1546 1547 if (auto ale = e1.isArrayLiteralExp()) 1548 { 1549 if (indx >= ale.elements.dim) 1550 { 1551 error(loc, "array index %llu is out of bounds `%s[0 .. %llu]`", indx, e1.toChars(), cast(ulong)ale.elements.dim); 1552 return CTFEExp.cantexp; 1553 } 1554 Expression e = (*ale.elements)[cast(size_t)indx]; 1555 return paintTypeOntoLiteral(pue, type, e); 1556 } 1557 1558 assert(0); 1559 } 1560 1561 Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e) 1562 { 1563 Expression paint() 1564 { 1565 return paintTypeOntoLiteral(pue, to, e); 1566 } 1567 1568 if (e.op == TOK.null_) 1569 return paint(); 1570 1571 if (e.op == TOK.classReference) 1572 { 1573 // Disallow reinterpreting class casts. Do this by ensuring that 1574 // the original class can implicitly convert to the target class 1575 ClassDeclaration originalClass = (cast(ClassReferenceExp)e).originalClass(); 1576 if (originalClass.type.implicitConvTo(to.mutableOf())) 1577 return paint(); 1578 else 1579 { 1580 emplaceExp!(NullExp)(pue, loc, to); 1581 return pue.exp(); 1582 } 1583 } 1584 1585 // Allow TypeInfo type painting 1586 if (isTypeInfo_Class(e.type) && e.type.implicitConvTo(to)) 1587 return paint(); 1588 1589 // Allow casting away const for struct literals 1590 if (e.op == TOK.structLiteral && e.type.toBasetype().castMod(0) == to.toBasetype().castMod(0)) 1591 return paint(); 1592 1593 Expression r; 1594 if (e.type.equals(type) && type.equals(to)) 1595 { 1596 // necessary not to change e's address for pointer comparisons 1597 r = e; 1598 } 1599 else if (to.toBasetype().ty == Tarray && 1600 type.toBasetype().ty == Tarray && 1601 to.toBasetype().nextOf().size() == type.toBasetype().nextOf().size()) 1602 { 1603 // https://issues.dlang.org/show_bug.cgi?id=12495 1604 // Array reinterpret casts: eg. string to immutable(ubyte)[] 1605 return paint(); 1606 } 1607 else 1608 { 1609 *pue = Cast(loc, type, to, e); 1610 r = pue.exp(); 1611 } 1612 1613 if (CTFEExp.isCantExp(r)) 1614 error(loc, "cannot cast `%s` to `%s` at compile time", e.toChars(), to.toChars()); 1615 1616 if (auto ae = e.isArrayLiteralExp()) 1617 ae.ownedByCtfe = OwnedBy.ctfe; 1618 1619 if (auto se = e.isStringExp()) 1620 se.ownedByCtfe = OwnedBy.ctfe; 1621 1622 return r; 1623 } 1624 1625 /******** Assignment helper functions ***************************/ 1626 /* Set dest = src, where both dest and src are container value literals 1627 * (ie, struct literals, or static arrays (can be an array literal or a string)) 1628 * Assignment is recursively in-place. 1629 * Purpose: any reference to a member of 'dest' will remain valid after the 1630 * assignment. 1631 */ 1632 void assignInPlace(Expression dest, Expression src) 1633 { 1634 if (!(dest.op == TOK.structLiteral || dest.op == TOK.arrayLiteral || dest.op == TOK.string_)) 1635 { 1636 printf("invalid op %d %d\n", src.op, dest.op); 1637 assert(0); 1638 } 1639 Expressions* oldelems; 1640 Expressions* newelems; 1641 if (dest.op == TOK.structLiteral) 1642 { 1643 assert(dest.op == src.op); 1644 oldelems = (cast(StructLiteralExp)dest).elements; 1645 newelems = (cast(StructLiteralExp)src).elements; 1646 auto sd = (cast(StructLiteralExp)dest).sd; 1647 const nfields = sd.nonHiddenFields(); 1648 const nvthis = sd.fields.dim - nfields; 1649 if (nvthis && oldelems.dim >= nfields && oldelems.dim < newelems.dim) 1650 foreach (_; 0 .. newelems.dim - oldelems.dim) 1651 oldelems.push(null); 1652 } 1653 else if (dest.op == TOK.arrayLiteral && src.op == TOK.arrayLiteral) 1654 { 1655 oldelems = (cast(ArrayLiteralExp)dest).elements; 1656 newelems = (cast(ArrayLiteralExp)src).elements; 1657 } 1658 else if (dest.op == TOK.string_ && src.op == TOK.string_) 1659 { 1660 sliceAssignStringFromString(cast(StringExp)dest, cast(StringExp)src, 0); 1661 return; 1662 } 1663 else if (dest.op == TOK.arrayLiteral && src.op == TOK.string_) 1664 { 1665 sliceAssignArrayLiteralFromString(cast(ArrayLiteralExp)dest, cast(StringExp)src, 0); 1666 return; 1667 } 1668 else if (src.op == TOK.arrayLiteral && dest.op == TOK.string_) 1669 { 1670 sliceAssignStringFromArrayLiteral(cast(StringExp)dest, cast(ArrayLiteralExp)src, 0); 1671 return; 1672 } 1673 else 1674 { 1675 printf("invalid op %d %d\n", src.op, dest.op); 1676 assert(0); 1677 } 1678 assert(oldelems.dim == newelems.dim); 1679 foreach (size_t i; 0 .. oldelems.dim) 1680 { 1681 Expression e = (*newelems)[i]; 1682 Expression o = (*oldelems)[i]; 1683 if (e.op == TOK.structLiteral) 1684 { 1685 assert(o.op == e.op); 1686 assignInPlace(o, e); 1687 } 1688 else if (e.type.ty == Tsarray && e.op != TOK.void_ && o.type.ty == Tsarray) 1689 { 1690 assignInPlace(o, e); 1691 } 1692 else 1693 { 1694 (*oldelems)[i] = (*newelems)[i]; 1695 } 1696 } 1697 } 1698 1699 // Given an AA literal aae, set aae[index] = newval and return newval. 1700 Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) 1701 { 1702 /* Create new associative array literal reflecting updated key/value 1703 */ 1704 Expressions* keysx = aae.keys; 1705 Expressions* valuesx = aae.values; 1706 int updated = 0; 1707 for (size_t j = valuesx.dim; j;) 1708 { 1709 j--; 1710 Expression ekey = (*aae.keys)[j]; 1711 int eq = ctfeEqual(loc, TOK.equal, ekey, index); 1712 if (eq) 1713 { 1714 (*valuesx)[j] = newval; 1715 updated = 1; 1716 } 1717 } 1718 if (!updated) 1719 { 1720 // Append index/newval to keysx[]/valuesx[] 1721 valuesx.push(newval); 1722 keysx.push(index); 1723 } 1724 return newval; 1725 } 1726 1727 /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length 1728 /// oldlen, change its length to newlen. If the newlen is longer than oldlen, 1729 /// all new elements will be set to the default initializer for the element type. 1730 UnionExp changeArrayLiteralLength(const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) 1731 { 1732 UnionExp ue; 1733 Type elemType = arrayType.next; 1734 assert(elemType); 1735 Expression defaultElem = elemType.defaultInitLiteral(loc); 1736 auto elements = new Expressions(newlen); 1737 // Resolve slices 1738 size_t indxlo = 0; 1739 if (oldval.op == TOK.slice) 1740 { 1741 indxlo = cast(size_t)(cast(SliceExp)oldval).lwr.toInteger(); 1742 oldval = (cast(SliceExp)oldval).e1; 1743 } 1744 size_t copylen = oldlen < newlen ? oldlen : newlen; 1745 if (oldval.op == TOK.string_) 1746 { 1747 StringExp oldse = cast(StringExp)oldval; 1748 void* s = mem.xcalloc(newlen + 1, oldse.sz); 1749 const data = oldse.peekData(); 1750 memcpy(s, data.ptr, copylen * oldse.sz); 1751 const defaultValue = cast(uint)defaultElem.toInteger(); 1752 foreach (size_t elemi; copylen .. newlen) 1753 { 1754 switch (oldse.sz) 1755 { 1756 case 1: 1757 (cast(char*)s)[cast(size_t)(indxlo + elemi)] = cast(char)defaultValue; 1758 break; 1759 case 2: 1760 (cast(wchar*)s)[cast(size_t)(indxlo + elemi)] = cast(wchar)defaultValue; 1761 break; 1762 case 4: 1763 (cast(dchar*)s)[cast(size_t)(indxlo + elemi)] = cast(dchar)defaultValue; 1764 break; 1765 default: 1766 assert(0); 1767 } 1768 } 1769 emplaceExp!(StringExp)(&ue, loc, s[0 .. newlen * oldse.sz], newlen, oldse.sz); 1770 StringExp se = cast(StringExp)ue.exp(); 1771 se.type = arrayType; 1772 se.sz = oldse.sz; 1773 se.committed = oldse.committed; 1774 se.ownedByCtfe = OwnedBy.ctfe; 1775 } 1776 else 1777 { 1778 if (oldlen != 0) 1779 { 1780 assert(oldval.op == TOK.arrayLiteral); 1781 ArrayLiteralExp ae = cast(ArrayLiteralExp)oldval; 1782 foreach (size_t i; 0 .. copylen) 1783 (*elements)[i] = (*ae.elements)[indxlo + i]; 1784 } 1785 if (elemType.ty == Tstruct || elemType.ty == Tsarray) 1786 { 1787 /* If it is an aggregate literal representing a value type, 1788 * we need to create a unique copy for each element 1789 */ 1790 foreach (size_t i; copylen .. newlen) 1791 (*elements)[i] = copyLiteral(defaultElem).copy(); 1792 } 1793 else 1794 { 1795 foreach (size_t i; copylen .. newlen) 1796 (*elements)[i] = defaultElem; 1797 } 1798 emplaceExp!(ArrayLiteralExp)(&ue, loc, arrayType, elements); 1799 ArrayLiteralExp aae = cast(ArrayLiteralExp)ue.exp(); 1800 aae.ownedByCtfe = OwnedBy.ctfe; 1801 } 1802 return ue; 1803 } 1804 1805 /*************************** CTFE Sanity Checks ***************************/ 1806 1807 bool isCtfeValueValid(Expression newval) 1808 { 1809 Type tb = newval.type.toBasetype(); 1810 switch (newval.op) 1811 { 1812 case TOK.int64: 1813 case TOK.float64: 1814 case TOK.char_: 1815 case TOK.complex80: 1816 return tb.isscalar(); 1817 1818 case TOK.null_: 1819 return tb.ty == Tnull || 1820 tb.ty == Tpointer || 1821 tb.ty == Tarray || 1822 tb.ty == Taarray || 1823 tb.ty == Tclass || 1824 tb.ty == Tdelegate; 1825 1826 case TOK.string_: 1827 return true; // CTFE would directly use the StringExp in AST. 1828 1829 case TOK.arrayLiteral: 1830 return true; //((ArrayLiteralExp *)newval)->ownedByCtfe; 1831 1832 case TOK.assocArrayLiteral: 1833 return true; //((AssocArrayLiteralExp *)newval)->ownedByCtfe; 1834 1835 case TOK.structLiteral: 1836 return true; //((StructLiteralExp *)newval)->ownedByCtfe; 1837 1838 case TOK.classReference: 1839 return true; 1840 1841 case TOK.type: 1842 return true; 1843 1844 case TOK.vector: 1845 return true; // vector literal 1846 1847 case TOK.function_: 1848 return true; // function literal or delegate literal 1849 1850 case TOK.delegate_: 1851 { 1852 // &struct.func or &clasinst.func 1853 // &nestedfunc 1854 Expression ethis = (cast(DelegateExp)newval).e1; 1855 return (ethis.op == TOK.structLiteral || ethis.op == TOK.classReference || ethis.op == TOK.variable && (cast(VarExp)ethis).var == (cast(DelegateExp)newval).func); 1856 } 1857 1858 case TOK.symbolOffset: 1859 { 1860 // function pointer, or pointer to static variable 1861 Declaration d = (cast(SymOffExp)newval).var; 1862 return d.isFuncDeclaration() || d.isDataseg(); 1863 } 1864 1865 case TOK.typeid_: 1866 { 1867 // always valid 1868 return true; 1869 } 1870 1871 case TOK.address: 1872 { 1873 // e1 should be a CTFE reference 1874 Expression e1 = (cast(AddrExp)newval).e1; 1875 return tb.ty == Tpointer && 1876 ( 1877 (e1.op == TOK.structLiteral || e1.op == TOK.arrayLiteral) && isCtfeValueValid(e1) || 1878 e1.op == TOK.variable || 1879 e1.op == TOK.dotVariable && isCtfeReferenceValid(e1) || 1880 e1.op == TOK.index && isCtfeReferenceValid(e1) || 1881 e1.op == TOK.slice && e1.type.toBasetype().ty == Tsarray 1882 ); 1883 } 1884 1885 case TOK.slice: 1886 { 1887 // e1 should be an array aggregate 1888 const SliceExp se = cast(SliceExp)newval; 1889 assert(se.lwr && se.lwr.op == TOK.int64); 1890 assert(se.upr && se.upr.op == TOK.int64); 1891 return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == TOK.string_ || se.e1.op == TOK.arrayLiteral); 1892 } 1893 1894 case TOK.void_: 1895 return true; // uninitialized value 1896 1897 default: 1898 newval.error("CTFE internal error: illegal CTFE value `%s`", newval.toChars()); 1899 return false; 1900 } 1901 } 1902 1903 bool isCtfeReferenceValid(Expression newval) 1904 { 1905 switch (newval.op) 1906 { 1907 case TOK.this_: 1908 return true; 1909 1910 case TOK.variable: 1911 { 1912 const VarDeclaration v = (cast(VarExp)newval).var.isVarDeclaration(); 1913 assert(v); 1914 // Must not be a reference to a reference 1915 return true; 1916 } 1917 1918 case TOK.index: 1919 { 1920 const Expression eagg = (cast(IndexExp)newval).e1; 1921 return eagg.op == TOK.string_ || eagg.op == TOK.arrayLiteral || eagg.op == TOK.assocArrayLiteral; 1922 } 1923 1924 case TOK.dotVariable: 1925 { 1926 Expression eagg = (cast(DotVarExp)newval).e1; 1927 return (eagg.op == TOK.structLiteral || eagg.op == TOK.classReference) && isCtfeValueValid(eagg); 1928 } 1929 1930 default: 1931 // Internally a ref variable may directly point a stack memory. 1932 // e.g. ref int v = 1; 1933 return isCtfeValueValid(newval); 1934 } 1935 } 1936 1937 // Used for debugging only 1938 void showCtfeExpr(Expression e, int level = 0) 1939 { 1940 for (int i = level; i > 0; --i) 1941 printf(" "); 1942 Expressions* elements = null; 1943 // We need the struct definition to detect block assignment 1944 StructDeclaration sd = null; 1945 ClassDeclaration cd = null; 1946 if (e.op == TOK.structLiteral) 1947 { 1948 elements = (cast(StructLiteralExp)e).elements; 1949 sd = (cast(StructLiteralExp)e).sd; 1950 printf("STRUCT type = %s %p:\n", e.type.toChars(), e); 1951 } 1952 else if (e.op == TOK.classReference) 1953 { 1954 elements = (cast(ClassReferenceExp)e).value.elements; 1955 cd = (cast(ClassReferenceExp)e).originalClass(); 1956 printf("CLASS type = %s %p:\n", e.type.toChars(), (cast(ClassReferenceExp)e).value); 1957 } 1958 else if (e.op == TOK.arrayLiteral) 1959 { 1960 elements = (cast(ArrayLiteralExp)e).elements; 1961 printf("ARRAY LITERAL type=%s %p:\n", e.type.toChars(), e); 1962 } 1963 else if (e.op == TOK.assocArrayLiteral) 1964 { 1965 printf("AA LITERAL type=%s %p:\n", e.type.toChars(), e); 1966 } 1967 else if (e.op == TOK.string_) 1968 { 1969 printf("STRING %s %p\n", e.toChars(), e.isStringExp.peekString.ptr); 1970 } 1971 else if (e.op == TOK.slice) 1972 { 1973 printf("SLICE %p: %s\n", e, e.toChars()); 1974 showCtfeExpr((cast(SliceExp)e).e1, level + 1); 1975 } 1976 else if (e.op == TOK.variable) 1977 { 1978 printf("VAR %p %s\n", e, e.toChars()); 1979 VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); 1980 if (v && getValue(v)) 1981 showCtfeExpr(getValue(v), level + 1); 1982 } 1983 else if (e.op == TOK.address) 1984 { 1985 // This is potentially recursive. We mustn't try to print the thing we're pointing to. 1986 printf("POINTER %p to %p: %s\n", e, (cast(AddrExp)e).e1, e.toChars()); 1987 } 1988 else 1989 printf("VALUE %p: %s\n", e, e.toChars()); 1990 if (elements) 1991 { 1992 size_t fieldsSoFar = 0; 1993 for (size_t i = 0; i < elements.dim; i++) 1994 { 1995 Expression z = null; 1996 VarDeclaration v = null; 1997 if (i > 15) 1998 { 1999 printf("...(total %d elements)\n", cast(int)elements.dim); 2000 return; 2001 } 2002 if (sd) 2003 { 2004 v = sd.fields[i]; 2005 z = (*elements)[i]; 2006 } 2007 else if (cd) 2008 { 2009 while (i - fieldsSoFar >= cd.fields.dim) 2010 { 2011 fieldsSoFar += cd.fields.dim; 2012 cd = cd.baseClass; 2013 for (int j = level; j > 0; --j) 2014 printf(" "); 2015 printf(" BASE CLASS: %s\n", cd.toChars()); 2016 } 2017 v = cd.fields[i - fieldsSoFar]; 2018 assert((elements.dim + i) >= (fieldsSoFar + cd.fields.dim)); 2019 size_t indx = (elements.dim - fieldsSoFar) - cd.fields.dim + i; 2020 assert(indx < elements.dim); 2021 z = (*elements)[indx]; 2022 } 2023 if (!z) 2024 { 2025 for (int j = level; j > 0; --j) 2026 printf(" "); 2027 printf(" void\n"); 2028 continue; 2029 } 2030 if (v) 2031 { 2032 // If it is a void assignment, use the default initializer 2033 if ((v.type.ty != z.type.ty) && v.type.ty == Tsarray) 2034 { 2035 for (int j = level; --j;) 2036 printf(" "); 2037 printf(" field: block initialized static array\n"); 2038 continue; 2039 } 2040 } 2041 showCtfeExpr(z, level + 1); 2042 } 2043 } 2044 } 2045 2046 /*************************** Void initialization ***************************/ 2047 UnionExp voidInitLiteral(Type t, VarDeclaration var) 2048 { 2049 UnionExp ue; 2050 if (t.ty == Tsarray) 2051 { 2052 TypeSArray tsa = cast(TypeSArray)t; 2053 Expression elem = voidInitLiteral(tsa.next, var).copy(); 2054 // For aggregate value types (structs, static arrays) we must 2055 // create an a separate copy for each element. 2056 const mustCopy = (elem.op == TOK.arrayLiteral || elem.op == TOK.structLiteral); 2057 const d = cast(size_t)tsa.dim.toInteger(); 2058 auto elements = new Expressions(d); 2059 foreach (i; 0 .. d) 2060 { 2061 if (mustCopy && i > 0) 2062 elem = copyLiteral(elem).copy(); 2063 (*elements)[i] = elem; 2064 } 2065 emplaceExp!(ArrayLiteralExp)(&ue, var.loc, tsa, elements); 2066 ArrayLiteralExp ae = cast(ArrayLiteralExp)ue.exp(); 2067 ae.ownedByCtfe = OwnedBy.ctfe; 2068 } 2069 else if (t.ty == Tstruct) 2070 { 2071 TypeStruct ts = cast(TypeStruct)t; 2072 auto exps = new Expressions(ts.sym.fields.dim); 2073 foreach (size_t i; 0 .. ts.sym.fields.dim) 2074 { 2075 (*exps)[i] = voidInitLiteral(ts.sym.fields[i].type, ts.sym.fields[i]).copy(); 2076 } 2077 emplaceExp!(StructLiteralExp)(&ue, var.loc, ts.sym, exps); 2078 StructLiteralExp se = cast(StructLiteralExp)ue.exp(); 2079 se.type = ts; 2080 se.ownedByCtfe = OwnedBy.ctfe; 2081 } 2082 else 2083 emplaceExp!(VoidInitExp)(&ue, var); 2084 return ue; 2085 }