aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/escape.c
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /gcc/d/dmd/escape.c
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'gcc/d/dmd/escape.c')
-rw-r--r--gcc/d/dmd/escape.c1234
1 files changed, 1234 insertions, 0 deletions
diff --git a/gcc/d/dmd/escape.c b/gcc/d/dmd/escape.c
new file mode 100644
index 0000000..353e56f
--- /dev/null
+++ b/gcc/d/dmd/escape.c
@@ -0,0 +1,1234 @@
+
+/* Compiler implementation of the D programming language
+ * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved
+ * written by Walter Bright
+ * http://www.digitalmars.com
+ * Distributed under the Boost Software License, Version 1.0.
+ * http://www.boost.org/LICENSE_1_0.txt
+ * https://github.com/D-Programming-Language/dmd/blob/master/src/escape.c
+ */
+
+#include "mars.h"
+#include "init.h"
+#include "expression.h"
+#include "scope.h"
+#include "aggregate.h"
+#include "declaration.h"
+#include "module.h"
+
+/************************************
+ * Aggregate the data collected by the escapeBy??() functions.
+ */
+struct EscapeByResults
+{
+ VarDeclarations byref; // array into which variables being returned by ref are inserted
+ VarDeclarations byvalue; // array into which variables with values containing pointers are inserted
+ FuncDeclarations byfunc; // nested functions that are turned into delegates
+ Expressions byexp; // array into which temporaries being returned by ref are inserted
+};
+
+static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag);
+static void inferReturn(FuncDeclaration *fd, VarDeclaration *v);
+static void escapeByValue(Expression *e, EscapeByResults *er);
+static void escapeByRef(Expression *e, EscapeByResults *er);
+static void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars);
+
+/* 'v' is assigned unsafely to 'par'
+*/
+static void unsafeAssign(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag,
+ bool &result, VarDeclaration *v, const char *desc)
+{
+ if (global.params.vsafe && sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(arg->loc, "%s %s assigned to non-scope parameter %s calling %s",
+ desc, v->toChars(),
+ par ? par->toChars() : "unnamed",
+ fdc ? fdc->toPrettyChars() : "indirectly");
+ result = true;
+ }
+}
+
+/****************************************
+ * Function parameter par is being initialized to arg,
+ * and par may escape.
+ * Detect if scoped values can escape this way.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * par = identifier of function parameter
+ * arg = initializer for param
+ * gag = do not print error messages
+ * Returns:
+ * true if pointers to the stack can escape via assignment
+ */
+bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag)
+{
+ //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars());
+ //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers());
+
+ if (!arg->type->hasPointers())
+ return false;
+
+ EscapeByResults er;
+
+ escapeByValue(arg, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+
+ for (size_t i = 0; i < er.byvalue.dim; i++)
+ {
+ //printf("byvalue %s\n", v->toChars());
+ VarDeclaration *v = er.byvalue[i];
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ v->storage_class &= ~STCmaybescope;
+
+ if (v->isScope())
+ {
+ unsafeAssign(sc, fdc, par, arg, gag, result, v, "scope variable");
+ }
+ else if (v->storage_class & STCvariadic && p == sc->func)
+ {
+ Type *tb = v->type->toBasetype();
+ if (tb->ty == Tarray || tb->ty == Tsarray)
+ {
+ unsafeAssign(sc, fdc, par, arg, gag, result, v, "variadic variable");
+ }
+ }
+ else
+ {
+ /* v is not 'scope', and is assigned to a parameter that may escape.
+ * Therefore, v can never be 'scope'.
+ */
+ v->doNotInferScope = true;
+ }
+ }
+
+ for (size_t i = 0; i < er.byref.dim; i++)
+ {
+ VarDeclaration *v = er.byref[i];
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ v->storage_class &= ~STCmaybescope;
+
+ if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
+ {
+ unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local variable");
+ continue;
+ }
+ }
+
+ for (size_t i = 0; i < er.byfunc.dim; i++)
+ {
+ FuncDeclaration *fd = er.byfunc[i];
+ //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
+ VarDeclarations vars;
+ findAllOuterAccessedVariables(fd, &vars);
+
+ for (size_t j = 0; j < vars.dim; j++)
+ {
+ VarDeclaration *v = vars[j];
+ //printf("v = %s\n", v->toChars());
+ assert(!v->isDataseg()); // these are not put in the closureVars[]
+
+ Dsymbol *p = v->toParent2();
+
+ v->storage_class &= ~STCmaybescope;
+
+ if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
+ {
+ unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local");
+ continue;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < er.byexp.dim; i++)
+ {
+ Expression *ee = er.byexp[i];
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope parameter %s",
+ ee->toChars(),
+ par ? par->toChars() : "unnamed");
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+/****************************************
+ * Given an AssignExp, determine if the lvalue will cause
+ * the contents of the rvalue to escape.
+ * Print error messages when these are detected.
+ * Infer 'scope' for the lvalue where possible, in order
+ * to eliminate the error.
+ * Params:
+ * sc = used to determine current function and module
+ * ae = AssignExp to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * true if pointers to the stack can escape via assignment
+ */
+bool checkAssignEscape(Scope *sc, Expression *e, bool gag)
+{
+ //printf("checkAssignEscape(e: %s)\n", e->toChars());
+ if (e->op != TOKassign && e->op != TOKblit && e->op != TOKconstruct)
+ return false;
+ AssignExp *ae = (AssignExp *)e;
+ Expression *e1 = ae->e1;
+ Expression *e2 = ae->e2;
+ //printf("type = %s, %d\n", e1->type->toChars(), e1->type->hasPointers());
+
+ if (!e1->type->hasPointers())
+ return false;
+
+ if (e1->op == TOKslice)
+ return false;
+
+ EscapeByResults er;
+
+ escapeByValue(e2, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
+ return false;
+
+ VarDeclaration *va = NULL;
+ while (e1->op == TOKdotvar)
+ e1 = ((DotVarExp *)e1)->e1;
+
+ if (e1->op == TOKvar)
+ va = ((VarExp *)e1)->var->isVarDeclaration();
+ else if (e1->op == TOKthis)
+ va = ((ThisExp *)e1)->var->isVarDeclaration();
+ else if (e1->op == TOKindex)
+ {
+ IndexExp *ie = (IndexExp *)e1;
+ if (ie->e1->op == TOKvar && ie->e1->type->toBasetype()->ty == Tsarray)
+ va = ((VarExp *)ie->e1)->var->isVarDeclaration();
+ }
+
+ // Try to infer 'scope' for va if in a function not marked @system
+ bool inferScope = false;
+ if (va && sc->func && sc->func->type && sc->func->type->ty == Tfunction)
+ inferScope = ((TypeFunction *)sc->func->type)->trust != TRUSTsystem;
+
+ bool result = false;
+ for (size_t i = 0; i < er.byvalue.dim; i++)
+ {
+ VarDeclaration *v = er.byvalue[i];
+ //printf("byvalue: %s\n", v->toChars());
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ if (!(va && va->isScope()))
+ v->storage_class &= ~STCmaybescope;
+
+ if (v->isScope())
+ {
+ if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) &&
+ sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars());
+ result = true;
+ continue;
+ }
+
+ // If va's lifetime encloses v's, then error
+ if (va &&
+ ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) ||
+ // va is class reference
+ (ae->e1->op == TOKdotvar && va->type->toBasetype()->ty == Tclass && (va->enclosesLifetimeOf(v) || !va->isScope())) ||
+ va->storage_class & STCref) &&
+ sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "scope variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
+ result = true;
+ continue;
+ }
+
+ if (va && !va->isDataseg() && !va->doNotInferScope)
+ {
+ if (!va->isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va->toChars());
+ va->storage_class |= STCscope | STCscopeinferred;
+ va->storage_class |= v->storage_class & STCreturn;
+ }
+ continue;
+ }
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
+ result = true;
+ }
+ }
+ else if (v->storage_class & STCvariadic && p == sc->func)
+ {
+ Type *tb = v->type->toBasetype();
+ if (tb->ty == Tarray || tb->ty == Tsarray)
+ {
+ if (va && !va->isDataseg() && !va->doNotInferScope)
+ {
+ if (!va->isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va->toChars());
+ va->storage_class |= STCscope | STCscopeinferred;
+ }
+ continue;
+ }
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ /* v is not 'scope', and we didn't check the scope of where we assigned it to.
+ * It may escape via that assignment, therefore, v can never be 'scope'.
+ */
+ v->doNotInferScope = true;
+ }
+ }
+
+ for (size_t i = 0; i < er.byref.dim; i++)
+ {
+ VarDeclaration *v = er.byref[i];
+ //printf("byref: %s\n", v->toChars());
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ // If va's lifetime encloses v's, then error
+ if (va &&
+ ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || va->storage_class & STCref) &&
+ sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "address of variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
+ result = true;
+ continue;
+ }
+
+ if (!(va && va->isScope()))
+ v->storage_class &= ~STCmaybescope;
+
+ if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
+ {
+ if (va && !va->isDataseg() && !va->doNotInferScope)
+ {
+ if (!va->isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va->toChars());
+ va->storage_class |= STCscope | STCscopeinferred;
+ }
+ continue;
+ }
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+
+ for (size_t i = 0; i < er.byfunc.dim; i++)
+ {
+ FuncDeclaration *fd = er.byfunc[i];
+ //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
+ VarDeclarations vars;
+ findAllOuterAccessedVariables(fd, &vars);
+
+ for (size_t j = 0; j < vars.dim; j++)
+ {
+ VarDeclaration *v = vars[j];
+ //printf("v = %s\n", v->toChars());
+ assert(!v->isDataseg()); // these are not put in the closureVars[]
+
+ Dsymbol *p = v->toParent2();
+
+ if (!(va && va->isScope()))
+ v->storage_class &= ~STCmaybescope;
+
+ if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
+ {
+ if (va && !va->isDataseg() && !va->doNotInferScope)
+ {
+ /* Don't infer STCscope for va, because then a closure
+ * won't be generated for sc->func.
+ */
+ //if (!va->isScope() && inferScope)
+ //va->storage_class |= STCscope | STCscopeinferred;
+ continue;
+ }
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ae->loc, "reference to local %s assigned to non-scope %s in @safe code", v->toChars(), e1->toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < er.byexp.dim; i++)
+ {
+ Expression *ee = er.byexp[i];
+ if (va && !va->isDataseg() && !va->doNotInferScope)
+ {
+ if (!va->isScope() && inferScope)
+ { //printf("inferring scope for %s\n", va->toChars());
+ va->storage_class |= STCscope | STCscopeinferred;
+ }
+ continue;
+ }
+ if (sc->func->setUnsafe())
+ {
+ if (!gag)
+ error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope %s",
+ ee->toChars(), e1->toChars());
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+/************************************
+ * Detect cases where pointers to the stack can 'escape' the
+ * lifetime of the stack frame when throwing `e`.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * true if pointers to the stack can escape
+ */
+bool checkThrowEscape(Scope *sc, Expression *e, bool gag)
+{
+ //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars());
+ EscapeByResults er;
+
+ escapeByValue(e, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+ for (size_t i = 0; i < er.byvalue.dim; i++)
+ {
+ VarDeclaration *v = er.byvalue[i];
+ //printf("byvalue %s\n", v->toChars());
+ if (v->isDataseg())
+ continue;
+
+ if (v->isScope())
+ {
+ if (sc->_module && sc->_module->isRoot())
+ {
+ // Only look for errors if in module listed on command line
+ if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
+ {
+ if (!gag)
+ error(e->loc, "scope variable %s may not be thrown", v->toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ else
+ {
+ //printf("no infer for %s\n", v->toChars());
+ v->doNotInferScope = true;
+ }
+ }
+ return result;
+}
+
+/************************************
+ * Detect cases where pointers to the stack can 'escape' the
+ * lifetime of the stack frame by returning 'e' by value.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check for any pointers to the stack
+ * gag = do not print error messages
+ * Returns:
+ * true if pointers to the stack can escape
+ */
+
+bool checkReturnEscape(Scope *sc, Expression *e, bool gag)
+{
+ //printf("[%s] checkReturnEscape, e = %s\n", e->loc->toChars(), e->toChars());
+ return checkReturnEscapeImpl(sc, e, false, gag);
+}
+
+/************************************
+ * Detect cases where returning 'e' by ref can result in a reference to the stack
+ * being returned.
+ * Print error messages when these are detected.
+ * Params:
+ * sc = used to determine current function and module
+ * e = expression to check
+ * gag = do not print error messages
+ * Returns:
+ * true if references to the stack can escape
+ */
+bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag)
+{
+ //printf("[%s] checkReturnEscapeRef, e = %s\n", e->loc.toChars(), e->toChars());
+ //printf("current function %s\n", sc->func->toChars());
+ //printf("parent2 function %s\n", sc->func->toParent2()->toChars());
+
+ return checkReturnEscapeImpl(sc, e, true, gag);
+}
+
+static void escapingRef(VarDeclaration *v, Expression *e, bool &result, bool gag)
+{
+ if (!gag)
+ {
+ const char *msg;
+ if (v->storage_class & STCparameter)
+ msg = "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`";
+ else
+ msg = "returning `%s` escapes a reference to local variable `%s`";
+ error(e->loc, msg, e->toChars(), v->toChars());
+ }
+ result = true;
+}
+
+static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag)
+{
+ //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
+ EscapeByResults er;
+
+ if (refs)
+ escapeByRef(e, &er);
+ else
+ escapeByValue(e, &er);
+
+ if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
+ return false;
+
+ bool result = false;
+ for (size_t i = 0; i < er.byvalue.dim; i++)
+ {
+ VarDeclaration *v = er.byvalue[i];
+ //printf("byvalue %s\n", v->toChars());
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ if ((v->isScope() || (v->storage_class & STCmaybescope)) &&
+ !(v->storage_class & STCreturn) &&
+ v->isParameter() &&
+ sc->func->flags & FUNCFLAGreturnInprocess &&
+ p == sc->func)
+ {
+ inferReturn(sc->func, v); // infer addition of 'return'
+ continue;
+ }
+
+ if (v->isScope())
+ {
+ if (v->storage_class & STCreturn)
+ continue;
+
+ if (sc->_module && sc->_module->isRoot() &&
+ /* This case comes up when the ReturnStatement of a __foreachbody is
+ * checked for escapes by the caller of __foreachbody. Skip it.
+ *
+ * struct S { static int opApply(int delegate(S*) dg); }
+ * S* foo() {
+ * foreach (S* s; S) // create __foreachbody for body of foreach
+ * return s; // s is inferred as 'scope' but incorrectly tested in foo()
+ * return null; }
+ */
+ !(!refs && p->parent == sc->func))
+ {
+ // Only look for errors if in module listed on command line
+ if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
+ {
+ if (!gag)
+ error(e->loc, "scope variable %s may not be returned", v->toChars());
+ result = true;
+ }
+ continue;
+ }
+ }
+ else if (v->storage_class & STCvariadic && p == sc->func)
+ {
+ Type *tb = v->type->toBasetype();
+ if (tb->ty == Tarray || tb->ty == Tsarray)
+ {
+ if (!gag)
+ error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars());
+ result = false;
+ }
+ }
+ else
+ {
+ //printf("no infer for %s\n", v->toChars());
+ v->doNotInferScope = true;
+ }
+ }
+
+ for (size_t i = 0; i < er.byref.dim; i++)
+ {
+ VarDeclaration *v = er.byref[i];
+ //printf("byref %s\n", v->toChars());
+ if (v->isDataseg())
+ continue;
+
+ Dsymbol *p = v->toParent2();
+
+ if ((v->storage_class & (STCref | STCout)) == 0)
+ {
+ if (p == sc->func)
+ {
+ escapingRef(v, e, result, gag);
+ continue;
+ }
+ FuncDeclaration *fd = p->isFuncDeclaration();
+ if (fd && sc->func->flags & FUNCFLAGreturnInprocess)
+ {
+ /* Code like:
+ * int x;
+ * auto dg = () { return &x; }
+ * Making it:
+ * auto dg = () return { return &x; }
+ * Because dg.ptr points to x, this is returning dt.ptr+offset
+ */
+ if (global.params.vsafe)
+ sc->func->storage_class |= STCreturn;
+ }
+ }
+
+ /* Check for returning a ref variable by 'ref', but should be 'return ref'
+ * Infer the addition of 'return', or set result to be the offending expression.
+ */
+ if ( (v->storage_class & (STCref | STCout)) &&
+ !(v->storage_class & (STCreturn | STCforeach)))
+ {
+ if ((sc->func->flags & FUNCFLAGreturnInprocess) && p == sc->func)
+ {
+ inferReturn(sc->func, v); // infer addition of 'return'
+ }
+ else if (global.params.useDIP25 &&
+ sc->_module && sc->_module->isRoot())
+ {
+ // Only look for errors if in module listed on command line
+
+ if (p == sc->func)
+ {
+ //printf("escaping reference to local ref variable %s\n", v->toChars());
+ //printf("storage class = x%llx\n", v->storage_class);
+ escapingRef(v, e, result, gag);
+ continue;
+ }
+ // Don't need to be concerned if v's parent does not return a ref
+ FuncDeclaration *fd = p->isFuncDeclaration();
+ if (fd && fd->type && fd->type->ty == Tfunction)
+ {
+ TypeFunction *tf = (TypeFunction *)fd->type;
+ if (tf->isref)
+ {
+ if (!gag)
+ error(e->loc, "escaping reference to outer local variable %s", v->toChars());
+ result = true;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ for (size_t i = 0; i < er.byexp.dim; i++)
+ {
+ Expression *ee = er.byexp[i];
+ //printf("byexp %s\n", ee->toChars());
+ if (!gag)
+ error(ee->loc, "escaping reference to stack allocated value returned by %s", ee->toChars());
+ result = true;
+ }
+
+ return result;
+}
+
+
+/*************************************
+ * Variable v needs to have 'return' inferred for it.
+ * Params:
+ * fd = function that v is a parameter to
+ * v = parameter that needs to be STCreturn
+ */
+
+static void inferReturn(FuncDeclaration *fd, VarDeclaration *v)
+{
+ // v is a local in the current function
+
+ //printf("for function '%s' inferring 'return' for variable '%s'\n", fd->toChars(), v->toChars());
+ v->storage_class |= STCreturn;
+
+ TypeFunction *tf = (TypeFunction *)fd->type;
+ if (v == fd->vthis)
+ {
+ /* v is the 'this' reference, so mark the function
+ */
+ fd->storage_class |= STCreturn;
+ if (tf->ty == Tfunction)
+ {
+ //printf("'this' too %p %s\n", tf, sc->func->toChars());
+ tf->isreturn = true;
+ }
+ }
+ else
+ {
+ // Perform 'return' inference on parameter
+ if (tf->ty == Tfunction && tf->parameters)
+ {
+ const size_t dim = Parameter::dim(tf->parameters);
+ for (size_t i = 0; i < dim; i++)
+ {
+ Parameter *p = Parameter::getNth(tf->parameters, i);
+ if (p->ident == v->ident)
+ {
+ p->storageClass |= STCreturn;
+ break; // there can be only one
+ }
+ }
+ }
+ }
+}
+
+
+/****************************************
+ * e is an expression to be returned by value, and that value contains pointers.
+ * Walk e to determine which variables are possibly being
+ * returned by value, such as:
+ * int* function(int* p) { return p; }
+ * If e is a form of &p, determine which variables have content
+ * which is being returned as ref, such as:
+ * int* function(int i) { return &i; }
+ * Multiple variables can be inserted, because of expressions like this:
+ * int function(bool b, int i, int* p) { return b ? &i : p; }
+ *
+ * No side effects.
+ *
+ * Params:
+ * e = expression to be returned by value
+ * er = where to place collected data
+ */
+static void escapeByValue(Expression *e, EscapeByResults *er)
+{
+ //printf("[%s] escapeByValue, e: %s\n", e->loc.toChars(), e->toChars());
+
+ class EscapeVisitor : public Visitor
+ {
+ public:
+ EscapeByResults *er;
+
+ EscapeVisitor(EscapeByResults *er)
+ : er(er)
+ {
+ }
+
+ void visit(Expression *)
+ {
+ }
+
+ void visit(AddrExp *e)
+ {
+ escapeByRef(e->e1, er);
+ }
+
+ void visit(SymOffExp *e)
+ {
+ VarDeclaration *v = e->var->isVarDeclaration();
+ if (v)
+ er->byref.push(v);
+ }
+
+ void visit(VarExp *e)
+ {
+ VarDeclaration *v = e->var->isVarDeclaration();
+ if (v)
+ er->byvalue.push(v);
+ }
+
+ void visit(ThisExp *e)
+ {
+ if (e->var)
+ er->byvalue.push(e->var);
+ }
+
+ void visit(DotVarExp *e)
+ {
+ Type *t = e->e1->type->toBasetype();
+ if (t->ty == Tstruct)
+ e->e1->accept(this);
+ }
+
+ void visit(DelegateExp *e)
+ {
+ Type *t = e->e1->type->toBasetype();
+ if (t->ty == Tclass || t->ty == Tpointer)
+ escapeByValue(e->e1, er);
+ else
+ escapeByRef(e->e1, er);
+ er->byfunc.push(e->func);
+ }
+
+ void visit(FuncExp *e)
+ {
+ if (e->fd->tok == TOKdelegate)
+ er->byfunc.push(e->fd);
+ }
+
+ void visit(TupleExp *)
+ {
+ assert(0); // should have been lowered by now
+ }
+
+ void visit(ArrayLiteralExp *e)
+ {
+ Type *tb = e->type->toBasetype();
+ if (tb->ty == Tsarray || tb->ty == Tarray)
+ {
+ if (e->basis)
+ e->basis->accept(this);
+ for (size_t i = 0; i < e->elements->dim; i++)
+ {
+ Expression *el = (*e->elements)[i];
+ if (el)
+ el->accept(this);
+ }
+ }
+ }
+
+ void visit(StructLiteralExp *e)
+ {
+ if (e->elements)
+ {
+ for (size_t i = 0; i < e->elements->dim; i++)
+ {
+ Expression *ex = (*e->elements)[i];
+ if (ex)
+ ex->accept(this);
+ }
+ }
+ }
+
+ void visit(NewExp *e)
+ {
+ Type *tb = e->newtype->toBasetype();
+ if (tb->ty == Tstruct && !e->member && e->arguments)
+ {
+ for (size_t i = 0; i < e->arguments->dim; i++)
+ {
+ Expression *ex = (*e->arguments)[i];
+ if (ex)
+ ex->accept(this);
+ }
+ }
+ }
+
+ void visit(CastExp *e)
+ {
+ Type *tb = e->type->toBasetype();
+ if (tb->ty == Tarray &&
+ e->e1->type->toBasetype()->ty == Tsarray)
+ {
+ escapeByRef(e->e1, er);
+ }
+ else
+ e->e1->accept(this);
+ }
+
+ void visit(SliceExp *e)
+ {
+ if (e->e1->op == TOKvar)
+ {
+ VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
+ Type *tb = e->type->toBasetype();
+ if (v)
+ {
+ if (tb->ty == Tsarray)
+ return;
+ if (v->storage_class & STCvariadic)
+ {
+ er->byvalue.push(v);
+ return;
+ }
+ }
+ }
+ Type *t1b = e->e1->type->toBasetype();
+ if (t1b->ty == Tsarray)
+ {
+ Type *tb = e->type->toBasetype();
+ if (tb->ty != Tsarray)
+ escapeByRef(e->e1, er);
+ }
+ else
+ e->e1->accept(this);
+ }
+
+ void visit(BinExp *e)
+ {
+ Type *tb = e->type->toBasetype();
+ if (tb->ty == Tpointer)
+ {
+ e->e1->accept(this);
+ e->e2->accept(this);
+ }
+ }
+
+ void visit(BinAssignExp *e)
+ {
+ e->e1->accept(this);
+ }
+
+ void visit(AssignExp *e)
+ {
+ e->e1->accept(this);
+ }
+
+ void visit(CommaExp *e)
+ {
+ e->e2->accept(this);
+ }
+
+ void visit(CondExp *e)
+ {
+ e->e1->accept(this);
+ e->e2->accept(this);
+ }
+
+ void visit(CallExp *e)
+ {
+ //printf("CallExp(): %s\n", e->toChars());
+ /* Check each argument that is
+ * passed as 'return scope'.
+ */
+ Type *t1 = e->e1->type->toBasetype();
+ TypeFunction *tf = NULL;
+ TypeDelegate *dg = NULL;
+ if (t1->ty == Tdelegate)
+ {
+ dg = (TypeDelegate *)t1;
+ tf = (TypeFunction *)dg->next;
+ }
+ else if (t1->ty == Tfunction)
+ tf = (TypeFunction *)t1;
+ else
+ return;
+
+ if (e->arguments && e->arguments->dim)
+ {
+ /* j=1 if _arguments[] is first argument,
+ * skip it because it is not passed by ref
+ */
+ size_t j = (tf->linkage == LINKd && tf->varargs == 1);
+ for (size_t i = j; i < e->arguments->dim; ++i)
+ {
+ Expression *arg = (*e->arguments)[i];
+ size_t nparams = Parameter::dim(tf->parameters);
+ if (i - j < nparams && i >= j)
+ {
+ Parameter *p = Parameter::getNth(tf->parameters, i - j);
+ const StorageClass stc = tf->parameterStorageClass(p);
+ if ((stc & (STCscope)) && (stc & STCreturn))
+ arg->accept(this);
+ else if ((stc & (STCref)) && (stc & STCreturn))
+ escapeByRef(arg, er);
+ }
+ }
+ }
+ // If 'this' is returned, check it too
+ if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
+ {
+ DotVarExp *dve = (DotVarExp *)e->e1;
+ FuncDeclaration *fd = dve->var->isFuncDeclaration();
+ AggregateDeclaration *ad = NULL;
+ if (global.params.vsafe && tf->isreturn && fd && (ad = fd->isThis()) != NULL)
+ {
+ if (ad->isClassDeclaration() || tf->isscope) // this is 'return scope'
+ dve->e1->accept(this);
+ else if (ad->isStructDeclaration()) // this is 'return ref'
+ escapeByRef(dve->e1, er);
+ }
+ else if (dve->var->storage_class & STCreturn || tf->isreturn)
+ {
+ if (dve->var->storage_class & STCscope)
+ dve->e1->accept(this);
+ else if (dve->var->storage_class & STCref)
+ escapeByRef(dve->e1, er);
+ }
+ }
+
+ /* If returning the result of a delegate call, the .ptr
+ * field of the delegate must be checked.
+ */
+ if (dg)
+ {
+ if (tf->isreturn)
+ e->e1->accept(this);
+ }
+ }
+ };
+
+ EscapeVisitor v(er);
+ e->accept(&v);
+}
+
+/****************************************
+ * e is an expression to be returned by 'ref'.
+ * Walk e to determine which variables are possibly being
+ * returned by ref, such as:
+ * ref int function(int i) { return i; }
+ * If e is a form of *p, determine which variables have content
+ * which is being returned as ref, such as:
+ * ref int function(int* p) { return *p; }
+ * Multiple variables can be inserted, because of expressions like this:
+ * ref int function(bool b, int i, int* p) { return b ? i : *p; }
+ *
+ * No side effects.
+ *
+ * Params:
+ * e = expression to be returned by 'ref'
+ * er = where to place collected data
+ */
+static void escapeByRef(Expression *e, EscapeByResults *er)
+{
+ //printf("[%s] escapeByRef, e: %s\n", e->loc->toChars(), e->toChars());
+ class EscapeRefVisitor : public Visitor
+ {
+ public:
+ EscapeByResults *er;
+
+ EscapeRefVisitor(EscapeByResults *er)
+ : er(er)
+ {
+ }
+
+ void visit(Expression *)
+ {
+ }
+
+ void visit(VarExp *e)
+ {
+ VarDeclaration *v = e->var->isVarDeclaration();
+ if (v)
+ {
+ if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->_init)
+ {
+ /* If compiler generated ref temporary
+ * (ref v = ex; ex)
+ * look at the initializer instead
+ */
+ if (ExpInitializer *ez = v->_init->isExpInitializer())
+ {
+ assert(ez->exp && ez->exp->op == TOKconstruct);
+ Expression *ex = ((ConstructExp *)ez->exp)->e2;
+ ex->accept(this);
+ }
+ }
+ else
+ er->byref.push(v);
+ }
+ }
+
+ void visit(ThisExp *e)
+ {
+ if (e->var)
+ er->byref.push(e->var);
+ }
+
+ void visit(PtrExp *e)
+ {
+ escapeByValue(e->e1, er);
+ }
+
+ void visit(IndexExp *e)
+ {
+ Type *tb = e->e1->type->toBasetype();
+ if (e->e1->op == TOKvar)
+ {
+ VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
+ if (tb->ty == Tarray || tb->ty == Tsarray)
+ {
+ if (v->storage_class & STCvariadic)
+ {
+ er->byref.push(v);
+ return;
+ }
+ }
+ }
+ if (tb->ty == Tsarray)
+ {
+ e->e1->accept(this);
+ }
+ else if (tb->ty == Tarray)
+ {
+ escapeByValue(e->e1, er);
+ }
+ }
+
+ void visit(DotVarExp *e)
+ {
+ Type *t1b = e->e1->type->toBasetype();
+ if (t1b->ty == Tclass)
+ escapeByValue(e->e1, er);
+ else
+ e->e1->accept(this);
+ }
+
+ void visit(BinAssignExp *e)
+ {
+ e->e1->accept(this);
+ }
+
+ void visit(AssignExp *e)
+ {
+ e->e1->accept(this);
+ }
+
+ void visit(CommaExp *e)
+ {
+ e->e2->accept(this);
+ }
+
+ void visit(CondExp *e)
+ {
+ e->e1->accept(this);
+ e->e2->accept(this);
+ }
+
+ void visit(CallExp *e)
+ {
+ /* If the function returns by ref, check each argument that is
+ * passed as 'return ref'.
+ */
+ Type *t1 = e->e1->type->toBasetype();
+ TypeFunction *tf;
+ if (t1->ty == Tdelegate)
+ tf = (TypeFunction *)((TypeDelegate *)t1)->next;
+ else if (t1->ty == Tfunction)
+ tf = (TypeFunction *)t1;
+ else
+ return;
+ if (tf->isref)
+ {
+ if (e->arguments && e->arguments->dim)
+ {
+ /* j=1 if _arguments[] is first argument,
+ * skip it because it is not passed by ref
+ */
+ size_t j = (tf->linkage == LINKd && tf->varargs == 1);
+
+ for (size_t i = j; i < e->arguments->dim; ++i)
+ {
+ Expression *arg = (*e->arguments)[i];
+ size_t nparams = Parameter::dim(tf->parameters);
+ if (i - j < nparams && i >= j)
+ {
+ Parameter *p = Parameter::getNth(tf->parameters, i - j);
+ const StorageClass stc = tf->parameterStorageClass(p);
+ if ((stc & (STCout | STCref)) && (stc & STCreturn))
+ arg->accept(this);
+ else if ((stc & STCscope) && (stc & STCreturn))
+ {
+ if (arg->op == TOKdelegate)
+ {
+ DelegateExp *de = (DelegateExp *)arg;
+ if (de->func->isNested())
+ er->byexp.push(de);
+ }
+ else
+ escapeByValue(arg, er);
+ }
+ }
+ }
+ }
+
+ // If 'this' is returned by ref, check it too
+ if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
+ {
+ DotVarExp *dve = (DotVarExp *)e->e1;
+ if (dve->var->storage_class & STCreturn || tf->isreturn)
+ {
+ if ((dve->var->storage_class & STCscope) || tf->isscope)
+ escapeByValue(dve->e1, er);
+ else if ((dve->var->storage_class & STCref) || tf->isref)
+ dve->e1->accept(this);
+ }
+
+ }
+ // If it's a delegate, check it too
+ if (e->e1->op == TOKvar && t1->ty == Tdelegate)
+ {
+ escapeByValue(e->e1, er);
+ }
+ }
+ else
+ er->byexp.push(e);
+ }
+ };
+
+ EscapeRefVisitor v(er);
+ e->accept(&v);
+}
+
+/*************************
+ * Find all variables accessed by this delegate that are
+ * in functions enclosing it.
+ * Params:
+ * fd = function
+ * vars = array to append found variables to
+ */
+void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars)
+{
+ //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
+ for (Dsymbol *p = fd->parent; p; p = p->parent)
+ {
+ FuncDeclaration *fdp = p->isFuncDeclaration();
+ if (fdp)
+ {
+ for (size_t i = 0; i < fdp->closureVars.dim; i++)
+ {
+ VarDeclaration *v = fdp->closureVars[i];
+ for (size_t j = 0; j < v->nestedrefs.dim; j++)
+ {
+ FuncDeclaration *fdv = v->nestedrefs[j];
+ if (fdv == fd)
+ {
+ //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());
+ vars->push(v);
+ }
+ }
+ }
+ }
+ }
+}