[cxx-interop] Allow using C-like structs in public Swift interfaces

When compiling with C++ interop enabled, we enable extra safety checks to prevent library authors from accidentally exposing ABI-fragile C++ symbols in resilient Swift interfaces.

The heuristic we use is overly strict, and it prevents the compiler from being able to typecheck various modules from their interfaces when C++ interop is enabled. Darwin and System are two of such modules.

The underlying challenge is that there isn't a good distinction between C structs and C++ structs: whenever parsing a header file in C++ language mode, Clang assumes that every struct is a C++ struct.

This relaxes the heuristic to allow exposing C-like structs in resilient interfaces.

rdar://140203932
This commit is contained in:
Egor Zhdan
2024-12-06 20:18:53 +00:00
parent 7704534132
commit 8859b629fa
4 changed files with 36 additions and 2 deletions

View File

@@ -1885,9 +1885,11 @@ bool isFragileClangNode(const ClangNode &node) {
if (auto *typedefDecl = dyn_cast<clang::TypedefNameDecl>(decl))
return isFragileClangType(typedefDecl->getUnderlyingType());
if (auto *rd = dyn_cast<clang::RecordDecl>(decl)) {
if (!isa<clang::CXXRecordDecl>(rd))
auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(rd);
if (!cxxRecordDecl)
return false;
return !rd->getDeclContext()->isExternCContext();
return !cxxRecordDecl->isCLike() &&
!cxxRecordDecl->getDeclContext()->isExternCContext();
}
return true;
}

View File

@@ -0,0 +1,4 @@
module MyCLibrary {
header "my_c_header.h"
export *
}

View File

@@ -0,0 +1,3 @@
struct MyCStruct {
int x;
};

View File

@@ -0,0 +1,25 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// Build a Swift library that uses symbols from a C library without enabling C++ interop.
// RUN: %target-build-swift %t/uses-c-library.swift -emit-module -emit-library -enable-library-evolution -module-name UsesCLibrary -emit-module-path %t/artifacts/UsesCLibrary.swiftmodule -emit-module-interface-path %t/artifacts/UsesCLibrary.swiftinterface -I %S/Inputs
// Make sure the module interface can be type-checked with C++ interop enabled.
// RUN: %target-swift-frontend -typecheck-module-from-interface -cxx-interoperability-mode=default %t/artifacts/UsesCLibrary.swiftinterface -I %S/Inputs
// Make sure we can build a Swift executable that uses the library and enables C++ interop.
// RUN: %target-swift-frontend -typecheck -cxx-interoperability-mode=default -module-name Main %t/main.swift -I %t/artifacts -I %S/Inputs
//--- uses-c-library.swift
import MyCLibrary
public func getMyCStruct() -> MyCStruct {
return MyCStruct()
}
//--- main.swift
import UsesCLibrary
let _ = getMyCStruct()