Skip to content

Commit

Permalink
fix Issue 15544 - Escaping fields to a heap delegate must be disallow…
Browse files Browse the repository at this point in the history
…ed in @safe code
  • Loading branch information
WalterBright committed Aug 24, 2016
1 parent b487aff commit e4428f3
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 3 deletions.
81 changes: 79 additions & 2 deletions src/escape.d
Expand Up @@ -41,7 +41,8 @@ import ddmd.arraytypes;
*/
bool checkAssignEscape(Scope* sc, Expression e, bool gag)
{
if (e.op != TOKassign && e.op != TOKblit)
//printf("checkAssignEscape(e: %s)\n", e.toChars());
if (e.op != TOKassign && e.op != TOKblit && e.op != TOKconstruct)
return false;
auto ae = cast(AssignExp)e;
Expression e1 = ae.e1;
Expand All @@ -57,10 +58,12 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)

escapeByValue(e2, &er);

if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
return false;

VarDeclaration va;
while (e1.op == TOKdotvar)
e1 = (cast(DotVarExp)e1).e1;
if (e1.op == TOKvar)
va = (cast(VarExp)e1).var.isVarDeclaration();

Expand Down Expand Up @@ -133,6 +136,39 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag)
}
}

foreach (FuncDeclaration fd; er.byfunc)
{
//printf("fd = %s\n", fd.toChars());
VarDeclarations vars;
findAllOuterAccessedVariables(fd, &vars);

foreach (v; vars)
{
//printf("v = %s\n", v.toChars());
if (v.isDataseg())
continue;

Dsymbol p = v.toParent2();

if ((v.storage_class & (STCref | STCout | STCscope)) && p == sc.func)
{
if (va && !va.isDataseg())
{
if (!va.isScope())
va.storage_class |= STCscope;
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;
}
}
}

foreach (Expression ee; er.byexp)
{
if (va && !va.isDataseg())
Expand Down Expand Up @@ -415,6 +451,17 @@ private void escapeByValue(Expression e, EscapeByResults* er)
er.byvalue.push(v);
}

override void visit(DelegateExp e)
{
er.byfunc.push(e.func);
}

override void visit(FuncExp e)
{
if (e.fd.tok == TOKdelegate)
er.byfunc.push(e.fd);
}

override void visit(TupleExp e)
{
if (e.exps.dim)
Expand Down Expand Up @@ -772,5 +819,35 @@ private 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
}

/*************************
* 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)
{
for (auto p = fd.parent; p; p = p.parent)
{
auto fdp = p.isFuncDeclaration();
if (fdp)
{
foreach (v; fdp.closureVars)
{
foreach (const fdv; v.nestedrefs)
{
if (fdv == fd)
{
//printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
vars.push(v);
}
}
}
}
}
}
8 changes: 7 additions & 1 deletion test/compilable/interpret3.d
Expand Up @@ -4255,13 +4255,18 @@ static assert({ bug6851(); return true; }());
7876
**************************************************/

version (none)
{
// This code is now rejected by the compiler

int* bug7876(int n)
{
int x;
auto ptr = &x;
if (n == 2)
ptr = null;
return ptr;
return ptr; // possibility of returning pointer to stack
// means code is always rejected
}

struct S7876
Expand Down Expand Up @@ -4294,6 +4299,7 @@ static assert( is(typeof(compiles!(test7876(2)))));
static assert(!is(typeof(compiles!(test7876(0)))));
static assert( is(typeof(compiles!(test7876(11)))));
static assert(!is(typeof(compiles!(test7876(10)))));
}

/**************************************************
11824
Expand Down
51 changes: 51 additions & 0 deletions test/fail_compilation/test15544.d
@@ -0,0 +1,51 @@
/*
REQUIRED_ARGS:
PERMUTE_ARGS:
TEST_OUTPUT:
---
fail_compilation/test15544.d(21): Error: reference to local this assigned to non-scope _del in @safe code
fail_compilation/test15544.d(23): Error: reference to local this assigned to non-scope _del in @safe code
---
*/

// https://issues.dlang.org/show_bug.cgi?id=15544

void delegate() @safe _del;

struct S {
int x = 42;

@safe void test()
{
void foo() { assert(x == 42); }
_del = &foo;

_del = { assert(x == 42); };
}
}

/*
TEST_OUTPUT:
---
fail_compilation/test15544.d(47): Error: reference to local y assigned to non-scope dg in @safe code
---
*/

int delegate() dg;

void testClosure1()
{
int* x;
int bar() { return *x; }
dg = &bar;
}

@safe void testClosure2()
{
scope int* y;
int bar() { return *y; }
dg = &bar; // Error
auto dg2 = &bar;
}


0 comments on commit e4428f3

Please sign in to comment.