1 /** 2 * Handles operator overloading. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading) 5 * 6 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d) 10 * Documentation: https://dlang.org/phobos/dmd_opover.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d 12 */ 13 14 module dmd.opover; 15 16 import core.stdc.stdio; 17 import dmd.aggregate; 18 import dmd.aliasthis; 19 import dmd.arraytypes; 20 import dmd.astenums; 21 import dmd.dclass; 22 import dmd.declaration; 23 import dmd.dscope; 24 import dmd.dstruct; 25 import dmd.dsymbol; 26 import dmd.dtemplate; 27 import dmd.errors; 28 import dmd.expression; 29 import dmd.expressionsem; 30 import dmd.func; 31 import dmd.globals; 32 import dmd.hdrgen; 33 import dmd.id; 34 import dmd.identifier; 35 import dmd.location; 36 import dmd.mtype; 37 import dmd.statement; 38 import dmd.tokens; 39 import dmd.typesem; 40 import dmd.visitor; 41 42 /*********************************** 43 * Determine if operands of binary op can be reversed 44 * to fit operator overload. 45 */ 46 bool isCommutative(EXP op) 47 { 48 switch (op) 49 { 50 case EXP.add: 51 case EXP.mul: 52 case EXP.and: 53 case EXP.or: 54 case EXP.xor: 55 // EqualExp 56 case EXP.equal: 57 case EXP.notEqual: 58 // CmpExp 59 case EXP.lessThan: 60 case EXP.lessOrEqual: 61 case EXP.greaterThan: 62 case EXP.greaterOrEqual: 63 return true; 64 default: 65 break; 66 } 67 return false; 68 } 69 70 /*********************************** 71 * Get Identifier for operator overload. 72 */ 73 private Identifier opId(Expression e) 74 { 75 switch (e.op) 76 { 77 case EXP.uadd: return Id.uadd; 78 case EXP.negate: return Id.neg; 79 case EXP.tilde: return Id.com; 80 case EXP.cast_: return Id._cast; 81 case EXP.in_: return Id.opIn; 82 case EXP.plusPlus: return Id.postinc; 83 case EXP.minusMinus: return Id.postdec; 84 case EXP.add: return Id.add; 85 case EXP.min: return Id.sub; 86 case EXP.mul: return Id.mul; 87 case EXP.div: return Id.div; 88 case EXP.mod: return Id.mod; 89 case EXP.pow: return Id.pow; 90 case EXP.leftShift: return Id.shl; 91 case EXP.rightShift: return Id.shr; 92 case EXP.unsignedRightShift: return Id.ushr; 93 case EXP.and: return Id.iand; 94 case EXP.or: return Id.ior; 95 case EXP.xor: return Id.ixor; 96 case EXP.concatenate: return Id.cat; 97 case EXP.assign: return Id.assign; 98 case EXP.addAssign: return Id.addass; 99 case EXP.minAssign: return Id.subass; 100 case EXP.mulAssign: return Id.mulass; 101 case EXP.divAssign: return Id.divass; 102 case EXP.modAssign: return Id.modass; 103 case EXP.powAssign: return Id.powass; 104 case EXP.leftShiftAssign: return Id.shlass; 105 case EXP.rightShiftAssign: return Id.shrass; 106 case EXP.unsignedRightShiftAssign: return Id.ushrass; 107 case EXP.andAssign: return Id.andass; 108 case EXP.orAssign: return Id.orass; 109 case EXP.xorAssign: return Id.xorass; 110 case EXP.concatenateAssign: return Id.catass; 111 case EXP.equal: return Id.eq; 112 case EXP.lessThan: 113 case EXP.lessOrEqual: 114 case EXP.greaterThan: 115 case EXP.greaterOrEqual: return Id.cmp; 116 case EXP.array: return Id.index; 117 case EXP.star: return Id.opStar; 118 default: assert(0); 119 } 120 } 121 122 /*********************************** 123 * Get Identifier for reverse operator overload, 124 * `null` if not supported for this operator. 125 */ 126 private Identifier opId_r(Expression e) 127 { 128 switch (e.op) 129 { 130 case EXP.in_: return Id.opIn_r; 131 case EXP.add: return Id.add_r; 132 case EXP.min: return Id.sub_r; 133 case EXP.mul: return Id.mul_r; 134 case EXP.div: return Id.div_r; 135 case EXP.mod: return Id.mod_r; 136 case EXP.pow: return Id.pow_r; 137 case EXP.leftShift: return Id.shl_r; 138 case EXP.rightShift: return Id.shr_r; 139 case EXP.unsignedRightShift:return Id.ushr_r; 140 case EXP.and: return Id.iand_r; 141 case EXP.or: return Id.ior_r; 142 case EXP.xor: return Id.ixor_r; 143 case EXP.concatenate: return Id.cat_r; 144 default: return null; 145 } 146 } 147 148 /******************************************* 149 * Helper function to turn operator into template argument list 150 */ 151 Objects* opToArg(Scope* sc, EXP op) 152 { 153 /* Remove the = from op= 154 */ 155 switch (op) 156 { 157 case EXP.addAssign: 158 op = EXP.add; 159 break; 160 case EXP.minAssign: 161 op = EXP.min; 162 break; 163 case EXP.mulAssign: 164 op = EXP.mul; 165 break; 166 case EXP.divAssign: 167 op = EXP.div; 168 break; 169 case EXP.modAssign: 170 op = EXP.mod; 171 break; 172 case EXP.andAssign: 173 op = EXP.and; 174 break; 175 case EXP.orAssign: 176 op = EXP.or; 177 break; 178 case EXP.xorAssign: 179 op = EXP.xor; 180 break; 181 case EXP.leftShiftAssign: 182 op = EXP.leftShift; 183 break; 184 case EXP.rightShiftAssign: 185 op = EXP.rightShift; 186 break; 187 case EXP.unsignedRightShiftAssign: 188 op = EXP.unsignedRightShift; 189 break; 190 case EXP.concatenateAssign: 191 op = EXP.concatenate; 192 break; 193 case EXP.powAssign: 194 op = EXP.pow; 195 break; 196 default: 197 break; 198 } 199 Expression e = new StringExp(Loc.initial, EXPtoString(op)); 200 e = e.expressionSemantic(sc); 201 auto tiargs = new Objects(); 202 tiargs.push(e); 203 return tiargs; 204 } 205 206 // Try alias this on first operand 207 private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e) 208 { 209 if (!ad || !ad.aliasthis) 210 return null; 211 212 /* Rewrite (e1 op e2) as: 213 * (e1.aliasthis op e2) 214 */ 215 if (isRecursiveAliasThis(e.att1, e.e1.type)) 216 return null; 217 //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); 218 BinExp be = cast(BinExp)e.copy(); 219 // Resolve 'alias this' but in case of assigment don't resolve properties yet 220 // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2' 221 bool findOnly = (e.op == EXP.assign); 222 be.e1 = resolveAliasThis(sc, e.e1, true, findOnly); 223 if (!be.e1) 224 return null; 225 226 Expression result; 227 if (be.op == EXP.concatenateAssign) 228 result = be.op_overload(sc); 229 else 230 result = be.trySemantic(sc); 231 232 return result; 233 } 234 235 // Try alias this on second operand 236 private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e) 237 { 238 if (!ad || !ad.aliasthis) 239 return null; 240 /* Rewrite (e1 op e2) as: 241 * (e1 op e2.aliasthis) 242 */ 243 if (isRecursiveAliasThis(e.att2, e.e2.type)) 244 return null; 245 //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); 246 BinExp be = cast(BinExp)e.copy(); 247 be.e2 = resolveAliasThis(sc, e.e2, true); 248 if (!be.e2) 249 return null; 250 251 Expression result; 252 if (be.op == EXP.concatenateAssign) 253 result = be.op_overload(sc); 254 else 255 result = be.trySemantic(sc); 256 257 return result; 258 } 259 260 /************************************ 261 * Operator overload. 262 * Check for operator overload, if so, replace 263 * with function call. 264 * Params: 265 * e = expression with operator 266 * sc = context 267 * pop = if not null, is set to the operator that was actually overloaded, 268 * which may not be `e.op`. Happens when operands are reversed to 269 * match an overload 270 * Returns: 271 * `null` if not an operator overload, 272 * otherwise the lowered expression 273 */ 274 Expression op_overload(Expression e, Scope* sc, EXP* pop = null) 275 { 276 Expression visit(Expression e) 277 { 278 assert(0); 279 } 280 281 Expression visitUna(UnaExp e) 282 { 283 //printf("UnaExp::op_overload() (%s)\n", e.toChars()); 284 Expression result; 285 if (auto ae = e.e1.isArrayExp()) 286 { 287 ae.e1 = ae.e1.expressionSemantic(sc); 288 ae.e1 = resolveProperties(sc, ae.e1); 289 Expression ae1old = ae.e1; 290 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); 291 IntervalExp ie = null; 292 if (maybeSlice && ae.arguments.length) 293 { 294 ie = (*ae.arguments)[0].isIntervalExp(); 295 } 296 Type att = null; // first cyclic `alias this` type 297 while (true) 298 { 299 if (ae.e1.op == EXP.error) 300 { 301 return ae.e1; 302 } 303 Expression e0 = null; 304 Expression ae1save = ae.e1; 305 ae.lengthVar = null; 306 Type t1b = ae.e1.type.toBasetype(); 307 AggregateDeclaration ad = isAggregate(t1b); 308 if (!ad) 309 break; 310 if (search_function(ad, Id.opIndexUnary)) 311 { 312 // Deal with $ 313 result = resolveOpDollar(sc, ae, &e0); 314 if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) 315 goto Lfallback; 316 if (result.op == EXP.error) 317 return result; 318 /* Rewrite op(a[arguments]) as: 319 * a.opIndexUnary!(op)(arguments) 320 */ 321 Expressions* a = ae.arguments.copy(); 322 Objects* tiargs = opToArg(sc, e.op); 323 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); 324 result = new CallExp(e.loc, result, a); 325 if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() 326 result = result.trySemantic(sc); 327 else 328 result = result.expressionSemantic(sc); 329 if (result) 330 { 331 return Expression.combine(e0, result); 332 } 333 } 334 Lfallback: 335 if (maybeSlice && search_function(ad, Id.opSliceUnary)) 336 { 337 // Deal with $ 338 result = resolveOpDollar(sc, ae, ie, &e0); 339 if (result.op == EXP.error) 340 return result; 341 /* Rewrite op(a[i..j]) as: 342 * a.opSliceUnary!(op)(i, j) 343 */ 344 auto a = new Expressions(); 345 if (ie) 346 { 347 a.push(ie.lwr); 348 a.push(ie.upr); 349 } 350 Objects* tiargs = opToArg(sc, e.op); 351 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); 352 result = new CallExp(e.loc, result, a); 353 result = result.expressionSemantic(sc); 354 result = Expression.combine(e0, result); 355 return result; 356 } 357 // Didn't find it. Forward to aliasthis 358 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) 359 { 360 /* Rewrite op(a[arguments]) as: 361 * op(a.aliasthis[arguments]) 362 */ 363 ae.e1 = resolveAliasThis(sc, ae1save, true); 364 if (ae.e1) 365 continue; 366 } 367 break; 368 } 369 ae.e1 = ae1old; // recovery 370 ae.lengthVar = null; 371 } 372 e.e1 = e.e1.expressionSemantic(sc); 373 e.e1 = resolveProperties(sc, e.e1); 374 Type att = null; // first cyclic `alias this` type 375 while (1) 376 { 377 if (e.e1.op == EXP.error) 378 { 379 return e.e1; 380 } 381 382 AggregateDeclaration ad = isAggregate(e.e1.type); 383 if (!ad) 384 break; 385 386 Dsymbol fd = null; 387 /* Rewrite as: 388 * e1.opUnary!(op)() 389 */ 390 fd = search_function(ad, Id.opUnary); 391 if (fd) 392 { 393 Objects* tiargs = opToArg(sc, e.op); 394 result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); 395 result = new CallExp(e.loc, result); 396 result = result.expressionSemantic(sc); 397 return result; 398 } 399 // D1-style operator overloads, deprecated 400 if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) 401 { 402 auto id = opId(e); 403 fd = search_function(ad, id); 404 if (fd) 405 { 406 // @@@DEPRECATED_2.110@@@. 407 // Deprecated in 2.088, made an error in 2.100 408 e.error("`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); 409 return ErrorExp.get(); 410 } 411 } 412 // Didn't find it. Forward to aliasthis 413 if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) 414 { 415 /* Rewrite op(e1) as: 416 * op(e1.aliasthis) 417 */ 418 //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); 419 e.e1 = resolveAliasThis(sc, e.e1, true); 420 if (e.e1) 421 continue; 422 break; 423 } 424 break; 425 } 426 return result; 427 } 428 429 Expression visitArray(ArrayExp ae) 430 { 431 //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); 432 ae.e1 = ae.e1.expressionSemantic(sc); 433 ae.e1 = resolveProperties(sc, ae.e1); 434 Expression ae1old = ae.e1; 435 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); 436 IntervalExp ie = null; 437 if (maybeSlice && ae.arguments.length) 438 { 439 ie = (*ae.arguments)[0].isIntervalExp(); 440 } 441 Expression result; 442 Type att = null; // first cyclic `alias this` type 443 while (true) 444 { 445 if (ae.e1.op == EXP.error) 446 { 447 return ae.e1; 448 } 449 Expression e0 = null; 450 Expression ae1save = ae.e1; 451 ae.lengthVar = null; 452 Type t1b = ae.e1.type.toBasetype(); 453 AggregateDeclaration ad = isAggregate(t1b); 454 if (!ad) 455 { 456 // If the non-aggregate expression ae.e1 is indexable or sliceable, 457 // convert it to the corresponding concrete expression. 458 if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) 459 { 460 // Convert to SliceExp 461 if (maybeSlice) 462 { 463 result = new SliceExp(ae.loc, ae.e1, ie); 464 result = result.expressionSemantic(sc); 465 return result; 466 } 467 // Convert to IndexExp 468 if (ae.arguments.length == 1) 469 { 470 result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); 471 result = result.expressionSemantic(sc); 472 return result; 473 } 474 } 475 break; 476 } 477 if (search_function(ad, Id.index)) 478 { 479 // Deal with $ 480 result = resolveOpDollar(sc, ae, &e0); 481 if (!result) // a[i..j] might be: a.opSlice(i, j) 482 goto Lfallback; 483 if (result.op == EXP.error) 484 return result; 485 /* Rewrite e1[arguments] as: 486 * e1.opIndex(arguments) 487 */ 488 Expressions* a = ae.arguments.copy(); 489 result = new DotIdExp(ae.loc, ae.e1, Id.index); 490 result = new CallExp(ae.loc, result, a); 491 if (maybeSlice) // a[] might be: a.opSlice() 492 result = result.trySemantic(sc); 493 else 494 result = result.expressionSemantic(sc); 495 if (result) 496 { 497 return Expression.combine(e0, result); 498 } 499 } 500 Lfallback: 501 if (maybeSlice && ae.e1.op == EXP.type) 502 { 503 result = new SliceExp(ae.loc, ae.e1, ie); 504 result = result.expressionSemantic(sc); 505 result = Expression.combine(e0, result); 506 return result; 507 } 508 if (maybeSlice && search_function(ad, Id.slice)) 509 { 510 // Deal with $ 511 result = resolveOpDollar(sc, ae, ie, &e0); 512 513 if (result.op == EXP.error) 514 { 515 if (!e0 && !search_function(ad, Id.dollar)) { 516 ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); 517 } 518 return result; 519 } 520 /* Rewrite a[i..j] as: 521 * a.opSlice(i, j) 522 */ 523 auto a = new Expressions(); 524 if (ie) 525 { 526 a.push(ie.lwr); 527 a.push(ie.upr); 528 } 529 result = new DotIdExp(ae.loc, ae.e1, Id.slice); 530 result = new CallExp(ae.loc, result, a); 531 result = result.expressionSemantic(sc); 532 result = Expression.combine(e0, result); 533 return result; 534 } 535 // Didn't find it. Forward to aliasthis 536 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) 537 { 538 //printf("att arr e1 = %s\n", this.e1.type.toChars()); 539 /* Rewrite op(a[arguments]) as: 540 * op(a.aliasthis[arguments]) 541 */ 542 ae.e1 = resolveAliasThis(sc, ae1save, true); 543 if (ae.e1) 544 continue; 545 } 546 break; 547 } 548 ae.e1 = ae1old; // recovery 549 ae.lengthVar = null; 550 return result; 551 } 552 553 /*********************************************** 554 * This is mostly the same as UnaryExp::op_overload(), but has 555 * a different rewrite. 556 */ 557 Expression visitCast(CastExp e, Type att = null) 558 { 559 //printf("CastExp::op_overload() (%s)\n", e.toChars()); 560 Expression result; 561 AggregateDeclaration ad = isAggregate(e.e1.type); 562 if (ad) 563 { 564 Dsymbol fd = null; 565 /* Rewrite as: 566 * e1.opCast!(T)() 567 */ 568 fd = search_function(ad, Id._cast); 569 if (fd) 570 { 571 version (all) 572 { 573 // Backwards compatibility with D1 if opCast is a function, not a template 574 if (fd.isFuncDeclaration()) 575 { 576 // Rewrite as: e1.opCast() 577 return build_overload(e.loc, sc, e.e1, null, fd); 578 } 579 } 580 auto tiargs = new Objects(); 581 tiargs.push(e.to); 582 result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); 583 result = new CallExp(e.loc, result); 584 result = result.expressionSemantic(sc); 585 return result; 586 } 587 // Didn't find it. Forward to aliasthis 588 if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) 589 { 590 /* Rewrite op(e1) as: 591 * op(e1.aliasthis) 592 */ 593 if (auto e1 = resolveAliasThis(sc, e.e1, true)) 594 { 595 result = e.copy(); 596 (cast(UnaExp)result).e1 = e1; 597 result = visitCast(result.isCastExp(), att); 598 return result; 599 } 600 } 601 } 602 return result; 603 } 604 605 Expression visitBin(BinExp e) 606 { 607 //printf("BinExp::op_overload() (%s)\n", e.toChars()); 608 Identifier id = opId(e); 609 Identifier id_r = opId_r(e); 610 Expressions args1; 611 Expressions args2; 612 int argsset = 0; 613 AggregateDeclaration ad1 = isAggregate(e.e1.type); 614 AggregateDeclaration ad2 = isAggregate(e.e2.type); 615 if (e.op == EXP.assign && ad1 == ad2) 616 { 617 StructDeclaration sd = ad1.isStructDeclaration(); 618 if (sd && 619 (!sd.hasIdentityAssign || 620 /* Do a blit if we can and the rvalue is something like .init, 621 * where a postblit is not necessary. 622 */ 623 (sd.hasBlitAssign && !e.e2.isLvalue()))) 624 { 625 /* This is bitwise struct assignment. */ 626 return null; 627 } 628 } 629 Dsymbol s = null; 630 Dsymbol s_r = null; 631 Objects* tiargs = null; 632 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) 633 { 634 // Bug4099 fix 635 if (ad1 && search_function(ad1, Id.opUnary)) 636 return null; 637 } 638 if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) 639 { 640 /* Try opBinary and opBinaryRight 641 */ 642 if (ad1) 643 { 644 s = search_function(ad1, Id.opBinary); 645 if (s && !s.isTemplateDeclaration()) 646 { 647 e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars()); 648 return ErrorExp.get(); 649 } 650 } 651 if (ad2) 652 { 653 s_r = search_function(ad2, Id.opBinaryRight); 654 if (s_r && !s_r.isTemplateDeclaration()) 655 { 656 e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars()); 657 return ErrorExp.get(); 658 } 659 if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 660 s_r = null; 661 } 662 // Set tiargs, the template argument list, which will be the operator string 663 if (s || s_r) 664 { 665 id = Id.opBinary; 666 id_r = Id.opBinaryRight; 667 tiargs = opToArg(sc, e.op); 668 } 669 } 670 if (!s && !s_r) 671 { 672 // Try the D1-style operators, deprecated 673 if (ad1 && id) 674 { 675 s = search_function(ad1, id); 676 if (s && id != Id.assign) 677 { 678 // @@@DEPRECATED_2.110@@@. 679 // Deprecated in 2.088, made an error in 2.100 680 if (id == Id.postinc || id == Id.postdec) 681 e.error("`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); 682 else 683 e.error("`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); 684 return ErrorExp.get(); 685 } 686 } 687 if (ad2 && id_r) 688 { 689 s_r = search_function(ad2, id_r); 690 // https://issues.dlang.org/show_bug.cgi?id=12778 691 // If both x.opBinary(y) and y.opBinaryRight(x) found, 692 // and they are exactly same symbol, x.opBinary(y) should be preferred. 693 if (s_r && s_r == s) 694 s_r = null; 695 if (s_r) 696 { 697 // @@@DEPRECATED_2.110@@@. 698 // Deprecated in 2.088, made an error in 2.100 699 e.error("`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); 700 return ErrorExp.get(); 701 } 702 } 703 } 704 if (s || s_r) 705 { 706 /* Try: 707 * a.opfunc(b) 708 * b.opfunc_r(a) 709 * and see which is better. 710 */ 711 args1.setDim(1); 712 args1[0] = e.e1; 713 expandTuples(&args1); 714 args2.setDim(1); 715 args2[0] = e.e2; 716 expandTuples(&args2); 717 argsset = 1; 718 MatchAccumulator m; 719 if (s) 720 { 721 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2)); 722 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 723 { 724 return ErrorExp.get(); 725 } 726 } 727 FuncDeclaration lastf = m.lastf; 728 if (s_r) 729 { 730 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1)); 731 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 732 { 733 return ErrorExp.get(); 734 } 735 } 736 if (m.count > 1) 737 { 738 // Error, ambiguous 739 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 740 } 741 else if (m.last == MATCH.nomatch) 742 { 743 if (tiargs) 744 goto L1; 745 m.lastf = null; 746 } 747 if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) 748 { 749 // Kludge because operator overloading regards e++ and e-- 750 // as unary, but it's implemented as a binary. 751 // Rewrite (e1 ++ e2) as e1.postinc() 752 // Rewrite (e1 -- e2) as e1.postdec() 753 return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); 754 } 755 else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) 756 { 757 // Rewrite (e1 op e2) as e1.opfunc(e2) 758 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 759 } 760 else 761 { 762 // Rewrite (e1 op e2) as e2.opfunc_r(e1) 763 return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); 764 } 765 } 766 L1: 767 version (all) 768 { 769 // Retained for D1 compatibility 770 if (isCommutative(e.op) && !tiargs) 771 { 772 s = null; 773 s_r = null; 774 if (ad1 && id_r) 775 { 776 s_r = search_function(ad1, id_r); 777 } 778 if (ad2 && id) 779 { 780 s = search_function(ad2, id); 781 if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 782 s = null; 783 } 784 if (s || s_r) 785 { 786 /* Try: 787 * a.opfunc_r(b) 788 * b.opfunc(a) 789 * and see which is better. 790 */ 791 if (!argsset) 792 { 793 args1.setDim(1); 794 args1[0] = e.e1; 795 expandTuples(&args1); 796 args2.setDim(1); 797 args2[0] = e.e2; 798 expandTuples(&args2); 799 } 800 MatchAccumulator m; 801 if (s_r) 802 { 803 functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2)); 804 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 805 { 806 return ErrorExp.get(); 807 } 808 } 809 FuncDeclaration lastf = m.lastf; 810 if (s) 811 { 812 functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1)); 813 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 814 { 815 return ErrorExp.get(); 816 } 817 } 818 if (m.count > 1) 819 { 820 // Error, ambiguous 821 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 822 } 823 else if (m.last == MATCH.nomatch) 824 { 825 m.lastf = null; 826 } 827 828 if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) 829 { 830 // Rewrite (e1 op e2) as e1.opfunc_r(e2) 831 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); 832 } 833 else 834 { 835 // Rewrite (e1 op e2) as e2.opfunc(e1) 836 Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); 837 // When reversing operands of comparison operators, 838 // need to reverse the sense of the op 839 if (pop) 840 *pop = reverseRelation(e.op); 841 return result; 842 } 843 } 844 } 845 } 846 847 Expression rewrittenLhs; 848 if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 849 { 850 if (Expression result = checkAliasThisForLhs(ad1, sc, e)) 851 { 852 /* https://issues.dlang.org/show_bug.cgi?id=19441 853 * 854 * alias this may not be used for partial assignment. 855 * If a struct has a single member which is aliased this 856 * directly or aliased to a ref getter function that returns 857 * the mentioned member, then alias this may be 858 * used since the object will be fully initialised. 859 * If the struct is nested, the context pointer is considered 860 * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` 861 * condition. 862 */ 863 if (result.op != EXP.assign) 864 return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` 865 866 auto ae = result.isAssignExp(); 867 if (ae.e1.op != EXP.dotVariable) 868 return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` 869 870 auto dve = ae.e1.isDotVarExp(); 871 if (auto ad = dve.var.isMember2()) 872 { 873 // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` 874 // Ensure that `var` is the only field member in `ad` 875 if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) 876 { 877 if (dve.var == ad.aliasthis.sym) 878 return result; 879 } 880 } 881 rewrittenLhs = ae.e1; 882 } 883 } 884 if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 885 { 886 if (Expression result = checkAliasThisForRhs(ad2, sc, e)) 887 return result; 888 } 889 if (rewrittenLhs) 890 { 891 e.error("cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", 892 e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); 893 return ErrorExp.get(); 894 } 895 return null; 896 } 897 898 Expression visitEqual(EqualExp e) 899 { 900 //printf("EqualExp::op_overload() (%s)\n", e.toChars()); 901 Type t1 = e.e1.type.toBasetype(); 902 Type t2 = e.e2.type.toBasetype(); 903 904 /* Array equality is handled by expressionSemantic() potentially 905 * lowering to object.__equals(), which takes care of overloaded 906 * operators for the element types. 907 */ 908 if ((t1.ty == Tarray || t1.ty == Tsarray) && 909 (t2.ty == Tarray || t2.ty == Tsarray)) 910 { 911 return null; 912 } 913 914 /* Check for class equality with null literal or typeof(null). 915 */ 916 if (t1.ty == Tclass && e.e2.op == EXP.null_ || 917 t2.ty == Tclass && e.e1.op == EXP.null_) 918 { 919 e.error("use `%s` instead of `%s` when comparing with `null`", 920 EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, 921 EXPtoString(e.op).ptr); 922 return ErrorExp.get(); 923 } 924 if (t1.ty == Tclass && t2.ty == Tnull || 925 t1.ty == Tnull && t2.ty == Tclass) 926 { 927 // Comparing a class with typeof(null) should not call opEquals 928 return null; 929 } 930 931 /* Check for class equality. 932 */ 933 if (t1.ty == Tclass && t2.ty == Tclass) 934 { 935 ClassDeclaration cd1 = t1.isClassHandle(); 936 ClassDeclaration cd2 = t2.isClassHandle(); 937 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) 938 { 939 /* Rewrite as: 940 * .object.opEquals(e1, e2) 941 */ 942 if (!ClassDeclaration.object) 943 { 944 e.error("cannot compare classes for equality because `object.Object` was not declared"); 945 return null; 946 } 947 948 Expression e1x = e.e1; 949 Expression e2x = e.e2; 950 951 /* The explicit cast is necessary for interfaces 952 * https://issues.dlang.org/show_bug.cgi?id=4088 953 */ 954 Type to = ClassDeclaration.object.getType(); 955 if (cd1.isInterfaceDeclaration()) 956 e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); 957 if (cd2.isInterfaceDeclaration()) 958 e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); 959 960 Expression result = new IdentifierExp(e.loc, Id.empty); 961 result = new DotIdExp(e.loc, result, Id.object); 962 result = new DotIdExp(e.loc, result, Id.eq); 963 result = new CallExp(e.loc, result, e1x, e2x); 964 if (e.op == EXP.notEqual) 965 result = new NotExp(e.loc, result); 966 result = result.expressionSemantic(sc); 967 return result; 968 } 969 } 970 971 if (Expression result = compare_overload(e, sc, Id.eq, null)) 972 { 973 if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) 974 { 975 result = new NotExp(result.loc, result); 976 result = result.expressionSemantic(sc); 977 } 978 return result; 979 } 980 981 /* Check for pointer equality. 982 */ 983 if (t1.ty == Tpointer || t2.ty == Tpointer) 984 { 985 /* Rewrite: 986 * ptr1 == ptr2 987 * as: 988 * ptr1 is ptr2 989 * 990 * This is just a rewriting for deterministic AST representation 991 * as the backend input. 992 */ 993 auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; 994 Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); 995 return r.expressionSemantic(sc); 996 } 997 998 /* Check for struct equality without opEquals. 999 */ 1000 if (t1.ty == Tstruct && t2.ty == Tstruct) 1001 { 1002 auto sd = t1.isTypeStruct().sym; 1003 if (sd != t2.isTypeStruct().sym) 1004 return null; 1005 1006 import dmd.clone : needOpEquals; 1007 if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) 1008 { 1009 // Use bitwise equality. 1010 auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; 1011 Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); 1012 return r.expressionSemantic(sc); 1013 } 1014 1015 /* Do memberwise equality. 1016 * https://dlang.org/spec/expression.html#equality_expressions 1017 * Rewrite: 1018 * e1 == e2 1019 * as: 1020 * e1.tupleof == e2.tupleof 1021 * 1022 * If sd is a nested struct, and if it's nested in a class, it will 1023 * also compare the parent class's equality. Otherwise, compares 1024 * the identity of parent context through void*. 1025 */ 1026 e = e.copy().isEqualExp(); 1027 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); 1028 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); 1029 1030 auto sc2 = sc.push(); 1031 sc2.flags |= SCOPE.noaccesscheck; 1032 Expression r = e.expressionSemantic(sc2); 1033 sc2.pop(); 1034 return r; 1035 } 1036 1037 /* Check for tuple equality. 1038 */ 1039 if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) 1040 { 1041 auto tup1 = e.e1.isTupleExp(); 1042 auto tup2 = e.e2.isTupleExp(); 1043 size_t dim = tup1.exps.length; 1044 if (dim != tup2.exps.length) 1045 { 1046 e.error("mismatched tuple lengths, `%d` and `%d`", 1047 cast(int)dim, cast(int)tup2.exps.length); 1048 return ErrorExp.get(); 1049 } 1050 1051 Expression result; 1052 if (dim == 0) 1053 { 1054 // zero-length tuple comparison should always return true or false. 1055 result = IntegerExp.createBool(e.op == EXP.equal); 1056 } 1057 else 1058 { 1059 for (size_t i = 0; i < dim; i++) 1060 { 1061 auto ex1 = (*tup1.exps)[i]; 1062 auto ex2 = (*tup2.exps)[i]; 1063 auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); 1064 1065 if (!result) 1066 result = eeq; 1067 else if (e.op == EXP.equal) 1068 result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); 1069 else 1070 result = new LogicalExp(e.loc, EXP.orOr, result, eeq); 1071 } 1072 assert(result); 1073 } 1074 result = Expression.combine(tup1.e0, tup2.e0, result); 1075 result = result.expressionSemantic(sc); 1076 1077 return result; 1078 } 1079 return null; 1080 } 1081 1082 Expression visitCmp(CmpExp e) 1083 { 1084 //printf("CmpExp:: () (%s)\n", e.toChars()); 1085 return compare_overload(e, sc, Id.cmp, pop); 1086 } 1087 1088 /********************************* 1089 * Operator overloading for op= 1090 */ 1091 Expression visitBinAssign(BinAssignExp e) 1092 { 1093 //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); 1094 if (auto ae = e.e1.isArrayExp()) 1095 { 1096 ae.e1 = ae.e1.expressionSemantic(sc); 1097 ae.e1 = resolveProperties(sc, ae.e1); 1098 Expression ae1old = ae.e1; 1099 const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); 1100 IntervalExp ie = null; 1101 if (maybeSlice && ae.arguments.length) 1102 { 1103 ie = (*ae.arguments)[0].isIntervalExp(); 1104 } 1105 Type att = null; // first cyclic `alias this` type 1106 while (true) 1107 { 1108 if (ae.e1.op == EXP.error) 1109 { 1110 return ae.e1; 1111 } 1112 Expression e0 = null; 1113 Expression ae1save = ae.e1; 1114 ae.lengthVar = null; 1115 Type t1b = ae.e1.type.toBasetype(); 1116 AggregateDeclaration ad = isAggregate(t1b); 1117 if (!ad) 1118 break; 1119 if (search_function(ad, Id.opIndexOpAssign)) 1120 { 1121 // Deal with $ 1122 Expression result = resolveOpDollar(sc, ae, &e0); 1123 if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) 1124 goto Lfallback; 1125 if (result.op == EXP.error) 1126 return result; 1127 result = e.e2.expressionSemantic(sc); 1128 if (result.op == EXP.error) 1129 return result; 1130 e.e2 = result; 1131 /* Rewrite a[arguments] op= e2 as: 1132 * a.opIndexOpAssign!(op)(e2, arguments) 1133 */ 1134 Expressions* a = ae.arguments.copy(); 1135 a.insert(0, e.e2); 1136 Objects* tiargs = opToArg(sc, e.op); 1137 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); 1138 result = new CallExp(e.loc, result, a); 1139 if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) 1140 result = result.trySemantic(sc); 1141 else 1142 result = result.expressionSemantic(sc); 1143 if (result) 1144 { 1145 return Expression.combine(e0, result); 1146 } 1147 } 1148 Lfallback: 1149 if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) 1150 { 1151 // Deal with $ 1152 Expression result = resolveOpDollar(sc, ae, ie, &e0); 1153 if (result.op == EXP.error) 1154 return result; 1155 result = e.e2.expressionSemantic(sc); 1156 if (result.op == EXP.error) 1157 return result; 1158 e.e2 = result; 1159 /* Rewrite (a[i..j] op= e2) as: 1160 * a.opSliceOpAssign!(op)(e2, i, j) 1161 */ 1162 auto a = new Expressions(); 1163 a.push(e.e2); 1164 if (ie) 1165 { 1166 a.push(ie.lwr); 1167 a.push(ie.upr); 1168 } 1169 Objects* tiargs = opToArg(sc, e.op); 1170 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); 1171 result = new CallExp(e.loc, result, a); 1172 result = result.expressionSemantic(sc); 1173 result = Expression.combine(e0, result); 1174 return result; 1175 } 1176 // Didn't find it. Forward to aliasthis 1177 if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) 1178 { 1179 /* Rewrite (a[arguments] op= e2) as: 1180 * a.aliasthis[arguments] op= e2 1181 */ 1182 ae.e1 = resolveAliasThis(sc, ae1save, true); 1183 if (ae.e1) 1184 continue; 1185 } 1186 break; 1187 } 1188 ae.e1 = ae1old; // recovery 1189 ae.lengthVar = null; 1190 } 1191 Expression result = e.binSemanticProp(sc); 1192 if (result) 1193 return result; 1194 // Don't attempt 'alias this' if an error occurred 1195 if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) 1196 { 1197 return ErrorExp.get(); 1198 } 1199 Identifier id = opId(e); 1200 Expressions args2; 1201 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1202 Dsymbol s = null; 1203 Objects* tiargs = null; 1204 /* Try opOpAssign 1205 */ 1206 if (ad1) 1207 { 1208 s = search_function(ad1, Id.opOpAssign); 1209 if (s && !s.isTemplateDeclaration()) 1210 { 1211 e.error("`%s.opOpAssign` isn't a template", e.e1.toChars()); 1212 return ErrorExp.get(); 1213 } 1214 } 1215 // Set tiargs, the template argument list, which will be the operator string 1216 if (s) 1217 { 1218 id = Id.opOpAssign; 1219 tiargs = opToArg(sc, e.op); 1220 } 1221 1222 // Try D1-style operator overload, deprecated 1223 if (!s && ad1 && id) 1224 { 1225 s = search_function(ad1, id); 1226 if (s) 1227 { 1228 // @@@DEPRECATED_2.110@@@. 1229 // Deprecated in 2.088, made an error in 2.100 1230 scope char[] op = EXPtoString(e.op).dup; 1231 op[$-1] = '\0'; // remove trailing `=` 1232 e.error("`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); 1233 return ErrorExp.get(); 1234 } 1235 } 1236 1237 if (s) 1238 { 1239 /* Try: 1240 * a.opOpAssign(b) 1241 */ 1242 args2.setDim(1); 1243 args2[0] = e.e2; 1244 expandTuples(&args2); 1245 MatchAccumulator m; 1246 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2)); 1247 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 1248 { 1249 return ErrorExp.get(); 1250 } 1251 if (m.count > 1) 1252 { 1253 // Error, ambiguous 1254 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1255 } 1256 else if (m.last == MATCH.nomatch) 1257 { 1258 if (tiargs) 1259 goto L1; 1260 m.lastf = null; 1261 } 1262 // Rewrite (e1 op e2) as e1.opOpAssign(e2) 1263 return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1264 } 1265 L1: 1266 result = checkAliasThisForLhs(ad1, sc, e); 1267 if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs 1268 return result; 1269 1270 return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1271 } 1272 1273 if (pop) 1274 *pop = e.op; 1275 1276 switch (e.op) 1277 { 1278 case EXP.cast_ : return visitCast(e.isCastExp()); 1279 case EXP.array : return visitArray(e.isArrayExp()); 1280 1281 case EXP.notEqual : 1282 case EXP.equal : return visitEqual(e.isEqualExp()); 1283 1284 case EXP.lessOrEqual : 1285 case EXP.greaterThan : 1286 case EXP.greaterOrEqual: 1287 case EXP.lessThan : return visitCmp(cast(CmpExp)e); 1288 1289 default: 1290 if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex); 1291 if (auto ex = e.isBinExp()) return visitBin(ex); 1292 if (auto ex = e.isUnaExp()) return visitUna(ex); 1293 return visit(e); 1294 } 1295 } 1296 1297 /****************************************** 1298 * Common code for overloading of EqualExp and CmpExp 1299 */ 1300 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop) 1301 { 1302 //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); 1303 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1304 AggregateDeclaration ad2 = isAggregate(e.e2.type); 1305 Dsymbol s = null; 1306 Dsymbol s_r = null; 1307 if (ad1) 1308 { 1309 s = search_function(ad1, id); 1310 } 1311 if (ad2) 1312 { 1313 s_r = search_function(ad2, id); 1314 if (s == s_r) 1315 s_r = null; 1316 } 1317 Objects* tiargs = null; 1318 if (s || s_r) 1319 { 1320 /* Try: 1321 * a.opEquals(b) 1322 * b.opEquals(a) 1323 * and see which is better. 1324 */ 1325 Expressions args1 = Expressions(1); 1326 args1[0] = e.e1; 1327 expandTuples(&args1); 1328 Expressions args2 = Expressions(1); 1329 args2[0] = e.e2; 1330 expandTuples(&args2); 1331 MatchAccumulator m; 1332 if (0 && s && s_r) 1333 { 1334 printf("s : %s\n", s.toPrettyChars()); 1335 printf("s_r: %s\n", s_r.toPrettyChars()); 1336 } 1337 if (s) 1338 { 1339 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(&args2)); 1340 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 1341 return ErrorExp.get(); 1342 } 1343 FuncDeclaration lastf = m.lastf; 1344 int count = m.count; 1345 if (s_r) 1346 { 1347 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(&args1)); 1348 if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) 1349 return ErrorExp.get(); 1350 } 1351 if (m.count > 1) 1352 { 1353 /* The following if says "not ambiguous" if there's one match 1354 * from s and one from s_r, in which case we pick s. 1355 * This doesn't follow the spec, but is a workaround for the case 1356 * where opEquals was generated from templates and we cannot figure 1357 * out if both s and s_r came from the same declaration or not. 1358 * The test case is: 1359 * import std.typecons; 1360 * void main() { 1361 * assert(tuple("has a", 2u) == tuple("has a", 1)); 1362 * } 1363 */ 1364 if (!(m.lastf == lastf && m.count == 2 && count == 1)) 1365 { 1366 // Error, ambiguous 1367 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1368 } 1369 } 1370 else if (m.last == MATCH.nomatch) 1371 { 1372 m.lastf = null; 1373 } 1374 Expression result; 1375 if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) 1376 { 1377 // Rewrite (e1 op e2) as e1.opfunc(e2) 1378 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1379 } 1380 else 1381 { 1382 // Rewrite (e1 op e2) as e2.opfunc_r(e1) 1383 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); 1384 // When reversing operands of comparison operators, 1385 // need to reverse the sense of the op 1386 if (pop) 1387 *pop = reverseRelation(e.op); 1388 } 1389 return result; 1390 } 1391 /* 1392 * https://issues.dlang.org/show_bug.cgi?id=16657 1393 * at this point, no matching opEquals was found for structs, 1394 * so we should not follow the alias this comparison code. 1395 */ 1396 if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2) 1397 return null; 1398 Expression result = checkAliasThisForLhs(ad1, sc, e); 1399 return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1400 } 1401 1402 /*********************************** 1403 * Utility to build a function call out of this reference and argument. 1404 */ 1405 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) 1406 { 1407 assert(d); 1408 Expression e; 1409 Declaration decl = d.isDeclaration(); 1410 if (decl) 1411 e = new DotVarExp(loc, ethis, decl, false); 1412 else 1413 e = new DotIdExp(loc, ethis, d.ident); 1414 e = new CallExp(loc, e, earg); 1415 e = e.expressionSemantic(sc); 1416 return e; 1417 } 1418 1419 /*************************************** 1420 * Search for function funcid in aggregate ad. 1421 */ 1422 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) 1423 { 1424 Dsymbol s = ad.search(Loc.initial, funcid); 1425 if (s) 1426 { 1427 //printf("search_function: s = '%s'\n", s.kind()); 1428 Dsymbol s2 = s.toAlias(); 1429 //printf("search_function: s2 = '%s'\n", s2.kind()); 1430 FuncDeclaration fd = s2.isFuncDeclaration(); 1431 if (fd && fd.type.ty == Tfunction) 1432 return fd; 1433 TemplateDeclaration td = s2.isTemplateDeclaration(); 1434 if (td) 1435 return td; 1436 } 1437 return null; 1438 } 1439 1440 /************************************** 1441 * Figure out what is being foreach'd over by looking at the ForeachAggregate. 1442 * Params: 1443 * sc = context 1444 * isForeach = true for foreach, false for foreach_reverse 1445 * feaggr = ForeachAggregate 1446 * sapply = set to function opApply/opApplyReverse, or delegate, or null. 1447 * Overload resolution is not done. 1448 * Returns: 1449 * true if successfully figured it out; feaggr updated with semantic analysis. 1450 * false for failed, which is an error. 1451 */ 1452 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply) 1453 { 1454 //printf("inferForeachAggregate(%s)\n", feaggr.toChars()); 1455 bool sliced; 1456 Type att = null; 1457 auto aggr = feaggr; 1458 while (1) 1459 { 1460 aggr = aggr.expressionSemantic(sc); 1461 aggr = resolveProperties(sc, aggr); 1462 aggr = aggr.optimize(WANTvalue); 1463 if (!aggr.type || aggr.op == EXP.error) 1464 return false; 1465 Type tab = aggr.type.toBasetype(); 1466 switch (tab.ty) 1467 { 1468 case Tarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1469 case Tsarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1470 case Ttuple: // https://dlang.org/spec/statement.html#foreach_over_tuples 1471 case Taarray: // https://dlang.org/spec/statement.html#foreach_over_associative_arrays 1472 break; 1473 1474 case Tclass: 1475 case Tstruct: 1476 { 1477 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym 1478 : tab.isTypeStruct().sym; 1479 if (!sliced) 1480 { 1481 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse); 1482 if (sapply) 1483 { 1484 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes 1485 // opApply aggregate 1486 break; 1487 } 1488 if (feaggr.op != EXP.type) 1489 { 1490 /* See if rewriting `aggr` to `aggr[]` will work 1491 */ 1492 Expression rinit = new ArrayExp(aggr.loc, feaggr); 1493 rinit = rinit.trySemantic(sc); 1494 if (rinit) // if it worked 1495 { 1496 aggr = rinit; 1497 sliced = true; // only try it once 1498 continue; 1499 } 1500 } 1501 } 1502 if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback)) 1503 { 1504 // https://dlang.org/spec/statement.html#foreach-with-ranges 1505 // range aggregate 1506 break; 1507 } 1508 if (ad.aliasthis) 1509 { 1510 if (isRecursiveAliasThis(att, tab)) // error, circular alias this 1511 return false; 1512 aggr = resolveAliasThis(sc, aggr); 1513 continue; 1514 } 1515 return false; 1516 } 1517 1518 case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates 1519 if (auto de = aggr.isDelegateExp()) 1520 { 1521 sapply = de.func; 1522 } 1523 break; 1524 1525 case Terror: 1526 break; 1527 1528 default: 1529 return false; 1530 } 1531 feaggr = aggr; 1532 return true; 1533 } 1534 assert(0); 1535 } 1536 1537 /***************************************** 1538 * Given array of foreach parameters and an aggregate type, 1539 * find best opApply overload, 1540 * if any of the parameter types are missing, attempt to infer 1541 * them from the aggregate type. 1542 * Params: 1543 * fes = the foreach statement 1544 * sc = context 1545 * sapply = null or opApply or delegate, overload resolution has not been done. 1546 * Do overload resolution on sapply. 1547 * Returns: 1548 * false for errors 1549 */ 1550 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) 1551 { 1552 if (!fes.parameters || !fes.parameters.length) 1553 return false; 1554 if (sapply) // prefer opApply 1555 { 1556 foreach (Parameter p; *fes.parameters) 1557 { 1558 if (p.type) 1559 { 1560 p.type = p.type.typeSemantic(fes.loc, sc); 1561 p.type = p.type.addStorageClass(p.storageClass); 1562 } 1563 } 1564 1565 // Determine ethis for sapply 1566 Expression ethis; 1567 Type tab = fes.aggr.type.toBasetype(); 1568 if (tab.ty == Tclass || tab.ty == Tstruct) 1569 ethis = fes.aggr; 1570 else 1571 { 1572 assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_); 1573 ethis = fes.aggr.isDelegateExp().e1; 1574 } 1575 1576 /* Look for like an 1577 * int opApply(int delegate(ref Type [, ...]) dg); 1578 * overload 1579 */ 1580 if (FuncDeclaration fd = sapply.isFuncDeclaration()) 1581 { 1582 if (auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters)) 1583 { 1584 // Fill in any missing types on foreach parameters[] 1585 matchParamsToOpApply(fdapply.type.isTypeFunction(), fes.parameters, true); 1586 sapply = fdapply; 1587 return true; 1588 } 1589 return false; 1590 } 1591 return true; // shouldn't this be false? 1592 } 1593 1594 Parameter p = (*fes.parameters)[0]; 1595 Type taggr = fes.aggr.type; 1596 assert(taggr); 1597 Type tab = taggr.toBasetype(); 1598 switch (tab.ty) 1599 { 1600 case Tarray: 1601 case Tsarray: 1602 case Ttuple: 1603 if (fes.parameters.length == 2) 1604 { 1605 if (!p.type) 1606 { 1607 p.type = Type.tsize_t; // key type 1608 p.type = p.type.addStorageClass(p.storageClass); 1609 } 1610 p = (*fes.parameters)[1]; 1611 } 1612 if (!p.type && tab.ty != Ttuple) 1613 { 1614 p.type = tab.nextOf(); // value type 1615 p.type = p.type.addStorageClass(p.storageClass); 1616 } 1617 break; 1618 1619 case Taarray: 1620 { 1621 TypeAArray taa = tab.isTypeAArray(); 1622 if (fes.parameters.length == 2) 1623 { 1624 if (!p.type) 1625 { 1626 p.type = taa.index; // key type 1627 p.type = p.type.addStorageClass(p.storageClass); 1628 if (p.storageClass & STC.ref_) // key must not be mutated via ref 1629 p.type = p.type.addMod(MODFlags.const_); 1630 } 1631 p = (*fes.parameters)[1]; 1632 } 1633 if (!p.type) 1634 { 1635 p.type = taa.next; // value type 1636 p.type = p.type.addStorageClass(p.storageClass); 1637 } 1638 break; 1639 } 1640 1641 case Tclass: 1642 case Tstruct: 1643 { 1644 AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym 1645 : tab.isTypeStruct().sym; 1646 if (fes.parameters.length == 1) 1647 { 1648 if (!p.type) 1649 { 1650 /* Look for a front() or back() overload 1651 */ 1652 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback; 1653 Dsymbol s = ad.search(Loc.initial, id); 1654 FuncDeclaration fd = s ? s.isFuncDeclaration() : null; 1655 if (fd) 1656 { 1657 // Resolve inout qualifier of front type 1658 p.type = fd.type.nextOf(); 1659 if (p.type) 1660 { 1661 p.type = p.type.substWildTo(tab.mod); 1662 p.type = p.type.addStorageClass(p.storageClass); 1663 } 1664 } 1665 else if (s && s.isTemplateDeclaration()) 1666 { 1667 } 1668 else if (s && s.isDeclaration()) 1669 p.type = s.isDeclaration().type; 1670 else 1671 break; 1672 } 1673 break; 1674 } 1675 break; 1676 } 1677 1678 case Tdelegate: 1679 { 1680 auto td = tab.isTypeDelegate(); 1681 if (!matchParamsToOpApply(td.next.isTypeFunction(), fes.parameters, true)) 1682 return false; 1683 break; 1684 } 1685 1686 default: 1687 break; // ignore error, caught later 1688 } 1689 return true; 1690 } 1691 1692 /********************************************* 1693 * Find best overload match on fstart given ethis and parameters[]. 1694 * Params: 1695 * ethis = expression to use for `this` 1696 * fstart = opApply or foreach delegate 1697 * parameters = ForeachTypeList (i.e. foreach parameters) 1698 * Returns: 1699 * best match if there is one, null if error 1700 */ 1701 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters) 1702 { 1703 MOD mod = ethis.type.mod; 1704 MATCH match = MATCH.nomatch; 1705 FuncDeclaration fd_best; 1706 FuncDeclaration fd_ambig; 1707 1708 overloadApply(fstart, (Dsymbol s) 1709 { 1710 auto f = s.isFuncDeclaration(); 1711 if (!f) 1712 return 0; // continue 1713 auto tf = f.type.isTypeFunction(); 1714 MATCH m = MATCH.exact; 1715 if (f.isThis()) 1716 { 1717 if (!MODimplicitConv(mod, tf.mod)) 1718 m = MATCH.nomatch; 1719 else if (mod != tf.mod) 1720 m = MATCH.constant; 1721 } 1722 if (!matchParamsToOpApply(tf, parameters, false)) 1723 m = MATCH.nomatch; 1724 if (m > match) 1725 { 1726 fd_best = f; 1727 fd_ambig = null; 1728 match = m; 1729 } 1730 else if (m == match && m > MATCH.nomatch) 1731 { 1732 assert(fd_best); 1733 auto bestTf = fd_best.type.isTypeFunction(); 1734 assert(bestTf); 1735 1736 // Found another overload with different attributes? 1737 // e.g. @system vs. @safe opApply 1738 // @@@DEPRECATED_2.112@@@ 1739 // See semantic2.d Semantic2Visitor.visit(FuncDeclaration): 1740 // Remove `false` after deprecation period is over. 1741 bool ambig = tf.attributesEqual(bestTf, false); 1742 1743 // opApplies with identical attributes could still accept 1744 // different function bodies as delegate 1745 // => different parameters or attributes 1746 if (ambig) 1747 { 1748 // Fetch the delegates that receive the function body 1749 auto tfBody = tf.parameterList[0].type.isTypeDelegate().next; 1750 assert(tfBody); 1751 1752 auto bestBody = bestTf.parameterList[0].type.isTypeDelegate().next; 1753 assert(bestBody); 1754 1755 // Ignore covariant matches, as later on it can be redone 1756 // after the opApply delegate has its attributes inferred. 1757 ambig = !(tfBody.covariant(bestBody) == Covariant.yes || bestBody.covariant(tfBody) == Covariant.yes); 1758 } 1759 1760 if (ambig) 1761 fd_ambig = f; // not covariant, so ambiguous 1762 } 1763 return 0; // continue 1764 }); 1765 1766 if (fd_ambig) 1767 { 1768 .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`: `%s`\nand:\n`%s`: `%s`", 1769 ethis.toChars(), fstart.ident.toChars(), 1770 fd_best.loc.toChars(), fd_best.type.toChars(), 1771 fd_ambig.loc.toChars(), fd_ambig.type.toChars()); 1772 return null; 1773 } 1774 1775 return fd_best; 1776 } 1777 1778 /****************************** 1779 * Determine if foreach parameters match opApply parameters. 1780 * Infer missing foreach parameter types from type of opApply delegate. 1781 * Params: 1782 * tf = type of opApply or delegate 1783 * parameters = foreach parameters 1784 * infer = infer missing parameter types 1785 * Returns: 1786 * true for match for this function 1787 * false for no match for this function 1788 */ 1789 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer) 1790 { 1791 enum nomatch = false; 1792 1793 /* opApply/delegate has exactly one parameter, and that parameter 1794 * is a delegate that looks like: 1795 * int opApply(int delegate(ref Type [, ...]) dg); 1796 */ 1797 if (tf.parameterList.length != 1) 1798 return nomatch; 1799 1800 /* Get the type of opApply's dg parameter 1801 */ 1802 Parameter p0 = tf.parameterList[0]; 1803 auto de = p0.type.isTypeDelegate(); 1804 if (!de) 1805 return nomatch; 1806 TypeFunction tdg = de.next.isTypeFunction(); 1807 1808 /* We now have tdg, the type of the delegate. 1809 * tdg's parameters must match that of the foreach arglist (i.e. parameters). 1810 * Fill in missing types in parameters. 1811 */ 1812 const nparams = tdg.parameterList.length; 1813 if (nparams == 0 || nparams != parameters.length || tdg.parameterList.varargs != VarArg.none) 1814 return nomatch; // parameter mismatch 1815 1816 foreach (u, p; *parameters) 1817 { 1818 Parameter param = tdg.parameterList[u]; 1819 if (p.type) 1820 { 1821 if (!p.type.equals(param.type)) 1822 return nomatch; 1823 } 1824 else if (infer) 1825 { 1826 p.type = param.type; 1827 p.type = p.type.addStorageClass(p.storageClass); 1828 } 1829 } 1830 return true; 1831 } 1832 1833 /** 1834 * Reverse relational operator, eg >= becomes <= 1835 * Note this is not negation. 1836 * Params: 1837 * op = comparison operator to reverse 1838 * Returns: 1839 * reverse of op 1840 */ 1841 private EXP reverseRelation(EXP op) pure 1842 { 1843 switch (op) 1844 { 1845 case EXP.greaterOrEqual: op = EXP.lessOrEqual; break; 1846 case EXP.greaterThan: op = EXP.lessThan; break; 1847 case EXP.lessOrEqual: op = EXP.greaterOrEqual; break; 1848 case EXP.lessThan: op = EXP.greaterThan; break; 1849 default: break; 1850 } 1851 return op; 1852 }