diff --git a/src/escape.d b/src/escape.d index 23fd38bd9ee0..4c336dfc045d 100644 --- a/src/escape.d +++ b/src/escape.d @@ -427,6 +427,7 @@ private bool checkEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) bool result = false; foreach (VarDeclaration v; er.byvalue) { + //printf("byvalue %s\n", v.toChars()); if (v.isDataseg()) continue; @@ -483,17 +484,34 @@ private bool checkEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) foreach (VarDeclaration v; er.byref) { + //printf("byref %s\n", v.toChars()); if (v.isDataseg()) continue; Dsymbol p = v.toParent2(); - if ((v.storage_class & (STCref | STCout)) == 0 && p == sc.func) + if ((v.storage_class & (STCref | STCout)) == 0) { - if (!gag) - error(e.loc, "escaping reference to local variable %s", v.toChars()); - result = true; - continue; + if (p == sc.func) + { + if (!gag) + error(e.loc, "escaping reference to local variable %s", v.toChars()); + result = true; + 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 + */ + sc.func.storage_class |= STCreturn; + } + } /* Check for returning a ref variable by 'ref', but should be 'return ref' @@ -540,6 +558,7 @@ private bool checkEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) foreach (Expression ee; er.byexp) { + //printf("byexp %s\n", ee.toChars()); if (!gag) error(ee.loc, "escaping reference to stack allocated value returned by %s", ee.toChars()); result = true; @@ -795,8 +814,12 @@ private void escapeByValue(Expression e, EscapeByResults* er) */ Type t1 = e.e1.type.toBasetype(); TypeFunction tf; + TypeDelegate dg; if (t1.ty == Tdelegate) + { + dg = cast(TypeDelegate)t1; tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; + } else if (t1.ty == Tfunction) tf = cast(TypeFunction)t1; else @@ -835,6 +858,15 @@ private void escapeByValue(Expression e, EscapeByResults* er) 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); + } } } diff --git a/src/expression.d b/src/expression.d index 404477032f80..4e102303beb2 100644 --- a/src/expression.d +++ b/src/expression.d @@ -10600,9 +10600,18 @@ extern (C++) final class AddrExp : UnaExp } if (sc.func && !sc.intypeof && !v.isDataseg()) { - if (sc.func.setUnsafe()) + const(char)* p = v.isParameter() ? "parameter" : "local"; + if (global.params.safe) + { + v.storage_class &= ~STCmaybescope; + if (v.storage_class & STCscope && sc.func.setUnsafe()) + { + error("cannot take address of scope %s %s in @safe function %s", p, v.toChars(), sc.func.toChars()); + return false; + } + } + else if (sc.func.setUnsafe()) { - const(char)* p = v.isParameter() ? "parameter" : "local"; error("cannot take address of %s %s in @safe function %s", p, v.toChars(), sc.func.toChars()); return false; } diff --git a/src/func.d b/src/func.d index 95ddcc16edb1..ff6ec508f80e 100644 --- a/src/func.d +++ b/src/func.d @@ -750,8 +750,11 @@ extern (C++) class FuncDeclaration : Declaration error("functions cannot be scope"); } - if (f.isreturn && !needThis()) + if (f.isreturn && !needThis() && !isNested()) { + /* Non-static nested functions have a hidden 'this' pointer to which + * the 'return' applies + */ error("static member has no 'this' to which 'return' can apply"); } @@ -2220,7 +2223,18 @@ extern (C++) class FuncDeclaration : Declaration f.isnogc = true; } - flags &= ~(FUNCFLAGreturnInprocess | FUNCFLAGinferScope); + if (flags & FUNCFLAGreturnInprocess) + { + flags &= ~FUNCFLAGreturnInprocess; + if (storage_class & STCreturn) + { + if (type == f) + f = cast(TypeFunction)f.copy(); + f.isreturn = true; + } + } + + flags &= ~FUNCFLAGinferScope; // Infer STCscope if (parameters) diff --git a/test/fail_compilation/retscope.d b/test/fail_compilation/retscope.d index daf0cc1e42d6..fef2f027f31b 100644 --- a/test/fail_compilation/retscope.d +++ b/test/fail_compilation/retscope.d @@ -214,3 +214,56 @@ void* escape3 (scope void* p) @safe { return dg(); } +/**************************************************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/retscope.d(230): Error: scope variable ptr may not be returned +--- +*/ + +alias dg_t = void* delegate () return scope @safe; + +void* funretscope(scope dg_t ptr) @safe +{ + return ptr(); +} + +/*****************************************************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/retscope.d(247): Error: cannot implicitly convert expression (__lambda1) of type void* delegate() pure nothrow @nogc return @safe to void* delegate() @safe +fail_compilation/retscope.d(247): Error: cannot implicitly convert expression (__lambda1) of type void* delegate() pure nothrow @nogc return @safe to void* delegate() @safe +fail_compilation/retscope.d(248): Error: cannot implicitly convert expression (__lambda2) of type void* delegate() pure nothrow @nogc return @safe to void* delegate() @safe +fail_compilation/retscope.d(248): Error: cannot implicitly convert expression (__lambda2) of type void* delegate() pure nothrow @nogc return @safe to void* delegate() @safe +--- +*/ + +void escape4() @safe +{ + alias FunDG = void* delegate () @safe; + int x = 42; + scope FunDG f = () return { return &x; }; + scope FunDG g = () { return &x; }; +} + +/**************************************************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/retscope.d(267): Error: cannot take address of scope local p in @safe function escape5 +--- +*/ + +void escape5() @safe +{ + int* q; + scope int* p; + scope int** pp = &q; // ok + pp = &p; // error +} +