Skip to content

Commit 36f25c3

Browse files
committed
ClangImporter: teach clang importer to import Clang SPI symbols and model them similarly as Swift SPIs
For clang symbols marked with SPI_AVAILABLE, we add SPIAccessControlAttr to them so they will be considered as SPIs in the AST. To be able to use all these symbols, we also add an implicit SPI import statement for all clang modules. All clang SPIs belong to the same SPI group named "OBJC_DEFUALT_SPI_GROUP" because clang currently doesn't support custom SPI group. rdar://73902734
1 parent b6f6713 commit 36f25c3

File tree

8 files changed

+95
-1
lines changed

8 files changed

+95
-1
lines changed

include/swift/Strings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ constexpr static const StringLiteral DEFAULT_ACTOR_STORAGE_FIELD_NAME =
5858
/// The name of the Builtin type prefix
5959
constexpr static const StringLiteral BUILTIN_TYPE_NAME_PREFIX = "Builtin.";
6060

61+
/// The default SPI group name to associate with Clang SPIs.
62+
constexpr static const StringLiteral CLANG_MODULE_DEFUALT_SPI_GROUP_NAME =
63+
"OBJC_DEFAULT_SPI_GROUP";
64+
6165
/// A composition class containing a StringLiteral for the names of
6266
/// Swift builtins. The reason we use this is to ensure that we when
6367
/// necessary slice off the "Builtin." prefix from these names in a

lib/ClangImporter/ImportDecl.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8545,6 +8545,21 @@ Optional<bool> swift::importer::isMainActorAttr(
85458545
return None;
85468546
}
85478547

8548+
static bool isUsingMacroName(clang::SourceManager &SM,
8549+
clang::SourceLocation loc,
8550+
StringRef MacroName) {
8551+
if (!loc.isMacroID())
8552+
return false;
8553+
auto Sloc = SM.getExpansionLoc(loc);
8554+
if (Sloc.isInvalid())
8555+
return false;
8556+
auto Eloc = Sloc.getLocWithOffset(MacroName.size());
8557+
if (Eloc.isInvalid())
8558+
return false;
8559+
StringRef content(SM.getCharacterData(Sloc), MacroName.size());
8560+
return content == MacroName;
8561+
}
8562+
85488563
/// Import Clang attributes as Swift attributes.
85498564
void ClangImporter::Implementation::importAttributes(
85508565
const clang::NamedDecl *ClangDecl,
@@ -8668,6 +8683,17 @@ void ClangImporter::Implementation::importAttributes(
86688683
AnyUnavailable = true;
86698684
}
86708685

8686+
if (isUsingMacroName(getClangASTContext().getSourceManager(),
8687+
avail->getLoc(), "SPI_AVAILABLE") ||
8688+
isUsingMacroName(getClangASTContext().getSourceManager(),
8689+
avail->getLoc(), "__SPI_AVAILABLE")) {
8690+
// The decl has been marked as SPI in the header by using the SPI macro,
8691+
// thus we add the SPI attribute to it with a default group name.
8692+
MappedDecl->getAttrs().add(SPIAccessControlAttr::create(SwiftContext,
8693+
SourceLoc(), SourceRange(),
8694+
SwiftContext.getIdentifier(CLANG_MODULE_DEFUALT_SPI_GROUP_NAME)));
8695+
}
8696+
86718697
StringRef message = avail->getMessage();
86728698

86738699
llvm::VersionTuple deprecated = avail->getDeprecated();

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
642642

643643
/// The DWARF importer delegate, if installed.
644644
DWARFImporterDelegate *DWARFImporter = nullptr;
645+
645646
public:
646647
/// Only used for testing.
647648
void setDWARFImporterDelegate(DWARFImporterDelegate &delegate);

lib/Sema/ImportResolution.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/ClangImporter/ClangModule.h"
2929
#include "swift/Parse/Parser.h"
3030
#include "swift/Subsystems.h"
31+
#include "swift/Strings.h"
3132
#include "clang/Basic/Module.h"
3233
#include "llvm/ADT/DenseMap.h"
3334
#include "llvm/ADT/TinyPtrVector.h"
@@ -317,6 +318,23 @@ void ImportResolver::bindImport(UnboundImport &&I) {
317318
ID.get()->setModule(nullptr);
318319
return;
319320
}
321+
// If the imported module is a clang module, add an implicit import statement
322+
// to request the SPIs from the module.
323+
if (M->isNonSwiftModule() && ID &&
324+
!ID.get()->getAttrs().hasAttribute<SPIAccessControlAttr>()) {
325+
ImportDecl *id = ID.get();
326+
auto *newId = ImportDecl::create(id->getASTContext(), id->getDeclContext(),
327+
SourceLoc(), id->getImportKind(), SourceLoc(), id->getImportPath());
328+
// Copy all the existing attribute from the actual import statement.
329+
llvm::for_each(id->getAttrs(),
330+
[&](DeclAttribute *attr) {newId->getAttrs().add(attr);});
331+
// Add SPI attribute with the default group name.
332+
newId->getAttrs().add(SPIAccessControlAttr::create(id->getASTContext(),
333+
SourceLoc(), SourceRange(),
334+
{ ctx.getIdentifier(CLANG_MODULE_DEFUALT_SPI_GROUP_NAME) }));
335+
// So we'll resolve the new import.
336+
unboundImports.push_back(UnboundImport(newId));
337+
}
320338

321339
auto topLevelModule = I.getTopLevelModule(M, SF);
322340

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#import <Foundation/Foundation.h>
2+
3+
#ifdef SPI_AVAILABLE
4+
#undef SPI_AVAILABLE
5+
#define SPI_AVAILABLE API_AVAILABLE
6+
#endif
7+
8+
#ifdef __SPI_AVAILABLE
9+
#undef __SPI_AVAILABLE
10+
#define __SPI_AVAILABLE API_AVAILABLE
11+
#endif
12+
13+
SPI_AVAILABLE(macos(10.7))
14+
@interface SPIInterface1
15+
@end
16+
17+
__SPI_AVAILABLE(macos(10.7))
18+
@interface SPIInterface2
19+
@end
20+
21+
@interface SharedInterface
22+
+ (NSInteger)foo SPI_AVAILABLE(macos(10.7));
23+
@end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
framework module SPIContainer {
2+
umbrella header "SPIContainer.h"
3+
export *
4+
module * {
5+
export *
6+
}
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// REQUIRES: OS=macosx
2+
// RUN: %target-swift-frontend -typecheck %s -F %S/Inputs/frameworks -verify
3+
4+
import SPIContainer
5+
6+
@_spi(a) public let a: SPIInterface1
7+
@_spi(a) public let b: SPIInterface2
8+
9+
public let c: SPIInterface1 // expected-error{{cannot use class 'SPIInterface1' here; it is an SPI imported from 'SPIContainer'}}
10+
public let d: SPIInterface2 // expected-error{{cannot use class 'SPIInterface2' here; it is an SPI imported from 'SPIContainer'}}
11+
12+
@inlinable
13+
public func inlinableUsingSPI() {
14+
SharedInterface.foo() // expected-error{{class method 'foo()' cannot be used in an '@inlinable' function because it is an SPI imported from 'SPIContainer'}}
15+
}

validation-test/Serialization/bridging-header-first.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// CHECK-DUMP: IMPORTED_MODULE
1919
// CHECK-DUMP-SAME: 'Module'
2020
// CHECK-DUMP: IMPORTED_MODULE
21-
// CHECK-DUMP-SAME: 'Swift'
21+
// CHECK-DUMP: 'Swift'
2222

2323

2424
import Module

0 commit comments

Comments
 (0)