1 /** 2 * Handles operator overloading. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading) 5 * 6 * Copyright: Copyright (C) 1999-2021 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.equivalent(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.equivalent(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 && !(ae.att1 && t1b.equivalent(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.att1 && e.e1.type.equivalent(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 && !(ae.att1 && t1b.equivalent(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 auto t1b = e.e1.type.toBasetype(); 599 if (ad.aliasthis && !(e.att1 && t1b.equivalent(e.att1))) 600 { 601 if (!e.att1 && t1b.checkAliasThisRec()) 602 e.att1 = t1b; 603 /* Rewrite op(e1) as: 604 * op(e1.aliasthis) 605 */ 606 Expression e1 = resolveAliasThis(sc, e.e1); 607 result = e.copy(); 608 (cast(UnaExp)result).e1 = e1; 609 result = result.op_overload(sc); 610 return; 611 } 612 } 613 } 614 615 override void visit(BinExp e) 616 { 617 //printf("BinExp::op_overload() (%s)\n", e.toChars()); 618 Identifier id = opId(e); 619 Identifier id_r = opId_r(e); 620 Expressions args1; 621 Expressions args2; 622 int argsset = 0; 623 AggregateDeclaration ad1 = isAggregate(e.e1.type); 624 AggregateDeclaration ad2 = isAggregate(e.e2.type); 625 if (e.op == TOK.assign && ad1 == ad2) 626 { 627 StructDeclaration sd = ad1.isStructDeclaration(); 628 if (sd && 629 (!sd.hasIdentityAssign || 630 /* Do a blit if we can and the rvalue is something like .init, 631 * where a postblit is not necessary. 632 */ 633 (sd.hasBlitAssign && !e.e2.isLvalue()))) 634 { 635 /* This is bitwise struct assignment. */ 636 return; 637 } 638 } 639 Dsymbol s = null; 640 Dsymbol s_r = null; 641 Objects* tiargs = null; 642 if (e.op == TOK.plusPlus || e.op == TOK.minusMinus) 643 { 644 // Bug4099 fix 645 if (ad1 && search_function(ad1, Id.opUnary)) 646 return; 647 } 648 if (e.op != TOK.equal && e.op != TOK.notEqual && e.op != TOK.assign && e.op != TOK.plusPlus && e.op != TOK.minusMinus) 649 { 650 /* Try opBinary and opBinaryRight 651 */ 652 if (ad1) 653 { 654 s = search_function(ad1, Id.opBinary); 655 if (s && !s.isTemplateDeclaration()) 656 { 657 e.e1.error("`%s.opBinary` isn't a template", e.e1.toChars()); 658 result = ErrorExp.get(); 659 return; 660 } 661 } 662 if (ad2) 663 { 664 s_r = search_function(ad2, Id.opBinaryRight); 665 if (s_r && !s_r.isTemplateDeclaration()) 666 { 667 e.e2.error("`%s.opBinaryRight` isn't a template", e.e2.toChars()); 668 result = ErrorExp.get(); 669 return; 670 } 671 if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 672 s_r = null; 673 } 674 // Set tiargs, the template argument list, which will be the operator string 675 if (s || s_r) 676 { 677 id = Id.opBinary; 678 id_r = Id.opBinaryRight; 679 tiargs = opToArg(sc, e.op); 680 } 681 } 682 if (!s && !s_r) 683 { 684 // Try the D1-style operators, deprecated 685 if (ad1 && id) 686 { 687 s = search_function(ad1, id); 688 if (s && id != Id.assign) 689 { 690 // @@@DEPRECATED_2.098@@@. 691 // Deprecated in 2.088 692 // Make an error in 2.098 693 if (id == Id.postinc || id == Id.postdec) 694 e.deprecation("`%s` is deprecated. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op)); 695 else 696 e.deprecation("`%s` is deprecated. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), Token.toChars(e.op)); 697 } 698 } 699 if (ad2 && id_r) 700 { 701 s_r = search_function(ad2, id_r); 702 // https://issues.dlang.org/show_bug.cgi?id=12778 703 // If both x.opBinary(y) and y.opBinaryRight(x) found, 704 // and they are exactly same symbol, x.opBinary(y) should be preferred. 705 if (s_r && s_r == s) 706 s_r = null; 707 if (s_r) 708 { 709 // @@@DEPRECATED_2.098@@@. 710 // Deprecated in 2.088 711 // Make an error in 2.098 712 e.deprecation("`%s` is deprecated. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), Token.toChars(e.op)); 713 } 714 } 715 } 716 if (s || s_r) 717 { 718 /* Try: 719 * a.opfunc(b) 720 * b.opfunc_r(a) 721 * and see which is better. 722 */ 723 args1.setDim(1); 724 args1[0] = e.e1; 725 expandTuples(&args1); 726 args2.setDim(1); 727 args2[0] = e.e2; 728 expandTuples(&args2); 729 argsset = 1; 730 MatchAccumulator m; 731 if (s) 732 { 733 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2); 734 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 735 { 736 result = ErrorExp.get(); 737 return; 738 } 739 } 740 FuncDeclaration lastf = m.lastf; 741 if (s_r) 742 { 743 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1); 744 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 745 { 746 result = ErrorExp.get(); 747 return; 748 } 749 } 750 if (m.count > 1) 751 { 752 // Error, ambiguous 753 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 754 } 755 else if (m.last <= MATCH.nomatch) 756 { 757 if (tiargs) 758 goto L1; 759 m.lastf = null; 760 } 761 if (e.op == TOK.plusPlus || e.op == TOK.minusMinus) 762 { 763 // Kludge because operator overloading regards e++ and e-- 764 // as unary, but it's implemented as a binary. 765 // Rewrite (e1 ++ e2) as e1.postinc() 766 // Rewrite (e1 -- e2) as e1.postdec() 767 result = build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); 768 } 769 else if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch) 770 { 771 // Rewrite (e1 op e2) as e1.opfunc(e2) 772 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 773 } 774 else 775 { 776 // Rewrite (e1 op e2) as e2.opfunc_r(e1) 777 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); 778 } 779 return; 780 } 781 L1: 782 version (all) 783 { 784 // Retained for D1 compatibility 785 if (isCommutative(e.op) && !tiargs) 786 { 787 s = null; 788 s_r = null; 789 if (ad1 && id_r) 790 { 791 s_r = search_function(ad1, id_r); 792 } 793 if (ad2 && id) 794 { 795 s = search_function(ad2, id); 796 if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 797 s = null; 798 } 799 if (s || s_r) 800 { 801 /* Try: 802 * a.opfunc_r(b) 803 * b.opfunc(a) 804 * and see which is better. 805 */ 806 if (!argsset) 807 { 808 args1.setDim(1); 809 args1[0] = e.e1; 810 expandTuples(&args1); 811 args2.setDim(1); 812 args2[0] = e.e2; 813 expandTuples(&args2); 814 } 815 MatchAccumulator m; 816 if (s_r) 817 { 818 functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, &args2); 819 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 820 { 821 result = ErrorExp.get(); 822 return; 823 } 824 } 825 FuncDeclaration lastf = m.lastf; 826 if (s) 827 { 828 functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, &args1); 829 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 830 { 831 result = ErrorExp.get(); 832 return; 833 } 834 } 835 if (m.count > 1) 836 { 837 // Error, ambiguous 838 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 839 } 840 else if (m.last <= MATCH.nomatch) 841 { 842 m.lastf = null; 843 } 844 845 if (lastf && m.lastf == lastf || !s && m.last <= MATCH.nomatch) 846 { 847 // Rewrite (e1 op e2) as e1.opfunc_r(e2) 848 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); 849 } 850 else 851 { 852 // Rewrite (e1 op e2) as e2.opfunc(e1) 853 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); 854 } 855 // When reversing operands of comparison operators, 856 // need to reverse the sense of the op 857 if (pop) 858 *pop = reverseRelation(e.op); 859 return; 860 } 861 } 862 } 863 864 Expression tempResult; 865 if (!(e.op == TOK.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 866 { 867 result = checkAliasThisForLhs(ad1, sc, e); 868 if (result) 869 { 870 /* https://issues.dlang.org/show_bug.cgi?id=19441 871 * 872 * alias this may not be used for partial assignment. 873 * If a struct has a single member which is aliased this 874 * directly or aliased to a ref getter function that returns 875 * the mentioned member, then alias this may be 876 * used since the object will be fully initialised. 877 * If the struct is nested, the context pointer is considered 878 * one of the members, hence the `ad1.fields.dim == 2 && ad1.vthis` 879 * condition. 880 */ 881 if (e.op != TOK.assign || e.e1.op == TOK.type) 882 return; 883 884 if (ad1.fields.dim == 1 || (ad1.fields.dim == 2 && ad1.vthis)) 885 { 886 auto var = ad1.aliasthis.sym.isVarDeclaration(); 887 if (var && var.type == ad1.fields[0].type) 888 return; 889 890 auto func = ad1.aliasthis.sym.isFuncDeclaration(); 891 auto tf = cast(TypeFunction)(func.type); 892 if (tf.isref && ad1.fields[0].type == tf.next) 893 return; 894 } 895 tempResult = result; 896 } 897 } 898 if (!(e.op == TOK.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 899 { 900 result = checkAliasThisForRhs(ad2, sc, e); 901 if (result) 902 return; 903 } 904 905 // @@@DEPRECATED_2019-02@@@ 906 // 1. Deprecation for 1 year 907 // 2. Turn to error after 908 if (tempResult) 909 { 910 // move this line where tempResult is assigned to result and turn to error when derecation period is over 911 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()); 912 // delete this line when deprecation period is over 913 result = tempResult; 914 } 915 } 916 917 override void visit(EqualExp e) 918 { 919 //printf("EqualExp::op_overload() (%s)\n", e.toChars()); 920 Type t1 = e.e1.type.toBasetype(); 921 Type t2 = e.e2.type.toBasetype(); 922 923 /* Array equality is handled by expressionSemantic() potentially 924 * lowering to object.__equals(), which takes care of overloaded 925 * operators for the element types. 926 */ 927 if ((t1.ty == Tarray || t1.ty == Tsarray) && 928 (t2.ty == Tarray || t2.ty == Tsarray)) 929 { 930 return; 931 } 932 933 /* Check for class equality with null literal or typeof(null). 934 */ 935 if (t1.ty == Tclass && e.e2.op == TOK.null_ || 936 t2.ty == Tclass && e.e1.op == TOK.null_) 937 { 938 e.error("use `%s` instead of `%s` when comparing with `null`", 939 Token.toChars(e.op == TOK.equal ? TOK.identity : TOK.notIdentity), 940 Token.toChars(e.op)); 941 result = ErrorExp.get(); 942 return; 943 } 944 if (t1.ty == Tclass && t2.ty == Tnull || 945 t1.ty == Tnull && t2.ty == Tclass) 946 { 947 // Comparing a class with typeof(null) should not call opEquals 948 return; 949 } 950 951 /* Check for class equality. 952 */ 953 if (t1.ty == Tclass && t2.ty == Tclass) 954 { 955 ClassDeclaration cd1 = t1.isClassHandle(); 956 ClassDeclaration cd2 = t2.isClassHandle(); 957 if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) 958 { 959 /* Rewrite as: 960 * .object.opEquals(e1, e2) 961 */ 962 Expression e1x = e.e1; 963 Expression e2x = e.e2; 964 965 /* The explicit cast is necessary for interfaces 966 * https://issues.dlang.org/show_bug.cgi?id=4088 967 */ 968 Type to = ClassDeclaration.object.getType(); 969 if (cd1.isInterfaceDeclaration()) 970 e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); 971 if (cd2.isInterfaceDeclaration()) 972 e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); 973 974 result = new IdentifierExp(e.loc, Id.empty); 975 result = new DotIdExp(e.loc, result, Id.object); 976 result = new DotIdExp(e.loc, result, Id.eq); 977 result = new CallExp(e.loc, result, e1x, e2x); 978 if (e.op == TOK.notEqual) 979 result = new NotExp(e.loc, result); 980 result = result.expressionSemantic(sc); 981 return; 982 } 983 } 984 985 result = compare_overload(e, sc, Id.eq, null); 986 if (result) 987 { 988 if (lastComma(result).op == TOK.call && e.op == TOK.notEqual) 989 { 990 result = new NotExp(result.loc, result); 991 result = result.expressionSemantic(sc); 992 } 993 return; 994 } 995 996 /* Check for pointer equality. 997 */ 998 if (t1.ty == Tpointer || t2.ty == Tpointer) 999 { 1000 /* Rewrite: 1001 * ptr1 == ptr2 1002 * as: 1003 * ptr1 is ptr2 1004 * 1005 * This is just a rewriting for deterministic AST representation 1006 * as the backend input. 1007 */ 1008 auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity; 1009 result = new IdentityExp(op2, e.loc, e.e1, e.e2); 1010 result = result.expressionSemantic(sc); 1011 return; 1012 } 1013 1014 /* Check for struct equality without opEquals. 1015 */ 1016 if (t1.ty == Tstruct && t2.ty == Tstruct) 1017 { 1018 auto sd = (cast(TypeStruct)t1).sym; 1019 if (sd != (cast(TypeStruct)t2).sym) 1020 return; 1021 1022 import dmd.clone : needOpEquals; 1023 if (!global.params.fieldwise && !needOpEquals(sd)) 1024 { 1025 // Use bitwise equality. 1026 auto op2 = e.op == TOK.equal ? TOK.identity : TOK.notIdentity; 1027 result = new IdentityExp(op2, e.loc, e.e1, e.e2); 1028 result = result.expressionSemantic(sc); 1029 return; 1030 } 1031 1032 /* Do memberwise equality. 1033 * https://dlang.org/spec/expression.html#equality_expressions 1034 * Rewrite: 1035 * e1 == e2 1036 * as: 1037 * e1.tupleof == e2.tupleof 1038 * 1039 * If sd is a nested struct, and if it's nested in a class, it will 1040 * also compare the parent class's equality. Otherwise, compares 1041 * the identity of parent context through void*. 1042 */ 1043 if (e.att1 && t1.equivalent(e.att1)) return; 1044 if (e.att2 && t2.equivalent(e.att2)) return; 1045 1046 e = cast(EqualExp)e.copy(); 1047 if (!e.att1) e.att1 = t1; 1048 if (!e.att2) e.att2 = t2; 1049 e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); 1050 e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); 1051 1052 auto sc2 = sc.push(); 1053 sc2.flags = (sc2.flags & ~SCOPE.onlysafeaccess) | SCOPE.noaccesscheck; 1054 result = e.expressionSemantic(sc2); 1055 sc2.pop(); 1056 1057 /* https://issues.dlang.org/show_bug.cgi?id=15292 1058 * if the rewrite result is same with the original, 1059 * the equality is unresolvable because it has recursive definition. 1060 */ 1061 if (result.op == e.op && 1062 (cast(EqualExp)result).e1.type.toBasetype() == t1) 1063 { 1064 e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition", 1065 t1.toChars()); 1066 result = ErrorExp.get(); 1067 } 1068 return; 1069 } 1070 1071 /* Check for tuple equality. 1072 */ 1073 if (e.e1.op == TOK.tuple && e.e2.op == TOK.tuple) 1074 { 1075 auto tup1 = cast(TupleExp)e.e1; 1076 auto tup2 = cast(TupleExp)e.e2; 1077 size_t dim = tup1.exps.dim; 1078 if (dim != tup2.exps.dim) 1079 { 1080 e.error("mismatched tuple lengths, `%d` and `%d`", 1081 cast(int)dim, cast(int)tup2.exps.dim); 1082 result = ErrorExp.get(); 1083 return; 1084 } 1085 1086 if (dim == 0) 1087 { 1088 // zero-length tuple comparison should always return true or false. 1089 result = IntegerExp.createBool(e.op == TOK.equal); 1090 } 1091 else 1092 { 1093 for (size_t i = 0; i < dim; i++) 1094 { 1095 auto ex1 = (*tup1.exps)[i]; 1096 auto ex2 = (*tup2.exps)[i]; 1097 auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); 1098 eeq.att1 = e.att1; 1099 eeq.att2 = e.att2; 1100 1101 if (!result) 1102 result = eeq; 1103 else if (e.op == TOK.equal) 1104 result = new LogicalExp(e.loc, TOK.andAnd, result, eeq); 1105 else 1106 result = new LogicalExp(e.loc, TOK.orOr, result, eeq); 1107 } 1108 assert(result); 1109 } 1110 result = Expression.combine(tup1.e0, tup2.e0, result); 1111 result = result.expressionSemantic(sc); 1112 1113 return; 1114 } 1115 } 1116 1117 override void visit(CmpExp e) 1118 { 1119 //printf("CmpExp:: () (%s)\n", e.toChars()); 1120 result = compare_overload(e, sc, Id.cmp, pop); 1121 } 1122 1123 /********************************* 1124 * Operator overloading for op= 1125 */ 1126 override void visit(BinAssignExp e) 1127 { 1128 //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); 1129 if (e.e1.op == TOK.array) 1130 { 1131 ArrayExp ae = cast(ArrayExp)e.e1; 1132 ae.e1 = ae.e1.expressionSemantic(sc); 1133 ae.e1 = resolveProperties(sc, ae.e1); 1134 Expression ae1old = ae.e1; 1135 const(bool) maybeSlice = (ae.arguments.dim == 0 || ae.arguments.dim == 1 && (*ae.arguments)[0].op == TOK.interval); 1136 IntervalExp ie = null; 1137 if (maybeSlice && ae.arguments.dim) 1138 { 1139 assert((*ae.arguments)[0].op == TOK.interval); 1140 ie = cast(IntervalExp)(*ae.arguments)[0]; 1141 } 1142 while (true) 1143 { 1144 if (ae.e1.op == TOK.error) 1145 { 1146 result = ae.e1; 1147 return; 1148 } 1149 Expression e0 = null; 1150 Expression ae1save = ae.e1; 1151 ae.lengthVar = null; 1152 Type t1b = ae.e1.type.toBasetype(); 1153 AggregateDeclaration ad = isAggregate(t1b); 1154 if (!ad) 1155 break; 1156 if (search_function(ad, Id.opIndexOpAssign)) 1157 { 1158 // Deal with $ 1159 result = resolveOpDollar(sc, ae, &e0); 1160 if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) 1161 goto Lfallback; 1162 if (result.op == TOK.error) 1163 return; 1164 result = e.e2.expressionSemantic(sc); 1165 if (result.op == TOK.error) 1166 return; 1167 e.e2 = result; 1168 /* Rewrite a[arguments] op= e2 as: 1169 * a.opIndexOpAssign!(op)(e2, arguments) 1170 */ 1171 Expressions* a = ae.arguments.copy(); 1172 a.insert(0, e.e2); 1173 Objects* tiargs = opToArg(sc, e.op); 1174 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); 1175 result = new CallExp(e.loc, result, a); 1176 if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) 1177 result = result.trySemantic(sc); 1178 else 1179 result = result.expressionSemantic(sc); 1180 if (result) 1181 { 1182 result = Expression.combine(e0, result); 1183 return; 1184 } 1185 } 1186 Lfallback: 1187 if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) 1188 { 1189 // Deal with $ 1190 result = resolveOpDollar(sc, ae, ie, &e0); 1191 if (result.op == TOK.error) 1192 return; 1193 result = e.e2.expressionSemantic(sc); 1194 if (result.op == TOK.error) 1195 return; 1196 e.e2 = result; 1197 /* Rewrite (a[i..j] op= e2) as: 1198 * a.opSliceOpAssign!(op)(e2, i, j) 1199 */ 1200 auto a = new Expressions(); 1201 a.push(e.e2); 1202 if (ie) 1203 { 1204 a.push(ie.lwr); 1205 a.push(ie.upr); 1206 } 1207 Objects* tiargs = opToArg(sc, e.op); 1208 result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); 1209 result = new CallExp(e.loc, result, a); 1210 result = result.expressionSemantic(sc); 1211 result = Expression.combine(e0, result); 1212 return; 1213 } 1214 // Didn't find it. Forward to aliasthis 1215 if (ad.aliasthis && !(ae.att1 && t1b.equivalent(ae.att1))) 1216 { 1217 if (!ae.att1 && t1b.checkAliasThisRec()) 1218 ae.att1 = t1b; 1219 /* Rewrite (a[arguments] op= e2) as: 1220 * a.aliasthis[arguments] op= e2 1221 */ 1222 ae.e1 = resolveAliasThis(sc, ae1save, true); 1223 if (ae.e1) 1224 continue; 1225 } 1226 break; 1227 } 1228 ae.e1 = ae1old; // recovery 1229 ae.lengthVar = null; 1230 } 1231 result = e.binSemanticProp(sc); 1232 if (result) 1233 return; 1234 // Don't attempt 'alias this' if an error occurred 1235 if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) 1236 { 1237 result = ErrorExp.get(); 1238 return; 1239 } 1240 Identifier id = opId(e); 1241 Expressions args2; 1242 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1243 Dsymbol s = null; 1244 Objects* tiargs = null; 1245 /* Try opOpAssign 1246 */ 1247 if (ad1) 1248 { 1249 s = search_function(ad1, Id.opOpAssign); 1250 if (s && !s.isTemplateDeclaration()) 1251 { 1252 e.error("`%s.opOpAssign` isn't a template", e.e1.toChars()); 1253 result = ErrorExp.get(); 1254 return; 1255 } 1256 } 1257 // Set tiargs, the template argument list, which will be the operator string 1258 if (s) 1259 { 1260 id = Id.opOpAssign; 1261 tiargs = opToArg(sc, e.op); 1262 } 1263 1264 // Try D1-style operator overload, deprecated 1265 if (!s && ad1 && id) 1266 { 1267 s = search_function(ad1, id); 1268 if (s) 1269 { 1270 // @@@DEPRECATED_2.098@@@. 1271 // Deprecated in 2.088 1272 // Make an error in 2.098 1273 scope char[] op = Token.toString(e.op).dup; 1274 op[$-1] = '\0'; // remove trailing `=` 1275 e.deprecation("`%s` is deprecated. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); 1276 } 1277 } 1278 1279 if (s) 1280 { 1281 /* Try: 1282 * a.opOpAssign(b) 1283 */ 1284 args2.setDim(1); 1285 args2[0] = e.e2; 1286 expandTuples(&args2); 1287 MatchAccumulator m; 1288 if (s) 1289 { 1290 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2); 1291 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1292 { 1293 result = ErrorExp.get(); 1294 return; 1295 } 1296 } 1297 if (m.count > 1) 1298 { 1299 // Error, ambiguous 1300 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1301 } 1302 else if (m.last <= MATCH.nomatch) 1303 { 1304 if (tiargs) 1305 goto L1; 1306 m.lastf = null; 1307 } 1308 // Rewrite (e1 op e2) as e1.opOpAssign(e2) 1309 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1310 return; 1311 } 1312 L1: 1313 result = checkAliasThisForLhs(ad1, sc, e); 1314 if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs 1315 return; 1316 1317 result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1318 } 1319 } 1320 1321 if (pop) 1322 *pop = e.op; 1323 scope OpOverload v = new OpOverload(sc, pop); 1324 e.accept(v); 1325 return v.result; 1326 } 1327 1328 /****************************************** 1329 * Common code for overloading of EqualExp and CmpExp 1330 */ 1331 private Expression compare_overload(BinExp e, Scope* sc, Identifier id, TOK* pop) 1332 { 1333 //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); 1334 AggregateDeclaration ad1 = isAggregate(e.e1.type); 1335 AggregateDeclaration ad2 = isAggregate(e.e2.type); 1336 Dsymbol s = null; 1337 Dsymbol s_r = null; 1338 if (ad1) 1339 { 1340 s = search_function(ad1, id); 1341 } 1342 if (ad2) 1343 { 1344 s_r = search_function(ad2, id); 1345 if (s == s_r) 1346 s_r = null; 1347 } 1348 Objects* tiargs = null; 1349 if (s || s_r) 1350 { 1351 /* Try: 1352 * a.opEquals(b) 1353 * b.opEquals(a) 1354 * and see which is better. 1355 */ 1356 Expressions args1 = Expressions(1); 1357 args1[0] = e.e1; 1358 expandTuples(&args1); 1359 Expressions args2 = Expressions(1); 1360 args2[0] = e.e2; 1361 expandTuples(&args2); 1362 MatchAccumulator m; 1363 if (0 && s && s_r) 1364 { 1365 printf("s : %s\n", s.toPrettyChars()); 1366 printf("s_r: %s\n", s_r.toPrettyChars()); 1367 } 1368 if (s) 1369 { 1370 functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, &args2); 1371 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1372 return ErrorExp.get(); 1373 } 1374 FuncDeclaration lastf = m.lastf; 1375 int count = m.count; 1376 if (s_r) 1377 { 1378 functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, &args1); 1379 if (m.lastf && (m.lastf.errors || m.lastf.semantic3Errors)) 1380 return ErrorExp.get(); 1381 } 1382 if (m.count > 1) 1383 { 1384 /* The following if says "not ambiguous" if there's one match 1385 * from s and one from s_r, in which case we pick s. 1386 * This doesn't follow the spec, but is a workaround for the case 1387 * where opEquals was generated from templates and we cannot figure 1388 * out if both s and s_r came from the same declaration or not. 1389 * The test case is: 1390 * import std.typecons; 1391 * void main() { 1392 * assert(tuple("has a", 2u) == tuple("has a", 1)); 1393 * } 1394 */ 1395 if (!(m.lastf == lastf && m.count == 2 && count == 1)) 1396 { 1397 // Error, ambiguous 1398 e.error("overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); 1399 } 1400 } 1401 else if (m.last <= MATCH.nomatch) 1402 { 1403 m.lastf = null; 1404 } 1405 Expression result; 1406 if (lastf && m.lastf == lastf || !s_r && m.last <= MATCH.nomatch) 1407 { 1408 // Rewrite (e1 op e2) as e1.opfunc(e2) 1409 result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); 1410 } 1411 else 1412 { 1413 // Rewrite (e1 op e2) as e2.opfunc_r(e1) 1414 result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); 1415 // When reversing operands of comparison operators, 1416 // need to reverse the sense of the op 1417 if (pop) 1418 *pop = reverseRelation(e.op); 1419 } 1420 return result; 1421 } 1422 /* 1423 * https://issues.dlang.org/show_bug.cgi?id=16657 1424 * at this point, no matching opEquals was found for structs, 1425 * so we should not follow the alias this comparison code. 1426 */ 1427 if ((e.op == TOK.equal || e.op == TOK.notEqual) && ad1 == ad2) 1428 return null; 1429 Expression result = checkAliasThisForLhs(ad1, sc, e); 1430 return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); 1431 } 1432 1433 /*********************************** 1434 * Utility to build a function call out of this reference and argument. 1435 */ 1436 Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) 1437 { 1438 assert(d); 1439 Expression e; 1440 Declaration decl = d.isDeclaration(); 1441 if (decl) 1442 e = new DotVarExp(loc, ethis, decl, false); 1443 else 1444 e = new DotIdExp(loc, ethis, d.ident); 1445 e = new CallExp(loc, e, earg); 1446 e = e.expressionSemantic(sc); 1447 return e; 1448 } 1449 1450 /*************************************** 1451 * Search for function funcid in aggregate ad. 1452 */ 1453 Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) 1454 { 1455 Dsymbol s = ad.search(Loc.initial, funcid); 1456 if (s) 1457 { 1458 //printf("search_function: s = '%s'\n", s.kind()); 1459 Dsymbol s2 = s.toAlias(); 1460 //printf("search_function: s2 = '%s'\n", s2.kind()); 1461 FuncDeclaration fd = s2.isFuncDeclaration(); 1462 if (fd && fd.type.ty == Tfunction) 1463 return fd; 1464 TemplateDeclaration td = s2.isTemplateDeclaration(); 1465 if (td) 1466 return td; 1467 } 1468 return null; 1469 } 1470 1471 /************************************** 1472 * Figure out what is being foreach'd over by looking at the ForeachAggregate. 1473 * Params: 1474 * sc = context 1475 * isForeach = true for foreach, false for foreach_reverse 1476 * feaggr = ForeachAggregate 1477 * sapply = set to function opApply/opApplyReverse, or delegate, or null. 1478 * Overload resolution is not done. 1479 * Returns: 1480 * true if successfully figured it out; feaggr updated with semantic analysis. 1481 * false for failed, which is an error. 1482 */ 1483 bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out Dsymbol sapply) 1484 { 1485 //printf("inferForeachAggregate(%s)\n", feaggr.toChars()); 1486 bool sliced; 1487 Type att = null; 1488 auto aggr = feaggr; 1489 while (1) 1490 { 1491 aggr = aggr.expressionSemantic(sc); 1492 aggr = resolveProperties(sc, aggr); 1493 aggr = aggr.optimize(WANTvalue); 1494 if (!aggr.type || aggr.op == TOK.error) 1495 return false; 1496 Type tab = aggr.type.toBasetype(); 1497 switch (tab.ty) 1498 { 1499 case Tarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1500 case Tsarray: // https://dlang.org/spec/statement.html#foreach_over_arrays 1501 case Ttuple: // https://dlang.org/spec/statement.html#foreach_over_tuples 1502 case Taarray: // https://dlang.org/spec/statement.html#foreach_over_associative_arrays 1503 break; 1504 1505 case Tclass: 1506 case Tstruct: 1507 { 1508 AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym 1509 : (cast(TypeStruct)tab).sym; 1510 if (!sliced) 1511 { 1512 sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse); 1513 if (sapply) 1514 { 1515 // https://dlang.org/spec/statement.html#foreach_over_struct_and_classes 1516 // opApply aggregate 1517 break; 1518 } 1519 if (feaggr.op != TOK.type) 1520 { 1521 /* See if rewriting `aggr` to `aggr[]` will work 1522 */ 1523 Expression rinit = new ArrayExp(aggr.loc, feaggr); 1524 rinit = rinit.trySemantic(sc); 1525 if (rinit) // if it worked 1526 { 1527 aggr = rinit; 1528 sliced = true; // only try it once 1529 continue; 1530 } 1531 } 1532 } 1533 if (ad.search(Loc.initial, isForeach ? Id.Ffront : Id.Fback)) 1534 { 1535 // https://dlang.org/spec/statement.html#foreach-with-ranges 1536 // range aggregate 1537 break; 1538 } 1539 if (ad.aliasthis) 1540 { 1541 if (att && tab.equivalent(att)) // error, circular alias this 1542 return false; 1543 if (!att && tab.checkAliasThisRec()) 1544 att = tab; 1545 aggr = resolveAliasThis(sc, aggr); 1546 continue; 1547 } 1548 return false; 1549 } 1550 1551 case Tdelegate: // https://dlang.org/spec/statement.html#foreach_over_delegates 1552 if (aggr.op == TOK.delegate_) 1553 { 1554 sapply = (cast(DelegateExp)aggr).func; 1555 } 1556 break; 1557 1558 case Terror: 1559 break; 1560 1561 default: 1562 return false; 1563 } 1564 feaggr = aggr; 1565 return true; 1566 } 1567 assert(0); 1568 } 1569 1570 /***************************************** 1571 * Given array of foreach parameters and an aggregate type, 1572 * find best opApply overload, 1573 * if any of the parameter types are missing, attempt to infer 1574 * them from the aggregate type. 1575 * Params: 1576 * fes = the foreach statement 1577 * sc = context 1578 * sapply = null or opApply or delegate 1579 * Returns: 1580 * false for errors 1581 */ 1582 bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) 1583 { 1584 if (!fes.parameters || !fes.parameters.dim) 1585 return false; 1586 if (sapply) // prefer opApply 1587 { 1588 foreach (Parameter p; *fes.parameters) 1589 { 1590 if (p.type) 1591 { 1592 p.type = p.type.typeSemantic(fes.loc, sc); 1593 p.type = p.type.addStorageClass(p.storageClass); 1594 } 1595 } 1596 1597 // Determine ethis for sapply 1598 Expression ethis; 1599 Type tab = fes.aggr.type.toBasetype(); 1600 if (tab.ty == Tclass || tab.ty == Tstruct) 1601 ethis = fes.aggr; 1602 else 1603 { 1604 assert(tab.ty == Tdelegate && fes.aggr.op == TOK.delegate_); 1605 ethis = (cast(DelegateExp)fes.aggr).e1; 1606 } 1607 1608 /* Look for like an 1609 * int opApply(int delegate(ref Type [, ...]) dg); 1610 * overload 1611 */ 1612 if (FuncDeclaration fd = sapply.isFuncDeclaration()) 1613 { 1614 auto fdapply = findBestOpApplyMatch(ethis, fd, fes.parameters); 1615 if (fdapply) 1616 { 1617 // Fill in any missing types on foreach parameters[] 1618 matchParamsToOpApply(cast(TypeFunction)fdapply.type, fes.parameters, true); 1619 sapply = fdapply; 1620 return true; 1621 } 1622 return false; 1623 } 1624 return sapply !is null; 1625 } 1626 1627 Parameter p = (*fes.parameters)[0]; 1628 Type taggr = fes.aggr.type; 1629 assert(taggr); 1630 Type tab = taggr.toBasetype(); 1631 switch (tab.ty) 1632 { 1633 case Tarray: 1634 case Tsarray: 1635 case Ttuple: 1636 if (fes.parameters.dim == 2) 1637 { 1638 if (!p.type) 1639 { 1640 p.type = Type.tsize_t; // key type 1641 p.type = p.type.addStorageClass(p.storageClass); 1642 } 1643 p = (*fes.parameters)[1]; 1644 } 1645 if (!p.type && tab.ty != Ttuple) 1646 { 1647 p.type = tab.nextOf(); // value type 1648 p.type = p.type.addStorageClass(p.storageClass); 1649 } 1650 break; 1651 1652 case Taarray: 1653 { 1654 TypeAArray taa = cast(TypeAArray)tab; 1655 if (fes.parameters.dim == 2) 1656 { 1657 if (!p.type) 1658 { 1659 p.type = taa.index; // key type 1660 p.type = p.type.addStorageClass(p.storageClass); 1661 if (p.storageClass & STC.ref_) // key must not be mutated via ref 1662 p.type = p.type.addMod(MODFlags.const_); 1663 } 1664 p = (*fes.parameters)[1]; 1665 } 1666 if (!p.type) 1667 { 1668 p.type = taa.next; // value type 1669 p.type = p.type.addStorageClass(p.storageClass); 1670 } 1671 break; 1672 } 1673 1674 case Tclass: 1675 case Tstruct: 1676 { 1677 AggregateDeclaration ad = (tab.ty == Tclass) ? (cast(TypeClass)tab).sym 1678 : (cast(TypeStruct)tab).sym; 1679 if (fes.parameters.dim == 1) 1680 { 1681 if (!p.type) 1682 { 1683 /* Look for a front() or back() overload 1684 */ 1685 Identifier id = (fes.op == TOK.foreach_) ? Id.Ffront : Id.Fback; 1686 Dsymbol s = ad.search(Loc.initial, id); 1687 FuncDeclaration fd = s ? s.isFuncDeclaration() : null; 1688 if (fd) 1689 { 1690 // Resolve inout qualifier of front type 1691 p.type = fd.type.nextOf(); 1692 if (p.type) 1693 { 1694 p.type = p.type.substWildTo(tab.mod); 1695 p.type = p.type.addStorageClass(p.storageClass); 1696 } 1697 } 1698 else if (s && s.isTemplateDeclaration()) 1699 { 1700 } 1701 else if (s && s.isDeclaration()) 1702 p.type = (cast(Declaration)s).type; 1703 else 1704 break; 1705 } 1706 break; 1707 } 1708 break; 1709 } 1710 1711 case Tdelegate: 1712 if (!matchParamsToOpApply(cast(TypeFunction)tab.nextOf(), fes.parameters, true)) 1713 return false; 1714 break; 1715 1716 default: 1717 break; // ignore error, caught later 1718 } 1719 return true; 1720 } 1721 1722 /********************************************* 1723 * Find best overload match on fstart given ethis and parameters[]. 1724 * Params: 1725 * ethis = expression to use for `this` 1726 * fstart = opApply or foreach delegate 1727 * parameters = ForeachTypeList (i.e. foreach parameters) 1728 * Returns: 1729 * best match if there is one, null if error 1730 */ 1731 private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration fstart, Parameters* parameters) 1732 { 1733 MOD mod = ethis.type.mod; 1734 MATCH match = MATCH.nomatch; 1735 FuncDeclaration fd_best; 1736 FuncDeclaration fd_ambig; 1737 1738 overloadApply(fstart, (Dsymbol s) 1739 { 1740 auto f = s.isFuncDeclaration(); 1741 if (!f) 1742 return 0; // continue 1743 auto tf = cast(TypeFunction)f.type; 1744 MATCH m = MATCH.exact; 1745 if (f.isThis()) 1746 { 1747 if (!MODimplicitConv(mod, tf.mod)) 1748 m = MATCH.nomatch; 1749 else if (mod != tf.mod) 1750 m = MATCH.constant; 1751 } 1752 if (!matchParamsToOpApply(tf, parameters, false)) 1753 m = MATCH.nomatch; 1754 if (m > match) 1755 { 1756 fd_best = f; 1757 fd_ambig = null; 1758 match = m; 1759 } 1760 else if (m == match && m > MATCH.nomatch) 1761 { 1762 assert(fd_best); 1763 /* Ignore covariant matches, as later on it can be redone 1764 * after the opApply delegate has its attributes inferred. 1765 */ 1766 if (tf.covariant(fd_best.type) != 1 && 1767 fd_best.type.covariant(tf) != 1) 1768 fd_ambig = f; // not covariant, so ambiguous 1769 } 1770 return 0; // continue 1771 }); 1772 1773 if (fd_ambig) 1774 { 1775 .error(ethis.loc, "`%s.%s` matches more than one declaration:\n`%s`: `%s`\nand:\n`%s`: `%s`", 1776 ethis.toChars(), fstart.ident.toChars(), 1777 fd_best.loc.toChars(), fd_best.type.toChars(), 1778 fd_ambig.loc.toChars(), fd_ambig.type.toChars()); 1779 return null; 1780 } 1781 1782 return fd_best; 1783 } 1784 1785 /****************************** 1786 * Determine if foreach parameters match opApply parameters. 1787 * Infer missing foreach parameter types from type of opApply delegate. 1788 * Params: 1789 * tf = type of opApply or delegate 1790 * parameters = foreach parameters 1791 * infer = infer missing parameter types 1792 * Returns: 1793 * true for match for this function 1794 * false for no match for this function 1795 */ 1796 private bool matchParamsToOpApply(TypeFunction tf, Parameters* parameters, bool infer) 1797 { 1798 enum nomatch = false; 1799 1800 /* opApply/delegate has exactly one parameter, and that parameter 1801 * is a delegate that looks like: 1802 * int opApply(int delegate(ref Type [, ...]) dg); 1803 */ 1804 if (tf.parameterList.length != 1) 1805 return nomatch; 1806 1807 /* Get the type of opApply's dg parameter 1808 */ 1809 Parameter p0 = tf.parameterList[0]; 1810 if (p0.type.ty != Tdelegate) 1811 return nomatch; 1812 TypeFunction tdg = cast(TypeFunction)p0.type.nextOf(); 1813 assert(tdg.ty == Tfunction); 1814 1815 /* We now have tdg, the type of the delegate. 1816 * tdg's parameters must match that of the foreach arglist (i.e. parameters). 1817 * Fill in missing types in parameters. 1818 */ 1819 const nparams = tdg.parameterList.length; 1820 if (nparams == 0 || nparams != parameters.dim || tdg.parameterList.varargs != VarArg.none) 1821 return nomatch; // parameter mismatch 1822 1823 foreach (u, p; *parameters) 1824 { 1825 Parameter param = tdg.parameterList[u]; 1826 if (p.type) 1827 { 1828 if (!p.type.equals(param.type)) 1829 return nomatch; 1830 } 1831 else if (infer) 1832 { 1833 p.type = param.type; 1834 p.type = p.type.addStorageClass(p.storageClass); 1835 } 1836 } 1837 return true; 1838 } 1839 1840 /** 1841 * Reverse relational operator, eg >= becomes <= 1842 * Note this is not negation. 1843 * Params: 1844 * op = comparison operator to reverse 1845 * Returns: 1846 * reverse of op 1847 */ 1848 private TOK reverseRelation(TOK op) pure 1849 { 1850 switch (op) 1851 { 1852 case TOK.greaterOrEqual: op = TOK.lessOrEqual; break; 1853 case TOK.greaterThan: op = TOK.lessThan; break; 1854 case TOK.lessOrEqual: op = TOK.greaterOrEqual; break; 1855 case TOK.lessThan: op = TOK.greaterThan; break; 1856 default: break; 1857 } 1858 return op; 1859 }