summaryrefslogtreecommitdiff
path: root/gcc/d/dmd/delegatize.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/delegatize.d')
-rw-r--r--gcc/d/dmd/delegatize.d305
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;
+}