Skip to content

Commit b84fa47

Browse files
authored
Fix 22977 - can escape scope pointer returned by nested function (#14236)
1 parent 4414c2a commit b84fa47

File tree

3 files changed

+94
-16
lines changed

3 files changed

+94
-16
lines changed

compiler/src/dmd/escape.d

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf,
158158

159159
void onRef(VarDeclaration v, bool transition) { eb.byref.push(v); }
160160
void onValue(VarDeclaration v) { eb.byvalue.push(v); }
161-
void onFunc(FuncDeclaration fd) {}
161+
void onFunc(FuncDeclaration fd, bool called) {}
162162
void onExp(Expression e, bool transition) {}
163163

164164
scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp);
@@ -417,7 +417,7 @@ bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parI
417417
}
418418
}
419419

420-
void onFunc(FuncDeclaration fd)
420+
void onFunc(FuncDeclaration fd, bool called)
421421
{
422422
//printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
423423
if (parStc & STC.scope_)
@@ -862,7 +862,7 @@ bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef)
862862
result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1);
863863
}
864864

865-
void onFunc(FuncDeclaration func)
865+
void onFunc(FuncDeclaration func, bool called)
866866
{
867867
if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf);
868868
VarDeclarations vars;
@@ -992,7 +992,7 @@ bool checkThrowEscape(ref Scope sc, Expression e, bool gag)
992992
notMaybeScope(v, new ThrowExp(e.loc, e));
993993
}
994994
}
995-
void onFunc(FuncDeclaration fd) {}
995+
void onFunc(FuncDeclaration fd, bool called) {}
996996
void onExp(Expression exp, bool retRefTransition) {}
997997

998998
scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp);
@@ -1124,7 +1124,7 @@ bool checkNewEscape(ref Scope sc, Expression e, bool gag)
11241124
}
11251125
}
11261126

1127-
void onFunc(FuncDeclaration fd) {}
1127+
void onFunc(FuncDeclaration fd, bool called) {}
11281128

11291129
void onExp(Expression ee, bool retRefTransition)
11301130
{
@@ -1236,15 +1236,20 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
12361236
* return s; // s is inferred as 'scope' but incorrectly tested in foo()
12371237
* return null; }
12381238
*/
1239-
!(!refs && p.parent == sc.func && pfunc.fes) &&
1239+
!(!refs && p.parent == sc.func && pfunc.fes)
1240+
)
1241+
{
12401242
/*
12411243
* auto p(scope string s) {
12421244
* string scfunc() { return s; }
12431245
* }
12441246
*/
1245-
!(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0)
1246-
)
1247-
{
1247+
if (sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0 &&
1248+
inferReturn(sc.func, sc.func.vthis, /*returnScope*/ !refs))
1249+
{
1250+
return;
1251+
}
1252+
12481253
if (v.isParameter() && !v.isReturn())
12491254
{
12501255
// https://issues.dlang.org/show_bug.cgi?id=23191
@@ -1411,7 +1416,11 @@ private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool g
14111416
}
14121417
}
14131418

1414-
void onFunc(FuncDeclaration fd) {}
1419+
void onFunc(FuncDeclaration fd, bool called)
1420+
{
1421+
if (called && fd.isNested())
1422+
result |= sc.setUnsafeDIP1000(gag, e.loc, "escaping local variable through nested function `%s`", fd);
1423+
}
14151424

14161425
void onExp(Expression ee, bool retRefTransition)
14171426
{
@@ -1603,13 +1612,13 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
16031612
escapeByValue(e.e1, er, retRefTransition);
16041613
else
16051614
escapeByRef(e.e1, er, retRefTransition);
1606-
er.byFunc(e.func);
1615+
er.byFunc(e.func, false);
16071616
}
16081617

16091618
void visitFunc(FuncExp e)
16101619
{
16111620
if (e.fd.tok == TOK.delegate_)
1612-
er.byFunc(e.fd);
1621+
er.byFunc(e.fd, false);
16131622
}
16141623

16151624
void visitTuple(TupleExp e)
@@ -1860,7 +1869,12 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
18601869
if (fd && fd.isNested())
18611870
{
18621871
if (tf.isreturn && tf.isScopeQual)
1863-
er.byExp(e, false);
1872+
{
1873+
if (tf.isreturnscope)
1874+
er.byFunc(fd, true);
1875+
else
1876+
er.byExp(e, false);
1877+
}
18641878
}
18651879
}
18661880

@@ -1881,7 +1895,12 @@ void escapeByValue(Expression e, ref scope EscapeByResults er, bool retRefTransi
18811895
if (fd && fd.isNested())
18821896
{
18831897
if (tf.isreturn && tf.isScopeQual)
1884-
er.byExp(e, false);
1898+
{
1899+
if (tf.isreturnscope)
1900+
er.byFunc(fd, true);
1901+
else
1902+
er.byExp(e, false);
1903+
}
18851904
}
18861905
}
18871906
}
@@ -2195,7 +2214,10 @@ struct EscapeByResults
21952214
/// called on variables with values containing pointers
21962215
void delegate(VarDeclaration) byValue;
21972216
/// called on nested functions that are turned into delegates
2198-
void delegate(FuncDeclaration) byFunc;
2217+
/// When `called` is true, it means the delegate escapes variables
2218+
/// from the closure through a call to it, while `false` means the
2219+
/// delegate itself escapes.
2220+
void delegate(FuncDeclaration, bool called) byFunc;
21992221
/// called when expression temporaries are being returned by ref / address
22002222
void delegate(Expression, bool retRefTransition) byExp;
22012223

compiler/src/dmd/ob.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar)
19961996
scope EscapeByResults er = EscapeByResults(
19971997
(VarDeclaration v, bool) => onVar(v),
19981998
onVar,
1999-
(FuncDeclaration f) {},
1999+
(FuncDeclaration f, bool) {},
20002000
(Expression e, bool) {},
20012001
true,
20022002
);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
REQUIRED_ARGS: -preview=dip1000
3+
TEST_OUTPUT:
4+
---
5+
fail_compilation/test22977.d(16): Error: escaping local variable through nested function `scfunc`
6+
fail_compilation/test22977.d(22): Error: escaping reference to stack allocated value returned by `scfunc2()`
7+
---
8+
*/
9+
10+
// Issue 22977 - [dip1000] can escape scope pointer returned by nested function
11+
// https://issues.dlang.org/show_bug.cgi?id=22977
12+
13+
auto p0(scope string s) @safe
14+
{
15+
string scfunc() { return s; }
16+
return scfunc();
17+
}
18+
19+
auto p1(scope string s) @safe
20+
{
21+
ref string scfunc2() { return s; }
22+
return scfunc2();
23+
}
24+
25+
// Reduced from Mir
26+
struct Tuple(T...)
27+
{
28+
T expand;
29+
}
30+
31+
auto autoExpandAndForward(alias value)()
32+
{
33+
return value.expand[0];
34+
}
35+
36+
struct MapIterator
37+
{
38+
int* p;
39+
int* foo() scope
40+
{
41+
auto t = Tuple!(int*)(p);
42+
return autoExpandAndForward!t;
43+
}
44+
}
45+
46+
// Reduced from Phobos
47+
float partial(alias fun)()
48+
{
49+
return fun();
50+
}
51+
52+
auto partialFunction() @safe
53+
{
54+
int function() f = () => 0;
55+
return &partial!(f);
56+
}

0 commit comments

Comments
 (0)