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