Skip to content

Commit 38d5ed3

Browse files
authored
Merge pull request #35327 from DougGregor/import-clang-swift-attrs
[Clang importer] Import Clang swift_attr attribute.
2 parents 871bb08 + 8c123e8 commit 38d5ed3

File tree

11 files changed

+216
-11
lines changed

11 files changed

+216
-11
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",

include/swift/Parse/Parser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,10 +1038,12 @@ class Parser {
10381038
SourceLoc Loc);
10391039

10401040
/// Parse a specific attribute.
1041-
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc);
1041+
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
1042+
bool isFromClangAttribute = false);
10421043

10431044
bool parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
1044-
DeclAttrKind DK);
1045+
DeclAttrKind DK,
1046+
bool isFromClangAttribute = false);
10451047

10461048
/// Parse a version tuple of the form x[.y[.z]]. Returns true if there was
10471049
/// an error parsing.

lib/AST/UnqualifiedLookup.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,12 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() {
273273
"performUnqualifedLookup",
274274
DC->getParentSourceFile());
275275

276-
if (Loc.isValid()) {
276+
if (Loc.isValid() && DC->getParentSourceFile()) {
277277
// Operator lookup is always global, for the time being.
278278
if (!Name.isOperator())
279279
lookInASTScopes();
280280
} else {
281-
assert(DC->isModuleScopeContext() &&
281+
assert((DC->isModuleScopeContext() || !DC->getParentSourceFile()) &&
282282
"Unqualified lookup without a source location must start from "
283283
"a module-scope context");
284284

lib/ClangImporter/ImportDecl.cpp

Lines changed: 103 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,61 @@ static bool isObjCMethodLikelyAsyncHandler(
77377738
return false;
77387739
}
77397740

7741+
Type ClangImporter::Implementation::getMainActorType() {
7742+
if (MainActorType)
7743+
return *MainActorType;
7744+
7745+
auto finish = [&](Type type) -> Type {
7746+
MainActorType = type;
7747+
return type;
7748+
};
7749+
7750+
if (!SwiftContext.LangOpts.EnableExperimentalConcurrency) {
7751+
return finish(Type());
7752+
}
7753+
7754+
auto module = SwiftContext.getLoadedModule(SwiftContext.Id_Concurrency);
7755+
if (!module)
7756+
return finish(Type());
7757+
7758+
SmallVector<ValueDecl *, 1> decls;
7759+
module->lookupValue(
7760+
SwiftContext.getIdentifier("MainActor"),
7761+
NLKind::QualifiedLookup, decls);
7762+
for (auto decl : decls) {
7763+
if (auto typeDecl = dyn_cast<TypeDecl>(decl))
7764+
return finish(typeDecl->getDeclaredInterfaceType());
7765+
}
7766+
7767+
return finish(Type());
7768+
}
7769+
7770+
unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
7771+
StringRef attributeText) {
7772+
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);
7773+
if (known != ClangSwiftAttrSourceBuffers.end())
7774+
return known->second;
7775+
7776+
// Create a new buffer with a copy of the attribute text, so we don't need to
7777+
// rely on Clang keeping it around.
7778+
auto &sourceMgr = SwiftContext.SourceMgr;
7779+
auto bufferID = sourceMgr.addMemBufferCopy(attributeText);
7780+
ClangSwiftAttrSourceBuffers.insert({attributeText, bufferID});
7781+
return bufferID;
7782+
}
7783+
7784+
SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
7785+
ModuleDecl &module) {
7786+
auto known = ClangSwiftAttrSourceFiles.find(&module);
7787+
if (known != ClangSwiftAttrSourceFiles.end())
7788+
return *known->second;
7789+
7790+
auto sourceFile = new (SwiftContext) SourceFile(
7791+
module, SourceFileKind::Library, None);
7792+
ClangSwiftAttrSourceFiles.insert({&module, sourceFile});
7793+
return *sourceFile;
7794+
}
7795+
77407796
/// Import Clang attributes as Swift attributes.
77417797
void ClangImporter::Implementation::importAttributes(
77427798
const clang::NamedDecl *ClangDecl,
@@ -7889,6 +7945,53 @@ void ClangImporter::Implementation::importAttributes(
78897945

78907946
MappedDecl->getAttrs().add(AvAttr);
78917947
}
7948+
7949+
// __attribute__((swift_attr("attribute")))
7950+
//
7951+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(*AI)) {
7952+
// FIXME: Hard-core @MainActor, because we don't have a point at which to
7953+
// do name lookup for imported entities.
7954+
if (swiftAttr->getAttribute() == "@MainActor") {
7955+
if (Type mainActorType = getMainActorType()) {
7956+
auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext);
7957+
auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr);
7958+
MappedDecl->getAttrs().add(attr);
7959+
}
7960+
7961+
continue;
7962+
}
7963+
7964+
// Dig out a buffer with the attribute text.
7965+
unsigned bufferID = getClangSwiftAttrSourceBuffer(
7966+
swiftAttr->getAttribute());
7967+
7968+
// Dig out a source file we can use for parsing.
7969+
auto &sourceFile = getClangSwiftAttrSourceFile(
7970+
*MappedDecl->getDeclContext()->getParentModule());
7971+
7972+
// Spin up a parser.
7973+
swift::Parser parser(
7974+
bufferID, sourceFile, &SwiftContext.Diags, nullptr, nullptr);
7975+
// Prime the lexer.
7976+
parser.consumeTokenWithoutFeedingReceiver();
7977+
7978+
SourceLoc atLoc;
7979+
if (parser.consumeIf(tok::at_sign, atLoc)) {
7980+
(void)parser.parseDeclAttribute(
7981+
MappedDecl->getAttrs(), atLoc, /*isFromClangAttribute=*/true);
7982+
} else {
7983+
// Complain about the missing '@'.
7984+
auto &clangSrcMgr = getClangASTContext().getSourceManager();
7985+
ClangSourceBufferImporter &bufferImporter =
7986+
getBufferImporterForDiagnostics();
7987+
SourceLoc attrLoc = bufferImporter.resolveSourceLocation(
7988+
clangSrcMgr, swiftAttr->getLocation());
7989+
SwiftContext.Diags.diagnose(
7990+
attrLoc, diag::clang_swift_attr_without_at,
7991+
swiftAttr->getAttribute());
7992+
}
7993+
continue;
7994+
}
78927995
}
78937996

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

lib/ClangImporter/ImporterImpl.h

Lines changed: 24 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,18 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
400401
/// Clang arguments used to create the Clang invocation.
401402
std::vector<std::string> ClangArgs;
402403

404+
/// The main actor type, populated the first time we look for it.
405+
Optional<Type> MainActorType;
406+
407+
/// Mapping from Clang swift_attr attribute text to the Swift source buffer
408+
/// IDs that contain that attribute text. These are re-used when parsing the
409+
/// Swift attributes on import.
410+
llvm::StringMap<unsigned> ClangSwiftAttrSourceBuffers;
411+
412+
/// Mapping from modules in which a Clang swift_attr attribute occurs, to be
413+
/// used when parsing the attribute text.
414+
llvm::SmallDenseMap<ModuleDecl *, SourceFile *> ClangSwiftAttrSourceFiles;
415+
403416
public:
404417
/// Mapping of already-imported declarations.
405418
llvm::DenseMap<std::pair<const clang::Decl *, Version>, Decl *> ImportedDecls;
@@ -768,6 +781,17 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
768781
/// Map a Clang identifier name to its imported Swift equivalent.
769782
StringRef getSwiftNameFromClangName(StringRef name);
770783

784+
/// Look for the MainActor type in the _Concurrency library.
785+
Type getMainActorType();
786+
787+
/// Retrieve the Swift source buffer ID that corresponds to the given
788+
/// swift_attr attribute text, creating one if necessary.
789+
unsigned getClangSwiftAttrSourceBuffer(StringRef attributeText);
790+
791+
/// Retrieve the placeholder source file for use in parsing Swift attributes
792+
/// in the given module.
793+
SourceFile &getClangSwiftAttrSourceFile(ModuleDecl &module);
794+
771795
/// Import attributes from the given Clang declaration to its Swift
772796
/// equivalent.
773797
///

lib/Parse/ParseDecl.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ void Parser::parseAllAvailabilityMacroArguments() {
15361536
}
15371537

15381538
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
1539-
DeclAttrKind DK) {
1539+
DeclAttrKind DK, bool isFromClangAttribute) {
15401540
// Ok, it is a valid attribute, eat it, and then process it.
15411541
StringRef AttrName = Tok.getText();
15421542
SourceLoc Loc = consumeToken();
@@ -1550,7 +1550,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
15501550
// Delay issuing the diagnostic until we parse the attribute.
15511551
DiscardAttribute = true;
15521552
}
1553-
1553+
15541554
// If this is a SIL-only attribute, reject it.
15551555
if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 &&
15561556
!isInSILMode()) {
@@ -1561,9 +1561,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
15611561
// If this attribute is only permitted when concurrency is enabled, reject it.
15621562
if (DeclAttribute::isConcurrencyOnly(DK) &&
15631563
!shouldParseExperimentalConcurrency()) {
1564-
diagnose(
1565-
Loc, diag::attr_requires_concurrency, AttrName,
1566-
DeclAttribute::isDeclModifier(DK));
1564+
// Ignore concurrency-only attributes that come from Clang.
1565+
if (!isFromClangAttribute) {
1566+
diagnose(
1567+
Loc, diag::attr_requires_concurrency, AttrName,
1568+
DeclAttribute::isDeclModifier(DK));
1569+
}
1570+
15671571
DiscardAttribute = true;
15681572
}
15691573

@@ -2682,7 +2686,8 @@ static PatternBindingInitializer *findAttributeInitContent(
26822686
/// Note that various attributes (like mutating, weak, and unowned) are parsed
26832687
/// but rejected since they have context-sensitive keywords.
26842688
///
2685-
ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) {
2689+
ParserStatus Parser::parseDeclAttribute(
2690+
DeclAttributes &Attributes, SourceLoc AtLoc, bool isFromClangAttribute) {
26862691
// If this not an identifier, the attribute is malformed.
26872692
if (Tok.isNot(tok::identifier) &&
26882693
Tok.isNot(tok::kw_in) &&
@@ -2789,7 +2794,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At
27892794
}
27902795

27912796
if (DK != DAK_Count && !DeclAttribute::shouldBeRejectedByParser(DK)) {
2792-
parseNewDeclAttribute(Attributes, AtLoc, DK);
2797+
parseNewDeclAttribute(Attributes, AtLoc, DK, isFromClangAttribute);
27932798
return makeParserSuccess();
27942799
}
27952800

test/ClangImporter/objc_async.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,22 @@ 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 2{{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+
82+
func mainActorMethod() {
83+
syncMethod() // expected-error{{actor-isolated instance method 'syncMethod()' can not be referenced from context of global actor 'MainActor'}}
84+
}
85+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify
2+
3+
// REQUIRES: objc_interop
4+
import Foundation
5+
import ObjCConcurrency
6+
7+
func testSlowServerSynchronous(slowServer: SlowServer) {
8+
// synchronous version
9+
let _: Int = slowServer.doSomethingConflicted("thinking")
10+
slowServer.poorlyNamed("hello") { (i: Int) in print(i) }
11+
slowServer.customize(with: "hello") { (i: Int) in print(i) }
12+
13+
slowServer.dance("jig") { s in print(s + "") }
14+
slowServer.leap(17) { s in print(s + "") }
15+
slowServer.repeatTrick("jump") { i in print(i + 1) }
16+
}
17+
18+
func testSlowServerOldSchool(slowServer: SlowServer) {
19+
slowServer.doSomethingSlow("mail") { i in
20+
_ = i
21+
}
22+
23+
_ = slowServer.allOperations
24+
}

test/IDE/print_clang_objc_async.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
// REQUIRES: objc_interop
77
// REQUIRES: concurrency
8+
import _Concurrency
89

910
// CHECK-LABEL: class SlowServer : NSObject, ServiceProvider {
1011
// CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
@@ -34,3 +35,10 @@
3435
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any)
3536
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool
3637
// CHECK-NEXT: {{^[}]$}}
38+
39+
// CHECK-LABEL: protocol ProtocolWithSwiftAttributes {
40+
// CHECK-NEXT: @actorIndependent func independentMethod()
41+
// CHECK-NEXT: @asyncHandler func asyncHandlerMethod()
42+
// CHECK-NEXT: @MainActor func mainActorMethod()
43+
// CHECK-NEXT: {{^}} optional func missingAtAttributeMethod()
44+
// CHECK-NEXT: {{^[}]$}}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,13 @@ 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+
-(void)mainActorMethod __attribute__((__swift_attr__("@MainActor")));
71+
72+
@optional
73+
-(void)missingAtAttributeMethod __attribute__((__swift_attr__("asyncHandler")));
74+
@end
75+
6776
#pragma clang assume_nonnull end

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,6 +2613,14 @@ static int doPrintModules(const CompilerInvocation &InitInvok,
26132613
return 1;
26142614
}
26152615

2616+
// If needed, load _Concurrency library so that the Clang importer can use it.
2617+
if (Context.LangOpts.EnableExperimentalConcurrency) {
2618+
if (!getModuleByFullName(Context, Context.Id_Concurrency)) {
2619+
llvm::errs() << "Failed loading _Concurrency library\n";
2620+
return 1;
2621+
}
2622+
}
2623+
26162624
int ExitCode = 0;
26172625

26182626
std::unique_ptr<ASTPrinter> Printer;

0 commit comments

Comments
 (0)