1 /** 2 * Implements conversion from expressions to delegates for lazy parameters. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters) 5 * 6 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d, _delegatize.d) 10 * Documentation: https://dlang.org/phobos/dmd_delegatize.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d 12 */ 13 14 module dmd.delegatize; 15 16 import core.stdc.stdio; 17 import dmd.apply; 18 import dmd.declaration; 19 import dmd.dscope; 20 import dmd.dsymbol; 21 import dmd.expression; 22 import dmd.expressionsem; 23 import dmd.func; 24 import dmd.globals; 25 import dmd.init; 26 import dmd.initsem; 27 import dmd.mtype; 28 import dmd.statement; 29 import dmd.tokens; 30 import dmd.visitor; 31 32 33 /********************************* 34 * Convert expression into a delegate. 35 * 36 * Used to convert the argument to a lazy parameter. 37 * 38 * Params: 39 * e = argument to convert to a delegate 40 * t = the type to be returned by the delegate 41 * sc = context 42 * Returns: 43 * A delegate literal 44 */ 45 Expression toDelegate(Expression e, Type t, Scope* sc) 46 { 47 //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars()); 48 Loc loc = e.loc; 49 auto tf = new TypeFunction(ParameterList(), t, LINK.d); 50 if (t.hasWild()) 51 tf.mod = MODFlags.wild; 52 auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null); 53 lambdaSetParent(e, fld); 54 55 sc = sc.push(); 56 sc.parent = fld; // set current function to be the delegate 57 bool r = lambdaCheckForNestedRef(e, sc); 58 sc = sc.pop(); 59 if (r) 60 return new ErrorExp(); 61 62 Statement s; 63 if (t.ty == Tvoid) 64 s = new ExpStatement(loc, e); 65 else 66 s = new ReturnStatement(loc, e); 67 fld.fbody = s; 68 e = new FuncExp(loc, fld); 69 e = e.expressionSemantic(sc); 70 return e; 71 } 72 73 /****************************************** 74 * Patch the parent of declarations to be the new function literal. 75 * 76 * Since the expression is going to be moved into a function literal, 77 * the parent for declarations in the expression needs to be 78 * reset to that function literal. 79 * Params: 80 * e = expression to check 81 * fd = function literal symbol (the new parent) 82 */ 83 private void lambdaSetParent(Expression e, FuncDeclaration fd) 84 { 85 extern (C++) final class LambdaSetParent : StoppableVisitor 86 { 87 alias visit = typeof(super).visit; 88 FuncDeclaration fd; 89 90 private void setParent(Dsymbol s) 91 { 92 VarDeclaration vd = s.isVarDeclaration(); 93 FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null; 94 s.parent = fd; 95 if (!vd || !pfd) 96 return; 97 // move to fd's closure when applicable 98 foreach (i; 0 .. pfd.closureVars.dim) 99 { 100 if (vd == pfd.closureVars[i]) 101 { 102 pfd.closureVars.remove(i); 103 fd.closureVars.push(vd); 104 break; 105 } 106 } 107 } 108 109 public: 110 extern (D) this(FuncDeclaration fd) 111 { 112 this.fd = fd; 113 } 114 115 override void visit(Expression) 116 { 117 } 118 119 override void visit(DeclarationExp e) 120 { 121 setParent(e.declaration); 122 e.declaration.accept(this); 123 } 124 125 override void visit(IndexExp e) 126 { 127 if (e.lengthVar) 128 { 129 //printf("lengthVar\n"); 130 setParent(e.lengthVar); 131 e.lengthVar.accept(this); 132 } 133 } 134 135 override void visit(SliceExp e) 136 { 137 if (e.lengthVar) 138 { 139 //printf("lengthVar\n"); 140 setParent(e.lengthVar); 141 e.lengthVar.accept(this); 142 } 143 } 144 145 override void visit(Dsymbol) 146 { 147 } 148 149 override void visit(VarDeclaration v) 150 { 151 if (v._init) 152 v._init.accept(this); 153 } 154 155 override void visit(Initializer) 156 { 157 } 158 159 override void visit(ExpInitializer ei) 160 { 161 walkPostorder(ei.exp ,this); 162 } 163 164 override void visit(StructInitializer si) 165 { 166 foreach (i, const id; si.field) 167 if (Initializer iz = si.value[i]) 168 iz.accept(this); 169 } 170 171 override void visit(ArrayInitializer ai) 172 { 173 foreach (i, ex; ai.index) 174 { 175 if (ex) 176 walkPostorder(ex, this); 177 if (Initializer iz = ai.value[i]) 178 iz.accept(this); 179 } 180 } 181 } 182 183 scope LambdaSetParent lsp = new LambdaSetParent(fd); 184 walkPostorder(e, lsp); 185 } 186 187 /******************************************* 188 * Look for references to variables in a scope enclosing the new function literal. 189 * 190 * Essentially just calls `checkNestedReference() for each variable reference in `e`. 191 * Params: 192 * sc = context 193 * e = expression to check 194 * Returns: 195 * true if error occurs. 196 */ 197 bool lambdaCheckForNestedRef(Expression e, Scope* sc) 198 { 199 extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor 200 { 201 alias visit = typeof(super).visit; 202 public: 203 Scope* sc; 204 bool result; 205 206 extern (D) this(Scope* sc) 207 { 208 this.sc = sc; 209 } 210 211 override void visit(Expression) 212 { 213 } 214 215 override void visit(SymOffExp e) 216 { 217 VarDeclaration v = e.var.isVarDeclaration(); 218 if (v) 219 result = v.checkNestedReference(sc, Loc.initial); 220 } 221 222 override void visit(VarExp e) 223 { 224 VarDeclaration v = e.var.isVarDeclaration(); 225 if (v) 226 result = v.checkNestedReference(sc, Loc.initial); 227 } 228 229 override void visit(ThisExp e) 230 { 231 if (e.var) 232 result = e.var.checkNestedReference(sc, Loc.initial); 233 } 234 235 override void visit(DeclarationExp e) 236 { 237 VarDeclaration v = e.declaration.isVarDeclaration(); 238 if (v) 239 { 240 result = v.checkNestedReference(sc, Loc.initial); 241 if (result) 242 return; 243 /* Some expressions cause the frontend to create a temporary. 244 * For example, structs with cpctors replace the original 245 * expression e with: 246 * __cpcttmp = __cpcttmp.cpctor(e); 247 * 248 * In this instance, we need to ensure that the original 249 * expression e does not have any nested references by 250 * checking the declaration initializer too. 251 */ 252 if (v._init && v._init.isExpInitializer()) 253 { 254 Expression ie = v._init.initializerToExpression(); 255 result = lambdaCheckForNestedRef(ie, sc); 256 } 257 } 258 } 259 } 260 261 scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc); 262 walkPostorder(e, v); 263 return v.result; 264 } 265 266 /***************************************** 267 * See if context `s` is nested within context `p`, meaning 268 * it `p` is reachable at runtime by walking the static links. 269 * If any of the intervening contexts are function literals, 270 * make sure they are delegates. 271 * Params: 272 * s = inner context 273 * p = outer context 274 * Returns: 275 * true means it is accessible by walking the context pointers at runtime 276 * References: 277 * for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack 278 */ 279 bool ensureStaticLinkTo(Dsymbol s, Dsymbol p) 280 { 281 while (s) 282 { 283 if (s == p) // hit! 284 return true; 285 286 if (auto fd = s.isFuncDeclaration()) 287 { 288 if (!fd.isThis() && !fd.isNested()) 289 break; 290 291 // https://issues.dlang.org/show_bug.cgi?id=15332 292 // change to delegate if fd is actually nested. 293 if (auto fld = fd.isFuncLiteralDeclaration()) 294 fld.tok = TOK.delegate_; 295 } 296 if (auto ad = s.isAggregateDeclaration()) 297 { 298 if (ad.storage_class & STC.static_) 299 break; 300 } 301 s = s.toParentP(p); 302 } 303 return false; 304 }