1 /** 2 * Performs the semantic2 stage, which deals with initializer expressions. 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/semantic2.d, _semantic2.d) 8 * Documentation: https://dlang.org/phobos/dmd_semantic2.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic2.d 10 */ 11 12 module dmd.semantic2; 13 14 import core.stdc.stdio; 15 import core.stdc..string; 16 17 import dmd.aggregate; 18 import dmd.aliasthis; 19 import dmd.arraytypes; 20 import dmd.astcodegen; 21 import dmd.attrib; 22 import dmd.blockexit; 23 import dmd.clone; 24 import dmd.dcast; 25 import dmd.dclass; 26 import dmd.declaration; 27 import dmd.denum; 28 import dmd.dimport; 29 import dmd.dinterpret; 30 import dmd.dmodule; 31 import dmd.dscope; 32 import dmd.dstruct; 33 import dmd.dsymbol; 34 import dmd.dsymbolsem; 35 import dmd.dtemplate; 36 import dmd.dversion; 37 import dmd.errors; 38 import dmd.escape; 39 import dmd.expression; 40 import dmd.expressionsem; 41 import dmd.func; 42 import dmd.globals; 43 import dmd.id; 44 import dmd.identifier; 45 import dmd.init; 46 import dmd.initsem; 47 import dmd.hdrgen; 48 import dmd.mtype; 49 import dmd.nogc; 50 import dmd.nspace; 51 import dmd.objc; 52 import dmd.opover; 53 import dmd.parse; 54 import dmd.root.filename; 55 import dmd.root.outbuffer; 56 import dmd.root.rmem; 57 import dmd.root.rootobject; 58 import dmd.sideeffect; 59 import dmd.statementsem; 60 import dmd.staticassert; 61 import dmd.tokens; 62 import dmd.utf; 63 import dmd.statement; 64 import dmd.target; 65 import dmd.templateparamsem; 66 import dmd.typesem; 67 import dmd.visitor; 68 69 enum LOG = false; 70 71 72 /************************************* 73 * Does semantic analysis on initializers and members of aggregates. 74 */ 75 extern(C++) void semantic2(Dsymbol dsym, Scope* sc) 76 { 77 scope v = new Semantic2Visitor(sc); 78 dsym.accept(v); 79 } 80 81 private extern(C++) final class Semantic2Visitor : Visitor 82 { 83 alias visit = Visitor.visit; 84 Scope* sc; 85 this(Scope* sc) 86 { 87 this.sc = sc; 88 } 89 90 override void visit(Dsymbol) {} 91 92 override void visit(StaticAssert sa) 93 { 94 //printf("StaticAssert::semantic2() %s\n", sa.toChars()); 95 auto sds = new ScopeDsymbol(); 96 sc = sc.push(sds); 97 sc.tinst = null; 98 sc.minst = null; 99 100 import dmd.staticcond; 101 bool errors; 102 bool result = evalStaticCondition(sc, sa.exp, sa.exp, errors); 103 sc = sc.pop(); 104 if (errors) 105 { 106 errorSupplemental(sa.loc, "while evaluating: `static assert(%s)`", sa.exp.toChars()); 107 } 108 else if (!result) 109 { 110 if (sa.msg) 111 { 112 sc = sc.startCTFE(); 113 sa.msg = sa.msg.expressionSemantic(sc); 114 sa.msg = resolveProperties(sc, sa.msg); 115 sc = sc.endCTFE(); 116 sa.msg = sa.msg.ctfeInterpret(); 117 if (StringExp se = sa.msg.toStringExp()) 118 { 119 // same with pragma(msg) 120 const slice = se.toUTF8(sc).peekString(); 121 error(sa.loc, "static assert: \"%.*s\"", cast(int)slice.length, slice.ptr); 122 } 123 else 124 error(sa.loc, "static assert: %s", sa.msg.toChars()); 125 } 126 else 127 error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); 128 if (sc.tinst) 129 sc.tinst.printInstantiationTrace(); 130 if (!global.gag) 131 fatal(); 132 } 133 } 134 135 override void visit(TemplateInstance tempinst) 136 { 137 if (tempinst.semanticRun >= PASS.semantic2) 138 return; 139 tempinst.semanticRun = PASS.semantic2; 140 static if (LOG) 141 { 142 printf("+TemplateInstance.semantic2('%s')\n", tempinst.toChars()); 143 } 144 if (!tempinst.errors && tempinst.members) 145 { 146 TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration(); 147 assert(tempdecl); 148 149 sc = tempdecl._scope; 150 assert(sc); 151 sc = sc.push(tempinst.argsym); 152 sc = sc.push(tempinst); 153 sc.tinst = tempinst; 154 sc.minst = tempinst.minst; 155 156 int needGagging = (tempinst.gagged && !global.gag); 157 uint olderrors = global.errors; 158 int oldGaggedErrors = -1; // dead-store to prevent spurious warning 159 if (needGagging) 160 oldGaggedErrors = global.startGagging(); 161 162 for (size_t i = 0; i < tempinst.members.dim; i++) 163 { 164 Dsymbol s = (*tempinst.members)[i]; 165 static if (LOG) 166 { 167 printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); 168 } 169 s.semantic2(sc); 170 if (tempinst.gagged && global.errors != olderrors) 171 break; 172 } 173 174 if (global.errors != olderrors) 175 { 176 if (!tempinst.errors) 177 { 178 if (!tempdecl.literal) 179 tempinst.error(tempinst.loc, "error instantiating"); 180 if (tempinst.tinst) 181 tempinst.tinst.printInstantiationTrace(); 182 } 183 tempinst.errors = true; 184 } 185 if (needGagging) 186 global.endGagging(oldGaggedErrors); 187 188 sc = sc.pop(); 189 sc.pop(); 190 } 191 static if (LOG) 192 { 193 printf("-TemplateInstance.semantic2('%s')\n", tempinst.toChars()); 194 } 195 } 196 197 override void visit(TemplateMixin tmix) 198 { 199 if (tmix.semanticRun >= PASS.semantic2) 200 return; 201 tmix.semanticRun = PASS.semantic2; 202 static if (LOG) 203 { 204 printf("+TemplateMixin.semantic2('%s')\n", tmix.toChars()); 205 } 206 if (tmix.members) 207 { 208 assert(sc); 209 sc = sc.push(tmix.argsym); 210 sc = sc.push(tmix); 211 for (size_t i = 0; i < tmix.members.dim; i++) 212 { 213 Dsymbol s = (*tmix.members)[i]; 214 static if (LOG) 215 { 216 printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); 217 } 218 s.semantic2(sc); 219 } 220 sc = sc.pop(); 221 sc.pop(); 222 } 223 static if (LOG) 224 { 225 printf("-TemplateMixin.semantic2('%s')\n", tmix.toChars()); 226 } 227 } 228 229 override void visit(VarDeclaration vd) 230 { 231 if (vd.semanticRun < PASS.semanticdone && vd.inuse) 232 return; 233 234 //printf("VarDeclaration::semantic2('%s')\n", toChars()); 235 236 if (vd.aliassym) // if it's a tuple 237 { 238 vd.aliassym.accept(this); 239 vd.semanticRun = PASS.semantic2done; 240 return; 241 } 242 243 UserAttributeDeclaration.checkGNUABITag(vd, vd.linkage); 244 245 if (vd._init && !vd.toParent().isFuncDeclaration()) 246 { 247 vd.inuse++; 248 249 /* https://issues.dlang.org/show_bug.cgi?id=20280 250 * 251 * Template instances may import modules that have not 252 * finished semantic1. 253 */ 254 if (!vd.type) 255 vd.dsymbolSemantic(sc); 256 257 258 // https://issues.dlang.org/show_bug.cgi?id=14166 259 // https://issues.dlang.org/show_bug.cgi?id=20417 260 // Don't run CTFE for the temporary variables inside typeof or __traits(compiles) 261 vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret); 262 vd.inuse--; 263 } 264 if (vd._init && vd.storage_class & STC.manifest) 265 { 266 /* Cannot initializer enums with CTFE classreferences and addresses of struct literals. 267 * Scan initializer looking for them. Issue error if found. 268 */ 269 if (ExpInitializer ei = vd._init.isExpInitializer()) 270 { 271 static bool hasInvalidEnumInitializer(Expression e) 272 { 273 static bool arrayHasInvalidEnumInitializer(Expressions* elems) 274 { 275 foreach (e; *elems) 276 { 277 if (e && hasInvalidEnumInitializer(e)) 278 return true; 279 } 280 return false; 281 } 282 283 if (e.op == TOK.classReference) 284 return true; 285 if (e.op == TOK.address && (cast(AddrExp)e).e1.op == TOK.structLiteral) 286 return true; 287 if (e.op == TOK.arrayLiteral) 288 return arrayHasInvalidEnumInitializer((cast(ArrayLiteralExp)e).elements); 289 if (e.op == TOK.structLiteral) 290 return arrayHasInvalidEnumInitializer((cast(StructLiteralExp)e).elements); 291 if (e.op == TOK.assocArrayLiteral) 292 { 293 AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e; 294 return arrayHasInvalidEnumInitializer(ae.values) || 295 arrayHasInvalidEnumInitializer(ae.keys); 296 } 297 return false; 298 } 299 300 if (hasInvalidEnumInitializer(ei.exp)) 301 vd.error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead."); 302 } 303 } 304 else if (vd._init && vd.isThreadlocal()) 305 { 306 // Cannot initialize a thread-local class or pointer to struct variable with a literal 307 // that itself is a thread-local reference and would need dynamic initialization also. 308 if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared()) 309 { 310 ExpInitializer ei = vd._init.isExpInitializer(); 311 if (ei && ei.exp.op == TOK.classReference) 312 vd.error("is a thread-local class and cannot have a static initializer. Use `static this()` to initialize instead."); 313 } 314 else if (vd.type.ty == Tpointer && vd.type.nextOf().ty == Tstruct && vd.type.nextOf().isMutable() && !vd.type.nextOf().isShared()) 315 { 316 ExpInitializer ei = vd._init.isExpInitializer(); 317 if (ei && ei.exp.op == TOK.address && (cast(AddrExp)ei.exp).e1.op == TOK.structLiteral) 318 vd.error("is a thread-local pointer to struct and cannot have a static initializer. Use `static this()` to initialize instead."); 319 } 320 } 321 vd.semanticRun = PASS.semantic2done; 322 } 323 324 override void visit(Module mod) 325 { 326 //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent); 327 if (mod.semanticRun != PASS.semanticdone) // semantic() not completed yet - could be recursive call 328 return; 329 mod.semanticRun = PASS.semantic2; 330 // Note that modules get their own scope, from scratch. 331 // This is so regardless of where in the syntax a module 332 // gets imported, it is unaffected by context. 333 Scope* sc = Scope.createGlobal(mod); // create root scope 334 //printf("Module = %p\n", sc.scopesym); 335 // Pass 2 semantic routines: do initializers and function bodies 336 for (size_t i = 0; i < mod.members.dim; i++) 337 { 338 Dsymbol s = (*mod.members)[i]; 339 s.semantic2(sc); 340 } 341 if (mod.userAttribDecl) 342 { 343 mod.userAttribDecl.semantic2(sc); 344 } 345 sc = sc.pop(); 346 sc.pop(); 347 mod.semanticRun = PASS.semantic2done; 348 //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent); 349 } 350 351 override void visit(FuncDeclaration fd) 352 { 353 import dmd.dmangle : mangleToFuncSignature; 354 355 if (fd.semanticRun >= PASS.semantic2done) 356 return; 357 assert(fd.semanticRun <= PASS.semantic2); 358 fd.semanticRun = PASS.semantic2; 359 360 //printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars()); 361 362 // https://issues.dlang.org/show_bug.cgi?id=18385 363 // Disable for 2.079, s.t. a deprecation cycle can be started with 2.080 364 if (0) 365 if (fd.overnext && !fd.errors) 366 { 367 OutBuffer buf1; 368 OutBuffer buf2; 369 370 // Always starts the lookup from 'this', because the conflicts with 371 // previous overloads are already reported. 372 auto f1 = fd; 373 mangleToFuncSignature(buf1, f1); 374 375 overloadApply(f1, (Dsymbol s) 376 { 377 auto f2 = s.isFuncDeclaration(); 378 if (!f2 || f1 == f2 || f2.errors) 379 return 0; 380 381 // Don't have to check conflict between declaration and definition. 382 if ((f1.fbody !is null) != (f2.fbody !is null)) 383 return 0; 384 385 /* Check for overload merging with base class member functions. 386 * 387 * class B { void foo() {} } 388 * class D : B { 389 * override void foo() {} // B.foo appears as f2 390 * alias foo = B.foo; 391 * } 392 */ 393 if (f1.overrides(f2)) 394 return 0; 395 396 // extern (C) functions always conflict each other. 397 if (f1.ident == f2.ident && 398 f1.toParent2() == f2.toParent2() && 399 (f1.linkage != LINK.d && f1.linkage != LINK.cpp) && 400 (f2.linkage != LINK.d && f2.linkage != LINK.cpp)) 401 { 402 /* Allow the hack that is actually used in druntime, 403 * to ignore function attributes for extern (C) functions. 404 * TODO: Must be reconsidered in the future. 405 * BUG: https://issues.dlang.org/show_bug.cgi?id=18206 406 * 407 * extern(C): 408 * alias sigfn_t = void function(int); 409 * alias sigfn_t2 = void function(int) nothrow @nogc; 410 * sigfn_t bsd_signal(int sig, sigfn_t func); 411 * sigfn_t2 bsd_signal(int sig, sigfn_t2 func) nothrow @nogc; // no error 412 */ 413 if (f1.fbody is null || f2.fbody is null) 414 return 0; 415 416 auto tf2 = cast(TypeFunction)f2.type; 417 error(f2.loc, "%s `%s%s` cannot be overloaded with %s`extern(%s)` function at %s", 418 f2.kind(), 419 f2.toPrettyChars(), 420 parametersTypeToChars(tf2.parameterList), 421 (f1.linkage == f2.linkage ? "another " : "").ptr, 422 linkageToChars(f1.linkage), f1.loc.toChars()); 423 f2.type = Type.terror; 424 f2.errors = true; 425 return 0; 426 } 427 428 buf2.reset(); 429 mangleToFuncSignature(buf2, f2); 430 431 auto s1 = buf1.peekChars(); 432 auto s2 = buf2.peekChars(); 433 434 //printf("+%s\n\ts1 = %s\n\ts2 = %s @ [%s]\n", toChars(), s1, s2, f2.loc.toChars()); 435 if (strcmp(s1, s2) == 0) 436 { 437 auto tf2 = cast(TypeFunction)f2.type; 438 error(f2.loc, "%s `%s%s` conflicts with previous declaration at %s", 439 f2.kind(), 440 f2.toPrettyChars(), 441 parametersTypeToChars(tf2.parameterList), 442 f1.loc.toChars()); 443 f2.type = Type.terror; 444 f2.errors = true; 445 } 446 return 0; 447 }); 448 } 449 if (!fd.type || fd.type.ty != Tfunction) 450 return; 451 TypeFunction f = cast(TypeFunction) fd.type; 452 453 UserAttributeDeclaration.checkGNUABITag(fd, fd.linkage); 454 //semantic for parameters' UDAs 455 foreach (i; 0 .. f.parameterList.length) 456 { 457 Parameter param = f.parameterList[i]; 458 if (param && param.userAttribDecl) 459 param.userAttribDecl.semantic2(sc); 460 } 461 } 462 463 override void visit(Import i) 464 { 465 //printf("Import::semantic2('%s')\n", toChars()); 466 if (i.mod) 467 { 468 i.mod.semantic2(null); 469 if (i.mod.needmoduleinfo) 470 { 471 //printf("module5 %s because of %s\n", sc.module.toChars(), mod.toChars()); 472 if (sc) 473 sc._module.needmoduleinfo = 1; 474 } 475 } 476 } 477 478 override void visit(Nspace ns) 479 { 480 if (ns.semanticRun >= PASS.semantic2) 481 return; 482 ns.semanticRun = PASS.semantic2; 483 static if (LOG) 484 { 485 printf("+Nspace::semantic2('%s')\n", ns.toChars()); 486 } 487 UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); 488 if (ns.members) 489 { 490 assert(sc); 491 sc = sc.push(ns); 492 sc.linkage = LINK.cpp; 493 foreach (s; *ns.members) 494 { 495 static if (LOG) 496 { 497 printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); 498 } 499 s.semantic2(sc); 500 } 501 sc.pop(); 502 } 503 static if (LOG) 504 { 505 printf("-Nspace::semantic2('%s')\n", ns.toChars()); 506 } 507 } 508 509 override void visit(AttribDeclaration ad) 510 { 511 Dsymbols* d = ad.include(sc); 512 if (d) 513 { 514 Scope* sc2 = ad.newScope(sc); 515 for (size_t i = 0; i < d.dim; i++) 516 { 517 Dsymbol s = (*d)[i]; 518 s.semantic2(sc2); 519 } 520 if (sc2 != sc) 521 sc2.pop(); 522 } 523 } 524 525 /** 526 * Run the DeprecatedDeclaration's semantic2 phase then its members. 527 * 528 * The message set via a `DeprecatedDeclaration` can be either of: 529 * - a string literal 530 * - an enum 531 * - a static immutable 532 * So we need to call ctfe to resolve it. 533 * Afterward forwards to the members' semantic2. 534 */ 535 override void visit(DeprecatedDeclaration dd) 536 { 537 getMessage(dd); 538 visit(cast(AttribDeclaration)dd); 539 } 540 541 override void visit(AlignDeclaration ad) 542 { 543 ad.getAlignment(sc); 544 visit(cast(AttribDeclaration)ad); 545 } 546 547 override void visit(CPPNamespaceDeclaration decl) 548 { 549 UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp); 550 visit(cast(AttribDeclaration)decl); 551 } 552 553 override void visit(UserAttributeDeclaration uad) 554 { 555 if (uad.decl && uad.atts && uad.atts.dim && uad._scope) 556 { 557 Expression* lastTag; 558 static void eval(Scope* sc, Expressions* exps, ref Expression* lastTag) 559 { 560 foreach (ref Expression e; *exps) 561 { 562 if (e) 563 { 564 e = e.expressionSemantic(sc); 565 if (definitelyValueParameter(e)) 566 e = e.ctfeInterpret(); 567 if (e.op == TOK.tuple) 568 { 569 TupleExp te = cast(TupleExp)e; 570 eval(sc, te.exps, lastTag); 571 } 572 573 // Handles compiler-recognized `core.attribute.gnuAbiTag` 574 if (UserAttributeDeclaration.isGNUABITag(e)) 575 doGNUABITagSemantic(e, lastTag); 576 } 577 } 578 } 579 580 uad._scope = null; 581 eval(sc, uad.atts, lastTag); 582 } 583 visit(cast(AttribDeclaration)uad); 584 } 585 586 override void visit(AggregateDeclaration ad) 587 { 588 //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", ad.toChars(), ad.type.toChars(), ad.errors); 589 if (!ad.members) 590 return; 591 592 if (ad._scope) 593 { 594 ad.error("has forward references"); 595 return; 596 } 597 598 UserAttributeDeclaration.checkGNUABITag( 599 ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); 600 601 auto sc2 = ad.newScope(sc); 602 603 ad.determineSize(ad.loc); 604 605 for (size_t i = 0; i < ad.members.dim; i++) 606 { 607 Dsymbol s = (*ad.members)[i]; 608 //printf("\t[%d] %s\n", i, s.toChars()); 609 s.semantic2(sc2); 610 } 611 612 sc2.pop(); 613 } 614 } 615 616 /** 617 * Perform semantic analysis specific to the GNU ABI tags 618 * 619 * The GNU ABI tags are a feature introduced in C++11, specific to g++ 620 * and the Itanium ABI. 621 * They are mandatory for C++ interfacing, simply because the templated struct 622 *`std::basic_string`, of which the ubiquitous `std::string` is a instantiation 623 * of, uses them. 624 * 625 * Params: 626 * e = Expression to perform semantic on 627 * See `Semantic2Visitor.visit(UserAttributeDeclaration)` 628 * lastTag = When `!is null`, we already saw an ABI tag. 629 * To simplify implementation and reflection code, 630 * only one ABI tag object is allowed per symbol 631 * (but it can have multiple tags as it's an array exp). 632 */ 633 private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) 634 { 635 import dmd.dmangle; 636 637 // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal 638 if (e.op == TOK.type) 639 { 640 e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars()); 641 return; 642 } 643 644 // Definition is in `core.attributes`. If it's not a struct literal, 645 // it shouldn't have passed semantic, hence the `assert`. 646 auto sle = e.isStructLiteralExp(); 647 if (sle is null) 648 { 649 assert(global.errors); 650 return; 651 } 652 // The definition of `gnuAttributes` only have 1 member, `string[] tags` 653 assert(sle.elements && sle.elements.length == 1); 654 // `gnuAbiTag`'s constructor is defined as `this(string[] tags...)` 655 auto ale = (*sle.elements)[0].isArrayLiteralExp(); 656 if (ale is null) 657 { 658 e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars()); 659 return; 660 } 661 662 // Check that it's the only tag on the symbol 663 if (lastTag !is null) 664 { 665 const str1 = (*lastTag.isStructLiteralExp().elements)[0].toString(); 666 const str2 = ale.toString(); 667 e.error("only one `@%s` allowed per symbol", Id.udaGNUAbiTag.toChars()); 668 e.errorSupplemental("instead of `@%s @%s`, use `@%s(%.*s, %.*s)`", 669 lastTag.toChars(), e.toChars(), Id.udaGNUAbiTag.toChars(), 670 // Avoid [ ... ] 671 cast(int)str1.length - 2, str1.ptr + 1, 672 cast(int)str2.length - 2, str2.ptr + 1); 673 return; 674 } 675 lastTag = &e; 676 677 // We already know we have a valid array literal of strings. 678 // Now checks that elements are valid. 679 foreach (idx, elem; *ale.elements) 680 { 681 const str = elem.toStringExp().peekString(); 682 if (!str.length) 683 { 684 e.error("argument `%d` to `@%s` cannot be %s", cast(int)(idx + 1), 685 Id.udaGNUAbiTag.toChars(), 686 elem.isNullExp() ? "`null`".ptr : "empty".ptr); 687 continue; 688 } 689 690 foreach (c; str) 691 { 692 if (!c.isValidMangling()) 693 { 694 e.error("`@%s` char `0x%02x` not allowed in mangling", 695 Id.udaGNUAbiTag.toChars(), c); 696 break; 697 } 698 } 699 // Valid element 700 } 701 // Since ABI tags need to be sorted, we sort them in place 702 // It might be surprising for users that inspects the UDAs, 703 // but it's a concession to practicality. 704 // Casts are unfortunately necessary as `implicitConvTo` is not 705 // `const` (and nor is `StringExp`, by extension). 706 static int predicate(const scope Expression* e1, const scope Expression* e2) nothrow 707 { 708 scope(failure) assert(0, "An exception was thrown"); 709 return (cast(Expression*)e1).toStringExp().compare((cast(Expression*)e2).toStringExp()); 710 } 711 ale.elements.sort!predicate; 712 }