Skip to content

Commit 1d31970

Browse files
committed
Fix && refactoring closure issue in appendSplitPath by marking array as scope ref
1 parent a13aba5 commit 1d31970

12 files changed

+253
-12
lines changed

compiler/src/dmd/semantic3.d

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,10 +1729,12 @@ void semanticTypeInfoMembers(StructDeclaration sd)
17291729
* verify that such an allocation is allowed under the current compilation
17301730
* settings.
17311731
*
1732-
* Whenever an error is emitted, every nested function that actually closes
1733-
* over a variable is listed in a supplemental diagnostic, together with the
1734-
* location of the captured variable’s declaration. (This extra walk is
1735-
* skipped when the compiler is gagged.)
1732+
* Emits errors if:
1733+
* - A captured variable has a destructor, which is prohibited.
1734+
* - Closure allocation violates `@nogc` or `-betterC`.
1735+
*
1736+
* Additionally, provides supplemental diagnostics listing nested functions
1737+
* that close over variables, unless the compiler is gagged.
17361738
*
17371739
* See_Also:
17381740
* $(UL
@@ -1747,6 +1749,33 @@ extern (D) bool checkClosure(FuncDeclaration fd)
17471749
if (!fd.needsClosure())
17481750
return false;
17491751

1752+
// Preventing closure construction due to variables with destructor
1753+
foreach (v; fd.closureVars)
1754+
{
1755+
if (!v.type)
1756+
continue;
1757+
1758+
if (auto ts = v.type.isTypeStruct())
1759+
{
1760+
if (ts.sym && ts.sym.dtor)
1761+
{
1762+
.error(v.loc, "variable `%s` has scoped destruction, cannot build closure", v.toPrettyChars());
1763+
fd.errors = true;
1764+
return true;
1765+
}
1766+
}
1767+
else if (auto tc = v.type.isTypeClass())
1768+
{
1769+
if (tc.sym && tc.sym.dtor && (v.storage_class & STC.scope_))
1770+
{
1771+
.error(v.loc, "scoped class variable `%s` has destructor, cannot build closure", v.toPrettyChars());
1772+
fd.errors = true;
1773+
return true;
1774+
}
1775+
}
1776+
}
1777+
1778+
// Checking compilation restrictions
17501779
if (fd.setGC(fd.loc, "allocating a closure for `%s()`", fd))
17511780
{
17521781
.error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars());
@@ -1765,7 +1794,8 @@ extern (D) bool checkClosure(FuncDeclaration fd)
17651794
return false;
17661795
}
17671796

1768-
FuncDeclarations a;
1797+
// Additional diagnostics: who captures variables
1798+
FuncDeclarations reported;
17691799
foreach (v; fd.closureVars)
17701800
{
17711801
foreach (f; v.nestedrefs)
@@ -1778,18 +1808,25 @@ extern (D) bool checkClosure(FuncDeclaration fd)
17781808
auto fx = s.isFuncDeclaration();
17791809
if (!fx)
17801810
continue;
1811+
17811812
if (fx.isThis() ||
17821813
fx.tookAddressOf ||
17831814
checkEscapingSiblings(fx, fd))
17841815
{
1785-
foreach (f2; a)
1816+
bool alreadyReported = false;
1817+
foreach (r; reported)
17861818
{
1787-
if (f2 == f)
1788-
break LcheckAncestorsOfANestedRef;
1819+
if (r == f)
1820+
{
1821+
alreadyReported = true;
1822+
break;
1823+
}
17891824
}
1790-
a.push(f);
1791-
.errorSupplemental(f.loc, "%s `%s` closes over variable `%s`",
1792-
f.kind, f.toErrMsg(), v.toChars());
1825+
if (alreadyReported)
1826+
break LcheckAncestorsOfANestedRef;
1827+
1828+
reported.push(f);
1829+
.errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", f.kind, f.toErrMsg(), v.toChars());
17931830
if (v.ident != Id.This)
17941831
.errorSupplemental(v.loc, "`%s` declared here", v.toChars());
17951832

compiler/src/dmd/toir.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,4 +1207,4 @@ RET retStyle(TypeFunction tf, bool needsThis)
12071207
{
12081208
//printf("TypeFunction.retStyle() %s\n", toChars());
12091209
return target.isReturnOnStack(tf, needsThis) ? RET.stack : RET.regs;
1210-
}
1210+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Test that capturing a class without destructor in a closure is allowed.
3+
*/
4+
5+
class C
6+
{
7+
int x;
8+
}
9+
10+
void main()
11+
{
12+
auto obj = new C();
13+
14+
auto dg = () { return obj; }; // OK: no destructor
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Test that capturing a local struct without destructor in a closure is allowed.
3+
*/
4+
5+
void main()
6+
{
7+
struct Local
8+
{
9+
int x;
10+
}
11+
12+
Local s;
13+
14+
auto dg = () { return s; }; // OK: no destructor
15+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Test that capturing a variable without destructor in a closure inside a nested function is allowed.
3+
*/
4+
5+
struct S
6+
{
7+
int x;
8+
}
9+
10+
void main()
11+
{
12+
S s;
13+
14+
void nested()
15+
{
16+
auto dg = () { return s; }; // OK: no destructor
17+
}
18+
19+
nested();
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Test that capturing a struct without destructor in a closure is allowed.
3+
*/
4+
5+
struct NoDtor
6+
{
7+
int x;
8+
}
9+
10+
void main()
11+
{
12+
NoDtor s;
13+
14+
auto dg = () { return s; }; // OK: no destructor
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Test that capturing `this` of a struct without destructor in a closure is allowed.
3+
*/
4+
5+
struct S
6+
{
7+
int x;
8+
9+
void foo()
10+
{
11+
auto dg = () { return this; }; // OK: no destructor
12+
}
13+
}
14+
15+
void main()
16+
{
17+
S s;
18+
s.foo();
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/closure_class_dtor.d(19): Error: scoped class variable `closure_class_dtor.main.obj` has destructor, cannot build closure
5+
---
6+
*/
7+
8+
/**
9+
* Test that capturing a class with destructor in a closure is forbidden.
10+
*/
11+
12+
class C
13+
{
14+
~this() {}
15+
}
16+
17+
void main()
18+
{
19+
scope obj = new C();
20+
21+
auto dg = () { return obj; }; // should be banned
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/closure_local_struct_dtor.d(20): Error: variable `closure_local_struct_dtor.main.s` has scoped destruction, cannot build closure
5+
---
6+
*/
7+
8+
/**
9+
* Test that capturing a local struct with destructor in a closure is forbidden.
10+
*/
11+
12+
void main()
13+
{
14+
struct Local
15+
{
16+
~this() {}
17+
int x;
18+
}
19+
20+
Local s;
21+
22+
auto dg = () { return s; }; // should be banned
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/closure_nested_func_dtor.d(19): Error: variable `closure_nested_func_dtor.main.s` has scoped destruction, cannot build closure
5+
---
6+
*/
7+
8+
/**
9+
* Test that capturing a variable with destructor in a closure inside a nested function is forbidden.
10+
*/
11+
12+
struct S
13+
{
14+
~this() {}
15+
}
16+
17+
void main()
18+
{
19+
S s;
20+
21+
void nested()
22+
{
23+
auto dg = () { return s; }; // should be banned
24+
}
25+
26+
nested();
27+
}

0 commit comments

Comments
 (0)