Skip to content

Commit e6a85f6

Browse files
authored
[ClangImporter] Resolve forward declarations before importing names. (#7555)
This allows a previously-working case of Objective-C forward-declaring a type in a /different/ Swift module to continue working, as long as the Swift context being compiled manages to import the other module properly (including its generated header). This isn't really our recommended pattern---that would be to @import the module in the bridging header and forego the forward declaration---but it doesn't cost much to keep it working. It's also a place where textual and precompiled bridging headers behaved differently, because precompiled ones are processed much earlier. https://bugs.swift.org/browse/SR-3798
1 parent 6dc1793 commit e6a85f6

File tree

5 files changed

+54
-18
lines changed

5 files changed

+54
-18
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4035,18 +4035,8 @@ namespace {
40354035
}
40364036

40374037
Decl *VisitObjCInterfaceDecl(const clang::ObjCInterfaceDecl *decl) {
4038-
Optional<ImportedName> correctSwiftName;
4039-
auto importedName = importFullName(decl, correctSwiftName);
4040-
if (!importedName) return nullptr;
4041-
4042-
// If we've been asked to produce a Swift 2 stub, handle it via a
4043-
// typealias.
4044-
if (correctSwiftName)
4045-
return importSwift2TypeAlias(decl, importedName, *correctSwiftName);
4046-
4047-
auto name = importedName.getDeclName().getBaseName();
4048-
4049-
auto createRootClass = [=](DeclContext *dc = nullptr) -> ClassDecl * {
4038+
auto createRootClass = [=](Identifier name,
4039+
DeclContext *dc = nullptr) -> ClassDecl * {
40504040
if (!dc) {
40514041
dc = Impl.getClangModuleForDecl(decl->getCanonicalDecl(),
40524042
/*allowForwardDeclaration=*/true);
@@ -4081,11 +4071,26 @@ namespace {
40814071
const ClassDecl *nsObjectDecl =
40824072
nsObjectTy->getClassOrBoundGenericClass();
40834073

4084-
auto result = createRootClass(nsObjectDecl->getDeclContext());
4074+
auto result = createRootClass(Impl.SwiftContext.Id_Protocol,
4075+
nsObjectDecl->getDeclContext());
40854076
result->setForeignClassKind(ClassDecl::ForeignKind::RuntimeOnly);
40864077
return result;
40874078
}
40884079

4080+
if (auto *definition = decl->getDefinition())
4081+
decl = definition;
4082+
4083+
Optional<ImportedName> correctSwiftName;
4084+
auto importedName = importFullName(decl, correctSwiftName);
4085+
if (!importedName) return nullptr;
4086+
4087+
// If we've been asked to produce a Swift 2 stub, handle it via a
4088+
// typealias.
4089+
if (correctSwiftName)
4090+
return importSwift2TypeAlias(decl, importedName, *correctSwiftName);
4091+
4092+
auto name = importedName.getDeclName().getBaseName();
4093+
40894094
if (!decl->hasDefinition()) {
40904095
// Check if this class is implemented in its adapter.
40914096
if (auto clangModule = Impl.getClangModuleForDecl(decl, true)) {
@@ -4097,7 +4102,7 @@ namespace {
40974102

40984103
if (Impl.ImportForwardDeclarations) {
40994104
// Fake it by making an unavailable opaque @objc root class.
4100-
auto result = createRootClass();
4105+
auto result = createRootClass(name);
41014106
result->setImplicit();
41024107
auto attr = AvailableAttr::createPlatformAgnostic(Impl.SwiftContext,
41034108
"This Objective-C class has only been forward-declared; "
@@ -4110,9 +4115,6 @@ namespace {
41104115
return nullptr;
41114116
}
41124117

4113-
decl = decl->getDefinition();
4114-
assert(decl);
4115-
41164118
auto dc =
41174119
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
41184120
if (!dc)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@class SwiftClass, SwiftClassWithCustomName;
2+
3+
@interface BridgingHeader
4+
+ (void)takeForward:(SwiftClass *)class;
5+
+ (void)takeRenamedForward:(SwiftClassWithCustomName *)class;
6+
@end

test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Manual PrintAsObjC for testing purposes.
2+
13
struct PureClangType {
24
int x;
35
int y;
@@ -16,7 +18,7 @@ struct PureClangType {
1618
# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA
1719
#endif
1820

19-
#ifndef SWIFT_EXTENSION(X)
21+
#ifndef SWIFT_EXTENSION
2022
# define SWIFT_EXTENSION(X) X##__LINE__
2123
#endif
2224

test/ClangImporter/MixedSource/Inputs/resolve-cross-language/Base.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ extension BaseClass {
3232
@objc public func getSwiftEnum() -> SwiftEnum { return .Quux }
3333
public init() {}
3434
}
35+
36+
@objc(RenamedClass) public class SwiftClass {}
37+
38+
public func getSwiftClass() -> SwiftClass {
39+
return SwiftClass()
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: cp -r %S/Inputs/mixed-framework/Mixed.framework %t
3+
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t/Mixed.framework/Modules/Mixed.swiftmodule/%target-swiftmodule-name %S/Inputs/mixed-framework/Mixed.swift -import-underlying-module -F %t -module-name Mixed -disable-objc-attr-requires-foundation-module
5+
6+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -import-objc-header %S/Inputs/import-mixed-framework-with-forward.h %s -verify
7+
8+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-pch -F %t %S/Inputs/import-mixed-framework-with-forward.h -o %t/bridge.pch
9+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %t -import-objc-header %t/bridge.pch %s -verify
10+
11+
12+
// REQUIRES: objc_interop
13+
14+
import Mixed
15+
16+
BridgingHeader.takeForward(SwiftClass(x: 42))
17+
BridgingHeader.takeRenamedForward(CustomNameClass())
18+
19+
// Check that we're compiling at all.
20+
BridgingHeader.takeRenamedForward(SwiftClass(x: 42)) // expected-error {{cannot convert value of type 'SwiftClass' to expected argument type 'CustomNameClass!'}}

0 commit comments

Comments
 (0)