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 * 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.098@@@. 417 // Deprecated in 2.088 418 // Make an error in 2.098 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 = ErrorExp.get(); 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 = ErrorExp.get(); 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.098@@@. 688 // Deprecated in 2.088 689 // Make an error in 2.098 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.098@@@. 707 // Deprecated in 2.088 708 // Make an error in 2.098 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 = ErrorExp.get(); 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 = ErrorExp.get(); 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 = ErrorExp.get(); 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 = ErrorExp.get(); 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 /* Array equality is handled by expressionSemantic() potentially 921 * lowering to object.__equals(), which takes care of overloaded 922 * operators for the element types. 923 */ 924 if ((t1.ty == Tarray || t1.ty == Tsarray) && 925 (t2.ty == Tarray || t2.ty == Tsarray)) 926 { 927 return; 928 } 929 930 /* Check for class equality with null literal or typeof(null). 931 */ 932 if (t1.ty == Tclass && e.e2.op == TOK.null_ || 933 t2.ty == Tclass && e.e1.op == TOK.null_) 934 { 935 e.error("use `%s` instead of `%s` when comparing with `null`", 936 Token.toChars(e.op == TOK.equal ? TOK.identity : TOK.notIdentity), 937 Token.toChars(e.op)); 938 result = ErrorExp.get(); 939 return; 940 } 941 if (t1.ty == Tclass && t2.ty == Tnull || 942 t1.ty == Tnull && t2.ty == Tclass) 943 { 944 // Comparing a class with typeof(null) should not call opEquals 945 return; 946 } 947 948 /* Check for class equality. 949 */ 950 if (t1.ty == Tclass && t2.ty == Tclass) 951 { 952 ClassDeclaration cd1 = t1.isClassHandle(); 953 ClassDeclaration cd2 = t2.isClassHandle(); 954 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) 955 { 956 /* Rewrite as: 957 * .object.opEquals(e1, e2) 958 */ 959 Expression e1x = e.e1; 960 Expression e2x = e.e2; 961 962 /* The explicit cast is necessary for interfaces 963 * https://issues.dlang.org/show_bug.cgi?id=4088 964 */ 965 Type to = ClassDeclaration.object.getType(); 966 if (cd1.isInterfaceDeclaration()) 967 e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); 968 if (cd2.isInterfaceDeclaration()) 969 e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); 970 971 result = new IdentifierExp(e.loc, Id.empty); 972 result = new DotIdExp(e.loc, result, Id.object); 973 result = new DotIdExp(e.loc, result, Id.eq); 974 result = new CallExp(e.loc, result, e1x, e2x); 975 if (e.op == TOK.notEqual) 976 result = new NotExp(e.loc, result); 977 result = result.expressionSemantic(sc); 978 return; 979 } 980 } 981 982 result = compare_overload(e, sc, Id.eq, null); 983 if (result) 984 { 985 if (result.op == TOK.call && e.op == TOK.notEqual) 986 { 987 result = new NotExp(result.loc, result); 988 result = result.expressionSemantic(sc); 989 } 990 return; 991 } 992 993 /* Check for pointer equality. 994 */ 995 if (t1.ty == Tpointer || t2.ty == Tpointer) 996 { 997 /* Rewrite: 998 * ptr1 == ptr2 999 * as: 1000 * ptr1 is ptr2 1001 * 1002 * This is just a rewriting for deterministic AST representation 1003 * as the backend input. 1004 */ 1005 auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity; 1006 result = new IdentityExp(op2, e.loc, e.e1, e.e2); 1007 result = result.expressionSemantic(sc); 1008 return; 1009 } 1010 1011 /* Check for struct equality without opEquals. 1012 */ 1013 if (t1.ty == Tstruct && t2.ty == Tstruct) 1014 { 1015 auto sd = (cast(TypeStruct)t1).sym; 1016 if (sd != (cast(TypeStruct)t2).sym) 1017 return; 1018 1019 import dmd.clone : needOpEquals; 1020 if (!global.params.fieldwise && !needOpEquals(sd)) 1021 { 1022 // Use bitwise equality. 1023 auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity; 1024 result = new IdentityExp(op2, e.loc, e.e1, e.e2); 1025 result = result.expressionSemantic(sc); 1026 return; 1027 } 1028 1029 /* Do memberwise equality. 1030 * https://dlang.org/spec/expression.html#equality_expressions 1031 * Rewrite: 1032 * e1 == e2 1033 * as: 1034 * e1.tupleof == e2.tupleof 1035 * 1036 * If sd is a nested struct, and if it's nested in a class, it will 1037 * also compare the parent class's equality. Otherwise, compares 1038 * the identity of parent context through void*. 1039 */ 1040 if (e.att1 && t1 == e.att1) return; 1041 if (e.att2 && t2 == e.att2) return; 1042 1043 e = cast(EqualExp)e.copy(); 1044 if (!e.att1) e.att1 = t1; 1045 if (!e.att2) e.att2 = t2; 1046 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); 1047 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); 1048 1049 auto sc2 = sc.push(); 1050 sc2.flags = (sc2.flags & ~SCOPE.onlysafeaccess) | SCOPE.noaccesscheck; 1051 result = e.expressionSemantic(sc2); 1052 sc2.pop(); 1053 1054 /* https://issues.dlang.org/show_bug.cgi?id=15292 1055 * if the rewrite result is same with the original, 1056 * the equality is unresolvable because it has recursive definition. 1057 */ 1058 if (result.op == e.op && 1059 (cast(EqualExp)result).e1.type.toBasetype() == t1) 1060 { 1061 e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition", 1062 t1.toChars()); 1063 result = ErrorExp.get(); 1064 } 1065 return; 1066 } 1067 1068 /* Check for tuple equality. 1069 */ 1070 if (e.e1.op == TOK.tuple && e.e2.op == TOK.tuple) 1071 { 1072 auto tup1 = cast(TupleExp)e.e1; 1073 auto tup2 = cast(TupleExp)e.e2; 1074 size_t dim = tup1.exps.dim; 1075 if (dim != tup2.exps.dim) 1076 { 1077 e.error("mismatched tuple lengths, `%d` and `%d`", 1078 cast(int)dim, cast(int)tup2.exps.dim); 1079 result = ErrorExp.get(); 1080 return; 1081 } 1082 1083 if (dim == 0) 1084 { 1085 // zero-length tuple comparison should always return true or false. 1086 result = IntegerExp.createBool(e.op == TOK.equal); 1087 } 1088 else 1089 { 1090 for (size_t i = 0; i < dim; i++) 1091 { 1092 auto ex1 = (*tup1.exps)[i]; 1093 auto ex2 = (*tup2.exps)[i]; 1094 auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); 1095 eeq.att1 = e.att1; 1096 eeq.att2 = e.att2; 1097 1098 if (!result) 1099 result = eeq; 1100 else if (e.op == TOK.equal) 1101 result = new LogicalExp(e.loc, TOK.andAnd, result, eeq); 1102 else 1103 result = new LogicalExp(e.loc, TOK.orOr, result, eeq); 1104 } 1105 assert(result); 1106 } 1107 result = Expression.combine(tup1.e0, tup2.e0, result); 1108 result = result.expressionSemantic(sc); 1109 1110 return; 1111 } 1112 } 1113 1114 override void visit(CmpExp e) 1115 { 1116 //printf("CmpExp:: () (%s)\n", e.toChars()); 1117 result = compare_overload(e, sc, Id.cmp, pop); 1118 } 1119 1120 /********************************* 1121 * Operator overloading for op= 1122 */ 1123 override void visit(BinAssignExp e) 1124 { 1125 //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); 1126 if (e.e1.op == TOK.array) 1127 { 1128 ArrayExp ae = cast(ArrayExp)e.e1; 1129 ae.e1 = ae.e1.expressionSemantic(sc); 1130 ae.e1 = resolveProperties(sc, ae.e1); 1131 Expression ae1old = ae.e1; 1132 const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval); 1133 IntervalExp ie = null; 1134 if (maybeSlice && ae.arguments.dim) 1135 { 1136 assert((*ae.arguments)[0].op == TOK.interval); 1137 ie = cast(IntervalExp)(*ae.arguments)[0]; 1138 } 1139 while (true) 1140 { 1141 if (ae.e1.op == TOK.error) 1142 { 1143 result = ae.e1; 1144 return; 1145 } 1146 Expression e0 = null; 1147 Expression ae1save = ae.e1; 1148 ae.lengthVar = null; 1149 Type t1b = ae.e1.type.toBasetype(); 1150 AggregateDeclaration ad = isAggregate(t1b); 1151 if (!ad) 1152 break; 1153 if (search_function(ad, Id.opIndexOpAssign)) 1154 { 1155 // Deal with $ 1156 result = resolveOpDollar(sc, ae, &e0); 1157 if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) 1158 goto Lfallback; 1159 if (result.op == TOK.error) 1160 return; 1161 result = e.e2.expressionSemantic(sc); 1162 if (result.op == TOK.error) 1163 return; 1164 e.e2 = result; 1165 /* Rewrite a[arguments] op= e2 as: 1166 * a.opIndexOpAssign!(op)(e2, arguments) 1167 */ 1168 Expressions* a = ae.arguments.copy(); 1169 a.insert(0, e.e2); 1170 Objects* tiargs = opToArg(sc, e.op); 1171 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); 1172 result = new CallExp(e.loc, result, a); 1173 if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) 1174 result = result.trySemantic(sc); 1175 else 1176 result = result.expressionSemantic(sc); 1177 if (result) 1178 { 1179 result = Expression.combine(e0, result); 1180 return; 1181 } 1182 } 1183 Lfallback: 1184 if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) 1185 { 1186 // Deal with $ 1187 result = resolveOpDollar(sc, ae, ie, &e0); 1188 if (result.op == TOK.error) 1189 return; 1190 result = e.e2.expressionSemantic(sc); 1191 if (result.op == TOK.error) 1192 return; 1193 e.e2 = result; 1194 /* Rewrite (a[i..j] op= e2) as: 1195 * a.opSliceOpAssign!(op)(e2, i, j) 1196 */ 1197 auto a = new Expressions(); 1198 a.push(e.e2); 1199 if (ie) 1200 { 1201 a.push(ie.lwr); 1202 a.push(ie.upr); 1203 } 1204 Objects* tiargs = opToArg(sc, e.op); 1205 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); 1206 result = new CallExp(e.loc, result, a); 1207 result = result.expressionSemantic(sc); 1208 result = Expression.combine(e0, result); 1209 return; 1210 } 1211 // Didn't find it. Forward to aliasthis 1212 if (ad.aliasthis && t1b != ae.att1) 1213 { 1214 if (!ae.att1 && t1b.checkAliasThisRec()) 1215 ae.att1 = t1b; 1216 /* Rewrite (a[arguments] op= e2) as: 1217 * a.aliasthis[arguments] op= e2 1218 */ 1219 ae.e1 = resolveAliasThis(sc, ae1save, true); 1220 if (ae.e1) 1221 continue; 1222 } 1223 break; 1224 } 1225 ae.e1 = ae1old; // recovery 1226 ae.lengthVar = null; 1227 } 1228 result = e.binSemanticProp(sc); 1229 if (result) 1230 return; 1231 // Don't attempt 'alias this' if an error occurred 1232 if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) 1233 { 1234 result = ErrorExp.get(); 1235 return; 1236 } 1237 Identifier id = opId(e); 1238 Expressions args2; 1239 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1240 Dsymbol s = null; 1241 Objects* tiargs = null; 1242 /* Try opOpAssign 1243 */ 1244 if (ad1) 1245 { 1246 s = search_function(ad1, Id.opOpAssign); 1247 if (s && !s.isTemplateDeclaration()) 1248 { 1249 e.error("`%s.opOpAssign` isn't a template", e.e1.toChars()); 1250 result = ErrorExp.get(); 1251 return; 1252 } 1253 } 1254 // Set tiargs, the template argument list, which will be the operator string 1255 if (s) 1256 { 1257 id = Id.opOpAssign; 1258 tiargs = opToArg(sc, e.op); 1259 } 1260 1261 // Try D1-style operator overload, deprecated 1262 if (!s && ad1 && id) 1263 { 1264 s = search_function(ad1, id); 1265 if (s) 1266 { 1267 // @@@DEPRECATED_2.098@@@. 1268 // Deprecated in 2.088 1269 // Make an error in 2.098 1270 scope char[] op = Token.toString(e.op).dup; 1271 op[$-1] = '\0'; // remove trailing `=` 1272 e.deprecation("`%s` is deprecated. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); 1273 } 1274 } 1275 1276 if (s) 1277 { 1278 /* Try: 1279 * a.opOpAssign(b) 1280 */ 1281 args2.setDim(1); 1282 args2[0] = e.e2; 1283 expandTuples(&args2); 1284 MatchAccumulator m; 1285 if (s) 1286 { 1287 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2); 1288 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1289 { 1290 result = ErrorExp.get(); 1291 return; 1292 } 1293 } 1294 if (m.count > 1) 1295 { 1296 // Error, ambiguous 1297 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1298 } 1299 else if (m.last <= MATCH.nomatch) 1300 { 1301 if (tiargs) 1302 goto L1; 1303 m.lastf = null; 1304 } 1305 // Rewrite (e1 op e2) as e1.opOpAssign(e2) 1306 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1307 return; 1308 } 1309 L1: 1310 result = checkAliasThisForLhs(ad1, sc, e); 1311 if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs 1312 return; 1313 1314 result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1315 } 1316 } 1317 1318 if (pop) 1319 *pop = e.op; 1320 scope OpOverload v = new OpOverload(sc, pop); 1321 e.accept(v); 1322 return v.result; 1323 } 1324 1325 /****************************************** 1326 * Common code for overloading of EqualExp and CmpExp 1327 */ 1328 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, TOK* pop) 1329 { 1330 //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); 1331 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1332 AggregateDeclaration ad2 = isAggregate(e.e2.type); 1333 Dsymbol s = null; 1334 Dsymbol s_r = null; 1335 if (ad1) 1336 { 1337 s = search_function(ad1, id); 1338 } 1339 if (ad2) 1340 { 1341 s_r = search_function(ad2, id); 1342 if (s == s_r) 1343 s_r = null; 1344 } 1345 Objects* tiargs = null; 1346 if (s || s_r) 1347 { 1348 /* Try: 1349 * a.opEquals(b) 1350 * b.opEquals(a) 1351 * and see which is better. 1352 */ 1353 Expressions args1 = Expressions(1); 1354 args1[0] = e.e1; 1355 expandTuples(&args1); 1356 Expressions args2 = Expressions(1); 1357 args2[0] = e.e2; 1358 expandTuples(&args2); 1359 MatchAccumulator m; 1360 if (0 && s && s_r) 1361 { 1362 printf("s : %s\n", s.toPrettyChars()); 1363 printf("s_r: %s\n", s_r.toPrettyChars()); 1364 } 1365 if (s) 1366 { 1367 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2); 1368 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1369 return ErrorExp.get(); 1370 } 1371 FuncDeclaration lastf = m.lastf; 1372 int count = m.count; 1373 if (s_r) 1374 { 1375 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1); 1376 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1377 return ErrorExp.get(); 1378 } 1379 if (m.count > 1) 1380 { 1381 /* The following if says "not ambiguous" if there's one match 1382 * from s and one from s_r, in which case we pick s. 1383 * This doesn't follow the spec, but is a workaround for the case 1384 * where opEquals was generated from templates and we cannot figure 1385 * out if both s and s_r came from the same declaration or not. 1386 * The test case is: 1387 * import std.typecons; 1388 * void main() { 1389 * assert(tuple("has a", 2u) == tuple("has a", 1)); 1390 * } 1391 */ 1392 if (!(m.lastf == lastf && m.count == 2 && count == 1)) 1393 { 1394 // Error, ambiguous 1395 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1396 } 1397 } 1398 else if (m.last <= MATCH.nomatch) 1399 { 1400 m.lastf = null; 1401 } 1402 Expression result; 1403 if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch) 1404 { 1405 // Rewrite (e1 op e2) as e1.opfunc(e2) 1406 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1407 } 1408 else 1409 { 1410 // Rewrite (e1 op e2) as e2.opfunc_r(e1) 1411 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); 1412 // When reversing operands of comparison operators, 1413 // need to reverse the sense of the op 1414 if (pop) 1415 *pop = reverseRelation(e.op); 1416 } 1417 return result; 1418 } 1419 /* 1420 * https://issues.dlang.org/show_bug.cgi?id=16657 1421 * at this point, no matching opEquals was found for structs, 1422 * so we should not follow the alias this comparison code. 1423 */ 1424 if ((e.op == TOK.equal || e.op == TOK.notEqual) && ad1 == ad2) 1425 return null; 1426 Expression result = checkAliasThisForLhs(ad1, sc, e); 1427 return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1428 } 1429 1430 /*********************************** 1431 * Utility to build a function call out of this reference and argument. 1432 */ 1433 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) 1434 { 1435 assert(d); 1436 Expression e; 1437 Declaration decl = d.isDeclaration(); 1438 if (decl) 1439 e = new DotVarExp(loc, ethis, decl, false); 1440 else 1441 e = new DotIdExp(loc, ethis, d.ident); 1442 e = new CallExp(loc, e, earg); 1443 e = e.expressionSemantic(sc); 1444 return e; 1445 } 1446 1447 /*************************************** 1448 * Search for function funcid in aggregate ad. 1449 */ 1450 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) 1451 { 1452 Dsymbol s = ad.search(Loc.initial, funcid); 1453 if (s) 1454 { 1455 //printf("search_function: s = '%s'\n", s.kind()); 1456 Dsymbol s2 = s.toAlias(); 1457 //printf("search_function: s2 = '%s'\n", s2.kind()); 1458 FuncDeclaration fd = s2.isFuncDeclaration(); 1459 if (fd && fd.type.ty == Tfunction) 1460 return fd; 1461 TemplateDeclaration td = s2.isTemplateDeclaration(); 1462 if (td) 1463 return td; 1464 } 1465 return null; 1466 } 1467 1468 /************************************** 1469 * Figure out what is being foreach'd over by looking at the ForeachAggregate. 1470 * Params: 1471 * sc = context 1472 * isForeach = true for foreach, false for foreach_reverse 1473 * feaggr = ForeachAggregate 1474 * sapply = set to function opApply/opApplyReverse, or delegate, or null. 1475 * Overload resolution is not done. 1476 * Returns: 1477 * true if successfully figured it out; feaggr updated with semantic analysis. 1478 * false for failed, which is an error. 1479 */ 1480 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply) 1481 { 1482 //printf("inferForeachAggregate(%s)\n", feaggr.toChars()); 1483 bool sliced; 1484 Type att = null; 1485 auto aggr = feaggr; 1486 while (1) 1487 { 1488 aggr = aggr.expressionSemantic(sc); 1489 aggr = resolveProperties(sc, aggr); 1490 aggr = aggr.optimize(WANTvalue); 1491 if (!aggr.type || aggr.op == TOK.error) 1492 return false; 1493 Type tab = aggr.type.toBasetype(); 1494 switch (tab.ty) 1495 { 1496 case Tarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1497 case Tsarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1498 case Ttuple: // https://dlang.org/spec/statement.html#foreach_over_tuples 1499 case Taarray: // https://dlang.org/spec/statement.html#foreach_over_associative_arrays 1500 break; 1501 1502 case Tclass: 1503 case Tstruct: 1504 { 1505 AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym 1506 : (cast(TypeStruct)tab).sym; 1507 if (!sliced) 1508 { 1509 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse); 1510 if (sapply) 1511 { 1512 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes 1513 // opApply aggregate 1514 break; 1515 } 1516 if (feaggr.op != TOK.type) 1517 { 1518 /* See if rewriting `aggr` to `aggr[]` will work 1519 */ 1520 Expression rinit = new ArrayExp(aggr.loc, feaggr); 1521 rinit = rinit.trySemantic(sc); 1522 if (rinit) // if it worked 1523 { 1524 aggr = rinit; 1525 sliced = true; // only try it once 1526 continue; 1527 } 1528 } 1529 } 1530 if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback)) 1531 { 1532 // https://dlang.org/spec/statement.html#foreach-with-ranges 1533 // range aggregate 1534 break; 1535 } 1536 if (ad.aliasthis) 1537 { 1538 if (att == tab) // error, circular alias this 1539 return false; 1540 if (!att && tab.checkAliasThisRec()) 1541 att = tab; 1542 aggr = resolveAliasThis(sc, aggr); 1543 continue; 1544 } 1545 return false; 1546 } 1547 1548 case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates 1549 if (aggr.op == TOK.delegate_) 1550 { 1551 sapply = (cast(DelegateExp)aggr).func; 1552 } 1553 break; 1554 1555 case Terror: 1556 break; 1557 1558 default: 1559 return false; 1560 } 1561 feaggr = aggr; 1562 return true; 1563 } 1564 assert(0); 1565 } 1566 1567 /***************************************** 1568 * Given array of foreach parameters and an aggregate type, 1569 * find best opApply overload, 1570 * if any of the parameter types are missing, attempt to infer 1571 * them from the aggregate type. 1572 * Params: 1573 * fes = the foreach statement 1574 * sc = context 1575 * sapply = null or opApply or delegate 1576 * Returns: 1577 * false for errors 1578 */ 1579 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) 1580 { 1581 if (!fes.parameters || !fes.parameters.dim) 1582 return false; 1583 if (sapply) // prefer opApply 1584 { 1585 foreach (Parameter p; *fes.parameters) 1586 { 1587 if (p.type) 1588 { 1589 p.type = p.type.typeSemantic(fes.loc, sc); 1590 p.type = p.type.addStorageClass(p.storageClass); 1591 } 1592 } 1593 1594 // Determine ethis for sapply 1595 Expression ethis; 1596 Type tab = fes.aggr.type.toBasetype(); 1597 if (tab.ty == Tclass || tab.ty == Tstruct) 1598 ethis = fes.aggr; 1599 else 1600 { 1601 assert(tab.ty == Tdelegate && fes.aggr.op == TOK.delegate_); 1602 ethis = (cast(DelegateExp)fes.aggr).e1; 1603 } 1604 1605 /* Look for like an 1606 * int opApply(int delegate(ref Type [, ...]) dg); 1607 * overload 1608 */ 1609 if (FuncDeclaration fd = sapply.isFuncDeclaration()) 1610 { 1611 auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters); 1612 if (fdapply) 1613 { 1614 // Fill in any missing types on foreach parameters[] 1615 matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true); 1616 sapply = fdapply; 1617 return true; 1618 } 1619 return false; 1620 } 1621 return sapply !is null; 1622 } 1623 1624 Parameter p = (*fes.parameters)[0]; 1625 Type taggr = fes.aggr.type; 1626 assert(taggr); 1627 Type tab = taggr.toBasetype(); 1628 switch (tab.ty) 1629 { 1630 case Tarray: 1631 case Tsarray: 1632 case Ttuple: 1633 if (fes.parameters.dim == 2) 1634 { 1635 if (!p.type) 1636 { 1637 p.type = Type.tsize_t; // key type 1638 p.type = p.type.addStorageClass(p.storageClass); 1639 } 1640 p = (*fes.parameters)[1]; 1641 } 1642 if (!p.type && tab.ty != Ttuple) 1643 { 1644 p.type = tab.nextOf(); // value type 1645 p.type = p.type.addStorageClass(p.storageClass); 1646 } 1647 break; 1648 1649 case Taarray: 1650 { 1651 TypeAArray taa = cast(TypeAArray)tab; 1652 if (fes.parameters.dim == 2) 1653 { 1654 if (!p.type) 1655 { 1656 p.type = taa.index; // key type 1657 p.type = p.type.addStorageClass(p.storageClass); 1658 if (p.storageClass & STC.ref_) // key must not be mutated via ref 1659 p.type = p.type.addMod(MODFlags.const_); 1660 } 1661 p = (*fes.parameters)[1]; 1662 } 1663 if (!p.type) 1664 { 1665 p.type = taa.next; // value type 1666 p.type = p.type.addStorageClass(p.storageClass); 1667 } 1668 break; 1669 } 1670 1671 case Tclass: 1672 case Tstruct: 1673 { 1674 AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym 1675 : (cast(TypeStruct)tab).sym; 1676 if (fes.parameters.dim == 1) 1677 { 1678 if (!p.type) 1679 { 1680 /* Look for a front() or back() overload 1681 */ 1682 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback; 1683 Dsymbol s = ad.search(Loc.initial, id); 1684 FuncDeclaration fd = s ? s.isFuncDeclaration() : null; 1685 if (fd) 1686 { 1687 // Resolve inout qualifier of front type 1688 p.type = fd.type.nextOf(); 1689 if (p.type) 1690 { 1691 p.type = p.type.substWildTo(tab.mod); 1692 p.type = p.type.addStorageClass(p.storageClass); 1693 } 1694 } 1695 else if (s && s.isTemplateDeclaration()) 1696 { 1697 } 1698 else if (s && s.isDeclaration()) 1699 p.type = (cast(Declaration)s).type; 1700 else 1701 break; 1702 } 1703 break; 1704 } 1705 break; 1706 } 1707 1708 case Tdelegate: 1709 if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true)) 1710 return false; 1711 break; 1712 1713 default: 1714 break; // ignore error, caught later 1715 } 1716 return true; 1717 } 1718 1719 /********************************************* 1720 * Find best overload match on fstart given ethis and parameters[]. 1721 * Params: 1722 * ethis = expression to use for `this` 1723 * fstart = opApply or foreach delegate 1724 * parameters = ForeachTypeList (i.e. foreach parameters) 1725 * Returns: 1726 * best match if there is one, null if error 1727 */ 1728 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters) 1729 { 1730 MOD mod = ethis.type.mod; 1731 MATCH match = MATCH.nomatch; 1732 FuncDeclaration fd_best; 1733 FuncDeclaration fd_ambig; 1734 1735 overloadApply(fstart, (Dsymbol s) 1736 { 1737 auto f = s.isFuncDeclaration(); 1738 if (!f) 1739 return 0; // continue 1740 auto tf = cast(TypeFunction)f.type; 1741 MATCH m = MATCH.exact; 1742 if (f.isThis()) 1743 { 1744 if (!MODimplicitConv(mod, tf.mod)) 1745 m = MATCH.nomatch; 1746 else if (mod != tf.mod) 1747 m = MATCH.constant; 1748 } 1749 if (!matchParamsToOpApply(tf, parameters, false)) 1750 m = MATCH.nomatch; 1751 if (m > match) 1752 { 1753 fd_best = f; 1754 fd_ambig = null; 1755 match = m; 1756 } 1757 else if (m == match && m > MATCH.nomatch) 1758 { 1759 assert(fd_best); 1760 /* Ignore covariant matches, as later on it can be redone 1761 * after the opApply delegate has its attributes inferred. 1762 */ 1763 if (tf.covariant(fd_best.type) != 1 && 1764 fd_best.type.covariant(tf) != 1) 1765 fd_ambig = f; // not covariant, so ambiguous 1766 } 1767 return 0; // continue 1768 }); 1769 1770 if (fd_ambig) 1771 { 1772 .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`: `%s`\nand:\n`%s`: `%s`", 1773 ethis.toChars(), fstart.ident.toChars(), 1774 fd_best.loc.toChars(), fd_best.type.toChars(), 1775 fd_ambig.loc.toChars(), fd_ambig.type.toChars()); 1776 return null; 1777 } 1778 1779 return fd_best; 1780 } 1781 1782 /****************************** 1783 * Determine if foreach parameters match opApply parameters. 1784 * Infer missing foreach parameter types from type of opApply delegate. 1785 * Params: 1786 * tf = type of opApply or delegate 1787 * parameters = foreach parameters 1788 * infer = infer missing parameter types 1789 * Returns: 1790 * true for match for this function 1791 * false for no match for this function 1792 */ 1793 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer) 1794 { 1795 enum nomatch = false; 1796 1797 /* opApply/delegate has exactly one parameter, and that parameter 1798 * is a delegate that looks like: 1799 * int opApply(int delegate(ref Type [, ...]) dg); 1800 */ 1801 if (tf.parameterList.length != 1) 1802 return nomatch; 1803 1804 /* Get the type of opApply's dg parameter 1805 */ 1806 Parameter p0 = tf.parameterList[0]; 1807 if (p0.type.ty != Tdelegate) 1808 return nomatch; 1809 TypeFunction tdg = cast(TypeFunction)p0.type.nextOf(); 1810 assert(tdg.ty == Tfunction); 1811 1812 /* We now have tdg, the type of the delegate. 1813 * tdg's parameters must match that of the foreach arglist (i.e. parameters). 1814 * Fill in missing types in parameters. 1815 */ 1816 const nparams = tdg.parameterList.length; 1817 if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none) 1818 return nomatch; // parameter mismatch 1819 1820 foreach (u, p; *parameters) 1821 { 1822 Parameter param = tdg.parameterList[u]; 1823 if (p.type) 1824 { 1825 if (!p.type.equals(param.type)) 1826 return nomatch; 1827 } 1828 else if (infer) 1829 { 1830 p.type = param.type; 1831 p.type = p.type.addStorageClass(p.storageClass); 1832 } 1833 } 1834 return true; 1835 } 1836 1837 /** 1838 * Reverse relational operator, eg >= becomes <= 1839 * Note this is not negation. 1840 * Params: 1841 * op = comparison operator to reverse 1842 * Returns: 1843 * reverse of op 1844 */ 1845 private TOK reverseRelation(TOK op) pure 1846 { 1847 switch (op) 1848 { 1849 case TOK.greaterOrEqual: op = TOK.lessOrEqual; break; 1850 case TOK.greaterThan: op = TOK.lessThan; break; 1851 case TOK.lessOrEqual: op = TOK.greaterOrEqual; break; 1852 case TOK.lessThan: op = TOK.greaterThan; break; 1853 default: break; 1854 } 1855 return op; 1856 }