1 
2 /* Compiler implementation of the D programming language
3  * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4  * written by Walter Bright
5  * http://www.digitalmars.com
6  * Distributed under the Boost Software License, Version 1.0.
7  * http://www.boost.org/LICENSE_1_0.txt
8  * https://github.com/D-Programming-Language/dmd/blob/master/src/delegatize.c
9  */
10 
11 #include "root/dsystem.h"
12 
13 #include "mars.h"
14 #include "expression.h"
15 #include "statement.h"
16 #include "mtype.h"
17 #include "utf.h"
18 #include "declaration.h"
19 #include "aggregate.h"
20 #include "scope.h"
21 #include "init.h"
22 #include "tokens.h"
23 
24 
25 bool walkPostorder(Expression *e, StoppableVisitor *v);
26 void lambdaSetParent(Expression *e, Scope *sc);
27 bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
28 Expression *semantic(Expression *e, Scope *sc);
29 
30 /********************************************
31  * Convert from expression to delegate that returns the expression,
32  * i.e. convert:
33  *      expr
34  * to:
35  *      typeof(expr) delegate() { return expr; }
36  */
toDelegate(Expression * e,Type * t,Scope * sc)37 Expression *toDelegate(Expression *e, Type* t, Scope *sc)
38 {
39     //printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), e->toChars());
40     Loc loc = e->loc;
41 
42     TypeFunction *tf = new TypeFunction(NULL, t, 0, LINKd);
43     if (t->hasWild())
44         tf->mod = MODwild;
45     FuncLiteralDeclaration *fld =
46         new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL);
47 
48     sc = sc->push();
49     sc->parent = fld;           // set current function to be the delegate
50     lambdaSetParent(e, sc);
51     bool r = lambdaCheckForNestedRef(e, sc);
52     sc = sc->pop();
53 
54     if (r)
55         return new ErrorExp();
56 
57     Statement *s;
58     if (t->ty == Tvoid)
59         s = new ExpStatement(loc, e);
60     else
61         s = new ReturnStatement(loc, e);
62     fld->fbody = s;
63 
64     e = new FuncExp(loc, fld);
65     e = semantic(e, sc);
66     return e;
67 }
68 
69 /******************************************
70  * Patch the parent of declarations to be the new function literal.
71  */
lambdaSetParent(Expression * e,Scope * sc)72 void lambdaSetParent(Expression *e, Scope *sc)
73 {
74     class LambdaSetParent : public StoppableVisitor
75     {
76         Scope *sc;
77     public:
78         LambdaSetParent(Scope *sc) : sc(sc) {}
79 
80         void visit(Expression *)
81         {
82         }
83 
84         void visit(DeclarationExp *e)
85         {
86             e->declaration->parent = sc->parent;
87         }
88 
89         void visit(IndexExp *e)
90         {
91             if (e->lengthVar)
92             {
93                 //printf("lengthVar\n");
94                 e->lengthVar->parent = sc->parent;
95             }
96         }
97 
98         void visit(SliceExp *e)
99         {
100             if (e->lengthVar)
101             {
102                 //printf("lengthVar\n");
103                 e->lengthVar->parent = sc->parent;
104             }
105         }
106     };
107 
108     LambdaSetParent lsp(sc);
109     walkPostorder(e, &lsp);
110 }
111 
112 /*******************************************
113  * Look for references to variables in a scope enclosing the new function literal.
114  * Returns true if error occurs.
115  */
lambdaCheckForNestedRef(Expression * e,Scope * sc)116 bool lambdaCheckForNestedRef(Expression *e, Scope *sc)
117 {
118     class LambdaCheckForNestedRef : public StoppableVisitor
119     {
120     public:
121         Scope *sc;
122         bool result;
123 
124         LambdaCheckForNestedRef(Scope *sc)
125             : sc(sc), result(false)
126         {
127         }
128 
129         void visit(Expression *)
130         {
131         }
132 
133         void visit(SymOffExp *e)
134         {
135             VarDeclaration *v = e->var->isVarDeclaration();
136             if (v)
137                 result = v->checkNestedReference(sc, Loc());
138         }
139 
140         void visit(VarExp *e)
141         {
142             VarDeclaration *v = e->var->isVarDeclaration();
143             if (v)
144                 result = v->checkNestedReference(sc, Loc());
145         }
146 
147         void visit(ThisExp *e)
148         {
149             if (e->var)
150                 result = e->var->checkNestedReference(sc, Loc());
151         }
152 
153         void visit(DeclarationExp *e)
154         {
155             VarDeclaration *v = e->declaration->isVarDeclaration();
156             if (v)
157             {
158                 result = v->checkNestedReference(sc, Loc());
159                 if (result)
160                     return;
161 
162                 /* Some expressions cause the frontend to create a temporary.
163                  * For example, structs with cpctors replace the original
164                  * expression e with:
165                  *  __cpcttmp = __cpcttmp.cpctor(e);
166                  *
167                  * In this instance, we need to ensure that the original
168                  * expression e does not have any nested references by
169                  * checking the declaration initializer too.
170                  */
171                 if (v->_init && v->_init->isExpInitializer())
172                 {
173                     Expression *ie = initializerToExpression(v->_init);
174                     result = lambdaCheckForNestedRef(ie, sc);
175                 }
176             }
177         }
178     };
179 
180     LambdaCheckForNestedRef v(sc);
181     walkPostorder(e, &v);
182     return v.result;
183 }
184 
checkNestedRef(Dsymbol * s,Dsymbol * p)185 bool checkNestedRef(Dsymbol *s, Dsymbol *p)
186 {
187     while (s)
188     {
189         if (s == p) // hit!
190             return false;
191 
192         if (FuncDeclaration *fd = s->isFuncDeclaration())
193         {
194             if (!fd->isThis() && !fd->isNested())
195                 break;
196 
197             // Bugzilla 15332: change to delegate if fd is actually nested.
198             if (FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration())
199                 fld->tok = TOKdelegate;
200         }
201         if (AggregateDeclaration *ad = s->isAggregateDeclaration())
202         {
203             if (ad->storage_class & STCstatic)
204                 break;
205         }
206         s = s->toParent2();
207     }
208     return true;
209 }
210