Skip to content

Commit 768d06c

Browse files
committed
Fix: Prevent ICE on final switch forward referencing its enum
1 parent 6476ceb commit 768d06c

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

compiler/src/dmd/statementsem.d

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,8 +1959,44 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
19591959
ed = ds.isEnumDeclaration(); // typedef'ed enum
19601960
if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
19611961
ed = ds.isEnumDeclaration();
1962+
1963+
// Circular references
1964+
if (ed)
1965+
{
1966+
// Check if inside the initializer of one of the enum's members
1967+
for (Scope* scx = sc; scx; scx = scx.enclosing)
1968+
{
1969+
if (scx.scopesym && scx.scopesym.isEnumDeclaration() && scx.scopesym == ed)
1970+
{
1971+
error(ss.loc, "cannot use `final switch` on enum `%s` while it is being defined", ed.toChars());
1972+
sc.pop();
1973+
return setError();
1974+
}
1975+
}
1976+
}
1977+
19621978
if (ed && ss.cases.length < ed.members.length)
19631979
{
1980+
// Add a check for incomplete enum declaration to prevent segfault
1981+
// when the enum is being defined while it's referenced in a final switch
1982+
bool isEnumIncomplete = false;
1983+
foreach (es; *ed.members)
1984+
{
1985+
EnumMember em = es.isEnumMember();
1986+
if (em && em.value is null)
1987+
{
1988+
isEnumIncomplete = true;
1989+
break;
1990+
}
1991+
}
1992+
1993+
if (isEnumIncomplete)
1994+
{
1995+
error(ss.loc, "cannot use `final switch` on enum `%s` while it is being defined", ed.toChars());
1996+
sc.pop();
1997+
return setError();
1998+
}
1999+
19642000
int missingMembers = 0;
19652001
const maxShown = global.params.v.errorSupplementCount();
19662002
Lmembers:
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
TEST_OUTPUT:
3+
---
4+
fail_compilation/fix20867.d(16): Error: cannot use `final switch` on enum `E` while it is being defined
5+
fail_compilation/fix20867.d(31): Error: cannot use `final switch` on enum `E2` while it is being defined
6+
fail_compilation/fix20867.d(63): Error: cannot use `final switch` on enum `E4` while it is being defined
7+
---
8+
*/
9+
10+
// Test case 1: The exact scenario Issue(From Github Issue(#20867))
11+
enum E
12+
{
13+
a = 3,
14+
b = () {
15+
E e;
16+
final switch (e) // This should error out instead of segfaulting
17+
{
18+
case E.a: break;
19+
}
20+
return 4;
21+
} ()
22+
}
23+
24+
// Test case 2: Variation with multiple members
25+
enum E2
26+
{
27+
x = 10,
28+
y = 20,
29+
z = () {
30+
E2 e;
31+
final switch (e) // Should also error out safely
32+
{
33+
case E2.x: return 30;
34+
case E2.y: return 40;
35+
}
36+
} ()
37+
}
38+
39+
// Test case 3: Regular use of final switch (this should still compile)
40+
enum E3
41+
{
42+
p = 1,
43+
q = 2
44+
}
45+
46+
void testE3()
47+
{
48+
E3 e = E3.p;
49+
final switch (e)
50+
{
51+
case E3.p: break;
52+
case E3.q: break;
53+
}
54+
}
55+
56+
// Test case 4: Nested circular reference
57+
enum E4
58+
{
59+
r = 5,
60+
s = () {
61+
int foo() {
62+
E4 e;
63+
final switch (e) // Should error out
64+
{
65+
case E4.r: return 6;
66+
}
67+
}
68+
return foo();
69+
} ()
70+
}

0 commit comments

Comments
 (0)