Skip to content

[cxx-interop] Synthesize C++ wrapper method when invoking a base method or accessor from a derived class in Swift #69222

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

Merged
merged 6 commits into from
Oct 24, 2023

Conversation

hyp
Copy link
Contributor

@hyp hyp commented Oct 17, 2023

Explanation:
In order to provide access to base class members in an imported derived class in Swift, the clang importer in Swift clones member signatures (for methods, subscripts and property accessors) and synthesizes a Swift thunk that invokes the base member. The previous implementation for method calls and getter accessors relied on casting the derived Swift type to the base Swift type before invoking the base member using the __swift_interopStaticCast helper function. However, this meant that Swift copied the base C++ class, sometimes more than once during this process, i.e. the base member got invoked on a copy of a base type, and not the underlying base value inside of the original derived value. This caused numerous problems in some adopters, e.g. it was impossible to iterate over a subclass of a C++ container that used base begin and end, as the iterator for sequence traversal that Swift constructed (in the C++ overlay) obtained begin and end iterators from copies of the container, which were immediately invalid after begin and end returned, as they were just temporary copies in the synthesized methods. This also presented a problem for non-copyable type support, and also was a general correctness/performance issue.

This change resolves the issue by changing the way the base member is invoked. Instead of invoking it directly from the synthesized Swift thunk, the importer generates an additional inline C++ member function that invokes the base member or accesses the base field directly from C++, which does the cast of the derived value this without copying the base value. This C++ member function is then imported into Swift, and invoked from the synthesized Swift thunk using the derived value directly. This avoids any copies of the base value.

This change handles method calls, getter accessors for subscripts and .pointee, and also getter accessors for base fields. C++ classes that derive from other classes also behave as expected, although their generated Swift thunks may go through multiple levels of generated derived-to-base C++ functions (e.g. derived-derived member calls derived member calls base member). Also, note that in order to preserve the "copy" semantics of a retainable foreign reference type C++ field, this change also allows the use of cf_returns_retained attribute to let C++ methods use @owned semantics for returned FRTs. Such base field accesses invoke the retain function inside of the generated C++ inline member to ensure that the +1 result convention is established correctly.

Fixes: #65876
rdar://115231857

Scope: C++ interop, importing base members of derived C++ classes into Swift.
Risk: Medium, non-trivial change to derived to base calls
Testing: Unit tests, some manual adopter testing
Original PR: #68846
Reviewer: @egorzhdan

hyp added 4 commits October 16, 2023 17:18
…d from a derived class synthesized method

The use of a synthesized C++ method allows us to avoid making a copy of self when invoking the base method from Swift
…d or subscript from a derived class synthesized method

The use of a synthesized C++ method allows us to avoid making a copy of self when accessing the base field or subscript from Swift
…t a retainable FRT value

This matches the semantics of accessing the same field from the base class
@hyp hyp added the c++ interop Feature: Interoperability with C++ label Oct 17, 2023
@hyp hyp requested a review from a team as a code owner October 17, 2023 00:37
@hyp hyp added the swift 5.10 label Oct 17, 2023
@hyp
Copy link
Contributor Author

hyp commented Oct 17, 2023

@swift-ci please test

@hyp
Copy link
Contributor Author

hyp commented Oct 17, 2023

@swift-ci please test source compatibility

@hyp
Copy link
Contributor Author

hyp commented Oct 23, 2023

@swift-ci please test

@hyp
Copy link
Contributor Author

hyp commented Oct 24, 2023

@swift-ci please test

@hyp
Copy link
Contributor Author

hyp commented Oct 24, 2023

@swift-ci please test source compatibility

@hyp hyp merged commit 1b9d8c8 into release/5.10 Oct 24, 2023
@hyp hyp deleted the eng/5.10/derived-to-base-calls branch October 24, 2023 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ interop Feature: Interoperability with C++ swift 5.10
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants