Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions src/escape.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
18 changes: 16 additions & 2 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down Expand Up @@ -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)
Expand Down
53 changes: 53 additions & 0 deletions test/fail_compilation/retscope.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lifetime of pp is shorter than the lifetime of p, so shouldn't this be allowed?
We could go with a conservative implemenation for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I'll file an enhancement request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}