File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -4502,10 +4502,20 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
45024502 if (auto cldec = ad.isClassDeclaration())
45034503 {
45044504 assert (cldec.cppDtorVtblIndex == - 1 ); // double-call check already by dd.type
4505- if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != - 1 )
4505+ // Walk up the base chain: an intermediate class may have no explicit
4506+ // dtor (cppDtorVtblIndex == -1) yet still inherit a dtor vtbl slot.
4507+ // https://github.com/dlang/dmd/issues/22709
4508+ int inheritedDtorVtblIndex = - 1 ;
4509+ for (auto base = cldec.baseClass; base; base = base.baseClass)
4510+ if (base.cppDtorVtblIndex != - 1 )
4511+ {
4512+ inheritedDtorVtblIndex = base.cppDtorVtblIndex;
4513+ break ;
4514+ }
4515+ if (inheritedDtorVtblIndex != - 1 )
45064516 {
45074517 // override the base virtual
4508- cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex ;
4518+ cldec.cppDtorVtblIndex = inheritedDtorVtblIndex ;
45094519 }
45104520 else if (! dd.isFinal())
45114521 {
Original file line number Diff line number Diff line change 1+ // https://github.com/dlang/dmd/issues/22709
2+ // extern(C++) destructor in base class should not be flagged as hidden
3+
4+ extern (C++ ):
5+ class A
6+ {
7+ ~this ();
8+ }
9+ class B : A
10+ {
11+ }
12+ class C : B
13+ {
14+ ~this ();
15+ }
Original file line number Diff line number Diff line change 1+ // https://github.com/dlang/dmd/issues/22709
2+ // Virtual dtor dispatch through an intermediate class with no explicit dtor.
3+ // Before the fix, C's vtbl had a stale entry for A's dtor at slot 0, causing
4+ // virtual dtor dispatch through A* to call A's dtor instead of C's.
5+ // EXTRA_CPP_SOURCES: cpp_dtor.cpp
6+
7+ extern (C ) __gshared int aDestroyed;
8+ extern (C ) __gshared int cDestroyed;
9+
10+ extern (C++ ) void runCPPTests();
11+
12+ extern (C++ ):
13+
14+ class A
15+ {
16+ ~this () { aDestroyed = 1 ; }
17+ }
18+
19+ class B : A
20+ {
21+ }
22+
23+ class C : B
24+ {
25+ ~this () { cDestroyed = 1 ; }
26+ }
27+
28+ // D-side factory: C++ calls this to get a C object typed as A*
29+ A makeC () { return new C; }
30+
31+ extern (D ) void main()
32+ {
33+ runCPPTests();
34+ }
Original file line number Diff line number Diff line change 1+ // https://github.com/dlang/dmd/issues/22709
2+ // C++ side: verify virtual dtor dispatch calls C's dtor (not A's) when
3+ // destroying a C object through an A*.
4+ #include < assert.h>
5+
6+ extern " C" int aDestroyed;
7+ extern " C" int cDestroyed;
8+
9+ // Forward declaration matching D's extern(C++) class A
10+ class A {
11+ public:
12+ virtual ~A ();
13+ };
14+
15+ // D-side factory
16+ extern " C++" A* makeC ();
17+
18+ void runCPPTests ()
19+ {
20+ A* obj = makeC ();
21+
22+ // Invoke the virtual destructor without freeing memory.
23+ // obj->~A() dispatches virtually (calls C's dtor) on all ABIs,
24+ // and does NOT call operator delete, so D-allocated memory is safe.
25+ aDestroyed = 0 ;
26+ cDestroyed = 0 ;
27+ obj->~A ();
28+
29+ // C's destructor must be dispatched, not A's
30+ assert (cDestroyed);
31+ // A's destructor must be chained from C's aggregate dtor
32+ assert (aDestroyed);
33+ }
You can’t perform that action at this time.
0 commit comments