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 _escape.d) 9 */ 10 11 module ddmd.escape; 12 13 import core.stdc.stdio : printf; 14 15 import ddmd.declaration; 16 import ddmd.dscope; 17 import ddmd.dsymbol; 18 import ddmd.errors; 19 import ddmd.expression; 20 import ddmd.func; 21 import ddmd.globals; 22 import ddmd.init; 23 import ddmd.mtype; 24 import ddmd.root.rootobject; 25 import ddmd.tokens; 26 import ddmd.visitor; 27 import ddmd.arraytypes; 28 29 30 31 /************************************ 32 * Detect cases where pointers to the stack can 'escape' the 33 * lifetime of the stack frame. 34 * Print error messages when these are detected. 35 * Params: 36 * sc = used to determine current function and module 37 * e = expression to check for any pointers to the stack 38 * gag = do not print error messages 39 * Returns: 40 * true if pointers to the stack can escape 41 */ 42 bool checkEscape(Scope* sc, Expression e, bool gag) 43 { 44 return checkEscapeImpl(sc, e, false, gag); 45 } 46 47 /************************************ 48 * Detect cases where returning 'e' by ref can result in a reference to the stack 49 * being returned. 50 * Print error messages when these are detected. 51 * Params: 52 * sc = used to determine current function and module 53 * e = expression to check 54 * gag = do not print error messages 55 * Returns: 56 * true if references to the stack can escape 57 */ 58 bool checkEscapeRef(Scope* sc, Expression e, bool gag) 59 { 60 version (none) 61 { 62 printf("[%s] checkEscapeRef, e = %s\n", e.loc.toChars(), e.toChars()); 63 printf("current function %s\n", sc.func.toChars()); 64 printf("parent2 function %s\n", sc.func.toParent2().toChars()); 65 } 66 67 return checkEscapeImpl(sc, e, true, gag); 68 } 69 70 private bool checkEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) 71 { 72 VarDeclarations byref, byvalue; 73 Expressions byexp; 74 75 if (refs) 76 escapeByRef(e, &byref, &byvalue, &byexp); 77 else 78 escapeByValue(e, &byref, &byvalue, &byexp); 79 80 if (!byref.dim && !byvalue.dim && !byexp.dim) 81 return false; 82 83 bool result = false; 84 foreach (VarDeclaration v; byvalue) 85 { 86 if (v.isDataseg()) 87 continue; 88 89 if (v.toParent2() != sc.func) 90 continue; 91 92 if (v.isScope()) 93 { 94 if (!gag) 95 error(e.loc, "scope variable %s may not be returned", v.toChars()); 96 result = true; 97 } 98 else if (v.storage_class & STCvariadic) 99 { 100 Type tb = v.type.toBasetype(); 101 if (tb.ty == Tarray || tb.ty == Tsarray) 102 { 103 if (!gag) 104 error(e.loc, "escaping reference to variadic parameter %s", v.toChars()); 105 result = false; 106 } 107 } 108 } 109 110 foreach (VarDeclaration v; byref) 111 { 112 if (v.isDataseg()) 113 continue; 114 115 Dsymbol p = v.toParent2(); 116 117 if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func) 118 { 119 if (!gag) 120 error(e.loc, "escaping reference to local variable %s", v.toChars()); 121 result = true; 122 continue; 123 } 124 125 /* Check for returning a ref variable by 'ref', but should be 'return ref' 126 * Infer the addition of 'return', or set result to be the offending expression. 127 */ 128 if (global.params.useDIP25 && 129 (v.storage_class & (STCref | STCout)) && 130 !(v.storage_class & (STCreturn | STCforeach))) 131 { 132 if (sc.func.flags & FUNCFLAGreturnInprocess && p == sc.func) 133 { 134 inferReturn(sc.func, v); // infer addition of 'return' 135 } 136 else if (sc._module && sc._module.isRoot()) 137 { 138 // Only look for errors if in module listed on command line 139 140 if (p == sc.func) 141 { 142 //printf("escaping reference to local ref variable %s\n", v.toChars()); 143 //printf("storage class = x%llx\n", v.storage_class); 144 if (!gag) 145 error(e.loc, "escaping reference to local variable %s", v.toChars()); 146 result = true; 147 continue; 148 } 149 // Don't need to be concerned if v's parent does not return a ref 150 FuncDeclaration fd = p.isFuncDeclaration(); 151 if (fd && fd.type && fd.type.ty == Tfunction) 152 { 153 TypeFunction tf = cast(TypeFunction)fd.type; 154 if (tf.isref) 155 { 156 if (!gag) 157 error(e.loc, "escaping reference to outer local variable %s", v.toChars()); 158 result = true; 159 continue; 160 } 161 } 162 163 } 164 } 165 } 166 167 foreach (Expression er; byexp) 168 { 169 if (!gag) 170 error(er.loc, "escaping reference to stack allocated value returned by %s", er.toChars()); 171 result = true; 172 } 173 174 return result; 175 } 176 177 178 /************************************* 179 * Variable v needs to have 'return' inferred for it. 180 * Params: 181 * fd = function that v is a parameter to 182 * v = parameter that needs to be STCreturn 183 */ 184 185 private void inferReturn(FuncDeclaration fd, VarDeclaration v) 186 { 187 // v is a local in the current function 188 189 //printf("inferring 'return' for variable '%s'\n", v.toChars()); 190 v.storage_class |= STCreturn; 191 192 TypeFunction tf = cast(TypeFunction)fd.type; 193 if (v == fd.vthis) 194 { 195 /* v is the 'this' reference, so mark the function 196 */ 197 fd.storage_class |= STCreturn; 198 if (tf.ty == Tfunction) 199 { 200 //printf("'this' too %p %s\n", tf, sc.func.toChars()); 201 tf.isreturn = true; 202 } 203 } 204 else 205 { 206 // Perform 'return' inference on parameter 207 if (tf.ty == Tfunction && tf.parameters) 208 { 209 const dim = Parameter.dim(tf.parameters); 210 foreach (const i; 0 .. dim) 211 { 212 Parameter p = Parameter.getNth(tf.parameters, i); 213 if (p.ident == v.ident) 214 { 215 p.storageClass |= STCreturn; 216 break; // there can be only one 217 } 218 } 219 } 220 } 221 } 222 223 224 /**************************************** 225 * e is an expression to be returned by value, and that value contains pointers. 226 * Walk e to determine which variables are possibly being 227 * returned by value, such as: 228 * int* function(int* p) { return p; } 229 * If e is a form of &p, determine which variables have content 230 * which is being returned as ref, such as: 231 * int* function(int i) { return &i; } 232 * Multiple variables can be inserted, because of expressions like this: 233 * int function(bool b, int i, int* p) { return b ? &i : p; } 234 * 235 * No side effects. 236 * 237 * Params: 238 * e = expression to be returned by value 239 * byref = array into which variables being returned by ref are inserted 240 * byvalue = array into which variables with values containing pointers are inserted 241 * byexp = array into which temporaries being returned by ref are inserted 242 */ 243 private void escapeByValue(Expression e, VarDeclarations* byref, VarDeclarations* byvalue, Expressions* byexp) 244 { 245 //printf("[%s] checkEscape, e = %s\n", e.loc.toChars(), e.toChars()); 246 extern (C++) final class EscapeVisitor : Visitor 247 { 248 alias visit = super.visit; 249 public: 250 VarDeclarations* byref; 251 VarDeclarations* byvalue; 252 Expressions* byexp; 253 254 extern (D) this(VarDeclarations* byref, VarDeclarations* byvalue, Expressions* byexp) 255 { 256 this.byref = byref; 257 this.byvalue = byvalue; 258 this.byexp = byexp; 259 } 260 261 override void visit(Expression e) 262 { 263 } 264 265 override void visit(AddrExp e) 266 { 267 escapeByRef(e.e1, byref, byvalue, byexp); 268 } 269 270 override void visit(SymOffExp e) 271 { 272 VarDeclaration v = e.var.isVarDeclaration(); 273 if (v) 274 byref.push(v); 275 } 276 277 override void visit(VarExp e) 278 { 279 VarDeclaration v = e.var.isVarDeclaration(); 280 if (v) 281 byvalue.push(v); 282 } 283 284 override void visit(TupleExp e) 285 { 286 if (e.exps.dim) 287 { 288 (*e.exps)[e.exps.dim - 1].accept(this); // last one only 289 } 290 } 291 292 override void visit(ArrayLiteralExp e) 293 { 294 Type tb = e.type.toBasetype(); 295 if (tb.ty == Tsarray || tb.ty == Tarray) 296 { 297 if (e.basis) 298 e.basis.accept(this); 299 foreach (el; *e.elements) 300 { 301 if (el) 302 el.accept(this); 303 } 304 } 305 } 306 307 override void visit(StructLiteralExp e) 308 { 309 if (e.elements) 310 { 311 foreach (ex; *e.elements) 312 { 313 if (ex) 314 ex.accept(this); 315 } 316 } 317 } 318 319 override void visit(NewExp e) 320 { 321 Type tb = e.newtype.toBasetype(); 322 if (tb.ty == Tstruct && !e.member && e.arguments) 323 { 324 foreach (ex; *e.arguments) 325 { 326 if (ex) 327 ex.accept(this); 328 } 329 } 330 } 331 332 override void visit(CastExp e) 333 { 334 Type tb = e.type.toBasetype(); 335 if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) 336 { 337 escapeByRef(e.e1, byref, byvalue, byexp); 338 } 339 } 340 341 override void visit(SliceExp e) 342 { 343 if (e.e1.op == TOKvar) 344 { 345 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 346 Type tb = e.type.toBasetype(); 347 if (v) 348 { 349 if (tb.ty == Tsarray) 350 return; 351 if (v.storage_class & STCvariadic) 352 { 353 byvalue.push(v); 354 return; 355 } 356 } 357 } 358 Type t1b = e.e1.type.toBasetype(); 359 if (t1b.ty == Tsarray) 360 escapeByRef(e.e1, byref, byvalue, byexp); 361 else 362 e.e1.accept(this); 363 } 364 365 override void visit(BinExp e) 366 { 367 Type tb = e.type.toBasetype(); 368 if (tb.ty == Tpointer) 369 { 370 e.e1.accept(this); 371 e.e2.accept(this); 372 } 373 } 374 375 override void visit(BinAssignExp e) 376 { 377 e.e2.accept(this); 378 } 379 380 override void visit(AssignExp e) 381 { 382 e.e2.accept(this); 383 } 384 385 override void visit(CommaExp e) 386 { 387 e.e2.accept(this); 388 } 389 390 override void visit(CondExp e) 391 { 392 e.e1.accept(this); 393 e.e2.accept(this); 394 } 395 } 396 397 scope EscapeVisitor v = new EscapeVisitor(byref, byvalue, byexp); 398 e.accept(v); 399 } 400 401 402 /**************************************** 403 * e is an expression to be returned by 'ref'. 404 * Walk e to determine which variables are possibly being 405 * returned by ref, such as: 406 * ref int function(int i) { return i; } 407 * If e is a form of *p, determine which variables have content 408 * which is being returned as ref, such as: 409 * ref int function(int* p) { return *p; } 410 * Multiple variables can be inserted, because of expressions like this: 411 * ref int function(bool b, int i, int* p) { return b ? i : *p; } 412 * 413 * No side effects. 414 * 415 * Params: 416 * e = expression to be returned by 'ref' 417 * byref = array into which variables being returned by ref are inserted 418 * byvalue = array into which variables with values containing pointers are inserted 419 * byexp = array into which temporaries being returned by ref are inserted 420 */ 421 private void escapeByRef(Expression e, VarDeclarations* byref, VarDeclarations *byvalue, Expressions* byexp) 422 { 423 extern (C++) final class EscapeRefVisitor : Visitor 424 { 425 alias visit = super.visit; 426 public: 427 VarDeclarations* byref; 428 VarDeclarations* byvalue; 429 Expressions* byexp; 430 431 extern (D) this(VarDeclarations* byref, VarDeclarations* byvalue, Expressions* byexp) 432 { 433 this.byref = byref; 434 this.byvalue = byvalue; 435 this.byexp = byexp; 436 } 437 438 override void visit(Expression e) 439 { 440 } 441 442 override void visit(VarExp e) 443 { 444 auto v = e.var.isVarDeclaration(); 445 if (v) 446 { 447 if (v.storage_class & STCref && v.storage_class & (STCforeach | STCtemp) && v._init) 448 { 449 /* If compiler generated ref temporary 450 * (ref v = ex; ex) 451 * look at the initializer instead 452 */ 453 if (ExpInitializer ez = v._init.isExpInitializer()) 454 { 455 assert(ez.exp && ez.exp.op == TOKconstruct); 456 Expression ex = (cast(ConstructExp)ez.exp).e2; 457 ex.accept(this); 458 } 459 } 460 else 461 byref.push(v); 462 } 463 } 464 465 override void visit(ThisExp e) 466 { 467 auto v = e.var.isVarDeclaration(); 468 if (v) 469 byref.push(v); 470 } 471 472 override void visit(PtrExp e) 473 { 474 escapeByValue(e.e1, byref, byvalue, byexp); 475 } 476 477 override void visit(IndexExp e) 478 { 479 Type tb = e.e1.type.toBasetype(); 480 if (e.e1.op == TOKvar) 481 { 482 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 483 if (tb.ty == Tarray || tb.ty == Tsarray) 484 { 485 if (v && v.storage_class & STCvariadic) 486 { 487 byref.push(v); 488 return; 489 } 490 } 491 } 492 if (tb.ty == Tsarray) 493 { 494 e.e1.accept(this); 495 } 496 else if (tb.ty == Tarray) 497 { 498 escapeByValue(e.e1, byref, byvalue, byexp); 499 } 500 } 501 502 override void visit(DotVarExp e) 503 { 504 Type t1b = e.e1.type.toBasetype(); 505 if (t1b.ty == Tclass) 506 escapeByValue(e.e1, byref, byvalue, byexp); 507 else 508 e.e1.accept(this); 509 } 510 511 override void visit(BinAssignExp e) 512 { 513 e.e1.accept(this); 514 } 515 516 override void visit(AssignExp e) 517 { 518 e.e1.accept(this); 519 } 520 521 override void visit(CommaExp e) 522 { 523 e.e2.accept(this); 524 } 525 526 override void visit(CondExp e) 527 { 528 e.e1.accept(this); 529 e.e2.accept(this); 530 } 531 532 override void visit(CallExp e) 533 { 534 /* If the function returns by ref, check each argument that is 535 * passed as 'return ref'. 536 */ 537 Type t1 = e.e1.type.toBasetype(); 538 TypeFunction tf; 539 if (t1.ty == Tdelegate) 540 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; 541 else if (t1.ty == Tfunction) 542 tf = cast(TypeFunction)t1; 543 else 544 return; 545 if (tf.isref) 546 { 547 if (e.arguments && e.arguments.dim) 548 { 549 /* j=1 if _arguments[] is first argument, 550 * skip it because it is not passed by ref 551 */ 552 int j = (tf.linkage == LINKd && tf.varargs == 1); 553 for (size_t i = j; i < e.arguments.dim; ++i) 554 { 555 Expression arg = (*e.arguments)[i]; 556 size_t nparams = Parameter.dim(tf.parameters); 557 if (i - j < nparams && i >= j) 558 { 559 Parameter p = Parameter.getNth(tf.parameters, i - j); 560 if ((p.storageClass & (STCout | STCref)) && (p.storageClass & STCreturn)) 561 arg.accept(this); 562 } 563 } 564 } 565 // If 'this' is returned by ref, check it too 566 if (e.e1.op == TOKdotvar && t1.ty == Tfunction) 567 { 568 DotVarExp dve = cast(DotVarExp)e.e1; 569 if (dve.var.storage_class & STCreturn || tf.isreturn) 570 dve.e1.accept(this); 571 } 572 } 573 else 574 byexp.push(e); 575 } 576 } 577 578 scope EscapeRefVisitor v = new EscapeRefVisitor(byref, byvalue, byexp); 579 e.accept(v); 580 } 581