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