Skip to content

Commit 1ae0d2a

Browse files
committed
[cxx-interop] Fixing usage of ObjC Categories in C++-Interop mode.
In C/ObjC interop mode a Clang AST with an ObjC Category resembles: TranslationUnitDecl `-ObjCCategoryDecl However in C++-Interop mode the same Category could potentially have an extern "C" linkage specifier (guarded with #ifdef __cplusplus) and resembles: TranslationUnitDecl |-LinkageSpecDecl `-ObjCCategoryDecl In the latter case when the ClangImporter attempts to import the category in swift::ClangImporter::Implementation::importDeclContextOr, prior to this patch, would bail because it is expecting the DeclContext above the ObjCCategoryDecl to be a TranslationUnitDecl and when it isn't it returns nullptr. Because of this, of course the category does not get imported as a swift extension and therefore any fields or methods are also not imported. In the case of UIKit, UIView has one of these categories that are extern-"C"'ed due to UIKIT_EXTERN containing the linkage specifier in -enable-cxx-interop mode. Since UIView is inherited by lots of other UIKit types (UILabel etc), many of these types also have lots of missing fields as well. This patch checks to see if the decl about to be imported in importDeclContextOr has a linkage specifier as it's context and makes sure to materialize the TU from the parent of the declcontext instead of just immediately bailing. Because of this, this patch enables c++-interop mode to compile code that uses UIKit without hitting this mis-compile.
1 parent 836782b commit 1ae0d2a

File tree

4 files changed

+36
-0
lines changed

4 files changed

+36
-0
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9331,6 +9331,13 @@ ClangImporter::Implementation::importDeclContextOf(
93319331
switch (context.getKind()) {
93329332
case EffectiveClangContext::DeclContext: {
93339333
auto dc = context.getAsDeclContext();
9334+
9335+
// For C++-Interop in cases where #ifdef __cplusplus surround an extern "C"
9336+
// you want to first check if the TU decl is the parent of this extern "C"
9337+
// decl (aka LinkageSpecDecl) and then proceed.
9338+
if (dc->getDeclKind() == clang::Decl::LinkageSpec)
9339+
dc = dc->getParent();
9340+
93349341
if (dc->isTranslationUnit()) {
93359342
if (auto *module = getClangModuleForDecl(decl))
93369343
return module;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@interface A
2+
@end
3+
4+
extern "C"
5+
@interface A (CAT1)
6+
- (int)foo;
7+
@end
8+
9+
@interface A (CAT2)
10+
- (int)bar;
11+
@end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ExternC {
2+
header "extern-c.h"
3+
requires cplusplus
4+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=ExternC -I %S/Inputs -source-filename=x -enable-cxx-interop -enable-objc-interop | %FileCheck %s
2+
3+
// CHECK: class A {
4+
// CHECK-NEXT: }
5+
6+
// CHECK: extension A {
7+
// CHECK-NEXT: class func foo() -> Int32
8+
// CHECK-NEXT: func foo() -> Int32
9+
// CHECK-NEXT: }
10+
11+
// CHECK: extension A {
12+
// CHECK-NEXT: class func bar() -> Int32
13+
// CHECK-NEXT: func bar() -> Int32
14+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)