Skip to content

[cxx-interop] Flip the switch: only import safe APIs. #59624

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 11 commits into from
Jul 20, 2022

Conversation

zoecarver
Copy link
Contributor

@zoecarver zoecarver commented Jun 22, 2022

This patch adds is the first of many to flip the switch and stop eagerly importing C++ APIs. We will now only import safe APIs or APIs that are annotated by the user. I will follow this patch up with improved diagnostics, testing, and documentation.

@zoecarver zoecarver added the c++ interop Feature: Interoperability with C++ label Jun 22, 2022
@zoecarver zoecarver requested a review from egorzhdan June 22, 2022 00:03

// Recursively checks that there are no pointers, user-provided copy
// constructors, or destructors in any fields or base classes.
static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to cache this.

Comment on lines 4562 to 3158
if (bridgingInfo.getClangName() != "begin" &&
bridgingInfo.getClangName() != "end" &&
!decl->isOverloadedOperator() && !decl->isStatic() &&
!hasUnsafeAPIAttr(decl) && !hasImportAsRefAttr(decl->getParent()))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add tests for all of these

inline const char *inNonTrivial() const { return "NonTrivial::inNonTrivial"; }
inline const char *inNonTrivialWithArgs(int a, int b) const {
inline const char *inNonTrivial() const
__attribute__((swift_attr("import_unsafe_api"))) {
Copy link
Contributor Author

@zoecarver zoecarver Jun 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review note: I was originally going to update all of these tests to be safe APIs, but I decided that should happen in a follow up PR. These are testing very specific things, so we should upgrade them slowly/incrementally to ensure we don't lose test coverage and regress.

@zoecarver zoecarver requested a review from hyp June 22, 2022 00:07
@egorzhdan
Copy link
Contributor

This essentially makes the enable_unsafe_eager_importing flag viral in a sense that if one of your Swift dependencies uses this flag, you also have to use it even if you don't reference any C++ APIs at all, otherwise you'll get SIL deserialization errors: the decls referenced by that dependency won't be found when compiling the current module.

This makes me think that ClangImporter might not be the right place for this logic. I think a better approach would be to import all APIs in ClangImporter, and then reject unsafe calls during type checking. Let's discuss offline :)

@zoecarver zoecarver force-pushed the disable-eager-import branch from bc301b4 to 44cb846 Compare July 1, 2022 01:16
@zoecarver zoecarver force-pushed the disable-eager-import branch from 44cb846 to 0198e0f Compare July 11, 2022 17:47
@@ -1,37 +0,0 @@
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not something that we want people to be able to do anymore. The correct way to do this is with the stdlib overlay, which we already have tests for.

@zoecarver zoecarver force-pushed the disable-eager-import branch from ea20131 to 494ff2d Compare July 15, 2022 20:36
@zoecarver zoecarver marked this pull request as ready for review July 15, 2022 20:37
@zoecarver
Copy link
Contributor Author

@swift-ci please test

@zoecarver zoecarver changed the title [cxx-interop] Flip the switch: only import safe APIs (pt. 1) [cxx-interop] Flip the switch: only import safe APIs. Jul 15, 2022
@zoecarver zoecarver force-pushed the disable-eager-import branch 2 times, most recently from abd1fd9 to f32c529 Compare July 15, 2022 21:49
@zoecarver
Copy link
Contributor Author

@swift-ci please test

@@ -0,0 +1,37 @@
# C++ Interop User Manual
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is user manual lacks introduction and basic context to be able to understand the sections below. Is it meant to be a manual for forward interop, or bidirectional whole interop? How is the user supposed to read it and understand what categories are without some sort of basic introduction.

return CxxRecordSemanticsKind::Owned;
}

if (hasIteratorAPIAttr(decl)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should require the user to annotate all iterators with an attribute. Swift can detect that the type is an iterator statically without an attribute by doing clangRecordDecl->lookup(getClangASTContext().Idents.get("iterator_category")) – if there is a subtype called iterator_category, then this type is an iterator (see swift::conformToCxxIteratorIfNeeded from #60001).

If the iterator type doesn't define iterator_category, then the user can add an attribute, but I don't think we should require attributes for something we can correctly deduce statically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline: we will use both to determine what's an iterator.

let count = string.count
self.init(count, value_type(0))
var string = string
string.withUTF8 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this would be a small perf improvement but actually seems to not work. We can investigate in a separate follow up patch.

!copyAssignOperatorIsDefaulted(decl)) ||
(decl->hasUserDeclaredDestructor() &&
!decl->getDestructor()->isDefaulted()))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand why a type with a custom copy constructor is unsafe. I think this is probably too restrictive, let's discuss.

The Swift compiler enforces certain API rules, not to ensure a completely safe C++ API interface, but to prevent especially unsafe patterns that will likely lead to bugs. Many of these unsafe patterns stem from the subtly different lifetime semantics in C++ and Swift and aim to prevent memory projections of owned types. The currently enforced rules are as follows:

* Unsafe lifetime types and un-importable types are not allowed to be used in any contexts.
* Unsafe pointer types are not allowed to represent unsafe memory projections from owned types. Note: unsafe pointer types *are* allowed to represent memory projections from reference types. Note: the Swift compiler assumes that global and static functions do not return projections.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you clarify that unsafe pointers are allowed to represent memory projections from immortal reference types here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have the idea of immortal vs other foreign reference types. Above it clarifies that foreign reference types must be immortal or manually managed, so I think it's fine. This states our current support, not our future plans.


import Test

// CHECK: note: record 'X' is not defined (incomplete)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NuriAmari thanks again for building all the infrastructure for these diagnostics. It made it super easy (one line) to add diagnostics for all these other cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My pleasure, glad it's being put to good use 👍

@zoecarver
Copy link
Contributor Author

@swift-ci please test.

1 similar comment
@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver zoecarver force-pushed the disable-eager-import branch from 781850c to 228a886 Compare July 18, 2022 21:16
@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver zoecarver force-pushed the disable-eager-import branch from 228a886 to 12933bf Compare July 19, 2022 00:06
@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver
Copy link
Contributor Author

@swift-ci please test.

For some reason this isn't working, so I reverted to the previous approach. We can try to re-visit this in the future for a potential perf improvement.
@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver
Copy link
Contributor Author

@swift-ci please smoke test linux.

1 similar comment
@zoecarver
Copy link
Contributor Author

@swift-ci please smoke test linux.

@zoecarver zoecarver force-pushed the disable-eager-import branch from 07a97f8 to e62ce9b Compare July 19, 2022 19:13
@zoecarver
Copy link
Contributor Author

@swift-ci please smoke test linux.

@zoecarver zoecarver force-pushed the disable-eager-import branch from e62ce9b to 20ffe0b Compare July 19, 2022 19:53
@zoecarver
Copy link
Contributor Author

@swift-ci please smoke test linux.

@zoecarver zoecarver force-pushed the disable-eager-import branch from 20ffe0b to ba7e6e4 Compare July 19, 2022 20:39
@zoecarver
Copy link
Contributor Author

@swift-ci please smoke test.

@zoecarver
Copy link
Contributor Author

@swift-ci please test.

@zoecarver zoecarver merged commit 33335f9 into swiftlang:main Jul 20, 2022
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++
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants