diff options
Diffstat (limited to 'gcc/d/dmd/delegatize.d')
-rw-r--r-- | gcc/d/dmd/delegatize.d | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d new file mode 100644 index 00000000000..07c1bbda650 --- /dev/null +++ b/gcc/d/dmd/delegatize.d @@ -0,0 +1,305 @@ +/** + * Implements conversion from expressions to delegates for lazy parameters. + * + * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters) + * + * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d, _delegatize.d) + * Documentation: https://dlang.org/phobos/dmd_delegatize.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d + */ + +module dmd.delegatize; + +import core.stdc.stdio; +import dmd.apply; +import dmd.astenums; +import dmd.declaration; +import dmd.dscope; +import dmd.dsymbol; +import dmd.expression; +import dmd.expressionsem; +import dmd.func; +import dmd.globals; +import dmd.init; +import dmd.initsem; +import dmd.mtype; +import dmd.statement; +import dmd.tokens; +import dmd.visitor; + + +/********************************* + * Convert expression into a delegate. + * + * Used to convert the argument to a lazy parameter. + * + * Params: + * e = argument to convert to a delegate + * t = the type to be returned by the delegate + * sc = context + * Returns: + * A delegate literal + */ +Expression toDelegate(Expression e, Type t, Scope* sc) +{ + //printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars()); + Loc loc = e.loc; + auto tf = new TypeFunction(ParameterList(), t, LINK.d); + if (t.hasWild()) + tf.mod = MODFlags.wild; + auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null); + lambdaSetParent(e, fld); + + sc = sc.push(); + sc.parent = fld; // set current function to be the delegate + bool r = lambdaCheckForNestedRef(e, sc); + sc = sc.pop(); + if (r) + return ErrorExp.get(); + + Statement s; + if (t.ty == Tvoid) + s = new ExpStatement(loc, e); + else + s = new ReturnStatement(loc, e); + fld.fbody = s; + e = new FuncExp(loc, fld); + e = e.expressionSemantic(sc); + return e; +} + +/****************************************** + * Patch the parent of declarations to be the new function literal. + * + * Since the expression is going to be moved into a function literal, + * the parent for declarations in the expression needs to be + * reset to that function literal. + * Params: + * e = expression to check + * fd = function literal symbol (the new parent) + */ +private void lambdaSetParent(Expression e, FuncDeclaration fd) +{ + extern (C++) final class LambdaSetParent : StoppableVisitor + { + alias visit = typeof(super).visit; + FuncDeclaration fd; + + private void setParent(Dsymbol s) + { + VarDeclaration vd = s.isVarDeclaration(); + FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null; + s.parent = fd; + if (!vd || !pfd) + return; + // move to fd's closure when applicable + foreach (i; 0 .. pfd.closureVars.dim) + { + if (vd == pfd.closureVars[i]) + { + pfd.closureVars.remove(i); + fd.closureVars.push(vd); + break; + } + } + } + + public: + extern (D) this(FuncDeclaration fd) + { + this.fd = fd; + } + + override void visit(Expression) + { + } + + override void visit(DeclarationExp e) + { + setParent(e.declaration); + e.declaration.accept(this); + } + + override void visit(IndexExp e) + { + if (e.lengthVar) + { + //printf("lengthVar\n"); + setParent(e.lengthVar); + e.lengthVar.accept(this); + } + } + + override void visit(SliceExp e) + { + if (e.lengthVar) + { + //printf("lengthVar\n"); + setParent(e.lengthVar); + e.lengthVar.accept(this); + } + } + + override void visit(Dsymbol) + { + } + + override void visit(VarDeclaration v) + { + if (v._init) + v._init.accept(this); + } + + override void visit(Initializer) + { + } + + override void visit(ExpInitializer ei) + { + walkPostorder(ei.exp ,this); + } + + override void visit(StructInitializer si) + { + foreach (i, const id; si.field) + if (Initializer iz = si.value[i]) + iz.accept(this); + } + + override void visit(ArrayInitializer ai) + { + foreach (i, ex; ai.index) + { + if (ex) + walkPostorder(ex, this); + if (Initializer iz = ai.value[i]) + iz.accept(this); + } + } + } + + scope LambdaSetParent lsp = new LambdaSetParent(fd); + walkPostorder(e, lsp); +} + +/******************************************* + * Look for references to variables in a scope enclosing the new function literal. + * + * Essentially just calls `checkNestedReference() for each variable reference in `e`. + * Params: + * sc = context + * e = expression to check + * Returns: + * true if error occurs. + */ +bool lambdaCheckForNestedRef(Expression e, Scope* sc) +{ + extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor + { + alias visit = typeof(super).visit; + public: + Scope* sc; + bool result; + + extern (D) this(Scope* sc) + { + this.sc = sc; + } + + override void visit(Expression) + { + } + + override void visit(SymOffExp e) + { + VarDeclaration v = e.var.isVarDeclaration(); + if (v) + result = v.checkNestedReference(sc, Loc.initial); + } + + override void visit(VarExp e) + { + VarDeclaration v = e.var.isVarDeclaration(); + if (v) + result = v.checkNestedReference(sc, Loc.initial); + } + + override void visit(ThisExp e) + { + if (e.var) + result = e.var.checkNestedReference(sc, Loc.initial); + } + + override void visit(DeclarationExp e) + { + VarDeclaration v = e.declaration.isVarDeclaration(); + if (v) + { + result = v.checkNestedReference(sc, Loc.initial); + if (result) + return; + /* Some expressions cause the frontend to create a temporary. + * For example, structs with cpctors replace the original + * expression e with: + * __cpcttmp = __cpcttmp.cpctor(e); + * + * In this instance, we need to ensure that the original + * expression e does not have any nested references by + * checking the declaration initializer too. + */ + if (v._init && v._init.isExpInitializer()) + { + Expression ie = v._init.initializerToExpression(); + result = lambdaCheckForNestedRef(ie, sc); + } + } + } + } + + scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc); + walkPostorder(e, v); + return v.result; +} + +/***************************************** + * See if context `s` is nested within context `p`, meaning + * it `p` is reachable at runtime by walking the static links. + * If any of the intervening contexts are function literals, + * make sure they are delegates. + * Params: + * s = inner context + * p = outer context + * Returns: + * true means it is accessible by walking the context pointers at runtime + * References: + * for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack + */ +bool ensureStaticLinkTo(Dsymbol s, Dsymbol p) +{ + while (s) + { + if (s == p) // hit! + return true; + + if (auto fd = s.isFuncDeclaration()) + { + if (!fd.isThis() && !fd.isNested()) + break; + + // https://issues.dlang.org/show_bug.cgi?id=15332 + // change to delegate if fd is actually nested. + if (auto fld = fd.isFuncLiteralDeclaration()) + fld.tok = TOK.delegate_; + } + if (auto ad = s.isAggregateDeclaration()) + { + if (ad.storage_class & STC.static_) + break; + } + s = s.toParentP(p); + } + return false; +} |