diff --git a/src/ddmd/escape.d b/src/ddmd/escape.d index a44ec36bb70c..b3ec60aca927 100644 --- a/src/ddmd/escape.d +++ b/src/ddmd/escape.d @@ -400,6 +400,59 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag) 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; + foreach (VarDeclaration v; er.byvalue) + { + //printf("byvalue %s\n", v.toChars()); + if (v.isDataseg()) + continue; + + Dsymbol p = v.toParent2(); + + 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. diff --git a/src/ddmd/statementsem.d b/src/ddmd/statementsem.d index 4e7e44ccb03e..53150d0158a9 100644 --- a/src/ddmd/statementsem.d +++ b/src/ddmd/statementsem.d @@ -3308,6 +3308,8 @@ else if (ts.exp.op == TOKerror) return setError(); + checkThrowEscape(sc, ts.exp, false); + ClassDeclaration cd = ts.exp.type.toBasetype().isClassHandle(); if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) { diff --git a/test/fail_compilation/retscope2.d b/test/fail_compilation/retscope2.d index 193ff2c743a3..571e44df146f 100644 --- a/test/fail_compilation/retscope2.d +++ b/test/fail_compilation/retscope2.d @@ -146,3 +146,19 @@ S700* escape700(int i) @safe return s.get1(); // 721 } +/*************************************************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/retscope2.d(804): Error: scope variable e may not be thrown +--- +*/ + +#line 800 + +void foo800() +{ + scope Exception e; + throw e; +}