Skip to content
Closed
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
10 changes: 8 additions & 2 deletions compiler/src/dmd/funcsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1713,8 +1713,14 @@ FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s,
OutBuffer buf;
buf.argExpTypesToCBuffer(fargs);
if (fd.isCtorDeclaration())
.error(loc, "none of the overloads of `%s` can construct a %sobject with argument types `(%s)`",
fd.toChars(), thisBuf.peekChars(), buf.peekChars());
{
if (tthis.mod & MODFlags.immutable_)
.error(loc, "none of the overloads of `%s` can construct an immutable object with argument types `(%s)`. Expected `immutable(%s)`",
fd.toChars(), buf.peekChars(), buf.peekChars());
else
.error(loc, "none of the overloads of `%s` can construct a %sobject with argument types `(%s)`",
fd.toChars(), thisBuf.peekChars(), buf.peekChars());
}
else
.error(loc, "none of the overloads of `%s` are callable using a %sobject with argument types `(%s)`",
fd.toChars(), thisBuf.peekChars(), buf.peekChars());
Expand Down
36 changes: 36 additions & 0 deletions compiler/src/dmd/statementsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1959,8 +1959,44 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
ed = ds.isEnumDeclaration(); // typedef'ed enum
if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
ed = ds.isEnumDeclaration();

// Detect circular references: enum being defined while it's used in a final switch
if (ed)
{
// Check if we're inside the initializer of one of the enum's members
for (Scope* scx = sc; scx; scx = scx.enclosing)
{
if (scx.scopesym && scx.scopesym.isEnumDeclaration() && scx.scopesym == ed)
{
error(ss.loc, "cannot use `final switch` on enum `%s` while it is being defined", ed.toChars());
sc.pop();
return setError();
}
}
}

if (ed && ss.cases.length < ed.members.length)
{
// Add a check for incomplete enum declaration to prevent segfault
// when the enum is being defined while it's referenced in a final switch
bool isEnumIncomplete = false;
foreach (es; *ed.members)
{
EnumMember em = es.isEnumMember();
if (em && em.value is null)
{
isEnumIncomplete = true;
break;
}
}

if (isEnumIncomplete)
{
error(ss.loc, "cannot use `final switch` on enum `%s` while it is being defined", ed.toChars());
sc.pop();
return setError();
}

int missingMembers = 0;
const maxShown = global.params.v.errorSupplementCount();
Lmembers:
Expand Down
21 changes: 21 additions & 0 deletions compiler/test/fail_compilation/fix20075.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
TEST_OUTPUT:
---
fail_compilation/fix20075.d(15): Error: none of the overloads of `this` can construct an immutable object with argument types `(int*)`. Expected `immutable(int*)`
fail_compilation/fix20075.d(11): Candidate is: `fix20075.Foo.this(immutable(int*) a) immutable`
---
*/

struct Foo {
@disable this();
immutable this(immutable int* a) {}
}

immutable(Foo) getFoo(int* a) {
return immutable Foo(a);
}

void main() {
int x;
auto foo = getFoo(&x);
}
70 changes: 70 additions & 0 deletions compiler/test/fail_compilation/fix20867.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
TEST_OUTPUT:
---
fail_compilation/fix20867.d(16): Error: cannot use `final switch` on enum `E` while it is being defined
fail_compilation/fix20867.d(31): Error: cannot use `final switch` on enum `E2` while it is being defined
fail_compilation/fix20867.d(63): Error: cannot use `final switch` on enum `E4` while it is being defined
---
*/

// Test case 1: The exact scenario from the GitHub issue
enum E
{
a = 3,
b = () {
E e;
final switch (e) // This should error out instead of segfaulting
{
case E.a: break;
}
return 4;
} ()
}

// Test case 2: Variation with multiple members
enum E2
{
x = 10,
y = 20,
z = () {
E2 e;
final switch (e) // Should also error out safely
{
case E2.x: return 30;
case E2.y: return 40;
}
} ()
}

// Test case 3: Regular use of final switch (this should still compile)
enum E3
{
p = 1,
q = 2
}

void testE3()
{
E3 e = E3.p;
final switch (e)
{
case E3.p: break;
case E3.q: break;
}
}

// Test case 4: Nested circular reference
enum E4
{
r = 5,
s = () {
int foo() {
E4 e;
final switch (e) // Should error out
{
case E4.r: return 6;
}
}
return foo();
} ()
}
2 changes: 1 addition & 1 deletion compiler/test/fail_compilation/testrvaluecpctor.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ TEST_OUTPUT:
---
fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo`
fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo`
fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `this` can construct a `immutable` object with argument types `(immutable(Foo!int))`
fail_compilation\testrvaluecpctor.d(24): Error: none of the overloads of `this` can construct an immutable object with argument types `(immutable(Foo!int))`. Expected `immutable(immutable(Foo!int))`
fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)`
fail_compilation/testrvaluecpctor.d(16): `this(Rhs, this This)(scope Rhs rhs)`
---
Expand Down
Loading