Skip to content
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

as-base cdtors for final classes #104

Open
urnathan opened this issue Jun 1, 2020 · 6 comments
Open

as-base cdtors for final classes #104

urnathan opened this issue Jun 1, 2020 · 6 comments

Comments

@urnathan
Copy link
Contributor

urnathan commented Jun 1, 2020

This is captured in GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95428
must as-base-cdtors be emitted for classes that cannot be (proper) bases? GCC10 no longer emits the as-base ctors of final classes, and this appears to break interoperability with Clang.

My guess is that Clang is calling the as-base ctor from the complete ctor (and doing the appropriate vbase stuff, when necessary). If the complete ctor is inlined, one can get left with a call to the as-base ctor, which if the compiler knows is emitted elsewhere will be an external reference.

I don't think the ABI specifies whether as-base ctors are for /proper/ bases or not, perhaps it should?

@zygoloid
Copy link
Contributor

zygoloid commented Jun 5, 2020

"5.2.5 Constructors and destructors" seems clear to me:

user-defined constructors or destructors, unless the function is declared inline, or has internal linkage, are emitted where defined, with their complete, and base object variants

There is no allowance to omit the base class constructor for a final class. I think an implementation is currently within its rights to "inline" the complete object constructor in cases where it knows what the complete object constructor must do.

This seems exactly analogous to issue #10. Following the argument there, this is something we could improve for ABI v2, but for now, implementations are always required to emit both symbols even if only one of them is "really" needed, because implementations rely on both symbols existing.

@urnathan
Copy link
Contributor Author

urnathan commented Jun 5, 2020

Well, when that was written, final wasn't a thing. But I guess the boat has sailed on amending the ABI for final classes in this way.

@zygoloid
Copy link
Contributor

zygoloid commented Jun 5, 2020

Perhaps it might be reasonable for the ABI document to require that implementations do not generate references to the base object constructor of a final class or the complete object constructor of an abstract class (and similarly for destructors), while still requiring that both symbols be emitted.

Once enough time passes with that rule that the ABI break becomes only a theoretical concern, we might be able to remove the requirement to emit them? (However, I'd expect that to be a fairly long timeframe.)

@rjmccall
Copy link
Collaborator

I agree with Richard's interpretation. It seems reasonable to amend the ABI to require that implementations not emit references to the "wrong" variant, but interoperation seems to require us to emit both symbols for at least a time.

@jicama
Copy link
Contributor

jicama commented Aug 21, 2020

There is no allowance to omit the base class constructor for a final class. I think an implementation is currently within its rights to "inline" the complete object constructor in cases where it knows what the complete object constructor must do.

This seems to assume that the complete tor calls the base tor, which surprises me; I've always thought they shouldn't depend on one another, but the ABI seems silent on this question. And it looks like even for a class with virtual bases, clang calls the base variant from the complete variant. OK then, guess I need to emit a functional but unreachable base variant even for a final class with virtual bases.

@zygoloid
Copy link
Contributor

zygoloid commented Aug 24, 2020

Here's a case for which Clang emits a reference to the base class ctor for a final class:

template<typename T> struct A final {
    A(int) { /*sufficiently large body*/ }
};
extern template struct A<int>;
A<int> a(0);

Here, we effectively generate "for inlining" versions of the complete ctor and base ctor. Then we effectively inline the complete ctor, leaving a reference to the base ctor. (This happens one of two ways: either we generate the C1 as an alias to the C2, or we emit a C1 function that calls the C2 function. Either way, the same outcome is reached: the global ctor for a directly invokes the C2 constructor for A<int>.)

One other consideration we've not discussed so far: under the proposed rule change, declaring a class (such as the example in this comment) as final would be an ABI break, even if no-one ever derives from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants