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