Skip to content

[Clang importer] Import Clang swift_attr attribute. #35327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none,
"please report this issue to the owners of '%0'",
(StringRef))

WARNING(clang_swift_attr_without_at,none,
"Swift attribute '%0' does not start with '@'", (StringRef))

WARNING(implicit_bridging_header_imported_from_module,none,
"implicit import of bridging header '%0' via module %1 "
"is deprecated and will be removed in a later version of Swift",
Expand Down
6 changes: 4 additions & 2 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1038,10 +1038,12 @@ class Parser {
SourceLoc Loc);

/// Parse a specific attribute.
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc);
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
bool isFromClangAttribute = false);

bool parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
DeclAttrKind DK);
DeclAttrKind DK,
bool isFromClangAttribute = false);

/// Parse a version tuple of the form x[.y[.z]]. Returns true if there was
/// an error parsing.
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/UnqualifiedLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,12 @@ void UnqualifiedLookupFactory::performUnqualifiedLookup() {
"performUnqualifedLookup",
DC->getParentSourceFile());

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

Expand Down
103 changes: 103 additions & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Config.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/Parser.h"
#include "swift/Strings.h"

#include "clang/AST/ASTContext.h"
Expand Down Expand Up @@ -7737,6 +7738,61 @@ static bool isObjCMethodLikelyAsyncHandler(
return false;
}

Type ClangImporter::Implementation::getMainActorType() {
if (MainActorType)
return *MainActorType;

auto finish = [&](Type type) -> Type {
MainActorType = type;
return type;
};

if (!SwiftContext.LangOpts.EnableExperimentalConcurrency) {
return finish(Type());
}

auto module = SwiftContext.getLoadedModule(SwiftContext.Id_Concurrency);
if (!module)
return finish(Type());

SmallVector<ValueDecl *, 1> decls;
module->lookupValue(
SwiftContext.getIdentifier("MainActor"),
NLKind::QualifiedLookup, decls);
for (auto decl : decls) {
if (auto typeDecl = dyn_cast<TypeDecl>(decl))
return finish(typeDecl->getDeclaredInterfaceType());
}

return finish(Type());
}

unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
StringRef attributeText) {
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);
if (known != ClangSwiftAttrSourceBuffers.end())
return known->second;

// Create a new buffer with a copy of the attribute text, so we don't need to
// rely on Clang keeping it around.
auto &sourceMgr = SwiftContext.SourceMgr;
auto bufferID = sourceMgr.addMemBufferCopy(attributeText);
ClangSwiftAttrSourceBuffers.insert({attributeText, bufferID});
return bufferID;
}

SourceFile &ClangImporter::Implementation::getClangSwiftAttrSourceFile(
ModuleDecl &module) {
auto known = ClangSwiftAttrSourceFiles.find(&module);
if (known != ClangSwiftAttrSourceFiles.end())
return *known->second;

auto sourceFile = new (SwiftContext) SourceFile(
module, SourceFileKind::Library, None);
ClangSwiftAttrSourceFiles.insert({&module, sourceFile});
return *sourceFile;
}

/// Import Clang attributes as Swift attributes.
void ClangImporter::Implementation::importAttributes(
const clang::NamedDecl *ClangDecl,
Expand Down Expand Up @@ -7889,6 +7945,53 @@ void ClangImporter::Implementation::importAttributes(

MappedDecl->getAttrs().add(AvAttr);
}

// __attribute__((swift_attr("attribute")))
//
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(*AI)) {
// FIXME: Hard-core @MainActor, because we don't have a point at which to
// do name lookup for imported entities.
if (swiftAttr->getAttribute() == "@MainActor") {
if (Type mainActorType = getMainActorType()) {
auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext);
auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr);
MappedDecl->getAttrs().add(attr);
}

continue;
}

// Dig out a buffer with the attribute text.
unsigned bufferID = getClangSwiftAttrSourceBuffer(
swiftAttr->getAttribute());

// Dig out a source file we can use for parsing.
auto &sourceFile = getClangSwiftAttrSourceFile(
*MappedDecl->getDeclContext()->getParentModule());

// Spin up a parser.
swift::Parser parser(
bufferID, sourceFile, &SwiftContext.Diags, nullptr, nullptr);
// Prime the lexer.
parser.consumeTokenWithoutFeedingReceiver();

SourceLoc atLoc;
if (parser.consumeIf(tok::at_sign, atLoc)) {
(void)parser.parseDeclAttribute(
MappedDecl->getAttrs(), atLoc, /*isFromClangAttribute=*/true);
} else {
// Complain about the missing '@'.
auto &clangSrcMgr = getClangASTContext().getSourceManager();
ClangSourceBufferImporter &bufferImporter =
getBufferImporterForDiagnostics();
SourceLoc attrLoc = bufferImporter.resolveSourceLocation(
clangSrcMgr, swiftAttr->getLocation());
SwiftContext.Diags.diagnose(
attrLoc, diag::clang_swift_attr_without_at,
swiftAttr->getAttribute());
}
continue;
}
}

if (auto method = dyn_cast<clang::ObjCMethodDecl>(ClangDecl)) {
Expand Down
24 changes: 24 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Path.h"
#include <set>
Expand Down Expand Up @@ -400,6 +401,18 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// Clang arguments used to create the Clang invocation.
std::vector<std::string> ClangArgs;

/// The main actor type, populated the first time we look for it.
Optional<Type> MainActorType;

/// Mapping from Clang swift_attr attribute text to the Swift source buffer
/// IDs that contain that attribute text. These are re-used when parsing the
/// Swift attributes on import.
llvm::StringMap<unsigned> ClangSwiftAttrSourceBuffers;

/// Mapping from modules in which a Clang swift_attr attribute occurs, to be
/// used when parsing the attribute text.
llvm::SmallDenseMap<ModuleDecl *, SourceFile *> ClangSwiftAttrSourceFiles;

public:
/// Mapping of already-imported declarations.
llvm::DenseMap<std::pair<const clang::Decl *, Version>, Decl *> ImportedDecls;
Expand Down Expand Up @@ -768,6 +781,17 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// Map a Clang identifier name to its imported Swift equivalent.
StringRef getSwiftNameFromClangName(StringRef name);

/// Look for the MainActor type in the _Concurrency library.
Type getMainActorType();

/// Retrieve the Swift source buffer ID that corresponds to the given
/// swift_attr attribute text, creating one if necessary.
unsigned getClangSwiftAttrSourceBuffer(StringRef attributeText);

/// Retrieve the placeholder source file for use in parsing Swift attributes
/// in the given module.
SourceFile &getClangSwiftAttrSourceFile(ModuleDecl &module);

/// Import attributes from the given Clang declaration to its Swift
/// equivalent.
///
Expand Down
19 changes: 12 additions & 7 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,7 @@ void Parser::parseAllAvailabilityMacroArguments() {
}

bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
DeclAttrKind DK) {
DeclAttrKind DK, bool isFromClangAttribute) {
// Ok, it is a valid attribute, eat it, and then process it.
StringRef AttrName = Tok.getText();
SourceLoc Loc = consumeToken();
Expand All @@ -1550,7 +1550,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
// Delay issuing the diagnostic until we parse the attribute.
DiscardAttribute = true;
}

// If this is a SIL-only attribute, reject it.
if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 &&
!isInSILMode()) {
Expand All @@ -1561,9 +1561,13 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
// If this attribute is only permitted when concurrency is enabled, reject it.
if (DeclAttribute::isConcurrencyOnly(DK) &&
!shouldParseExperimentalConcurrency()) {
diagnose(
Loc, diag::attr_requires_concurrency, AttrName,
DeclAttribute::isDeclModifier(DK));
// Ignore concurrency-only attributes that come from Clang.
if (!isFromClangAttribute) {
diagnose(
Loc, diag::attr_requires_concurrency, AttrName,
DeclAttribute::isDeclModifier(DK));
}

DiscardAttribute = true;
}

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

if (DK != DAK_Count && !DeclAttribute::shouldBeRejectedByParser(DK)) {
parseNewDeclAttribute(Attributes, AtLoc, DK);
parseNewDeclAttribute(Attributes, AtLoc, DK, isFromClangAttribute);
return makeParserSuccess();
}

Expand Down
19 changes: 19 additions & 0 deletions test/ClangImporter/objc_async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,22 @@ func testSlowServerOldSchool(slowServer: SlowServer) {

_ = slowServer.allOperations
}

// Check import of attributes
func globalAsync() async { }

actor class MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes {
func syncMethod() { } // expected-note 2{{calls to instance method 'syncMethod()' from outside of its actor context are implicitly asynchronous}}

func independentMethod() {
syncMethod() // expected-error{{ctor-isolated instance method 'syncMethod()' can not be referenced from an '@actorIndependent' context}}
}

func asyncHandlerMethod() {
await globalAsync() // okay because we infer @asyncHandler from the protocol
}

func mainActorMethod() {
syncMethod() // expected-error{{actor-isolated instance method 'syncMethod()' can not be referenced from context of global actor 'MainActor'}}
}
}
24 changes: 24 additions & 0 deletions test/ClangImporter/objc_async_attrs_noconcurrency.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify

// REQUIRES: objc_interop
import Foundation
import ObjCConcurrency

func testSlowServerSynchronous(slowServer: SlowServer) {
// synchronous version
let _: Int = slowServer.doSomethingConflicted("thinking")
slowServer.poorlyNamed("hello") { (i: Int) in print(i) }
slowServer.customize(with: "hello") { (i: Int) in print(i) }

slowServer.dance("jig") { s in print(s + "") }
slowServer.leap(17) { s in print(s + "") }
slowServer.repeatTrick("jump") { i in print(i + 1) }
}

func testSlowServerOldSchool(slowServer: SlowServer) {
slowServer.doSomethingSlow("mail") { i in
_ = i
}

_ = slowServer.allOperations
}
8 changes: 8 additions & 0 deletions test/IDE/print_clang_objc_async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

// REQUIRES: objc_interop
// REQUIRES: concurrency
import _Concurrency

// CHECK-LABEL: class SlowServer : NSObject, ServiceProvider {
// CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
Expand Down Expand Up @@ -34,3 +35,10 @@
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any)
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool
// CHECK-NEXT: {{^[}]$}}

// CHECK-LABEL: protocol ProtocolWithSwiftAttributes {
// CHECK-NEXT: @actorIndependent func independentMethod()
// CHECK-NEXT: @asyncHandler func asyncHandlerMethod()
// CHECK-NEXT: @MainActor func mainActorMethod()
// CHECK-NEXT: {{^}} optional func missingAtAttributeMethod()
// CHECK-NEXT: {{^[}]$}}
9 changes: 9 additions & 0 deletions test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,13 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
-(void)askUserToJumpThroughHoop:(NSString *)hoop completionHandler:(void (^ _Nullable)(NSString *))completionHandler;
@end

@protocol ProtocolWithSwiftAttributes
-(void)independentMethod __attribute__((__swift_attr__("@actorIndependent")));
-(void)asyncHandlerMethod __attribute__((__swift_attr__("@asyncHandler")));
-(void)mainActorMethod __attribute__((__swift_attr__("@MainActor")));

@optional
-(void)missingAtAttributeMethod __attribute__((__swift_attr__("asyncHandler")));
@end

#pragma clang assume_nonnull end
8 changes: 8 additions & 0 deletions tools/swift-ide-test/swift-ide-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,14 @@ static int doPrintModules(const CompilerInvocation &InitInvok,
return 1;
}

// If needed, load _Concurrency library so that the Clang importer can use it.
if (Context.LangOpts.EnableExperimentalConcurrency) {
if (!getModuleByFullName(Context, Context.Id_Concurrency)) {
llvm::errs() << "Failed loading _Concurrency library\n";
return 1;
}
}

int ExitCode = 0;

std::unique_ptr<ASTPrinter> Printer;
Expand Down