Skip to content

Commit cd0380b

Browse files
committed
[Clang importer] Import Clang swift_attr attribute.
The Clang swift_attr attribute allows C code to describe, via a Clang attribute, the Swift attributes that should be applied to the given declaration. When an __attribute__((__swift_attr__("@tribute"))) occurs on a Clang declaration, parse the attribute within the string literal as a Swift attribute, then attach that to the imported Swift declaration. Fixes rdar://70146633.
1 parent 0635054 commit cd0380b

File tree

6 files changed

+112
-0
lines changed

6 files changed

+112
-0
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none,
7575
"please report this issue to the owners of '%0'",
7676
(StringRef))
7777

78+
WARNING(clang_swift_attr_without_at,none,
79+
"Swift attribute '%0' does not start with '@'", (StringRef))
80+
7881
WARNING(implicit_bridging_header_imported_from_module,none,
7982
"implicit import of bridging header '%0' via module %1 "
8083
"is deprecated and will be removed in a later version of Swift",

lib/ClangImporter/ImportDecl.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include "swift/ClangImporter/ClangModule.h"
4646
#include "swift/Config.h"
4747
#include "swift/Parse/Lexer.h"
48+
#include "swift/Parse/Parser.h"
4849
#include "swift/Strings.h"
4950

5051
#include "clang/AST/ASTContext.h"
@@ -7737,6 +7738,32 @@ static bool isObjCMethodLikelyAsyncHandler(
77377738
return false;
77387739
}
77397740

7741+
unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
7742+
StringRef attributeText) {
7743+
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);
7744+
if (known != ClangSwiftAttrSourceBuffers.end())
7745+
return known->second;
7746+
7747+
// Create a new buffer with a copy of the attribute text, so we don't need to
7748+
// rely on Clang keeping it around.
7749+
auto &sourceMgr = SwiftContext.SourceMgr;
7750+
auto bufferID = sourceMgr.addMemBufferCopy(attributeText);
7751+
ClangSwiftAttrSourceBuffers.insert({attributeText, bufferID});
7752+
return bufferID;
7753+
}
7754+
7755+
SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
7756+
ModuleDecl &module) {
7757+
auto known = ClangSwiftAttrSourceFiles.find(&module);
7758+
if (known != ClangSwiftAttrSourceFiles.end())
7759+
return *known->second;
7760+
7761+
auto sourceFile = new (SwiftContext) SourceFile(
7762+
module, SourceFileKind::Library, None);
7763+
ClangSwiftAttrSourceFiles.insert({&module, sourceFile});
7764+
return *sourceFile;
7765+
}
7766+
77407767
/// Import Clang attributes as Swift attributes.
77417768
void ClangImporter::Implementation::importAttributes(
77427769
const clang::NamedDecl *ClangDecl,
@@ -7889,6 +7916,41 @@ void ClangImporter::Implementation::importAttributes(
78897916

78907917
MappedDecl->getAttrs().add(AvAttr);
78917918
}
7919+
7920+
// __attribute__((swift_attr("attribute")))
7921+
//
7922+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(*AI)) {
7923+
// Dig out a buffer with the attribute text.
7924+
unsigned bufferID = getClangSwiftAttrSourceBuffer(
7925+
swiftAttr->getAttribute());
7926+
7927+
// Dig out a source file we can use for parsing.
7928+
auto &sourceFile = getClangSwiftAttrSourceFile(
7929+
*MappedDecl->getDeclContext()->getParentModule());
7930+
7931+
// Spin up a parser.
7932+
swift::Parser parser(
7933+
bufferID, sourceFile, &SwiftContext.Diags, nullptr, nullptr);
7934+
// Prime the lexer.
7935+
parser.consumeTokenWithoutFeedingReceiver();
7936+
7937+
7938+
SourceLoc atLoc;
7939+
if (parser.consumeIf(tok::at_sign, atLoc)) {
7940+
(void)parser.parseDeclAttribute(MappedDecl->getAttrs(), atLoc);
7941+
} else {
7942+
// Complain about the missing '@'.
7943+
auto &clangSrcMgr = getClangASTContext().getSourceManager();
7944+
ClangSourceBufferImporter &bufferImporter =
7945+
getBufferImporterForDiagnostics();
7946+
SourceLoc attrLoc = bufferImporter.resolveSourceLocation(
7947+
clangSrcMgr, swiftAttr->getLocation());
7948+
SwiftContext.Diags.diagnose(
7949+
attrLoc, diag::clang_swift_attr_without_at,
7950+
swiftAttr->getAttribute());
7951+
}
7952+
continue;
7953+
}
78927954
}
78937955

78947956
if (auto method = dyn_cast<clang::ObjCMethodDecl>(ClangDecl)) {

lib/ClangImporter/ImporterImpl.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "llvm/ADT/IntrusiveRefCntPtr.h"
4242
#include "llvm/ADT/SmallBitVector.h"
4343
#include "llvm/ADT/SmallSet.h"
44+
#include "llvm/ADT/StringMap.h"
4445
#include "llvm/ADT/TinyPtrVector.h"
4546
#include "llvm/Support/Path.h"
4647
#include <set>
@@ -400,6 +401,15 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
400401
/// Clang arguments used to create the Clang invocation.
401402
std::vector<std::string> ClangArgs;
402403

404+
/// Mapping from Clang swift_attr attribute text to the Swift source buffer
405+
/// IDs that contain that attribute text. These are re-used when parsing the
406+
/// Swift attributes on import.
407+
llvm::StringMap<unsigned> ClangSwiftAttrSourceBuffers;
408+
409+
/// Mapping from modules in which a Clang swift_attr attribute occurs, to be
410+
/// used when parsing the attribute text.
411+
llvm::SmallDenseMap<ModuleDecl *, SourceFile *> ClangSwiftAttrSourceFiles;
412+
403413
public:
404414
/// Mapping of already-imported declarations.
405415
llvm::DenseMap<std::pair<const clang::Decl *, Version>, Decl *> ImportedDecls;
@@ -768,6 +778,14 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
768778
/// Map a Clang identifier name to its imported Swift equivalent.
769779
StringRef getSwiftNameFromClangName(StringRef name);
770780

781+
/// Retrieve the Swift source buffer ID that corresponds to the given
782+
/// swift_attr attribute text, creating one if necessary.
783+
unsigned getClangSwiftAttrSourceBuffer(StringRef attributeText);
784+
785+
/// Retrieve the placeholder source file for use in parsing Swift attributes
786+
/// in the given module.
787+
SourceFile &getClangSwiftAttrSourceFile(ModuleDecl &module);
788+
771789
/// Import attributes from the given Clang declaration to its Swift
772790
/// equivalent.
773791
///

test/ClangImporter/objc_async.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,18 @@ func testSlowServerOldSchool(slowServer: SlowServer) {
6464

6565
_ = slowServer.allOperations
6666
}
67+
68+
// Check import of attributes
69+
func globalAsync() async { }
70+
71+
actor class MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes {
72+
func syncMethod() { } // expected-note{{calls to instance method 'syncMethod()' from outside of its actor context are implicitly asynchronous}}
73+
74+
func independentMethod() {
75+
syncMethod() // expected-error{{ctor-isolated instance method 'syncMethod()' can not be referenced from an '@actorIndependent' context}}
76+
}
77+
78+
func asyncHandlerMethod() {
79+
await globalAsync() // okay because we infer @asyncHandler from the protocol
80+
}
81+
}

test/IDE/print_clang_objc_async.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@
3434
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any)
3535
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool
3636
// CHECK-NEXT: {{^[}]$}}
37+
38+
// CHECK-LABEL: protocol ProtocolWithSwiftAttributes {
39+
// CHECK-NEXT: @actorIndependent func independentMethod()
40+
// CHECK-NEXT: @asyncHandler func asyncHandlerMethod()
41+
// CHECK-NEXT: {{^}} optional func missingAtAttributeMethod()
42+
// CHECK-NEXT: {{^[}]$}}

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,12 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
6464
-(void)askUserToJumpThroughHoop:(NSString *)hoop completionHandler:(void (^ _Nullable)(NSString *))completionHandler;
6565
@end
6666

67+
@protocol ProtocolWithSwiftAttributes
68+
-(void)independentMethod __attribute__((__swift_attr__("@actorIndependent")));
69+
-(void)asyncHandlerMethod __attribute__((__swift_attr__("@asyncHandler")));
70+
71+
@optional
72+
-(void)missingAtAttributeMethod __attribute__((__swift_attr__("asyncHandler")));
73+
@end
74+
6775
#pragma clang assume_nonnull end

0 commit comments

Comments
 (0)