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 _delegatize.d)
9  */
10 
11 module ddmd.delegatize;
12 
13 import core.stdc.stdio;
14 import ddmd.apply;
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.mtype;
22 import ddmd.statement;
23 import ddmd.tokens;
24 import ddmd.visitor;
25 
26 extern (C++) Expression toDelegate(Expression e, Type t, Scope* sc)
27 {
28     //printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), e->toChars());
29     Loc loc = e.loc;
30     auto tf = new TypeFunction(null, t, 0, LINKd);
31     if (t.hasWild())
32         tf.mod = MODwild;
33     auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, null);
34     sc = sc.push();
35     sc.parent = fld; // set current function to be the delegate
36     lambdaSetParent(e, sc);
37     bool r = lambdaCheckForNestedRef(e, sc);
38     sc = sc.pop();
39     if (r)
40         return new ErrorExp();
41     Statement s;
42     if (t.ty == Tvoid)
43         s = new ExpStatement(loc, e);
44     else
45         s = new ReturnStatement(loc, e);
46     fld.fbody = s;
47     e = new FuncExp(loc, fld);
48     e = e.semantic(sc);
49     return e;
50 }
51 
52 /******************************************
53  * Patch the parent of declarations to be the new function literal.
54  */
55 extern (C++) void lambdaSetParent(Expression e, Scope* sc)
56 {
57     extern (C++) final class LambdaSetParent : StoppableVisitor
58     {
59         alias visit = super.visit;
60         Scope* sc;
61 
62     public:
63         extern (D) this(Scope* sc)
64         {
65             this.sc = sc;
66         }
67 
68         override void visit(Expression)
69         {
70         }
71 
72         override void visit(DeclarationExp e)
73         {
74             e.declaration.parent = sc.parent;
75         }
76 
77         override void visit(IndexExp e)
78         {
79             if (e.lengthVar)
80             {
81                 //printf("lengthVar\n");
82                 e.lengthVar.parent = sc.parent;
83             }
84         }
85 
86         override void visit(SliceExp e)
87         {
88             if (e.lengthVar)
89             {
90                 //printf("lengthVar\n");
91                 e.lengthVar.parent = sc.parent;
92             }
93         }
94     }
95 
96     scope LambdaSetParent lsp = new LambdaSetParent(sc);
97     walkPostorder(e, lsp);
98 }
99 
100 /*******************************************
101  * Look for references to variables in a scope enclosing the new function literal.
102  * Returns true if error occurs.
103  */
104 extern (C++) bool lambdaCheckForNestedRef(Expression e, Scope* sc)
105 {
106     extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
107     {
108         alias visit = super.visit;
109     public:
110         Scope* sc;
111         bool result;
112 
113         extern (D) this(Scope* sc)
114         {
115             this.sc = sc;
116         }
117 
118         override void visit(Expression)
119         {
120         }
121 
122         override void visit(SymOffExp e)
123         {
124             VarDeclaration v = e.var.isVarDeclaration();
125             if (v)
126                 result = v.checkNestedReference(sc, Loc());
127         }
128 
129         override void visit(VarExp e)
130         {
131             VarDeclaration v = e.var.isVarDeclaration();
132             if (v)
133                 result = v.checkNestedReference(sc, Loc());
134         }
135 
136         override void visit(ThisExp e)
137         {
138             if (e.var)
139                 result = e.var.checkNestedReference(sc, Loc());
140         }
141 
142         override void visit(DeclarationExp e)
143         {
144             VarDeclaration v = e.declaration.isVarDeclaration();
145             if (v)
146             {
147                 result = v.checkNestedReference(sc, Loc());
148                 if (result)
149                     return;
150                 /* Some expressions cause the frontend to create a temporary.
151                  * For example, structs with cpctors replace the original
152                  * expression e with:
153                  *  __cpcttmp = __cpcttmp.cpctor(e);
154                  *
155                  * In this instance, we need to ensure that the original
156                  * expression e does not have any nested references by
157                  * checking the declaration initializer too.
158                  */
159                 if (v._init && v._init.isExpInitializer())
160                 {
161                     Expression ie = v._init.toExpression();
162                     result = lambdaCheckForNestedRef(ie, sc);
163                 }
164             }
165         }
166     }
167 
168     scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
169     walkPostorder(e, v);
170     return v.result;
171 }
172 
173 bool checkNestedRef(Dsymbol s, Dsymbol p)
174 {
175     while (s)
176     {
177         if (s == p) // hit!
178             return false;
179 
180         if (auto fd = s.isFuncDeclaration())
181         {
182             if (!fd.isThis() && !fd.isNested())
183                 break;
184 
185             // Bugzilla 15332: change to delegate if fd is actually nested.
186             if (auto fld = fd.isFuncLiteralDeclaration())
187                 fld.tok = TOKdelegate;
188         }
189         if (auto ad = s.isAggregateDeclaration())
190         {
191             if (ad.storage_class & STCstatic)
192                 break;
193         }
194         s = s.toParent2();
195     }
196     return true;
197 }