1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _arrayop.d) 9 */ 10 11 module ddmd.arrayop; 12 13 import core.stdc.stdio; 14 import ddmd.arraytypes; 15 import ddmd.declaration; 16 import ddmd.dscope; 17 import ddmd.dsymbol; 18 import ddmd.expression; 19 import ddmd.func; 20 import ddmd.globals; 21 import ddmd.id; 22 import ddmd.identifier; 23 import ddmd.mtype; 24 import ddmd.root.outbuffer; 25 import ddmd.statement; 26 import ddmd.tokens; 27 import ddmd.visitor; 28 29 /************************************** 30 * Hash table of array op functions already generated or known about. 31 */ 32 private __gshared FuncDeclaration[void*] arrayfuncs; 33 34 35 /************************************** 36 * Structure to contain information needed to insert an array op call 37 */ 38 extern (C++) FuncDeclaration buildArrayOp(Identifier ident, BinExp exp, Scope* sc, Loc loc) 39 { 40 auto fparams = new Parameters(); 41 Expression loopbody = buildArrayLoop(exp, fparams); 42 43 /* Construct the function body: 44 * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) 45 * loopbody; 46 * return p; 47 */ 48 49 Parameter p = (*fparams)[0]; 50 // foreach (i; 0 .. p.length) 51 Statement s1 = new ForeachRangeStatement(Loc(), TOKforeach, 52 new Parameter(0, null, Id.p, null), 53 new IntegerExp(Loc(), 0, Type.tsize_t), 54 new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p.ident)), 55 new ExpStatement(Loc(), loopbody), 56 Loc()); 57 //printf("%s\n", s1.toChars()); 58 Statement s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p.ident)); 59 //printf("s2: %s\n", s2.toChars()); 60 Statement fbody = new CompoundStatement(Loc(), s1, s2); 61 62 // Built-in array ops should be @trusted, pure, nothrow and nogc 63 StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; 64 65 /* Construct the function 66 */ 67 auto ftype = new TypeFunction(fparams, exp.e1.type, 0, LINKc, stc); 68 //printf("fd: %s %s\n", ident.toChars(), ftype.toChars()); 69 //printf("fbody: %s\n", fbody.toChars()); 70 auto fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype); 71 fd.fbody = fbody; 72 fd.protection = Prot(PROTpublic); 73 fd.linkage = LINKc; 74 fd.isArrayOp = 1; 75 76 sc._module.importedFrom.members.push(fd); 77 78 sc = sc.push(); 79 sc.parent = sc._module.importedFrom; 80 sc.stc = 0; 81 sc.linkage = LINKc; 82 fd.semantic(sc); 83 fd.semantic2(sc); 84 uint errors = global.startGagging(); 85 fd.semantic3(sc); 86 if (global.endGagging(errors)) 87 { 88 fd.type = Type.terror; 89 fd.errors = true; 90 fd.fbody = null; 91 } 92 sc.pop(); 93 94 return fd; 95 } 96 97 /********************************************** 98 * Check that there are no uses of arrays without []. 99 */ 100 extern (C++) bool isArrayOpValid(Expression e) 101 { 102 if (e.op == TOKslice) 103 return true; 104 if (e.op == TOKarrayliteral) 105 { 106 Type t = e.type.toBasetype(); 107 while (t.ty == Tarray || t.ty == Tsarray) 108 t = t.nextOf().toBasetype(); 109 return (t.ty != Tvoid); 110 } 111 Type tb = e.type.toBasetype(); 112 if (tb.ty == Tarray || tb.ty == Tsarray) 113 { 114 if (isUnaArrayOp(e.op)) 115 { 116 return isArrayOpValid((cast(UnaExp)e).e1); 117 } 118 if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOKassign) 119 { 120 BinExp be = cast(BinExp)e; 121 return isArrayOpValid(be.e1) && isArrayOpValid(be.e2); 122 } 123 if (e.op == TOKconstruct) 124 { 125 BinExp be = cast(BinExp)e; 126 return be.e1.op == TOKslice && isArrayOpValid(be.e2); 127 } 128 if (e.op == TOKcall) 129 { 130 return false; // TODO: Decide if [] is required after arrayop calls. 131 } 132 else 133 { 134 return false; 135 } 136 } 137 return true; 138 } 139 140 extern (C++) bool isNonAssignmentArrayOp(Expression e) 141 { 142 if (e.op == TOKslice) 143 return isNonAssignmentArrayOp((cast(SliceExp)e).e1); 144 145 Type tb = e.type.toBasetype(); 146 if (tb.ty == Tarray || tb.ty == Tsarray) 147 { 148 return (isUnaArrayOp(e.op) || isBinArrayOp(e.op)); 149 } 150 return false; 151 } 152 153 extern (C++) bool checkNonAssignmentArrayOp(Expression e, bool suggestion = false) 154 { 155 if (isNonAssignmentArrayOp(e)) 156 { 157 const(char)* s = ""; 158 if (suggestion) 159 s = " (possible missing [])"; 160 e.error("array operation %s without destination memory not allowed%s", e.toChars(), s); 161 return true; 162 } 163 return false; 164 } 165 166 /*********************************** 167 * Construct the array operation expression. 168 */ 169 extern (C++) Expression arrayOp(BinExp e, Scope* sc) 170 { 171 //printf("BinExp.arrayOp() %s\n", toChars()); 172 Type tb = e.type.toBasetype(); 173 assert(tb.ty == Tarray || tb.ty == Tsarray); 174 Type tbn = tb.nextOf().toBasetype(); 175 if (tbn.ty == Tvoid) 176 { 177 e.error("cannot perform array operations on void[] arrays"); 178 return new ErrorExp(); 179 } 180 if (!isArrayOpValid(e)) 181 { 182 e.error("invalid array operation %s (possible missing [])", e.toChars()); 183 return new ErrorExp(); 184 } 185 186 auto arguments = new Expressions(); 187 188 /* The expression to generate an array operation for is mangled 189 * into a name to use as the array operation function name. 190 * Mangle in the operands and operators in RPN order, and type. 191 */ 192 OutBuffer buf; 193 buf.writestring("_array"); 194 buildArrayIdent(e, &buf, arguments); 195 buf.writeByte('_'); 196 197 /* Append deco of array element type 198 */ 199 buf.writestring(e.type.toBasetype().nextOf().toBasetype().mutableOf().deco); 200 201 auto ident = Identifier.idPool(buf.peekSlice()); 202 203 FuncDeclaration* pFd = cast(void*)ident in arrayfuncs; 204 FuncDeclaration fd; 205 if (pFd) 206 fd = *pFd; 207 else 208 fd = buildArrayOp(ident, e, sc, e.loc); 209 210 if (fd && fd.errors) 211 { 212 const(char)* fmt; 213 if (tbn.ty == Tstruct || tbn.ty == Tclass) 214 fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations"; 215 else if (!tbn.isscalar()) 216 fmt = "invalid array operation '%s' because %s is not a scalar type"; 217 else 218 fmt = "invalid array operation '%s' for element type %s"; 219 e.error(fmt, e.toChars(), tbn.toChars()); 220 return new ErrorExp(); 221 } 222 223 if (!pFd) 224 arrayfuncs[cast(void*)ident] = fd; 225 226 Expression ev = new VarExp(e.loc, fd); 227 Expression ec = new CallExp(e.loc, ev, arguments); 228 return ec.semantic(sc); 229 } 230 231 extern (C++) Expression arrayOp(BinAssignExp e, Scope* sc) 232 { 233 //printf("BinAssignExp.arrayOp() %s\n", toChars()); 234 235 /* Check that the elements of e1 can be assigned to 236 */ 237 Type tn = e.e1.type.toBasetype().nextOf(); 238 239 if (tn && (!tn.isMutable() || !tn.isAssignable())) 240 { 241 e.error("slice %s is not mutable", e.e1.toChars()); 242 return new ErrorExp(); 243 } 244 if (e.e1.op == TOKarrayliteral) 245 { 246 return e.e1.modifiableLvalue(sc, e.e1); 247 } 248 249 return arrayOp(cast(BinExp)e, sc); 250 } 251 252 /****************************************** 253 * Construct the identifier for the array operation function, 254 * and build the argument list to pass to it. 255 */ 256 extern (C++) void buildArrayIdent(Expression e, OutBuffer* buf, Expressions* arguments) 257 { 258 extern (C++) final class BuildArrayIdentVisitor : Visitor 259 { 260 alias visit = super.visit; 261 OutBuffer* buf; 262 Expressions* arguments; 263 264 public: 265 extern (D) this(OutBuffer* buf, Expressions* arguments) 266 { 267 this.buf = buf; 268 this.arguments = arguments; 269 } 270 271 override void visit(Expression e) 272 { 273 buf.writestring("Exp"); 274 arguments.shift(e); 275 } 276 277 override void visit(CastExp e) 278 { 279 Type tb = e.type.toBasetype(); 280 if (tb.ty == Tarray || tb.ty == Tsarray) 281 { 282 e.e1.accept(this); 283 } 284 else 285 visit(cast(Expression)e); 286 } 287 288 override void visit(ArrayLiteralExp e) 289 { 290 buf.writestring("Slice"); 291 arguments.shift(e); 292 } 293 294 override void visit(SliceExp e) 295 { 296 buf.writestring("Slice"); 297 arguments.shift(e); 298 } 299 300 override void visit(AssignExp e) 301 { 302 /* Evaluate assign expressions right to left 303 */ 304 e.e2.accept(this); 305 e.e1.accept(this); 306 buf.writestring("Assign"); 307 } 308 309 override void visit(BinAssignExp e) 310 { 311 /* Evaluate assign expressions right to left 312 */ 313 e.e2.accept(this); 314 e.e1.accept(this); 315 const(char)* s; 316 switch (e.op) 317 { 318 case TOKaddass: s = "Addass"; break; 319 case TOKminass: s = "Subass"; break; 320 case TOKmulass: s = "Mulass"; break; 321 case TOKdivass: s = "Divass"; break; 322 case TOKmodass: s = "Modass"; break; 323 case TOKxorass: s = "Xorass"; break; 324 case TOKandass: s = "Andass"; break; 325 case TOKorass: s = "Orass"; break; 326 case TOKpowass: s = "Powass"; break; 327 default: 328 assert(0); 329 } 330 buf.writestring(s); 331 } 332 333 override void visit(NegExp e) 334 { 335 e.e1.accept(this); 336 buf.writestring("Neg"); 337 } 338 339 override void visit(ComExp e) 340 { 341 e.e1.accept(this); 342 buf.writestring("Com"); 343 } 344 345 override void visit(BinExp e) 346 { 347 /* Evaluate assign expressions left to right 348 */ 349 const(char)* s = null; 350 switch (e.op) 351 { 352 case TOKadd: s = "Add"; break; 353 case TOKmin: s = "Sub"; break; 354 case TOKmul: s = "Mul"; break; 355 case TOKdiv: s = "Div"; break; 356 case TOKmod: s = "Mod"; break; 357 case TOKxor: s = "Xor"; break; 358 case TOKand: s = "And"; break; 359 case TOKor: s = "Or"; break; 360 case TOKpow: s = "Pow"; break; 361 default: 362 break; 363 } 364 if (s) 365 { 366 Type tb = e.type.toBasetype(); 367 Type t1 = e.e1.type.toBasetype(); 368 Type t2 = e.e2.type.toBasetype(); 369 e.e1.accept(this); 370 if (t1.ty == Tarray && 371 (t2.ty == Tarray && !t1.equivalent(tb) || 372 t2.ty != Tarray && !t1.nextOf().equivalent(e.e2.type))) 373 { 374 // Bugzilla 12780: if A is narrower than B 375 // A[] op B[] 376 // A[] op B 377 buf.writestring("Of"); 378 buf.writestring(t1.nextOf().mutableOf().deco); 379 } 380 e.e2.accept(this); 381 if (t2.ty == Tarray && 382 (t1.ty == Tarray && !t2.equivalent(tb) || 383 t1.ty != Tarray && !t2.nextOf().equivalent(e.e1.type))) 384 { 385 // Bugzilla 12780: if B is narrower than A: 386 // A[] op B[] 387 // A op B[] 388 buf.writestring("Of"); 389 buf.writestring(t2.nextOf().mutableOf().deco); 390 } 391 buf.writestring(s); 392 } 393 else 394 visit(cast(Expression)e); 395 } 396 } 397 398 scope BuildArrayIdentVisitor v = new BuildArrayIdentVisitor(buf, arguments); 399 e.accept(v); 400 } 401 402 /****************************************** 403 * Construct the inner loop for the array operation function, 404 * and build the parameter list. 405 */ 406 extern (C++) Expression buildArrayLoop(Expression e, Parameters* fparams) 407 { 408 extern (C++) final class BuildArrayLoopVisitor : Visitor 409 { 410 alias visit = super.visit; 411 Parameters* fparams; 412 Expression result; 413 414 public: 415 extern (D) this(Parameters* fparams) 416 { 417 this.fparams = fparams; 418 } 419 420 override void visit(Expression e) 421 { 422 Identifier id = Identifier.generateId("c", fparams.dim); 423 auto param = new Parameter(0, e.type, id, null); 424 fparams.shift(param); 425 result = new IdentifierExp(Loc(), id); 426 } 427 428 override void visit(CastExp e) 429 { 430 Type tb = e.type.toBasetype(); 431 if (tb.ty == Tarray || tb.ty == Tsarray) 432 { 433 e.e1.accept(this); 434 } 435 else 436 visit(cast(Expression)e); 437 } 438 439 override void visit(ArrayLiteralExp e) 440 { 441 Identifier id = Identifier.generateId("p", fparams.dim); 442 auto param = new Parameter(STCconst, e.type, id, null); 443 fparams.shift(param); 444 Expression ie = new IdentifierExp(Loc(), id); 445 Expression index = new IdentifierExp(Loc(), Id.p); 446 result = new ArrayExp(Loc(), ie, index); 447 } 448 449 override void visit(SliceExp e) 450 { 451 Identifier id = Identifier.generateId("p", fparams.dim); 452 auto param = new Parameter(STCconst, e.type, id, null); 453 fparams.shift(param); 454 Expression ie = new IdentifierExp(Loc(), id); 455 Expression index = new IdentifierExp(Loc(), Id.p); 456 result = new ArrayExp(Loc(), ie, index); 457 } 458 459 override void visit(AssignExp e) 460 { 461 /* Evaluate assign expressions right to left 462 */ 463 Expression ex2 = buildArrayLoop(e.e2); 464 /* Need the cast because: 465 * b = c + p[i]; 466 * where b is a byte fails because (c + p[i]) is an int 467 * which cannot be implicitly cast to byte. 468 */ 469 ex2 = new CastExp(Loc(), ex2, e.e1.type.nextOf()); 470 Expression ex1 = buildArrayLoop(e.e1); 471 Parameter param = (*fparams)[0]; 472 param.storageClass = 0; 473 result = new AssignExp(Loc(), ex1, ex2); 474 } 475 476 override void visit(BinAssignExp e) 477 { 478 /* Evaluate assign expressions right to left 479 */ 480 Expression ex2 = buildArrayLoop(e.e2); 481 Expression ex1 = buildArrayLoop(e.e1); 482 Parameter param = (*fparams)[0]; 483 param.storageClass = 0; 484 switch (e.op) 485 { 486 case TOKaddass: result = new AddAssignExp(e.loc, ex1, ex2); return; 487 case TOKminass: result = new MinAssignExp(e.loc, ex1, ex2); return; 488 case TOKmulass: result = new MulAssignExp(e.loc, ex1, ex2); return; 489 case TOKdivass: result = new DivAssignExp(e.loc, ex1, ex2); return; 490 case TOKmodass: result = new ModAssignExp(e.loc, ex1, ex2); return; 491 case TOKxorass: result = new XorAssignExp(e.loc, ex1, ex2); return; 492 case TOKandass: result = new AndAssignExp(e.loc, ex1, ex2); return; 493 case TOKorass: result = new OrAssignExp(e.loc, ex1, ex2); return; 494 case TOKpowass: result = new PowAssignExp(e.loc, ex1, ex2); return; 495 default: 496 assert(0); 497 } 498 } 499 500 override void visit(NegExp e) 501 { 502 Expression ex1 = buildArrayLoop(e.e1); 503 result = new NegExp(Loc(), ex1); 504 } 505 506 override void visit(ComExp e) 507 { 508 Expression ex1 = buildArrayLoop(e.e1); 509 result = new ComExp(Loc(), ex1); 510 } 511 512 override void visit(BinExp e) 513 { 514 if (isBinArrayOp(e.op)) 515 { 516 /* Evaluate assign expressions left to right 517 */ 518 BinExp be = cast(BinExp)e.copy(); 519 be.e1 = buildArrayLoop(be.e1); 520 be.e2 = buildArrayLoop(be.e2); 521 be.type = null; 522 result = be; 523 return; 524 } 525 else 526 { 527 visit(cast(Expression)e); 528 return; 529 } 530 } 531 532 Expression buildArrayLoop(Expression e) 533 { 534 e.accept(this); 535 return result; 536 } 537 } 538 539 scope BuildArrayLoopVisitor v = new BuildArrayLoopVisitor(fparams); 540 return v.buildArrayLoop(e); 541 } 542 543 /*********************************************** 544 * Test if expression is a unary array op. 545 */ 546 extern (C++) bool isUnaArrayOp(TOK op) 547 { 548 switch (op) 549 { 550 case TOKneg: 551 case TOKtilde: 552 return true; 553 default: 554 break; 555 } 556 return false; 557 } 558 559 /*********************************************** 560 * Test if expression is a binary array op. 561 */ 562 extern (C++) bool isBinArrayOp(TOK op) 563 { 564 switch (op) 565 { 566 case TOKadd: 567 case TOKmin: 568 case TOKmul: 569 case TOKdiv: 570 case TOKmod: 571 case TOKxor: 572 case TOKand: 573 case TOKor: 574 case TOKpow: 575 return true; 576 default: 577 break; 578 } 579 return false; 580 } 581 582 /*********************************************** 583 * Test if expression is a binary assignment array op. 584 */ 585 extern (C++) bool isBinAssignArrayOp(TOK op) 586 { 587 switch (op) 588 { 589 case TOKaddass: 590 case TOKminass: 591 case TOKmulass: 592 case TOKdivass: 593 case TOKmodass: 594 case TOKxorass: 595 case TOKandass: 596 case TOKorass: 597 case TOKpowass: 598 return true; 599 default: 600 break; 601 } 602 return false; 603 } 604 605 /*********************************************** 606 * Test if operand is a valid array op operand. 607 */ 608 extern (C++) bool isArrayOpOperand(Expression e) 609 { 610 //printf("Expression.isArrayOpOperand() %s\n", e->toChars()); 611 if (e.op == TOKslice) 612 return true; 613 if (e.op == TOKarrayliteral) 614 { 615 Type t = e.type.toBasetype(); 616 while (t.ty == Tarray || t.ty == Tsarray) 617 t = t.nextOf().toBasetype(); 618 return (t.ty != Tvoid); 619 } 620 Type tb = e.type.toBasetype(); 621 if (tb.ty == Tarray) 622 { 623 return (isUnaArrayOp(e.op) || 624 isBinArrayOp(e.op) || 625 isBinAssignArrayOp(e.op) || 626 e.op == TOKassign); 627 } 628 return false; 629 }