-
-
Notifications
You must be signed in to change notification settings - Fork 609
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issue 15589 - extern(C++) virtual destructors are not put in vtbl[] #8277
Changes from all commits
e985315
5ed7be2
64b3d10
9c1fe4d
4766b6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ module dmd.clone; | |
| import core.stdc.stdio; | ||
| import dmd.aggregate; | ||
| import dmd.arraytypes; | ||
| import dmd.dclass; | ||
| import dmd.declaration; | ||
| import dmd.dscope; | ||
| import dmd.dstruct; | ||
|
|
@@ -883,6 +884,34 @@ extern (C++) FuncDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) | |
| e = Expression.combine(ex, e); // combine in reverse order | ||
| } | ||
|
|
||
| /* extern(C++) destructors call into super to destruct the full hierarchy | ||
| */ | ||
| ClassDeclaration cldec = ad.isClassDeclaration(); | ||
| if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.dtor) | ||
| { | ||
| // WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before? | ||
| cldec.baseClass.dtor.functionSemantic(); | ||
|
|
||
| stc = mergeFuncAttrs(stc, cldec.baseClass.dtor); | ||
| if (!(stc & STC.disable)) | ||
| { | ||
| // super.__xdtor() | ||
|
|
||
| Expression ex = new SuperExp(loc); | ||
|
|
||
| // This is a hack so we can call destructors on const/immutable objects. | ||
| // Do it as a type 'paint'. | ||
| ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf()); | ||
| if (stc & STC.safe) | ||
| stc = (stc & ~STC.safe) | STC.trusted; | ||
|
|
||
| ex = new DotVarExp(loc, ex, cldec.baseClass.dtor, false); | ||
| ex = new CallExp(loc, ex); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice if you could forward the dtor argument here, so it doesn't get lost when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If C++ ever calls a destructor with non-0, it means an operator delete override exists beneath it in the hierarchy, and therefore is not supported by D. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I think it's only the top-level destructor that calls delete, you don't pass the request to delete down the hierarchy, otherwise it would call delete at each level of destruction... only the top-level destruction should finally delete? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
True. I just tried how a dtor of a derived class looks like, and it's the dtor with argument that goes into the vtable, but it calls a dtor without arguments that does the chaining into the base class. Only the one in the vtable optionally frees the memory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So forwarding the parameter would actually be wrong, passing 0 is ok. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. If we add the enhancement to support calling C++ delete, then we should be semantically compatible I think. |
||
|
|
||
| e = Expression.combine(e, ex); // super dtor last | ||
| } | ||
| } | ||
|
|
||
| /* Build our own "destructor" which executes e | ||
| */ | ||
| if (e || (stc & STC.disable)) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3735,7 +3735,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor | |
| if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic) | ||
| ad.dtors.push(dd); | ||
| if (!dd.type) | ||
| dd.type = new TypeFunction(null, Type.tvoid, false, LINK.d, dd.storage_class); | ||
| { | ||
| Parameters* params = null; | ||
| if (ad.classKind == ClassKind.cpp && !Target.cppDeletingDestructor) | ||
| { | ||
| // Windows doesn't use a deleting destructor, it takes an argument instead! | ||
| Parameter param = new Parameter(STC.undefined_, Type.tuns32, new Identifier("del"), new IntegerExp(Loc.initial, 0, Type.tuns32)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it can work so easily (AFAIU you are adding a parameter with a default value so it can still be called without arguments). This helps for explicit calls, but not for the dtor written to the TypeInfo. The latter needs a shim that adds the default value as an argument. Here's an example that calls the dtor this way: Output is: Works for Win64, but not for Win32 in dmd 2.080. (Falsely assumes that C++ dtor does not have an argument, though). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh no, you're killing me bro! >_< There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I was thinking this was a super clever and elegant solution :( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps I could address this in a follow-up? Situation in this PR is immeasurably better than what we had before, and I have people here that want to be using this code already. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe druntime can handle that case, too. Any user of the dtor member of TypeInfo_Struct/Class needs to know about it then, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is it that druntime isn't calling the destructor with the default arg supplied like everywhere else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you put this snippet in an issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because it uses a function pointer assuming it does not need an extra argument. See
https://issues.dlang.org/show_bug.cgi?id=18953 I just realized that calling the C++ dtor is pretty much broken for Win32 anyway because it doesn't use the __thiscall ABI. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll need that hack you suggest to generate a shim when assigning to a delegate I guess. |
||
| params = new Parameters; | ||
| (*params).push(param); | ||
| } | ||
| dd.type = new TypeFunction(params, Type.tvoid, false, LINK.d, dd.storage_class); | ||
| } | ||
|
|
||
| sc = sc.push(); | ||
| sc.stc &= ~STC.static_; // not a static destructor | ||
|
|
@@ -4767,6 +4777,27 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor | |
| { | ||
| auto s = (*cldec.members)[i]; | ||
| s.dsymbolSemantic(sc2); | ||
|
|
||
| if (cldec.classKind == ClassKind.cpp) | ||
| { | ||
| const DtorDeclaration dtor = s.isDtorDeclaration(); | ||
| if (dtor && cldec.cppDtorVtblIndex == -1) | ||
| { | ||
| if (cldec.baseClass && cldec.baseClass.cppDtorVtblIndex != -1) | ||
| { | ||
| // override the base virtual | ||
| cldec.cppDtorVtblIndex = cldec.baseClass.cppDtorVtblIndex; | ||
| } | ||
| else if (!dtor.isFinal()) | ||
| { | ||
| // reserve the dtor slot for the destructor (which we'll create later) | ||
| cldec.cppDtorVtblIndex = cast(int)cldec.vtbl.dim; | ||
| cldec.vtbl.push(s); | ||
| if (Target.cppDeletingDestructor) | ||
| cldec.vtbl.push(s); // deleting destructor uses a second slot | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!cldec.determineFields()) | ||
|
|
@@ -4854,6 +4885,20 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor | |
|
|
||
| cldec.dtor = buildDtor(cldec, sc2); | ||
|
|
||
| if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1) | ||
| { | ||
| // now we've built the aggregate destructor, we'll make it virtual and assign it to the reserved vtable slot | ||
| cldec.dtor.vtblIndex = cldec.cppDtorVtblIndex; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can see on this line that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I didn't look up far enough 😃. |
||
| cldec.vtbl[cldec.cppDtorVtblIndex] = cldec.dtor; | ||
|
|
||
| if (Target.cppDeletingDestructor) | ||
| { | ||
| // TODO: create a C++ compatible deleting destructor (call out to `operator delete`) | ||
| // for the mooment, we'll call the non-deleting destructor and leak | ||
| cldec.vtbl[cldec.cppDtorVtblIndex + 1] = cldec.dtor; | ||
| } | ||
| } | ||
|
|
||
| if (auto f = hasIdentityOpAssign(cldec, sc2)) | ||
| { | ||
| if (!(f.storage_class & STC.disable)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line.... do I need it?
When I step in there, it looks like it's always done it before.