If you have "S" with completely unsafe destructor, and you aggregate it into "SS":
//----
struct S
{
~this() //not nothrow, system, impure, gc etc...
{}
}
struct SS
{
S s;
~this() @safe pure nothrow @nogc
{}
}
//----
This compiles. This may or may not be wrong, depending on your point of view: The "code content" of the destructor is indeed actually safe etc...
The issue comes if you actually do try to use it in a safe context:
//----
void main() @safe pure nothrow @nogc
{
SS ss;
}
//----
Here is the error message:
//----
Error: pure function 'D main' cannot call impure function 'main.SS.~this'
Error: safe function 'D main' cannot call system function 'main.SS.~this'
Error: @nogc function 'D main' cannot call non-@nogc function 'main.SS.~this'
Error: 'main.SS.~this' is not nothrow
Error: function 'D main' is nothrow yet may throw
//----
The issue here is that it clearly states that "SS.~this" is the one that is unsafe, yet it is clearly marked as such.
IMO, the bug is that it should have never been legally marked as @safe to begin with.
Comment #1 by bugzilla — 2014-11-13T11:20:01Z
What's happening here is that there are actually two destructors for SS - the one for the s field, and the ~this(). The compiler combines all such destructors into one aggregate destructor. The aggregate destructor contains the most permissive combination of the attributes of all the aggregated destructors.
The error message is for the generated aggregated destructor.
Perhaps the error message could be better, but the feature is working as designed.
Reducing this issue to minor as the only problem is the error message.
Comment #2 by stanislav.blinov — 2021-12-08T18:00:46Z
Nowadays the error message is... better, for a conservative definition of "better". But it's __very__ verbose and most of all, wrong. It's referring to SS.~this, which is not at all the destructor in question - SS.__xdtor is. It's even referring to the line where SS.~this is declared! SS.~this has the attributes, and the error message shouldn't indicate that it doesn't.
main.d(16): Error: `pure` function `D main` cannot call impure destructor `SS.~this`
main.d(10): generated `SS.~this` is impure because of the following field's destructors:
main.d(9): - S s
main.d(3): impure `S.~this` is declared here
main.d(16): Error: `@safe` function `D main` cannot call `@system` destructor `SS.~this`
main.d(10): `SS.~this` is declared here
main.d(10): generated `SS.~this` is @system because of the following field's destructors:
main.d(9): - S s
main.d(3): @system `S.~this` is declared here
main.d(16): Error: `@nogc` function `D main` cannot call non-@nogc destructor `SS.~this`
main.d(10): generated `SS.~this` is non-@nogc because of the following field's destructors:
main.d(9): - S s
main.d(3): non-@nogc `S.~this` is declared here
main.d(16): Error: destructor `SS.~this` is not `nothrow`
main.d(10): generated `SS.~this` is not nothrow because of the following field's destructors:
main.d(9): - S s
main.d(3): not nothrow `S.~this` is declared here
main.d(14): Error: `nothrow` function `D main` may throw
Add more fields to SS, and this gets even longer.
With this code:
struct S
{
~this() //not nothrow, system, impure, gc etc...
{}
}
struct S2
{
~this() {}
}
struct SS
{
S s;
~this() @safe pure nothrow @nogc
{}
}
struct SSS
{
SS ss;
~this() @safe pure nothrow @nogc {}
}
void main() @safe pure nothrow @nogc
{
SSS ss;
}
...errors are page long :\
Suggested change:
main.d(16): Error: `@safe pure nothrow @nogc` function `D main` cannot call impure @system throwing GC-using generated destructor for `SS`
main.d(9): Generated destructor for `SS` is impure @system throwing GC-using because of destructor of field `s`
main.d(3): impure @system throwing GC-using destructor of field `s` is declared here
...with one relevant line per field, correctly distinguishing generated destructors and user-defined destructors.
Comment #3 by robert.schadek — 2024-12-13T18:29:36Z