Skip to content

Commit 573b7a9

Browse files
committed
Add a new flag to import objc forward declarations
Adds a new flag -enable-import-objc-forward-declarations that modifies the ClangImporter to import forward declared Objective-C interfaces and protocols and opaque types. The new flag is enabled by default in Swift versions 6 and on.
1 parent 5a7963f commit 573b7a9

24 files changed

+576
-15
lines changed

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ def enable_experimental_eager_clang_module_diagnostics :
304304
Flag<["-"], "enable-experimental-eager-clang-module-diagnostics">,
305305
HelpText<"Enable experimental eager diagnostics reporting on the importability of all referenced C, C++, and Objective-C libraries">;
306306

307+
def enable_import_objc_forward_declarations :
308+
Flag<["-"], "enable-import-objc-forward-declarations">,
309+
HelpText<"Attempt to import Objective-C forward declarations">;
310+
307311
def enable_experimental_pairwise_build_block :
308312
Flag<["-"], "enable-experimental-pairwise-build-block">,
309313
HelpText<"Enable experimental pairwise 'buildBlock' for result builders">;

lib/ClangImporter/ImportDecl.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4393,9 +4393,37 @@ namespace {
43934393
clangModule))
43944394
return native;
43954395

4396-
Impl.addImportDiagnostic(
4397-
decl, Diagnostic(diag::forward_declared_protocol_label, decl),
4398-
decl->getSourceRange().getBegin());
4396+
if (Impl.ImportForwardDeclarations) {
4397+
auto result = Impl.createDeclWithClangNode<ProtocolDecl>(
4398+
decl, AccessLevel::Public,
4399+
Impl.getClangModuleForDecl(decl->getCanonicalDecl(),
4400+
/*allowForwardDeclaration=*/true),
4401+
SourceLoc(), SourceLoc(), name,
4402+
ArrayRef<PrimaryAssociatedTypeName>(), None,
4403+
/*TrailingWhere=*/nullptr);
4404+
4405+
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result;
4406+
result->setSuperclass(Impl.getNSObjectProtocolType());
4407+
result->setAddedImplicitInitializers(); // suppress all initializers
4408+
addObjCAttribute(result,
4409+
Impl.importIdentifier(decl->getIdentifier()));
4410+
SmallVector<InheritedEntry, 4> inheritedTypes = {
4411+
TypeLoc::withoutLoc(Impl.getNSObjectProtocolType())};
4412+
result->setInherited(Impl.SwiftContext.AllocateCopy(inheritedTypes));
4413+
result->setImplicit();
4414+
auto attr = AvailableAttr::createPlatformAgnostic(
4415+
Impl.SwiftContext,
4416+
"This Objective-C protocol has only been forward-declared; "
4417+
"import its owning module to use it");
4418+
result->getAttrs().add(attr);
4419+
result->getAttrs().add(
4420+
new (Impl.SwiftContext) ForbidSerializingReferenceAttr(true));
4421+
return result;
4422+
} else {
4423+
Impl.addImportDiagnostic(
4424+
decl, Diagnostic(diag::forward_declared_protocol_label, decl),
4425+
decl->getSourceRange().getBegin());
4426+
}
43994427

44004428
forwardDeclaration = true;
44014429
return nullptr;
@@ -4464,7 +4492,7 @@ namespace {
44644492
nullptr, dc,
44654493
/*isActor*/false);
44664494
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result;
4467-
result->setSuperclass(Type());
4495+
result->setSuperclass(Impl.getNSObjectType());
44684496
result->setAddedImplicitInitializers(); // suppress all initializers
44694497
result->setHasMissingVTableEntries(false);
44704498
addObjCAttribute(result, Impl.importIdentifier(decl->getIdentifier()));

lib/Frontend/CompilerInvocation.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,8 @@ static bool ParseTypeCheckerArgs(TypeCheckerOptions &Opts, ArgList &Args,
12151215
static bool ParseClangImporterArgs(ClangImporterOptions &Opts,
12161216
ArgList &Args,
12171217
DiagnosticEngine &Diags,
1218-
StringRef workingDirectory) {
1218+
StringRef workingDirectory,
1219+
const LangOptions &LangOpts) {
12191220
using namespace options;
12201221

12211222
if (const Arg *a = Args.getLastArg(OPT_tools_directory)) {
@@ -1273,6 +1274,10 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts,
12731274

12741275
Opts.DumpClangDiagnostics |= Args.hasArg(OPT_dump_clang_diagnostics);
12751276

1277+
if (Args.hasArg(OPT_enable_import_objc_forward_declarations) || LangOpts.isSwiftVersionAtLeast(6)) {
1278+
Opts.ImportForwardDeclarations = true;
1279+
}
1280+
12761281
if (Args.hasArg(OPT_embed_bitcode))
12771282
Opts.Mode = ClangImporterOptions::Modes::EmbedBitcode;
12781283
else if (Args.hasArg(OPT_emit_pcm) || Args.hasArg(OPT_dump_pcm))
@@ -2616,7 +2621,7 @@ bool CompilerInvocation::parseArgs(
26162621
}
26172622

26182623
if (ParseClangImporterArgs(ClangImporterOpts, ParsedArgs, Diags,
2619-
workingDirectory)) {
2624+
workingDirectory, LangOpts)) {
26202625
return true;
26212626
}
26222627

lib/Serialization/Serialization.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -619,10 +619,6 @@ DeclID Serializer::addDeclRef(const Decl *D, bool allowTypeAliasXRef) {
619619
isa<PrecedenceGroupDecl>(D)) &&
620620
"cannot cross-reference this decl");
621621

622-
assert((!D || !isDeclXRef(D) ||
623-
!D->getAttrs().hasAttribute<ForbidSerializingReferenceAttr>()) &&
624-
"cannot cross-reference this decl");
625-
626622
assert((!D || allowTypeAliasXRef || !isa<TypeAliasDecl>(D) ||
627623
D->getModuleContext() == M) &&
628624
"cannot cross-reference typealiases directly (use the TypeAliasType)");
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface ForwardDeclaredInterface : NSObject
4+
- (id) init;
5+
- (void) doSomethingForwardDeclaredInterfacesCan;
6+
@end
7+
8+
@protocol ForwardDeclaredProtocol<NSObject>
9+
- (void) doSomethingForwardDeclaredProtocolsCan;
10+
@end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#import "complete-types.h"
2+
3+
@implementation ForwardDeclaredInterface
4+
- (id) init {
5+
return [super init];
6+
}
7+
- (void) doSomethingForwardDeclaredInterfacesCan {
8+
NSLog(@"Doing something forward declared interfaces can!");
9+
}
10+
@end
11+
12+
void takeACompleteInterface(ForwardDeclaredInterface* param) {
13+
NSLog(@"takeACompleteInterface");
14+
[param doSomethingForwardDeclaredInterfacesCan];
15+
}
16+
void takeACompleteProcotol(NSObject<ForwardDeclaredProtocol>* param) {
17+
NSLog(@"takeACompleteProcotol");
18+
[param doSomethingForwardDeclaredProtocolsCan];
19+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@class ForwardDeclaredInterface;
4+
@protocol ForwardDeclaredProtocol;
5+
6+
@interface IncompleteTypeConsumer1 : NSObject
7+
@property id<ForwardDeclaredProtocol> propertyUsingAForwardDeclaredProtocol1;
8+
@property ForwardDeclaredInterface *propertyUsingAForwardDeclaredInterface1;
9+
- (id)init;
10+
- (NSObject<ForwardDeclaredProtocol> *)methodReturningForwardDeclaredProtocol1;
11+
- (ForwardDeclaredInterface *)methodReturningForwardDeclaredInterface1;
12+
- (void)methodTakingAForwardDeclaredProtocol1:
13+
(id<ForwardDeclaredProtocol>)param;
14+
- (void)methodTakingAForwardDeclaredInterface1:
15+
(ForwardDeclaredInterface *)param;
16+
@end
17+
18+
ForwardDeclaredInterface *CFunctionReturningAForwardDeclaredInterface1();
19+
void CFunctionTakingAForwardDeclaredInterface1(
20+
ForwardDeclaredInterface *param);
21+
22+
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol1();
23+
void CFunctionTakingAForwardDeclaredProtocol1(
24+
id<ForwardDeclaredProtocol> param);
25+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#import "incomplete-type-library-1.h"
2+
#import "complete-types.h"
3+
4+
@interface TypeConformingToForwardDeclaredProtocol1: NSObject<ForwardDeclaredProtocol>
5+
- (id) init;
6+
- (void) doSomethingForwardDeclaredProtocolsCan;
7+
@end
8+
9+
@implementation TypeConformingToForwardDeclaredProtocol1
10+
- (id) init {
11+
return [super init];
12+
}
13+
- (void) doSomethingForwardDeclaredProtocolsCan {
14+
NSLog(@"Doing something forward declared protocols can version 1!");
15+
}
16+
@end
17+
18+
@implementation IncompleteTypeConsumer1
19+
- (id) init {
20+
self = [super init];
21+
if (self) {
22+
self.propertyUsingAForwardDeclaredInterface1 = [[ForwardDeclaredInterface alloc] init];
23+
self.propertyUsingAForwardDeclaredProtocol1 = [[TypeConformingToForwardDeclaredProtocol1 alloc] init];
24+
}
25+
return self;
26+
}
27+
- (NSObject<ForwardDeclaredProtocol> *)methodReturningForwardDeclaredProtocol1 {
28+
NSLog(@"methodReturningForwardDeclaredProtocol1");
29+
return [[TypeConformingToForwardDeclaredProtocol1 alloc] init];
30+
}
31+
- (ForwardDeclaredInterface *)methodReturningForwardDeclaredInterface1 {
32+
NSLog(@"methodReturningForwardDeclaredInterface1");
33+
return [[ForwardDeclaredInterface alloc] init];
34+
}
35+
- (void)methodTakingAForwardDeclaredProtocol1:
36+
(id<ForwardDeclaredProtocol>)param {
37+
NSLog(@"methodTakingAForwardDeclaredProtocol1");
38+
[param doSomethingForwardDeclaredProtocolsCan];
39+
}
40+
- (void)methodTakingAForwardDeclaredInterface1:
41+
(ForwardDeclaredInterface *)param {
42+
NSLog(@"methodTakingAForwardDeclaredInterface1");
43+
[param doSomethingForwardDeclaredInterfacesCan];
44+
}
45+
@end
46+
47+
ForwardDeclaredInterface *CFunctionReturningAForwardDeclaredInterface1() {
48+
NSLog(@"CFunctionReturningAForwardDeclaredInterface1");
49+
return [[ForwardDeclaredInterface alloc] init];
50+
}
51+
void CFunctionTakingAForwardDeclaredInterface1(
52+
ForwardDeclaredInterface *param) {
53+
NSLog(@"CFunctionTakingAForwardDeclaredInterface1");
54+
[param doSomethingForwardDeclaredInterfacesCan];
55+
}
56+
57+
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol1() {
58+
NSLog(@"CFunctionReturningAForwardDeclaredProtocol1");
59+
return [[TypeConformingToForwardDeclaredProtocol1 alloc] init];
60+
}
61+
void CFunctionTakingAForwardDeclaredProtocol1(
62+
id<ForwardDeclaredProtocol> param) {
63+
NSLog(@"CFunctionTakingAForwardDeclaredProtocol1");
64+
[param doSomethingForwardDeclaredProtocolsCan];
65+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@class ForwardDeclaredInterface;
4+
@protocol ForwardDeclaredProtocol;
5+
6+
@interface IncompleteTypeConsumer2 : NSObject
7+
@property id<ForwardDeclaredProtocol> propertyUsingAForwardDeclaredProtocol2;
8+
@property ForwardDeclaredInterface *propertyUsingAForwardDeclaredInterface2;
9+
- (id)init;
10+
- (NSObject<ForwardDeclaredProtocol> *)methodReturningForwardDeclaredProtocol2;
11+
- (ForwardDeclaredInterface *)methodReturningForwardDeclaredInterface2;
12+
- (void)methodTakingAForwardDeclaredProtocol2:
13+
(id<ForwardDeclaredProtocol>)param;
14+
- (void)methodTakingAForwardDeclaredInterface2:
15+
(ForwardDeclaredInterface *)param;
16+
@end
17+
18+
ForwardDeclaredInterface *CFunctionReturningAForwardDeclaredInterface2();
19+
void CFunctionTakingAForwardDeclaredInterface2(
20+
ForwardDeclaredInterface *param);
21+
22+
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol2();
23+
void CFunctionTakingAForwardDeclaredProtocol2(
24+
id<ForwardDeclaredProtocol> param);
25+
26+
27+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#import "incomplete-type-library-2.h"
2+
#import "complete-types.h"
3+
4+
@interface TypeConformingToForwardDeclaredProtocol2: NSObject<ForwardDeclaredProtocol>
5+
- (id) init;
6+
- (void) doSomethingForwardDeclaredProtocolsCan;
7+
@end
8+
9+
@implementation TypeConformingToForwardDeclaredProtocol2
10+
- (id) init {
11+
return [super init];
12+
}
13+
- (void) doSomethingForwardDeclaredProtocolsCan {
14+
NSLog(@"Doing something forward declared protocols can version 2!");
15+
}
16+
@end
17+
18+
@implementation IncompleteTypeConsumer2
19+
- (id) init {
20+
self = [super init];
21+
if (self) {
22+
self.propertyUsingAForwardDeclaredInterface2 = [[ForwardDeclaredInterface alloc] init];
23+
self.propertyUsingAForwardDeclaredProtocol2 = [[TypeConformingToForwardDeclaredProtocol2 alloc] init];
24+
}
25+
return self;
26+
}
27+
- (NSObject<ForwardDeclaredProtocol> *)methodReturningForwardDeclaredProtocol2 {
28+
NSLog(@"methodReturningForwardDeclaredProtocol2");
29+
return [[TypeConformingToForwardDeclaredProtocol2 alloc] init];
30+
}
31+
- (ForwardDeclaredInterface *)methodReturningForwardDeclaredInterface2 {
32+
NSLog(@"methodReturningForwardDeclaredInterface2");
33+
return [[ForwardDeclaredInterface alloc] init];
34+
}
35+
- (void)methodTakingAForwardDeclaredProtocol2:
36+
(id<ForwardDeclaredProtocol>)param {
37+
NSLog(@"methodTakingAForwardDeclaredProtocol2");
38+
[param doSomethingForwardDeclaredProtocolsCan];
39+
}
40+
- (void)methodTakingAForwardDeclaredInterface2:
41+
(ForwardDeclaredInterface *)param {
42+
NSLog(@"methodTakingAForwardDeclaredInterface2");
43+
[param doSomethingForwardDeclaredInterfacesCan];
44+
}
45+
@end
46+
47+
ForwardDeclaredInterface *CFunctionReturningAForwardDeclaredInterface2() {
48+
NSLog(@"CFunctionReturningAForwardDeclaredInterface2");
49+
return [[ForwardDeclaredInterface alloc] init];
50+
}
51+
void CFunctionTakingAForwardDeclaredInterface2(
52+
ForwardDeclaredInterface *param) {
53+
NSLog(@"CFunctionTakingAForwardDeclaredInterface2");
54+
[param doSomethingForwardDeclaredInterfacesCan];
55+
}
56+
57+
NSObject<ForwardDeclaredProtocol> *CFunctionReturningAForwardDeclaredProtocol2() {
58+
NSLog(@"CFunctionReturningAForwardDeclaredProtocol2");
59+
return [[TypeConformingToForwardDeclaredProtocol2 alloc] init];
60+
}
61+
void CFunctionTakingAForwardDeclaredProtocol2(
62+
id<ForwardDeclaredProtocol> param) {
63+
NSLog(@"CFunctionTakingAForwardDeclaredProtocol2");
64+
[param doSomethingForwardDeclaredProtocolsCan];
65+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module CompleteTypes {
2+
header "complete-types.h"
3+
}
4+
5+
module IncompleteTypeLibrary1 {
6+
header "incomplete-type-library-1.h"
7+
}
8+
9+
module IncompleteTypeLibrary2 {
10+
header "incomplete-type-library-2.h"
11+
}

test/ClangImporter/experimental_clang_importer_diagnostics_bridging_header.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-frontend -enable-objc-interop -import-objc-header %S/Inputs/experimental_clang_importer_diagnostics_bridging_header.h -typecheck %s 2>&1 | %FileCheck %s
1+
// RUN: not %target-swift-frontend -enable-objc-interop -swift-version 5 -import-objc-header %S/Inputs/experimental_clang_importer_diagnostics_bridging_header.h -typecheck %s 2>&1 | %FileCheck %s
22

33
let s: PartialImport
44
s.c = 5

test/ClangImporter/experimental_diagnostics_incomplete_types.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
1+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -swift-version 5 -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
22

33
// REQUIRES: objc_interop
44

test/ClangImporter/experimental_diagnostics_no_noise.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
1+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -swift-version 5 -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
22

33
// REQUIRES: objc_interop
44

test/ClangImporter/experimental_diagnostics_opt_out.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-experimental-clang-importer-diagnostics -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
1+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -swift-version 5 -disable-experimental-clang-importer-diagnostics -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
22

33
// REQUIRES: objc_interop
44

test/ClangImporter/experimental_eager_diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-eager-clang-module-diagnostics -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
1+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -swift-version 5 -enable-experimental-eager-clang-module-diagnostics -enable-objc-interop -typecheck %s 2>&1 | %FileCheck %s --strict-whitespace
22

33
// REQUIRES: objc_interop
44

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-clang %S/Inputs/custom-modules/IncompleteTypes/incomplete-type-library-1.m -c -o %t/incomplete-type-library-1.o
3+
// RUN: %target-clang %S/Inputs/custom-modules/IncompleteTypes/complete-types.m -c -o %t/complete-types.o
4+
5+
// RUN: %target-build-swift -Xfrontend -enable-import-objc-forward-declarations -enable-objc-interop -I %S/Inputs/custom-modules/IncompleteTypes %s %t/incomplete-type-library-1.o %t/complete-types.o -Xlinker -framework -Xlinker Foundation -o %t/a.out
6+
// RUN: %target-run %t/a.out
7+
8+
// RUN: %target-build-swift -swift-version 6 -enable-objc-interop -I %S/Inputs/custom-modules/IncompleteTypes %s %t/incomplete-type-library-1.o %t/complete-types.o -Xlinker -framework -Xlinker Foundation -o %t/a.out
9+
// RUN: %target-run %t/a.out
10+
11+
// REQUIRES: objc_interop
12+
13+
// Verify that Clang declarations referencing either of the forward declares types "ForwardDeclaredInterface" or
14+
// "ForwardDeclaredProtocol" are usable from Swift.
15+
16+
import IncompleteTypeLibrary1
17+
18+
let incompleteTypeConsumer = IncompleteTypeConsumer1()!
19+
20+
let incompleteInterface = incompleteTypeConsumer.methodReturningForwardDeclaredInterface1()!
21+
incompleteTypeConsumer.methodTakingAForwardDeclaredInterface1(incompleteInterface)
22+
incompleteTypeConsumer.propertyUsingAForwardDeclaredInterface1 = incompleteInterface
23+
_ = CFunctionReturningAForwardDeclaredInterface1()
24+
CFunctionTakingAForwardDeclaredInterface1(incompleteInterface)
25+
26+
let incompleteProtocol = incompleteTypeConsumer.methodReturningForwardDeclaredProtocol1()!
27+
incompleteTypeConsumer.methodTakingAForwardDeclaredProtocol1(incompleteProtocol)
28+
incompleteTypeConsumer.propertyUsingAForwardDeclaredProtocol1 = incompleteProtocol
29+
_ = CFunctionReturningAForwardDeclaredProtocol1()
30+
CFunctionTakingAForwardDeclaredProtocol1(incompleteProtocol)

0 commit comments

Comments
 (0)