Skip to content

Mark imported @completionHandlerAsync attrs as implicit #37170

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 4 commits into from
Apr 30, 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
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ SIMPLE_DECL_ATTR(reasync, AtReasync,
110)

DECL_ATTR(completionHandlerAsync, CompletionHandlerAsync,
OnAbstractFunction | ConcurrencyOnly | LongAttribute | UserInaccessible |
OnAbstractFunction | ConcurrencyOnly | LongAttribute |
ABIStableToAdd | ABIStableToRemove |
APIStableToAdd | APIStableToRemove,
111)
Expand Down
5 changes: 3 additions & 2 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2077,9 +2077,10 @@ class CompletionHandlerAsyncAttr final : public DeclAttribute {
CompletionHandlerAsyncAttr(AbstractFunctionDecl &asyncFunctionDecl,
size_t completionHandlerIndex,
SourceLoc completionHandlerIndexLoc,
SourceLoc atLoc, SourceRange range)
SourceLoc atLoc, SourceRange range,
bool implicit)
: DeclAttribute(DAK_CompletionHandlerAsync, atLoc, range,
/*implicit*/ false),
implicit),
AsyncFunctionDecl(&asyncFunctionDecl) ,
CompletionHandlerIndex(completionHandlerIndex),
CompletionHandlerIndexLoc(completionHandlerIndexLoc) {}
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
} else {
Printer << attr->AsyncFunctionName;
}
Printer << "\", completionHandleIndex: " <<
Printer << "\", completionHandlerIndex: " <<
attr->CompletionHandlerIndex << ')';
break;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8104,7 +8104,7 @@ void addCompletionHandlerAttribute(Decl *asyncImport,
member->getAttrs().add(
new (SwiftContext) CompletionHandlerAsyncAttr(
cast<AbstractFunctionDecl>(*asyncImport), completionIndex,
SourceLoc(), SourceLoc(), SourceRange()));
SourceLoc(), SourceLoc(), SourceRange(), /*implicit*/ true));
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4639,16 +4639,17 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
}

case decls_block::CompletionHandlerAsync_DECL_ATTR: {
bool isImplicit;
uint64_t handlerIndex;
uint64_t asyncFunctionDeclID;
serialization::decls_block::CompletionHandlerAsyncDeclAttrLayout::
readRecord(scratch, handlerIndex, asyncFunctionDeclID);
readRecord(scratch, isImplicit, handlerIndex, asyncFunctionDeclID);

auto mappedFunctionDecl =
cast<AbstractFunctionDecl>(MF.getDecl(asyncFunctionDeclID));
Attr = new (ctx) CompletionHandlerAsyncAttr(
*mappedFunctionDecl, handlerIndex, /*handlerIndexLoc*/ SourceLoc(),
/*atLoc*/ SourceLoc(), /*range*/ SourceRange());
/*atLoc*/ SourceLoc(), /*range*/ SourceRange(), isImplicit);
break;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 610; // async initializers for nominal types
const uint16_t SWIFTMODULE_VERSION_MINOR = 611; // implicit bit for CompletionHandlerAsyncAttr

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down Expand Up @@ -1930,6 +1930,7 @@ namespace decls_block {

using CompletionHandlerAsyncDeclAttrLayout = BCRecordLayout<
CompletionHandlerAsync_DECL_ATTR,
BCFixed<1>, // Implicit flag.
BCVBR<5>, // Completion handler index
DeclIDField // Mapped async function decl
>;
Expand Down
4 changes: 2 additions & 2 deletions lib/Serialization/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2633,8 +2633,8 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
auto asyncFuncDeclID = S.addDeclRef(attr->AsyncFunctionDecl);

CompletionHandlerAsyncDeclAttrLayout::emitRecord(
S.Out, S.ScratchRecord, abbrCode, attr->CompletionHandlerIndex,
asyncFuncDeclID);
S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(),
attr->CompletionHandlerIndex, asyncFuncDeclID);
return;
}
}
Expand Down
59 changes: 37 additions & 22 deletions test/IDE/print_clang_objc_async.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency > %t/ObjCConcurrency.printed.txt
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -print-implicit-attrs -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency > %t/ObjCConcurrency.printed.txt
// RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s

// REQUIRES: objc_interop
Expand All @@ -9,64 +9,78 @@ import _Concurrency

// CHECK-LABEL: class SlowServer : NSObject, ServiceProvider {

// CHECK: @completionHandlerAsync("doSomethingSlow(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("doSomethingSlow(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func doSomethingSlow(_ operation: String) async -> Int

// CHECK: @completionHandlerAsync("doSomethingDangerous(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("doSomethingDangerous(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func doSomethingDangerous(_ operation: String, completionHandler handler: ((String?, Error?) -> Void)? = nil)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func doSomethingDangerous(_ operation: String) async throws -> String

// CHECK: @completionHandlerAsync("checkAvailability()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("checkAvailability()", completionHandlerIndex: 0)
// CHECK-NEXT: func checkAvailability(completionHandler: @escaping (Bool) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func checkAvailability() async -> Bool

// CHECK: @completionHandlerAsync("anotherExample()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("anotherExample()", completionHandlerIndex: 0)
// CHECK-NEXT: func anotherExample(completionBlock block: @escaping (String) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func anotherExample() async -> String

// CHECK: @completionHandlerAsync("finalExample()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("finalExample()", completionHandlerIndex: 0)
// CHECK-NEXT: func finalExampleWithReply(to block: @escaping (String) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func finalExample() async -> String

// CHECK: @completionHandlerAsync("replyingOperation(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("replyingOperation(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func replyingOperation(_ operation: String, replyTo block: @escaping (String) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func replyingOperation(_ operation: String) async -> String

// CHECK: @completionHandlerAsync("findAnswer()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("findAnswer()", completionHandlerIndex: 0)
// CHECK-NEXT: func findAnswer(completionHandler handler: @escaping (String?, Error?) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func findAnswer() async throws -> String

// CHECK: @completionHandlerAsync("findAnswerFailingly()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("findAnswerFailingly()", completionHandlerIndex: 0)
// CHECK-NEXT: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func findAnswerFailingly() async throws -> String

// CHECK: @completionHandlerAsync("findQAndA()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("findQAndA()", completionHandlerIndex: 0)
// CHECK-NEXT: func findQAndA(completionHandler handler: @escaping (String?, String?, Error?) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func findQAndA() async throws -> (String?, String)

// CHECK: @completionHandlerAsync("findQuestionableAnswers()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("findQuestionableAnswers()", completionHandlerIndex: 0)
// CHECK-NEXT: func findQuestionableAnswers(completionHandler handler: @escaping CompletionHandler)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func findQuestionableAnswers() async throws -> (String, String?)

// CHECK: @completionHandlerAsync("doSomethingFun(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("doSomethingFun(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func doSomethingFun(_ operation: String, then completionHandler: @escaping () -> Void)
// CHECK-NEXT: func doSomethingFun(_ operation: String) async

// CHECK: @completionHandlerAsync("doSomethingConflicted(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("doSomethingConflicted(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func doSomethingConflicted(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func doSomethingConflicted(_ operation: String) async -> Int
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func doSomethingConflicted(_ operation: String) -> Int

// CHECK: func dance(_ step: String) async -> String
// CHECK: func __leap(_ height: Int) async -> String

// CHECK: @completionHandlerAsync("runOnMainThread()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("runOnMainThread()", completionHandlerIndex: 0)
// CHECK-NEXT: func runOnMainThread(completionHandler completion: (@MainActor (String) -> Void)? = nil)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func runOnMainThread() async -> String

// CHECK: @completionHandlerAsync("asyncImportSame(_:)", completionHandleIndex: 1)
// CHECK: @completionHandlerAsync("asyncImportSame(_:)", completionHandlerIndex: 1)
// CHECK-NEXT: func asyncImportSame(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func asyncImportSame(_ operation: String) async -> Int
// CHECK-NEXT: func asyncImportSame(_ operation: String, replyTo handler: @escaping (Int) -> Void)
// CHECK-NOT: func asyncImportSame(_ operation: String) async -> Int
Expand All @@ -76,20 +90,21 @@ import _Concurrency
// CHECK-LABEL: protocol RefrigeratorDelegate
// CHECK-NEXT: func someoneDidOpenRefrigerator(_ fridge: Any)
// CHECK-NEXT: func refrigerator(_ fridge: Any, didGetFilledWithItems items: [Any])
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didGetFilledWithIntegers items: UnsafeMutablePointer<Int>, count: Int)
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, willAddItem item: Any)
// CHECK-NEXT: {{^}} func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool
// CHECK-NEXT: {{^}} @objc func refrigerator(_ fridge: Any, didGetFilledWithIntegers items: UnsafeMutablePointer<Int>, count: Int)
// CHECK-NEXT: {{^}} @objc func refrigerator(_ fridge: Any, willAddItem item: Any)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: {{^}} @objc func refrigerator(_ fridge: Any, didRemoveItem item: Any) -> Bool
// CHECK-NEXT: {{^[}]$}}

// CHECK-LABEL: protocol ProtocolWithSwiftAttributes {
// CHECK-NEXT: @actorIndependent func independentMethod()
// CHECK-NEXT: func asyncHandlerMethod()
// CHECK-NEXT: {{^}} @MainActor func mainActorMethod()
// CHECK-NEXT: {{^}} @MainActor func uiActorMethod()
// CHECK-NEXT: {{^}} optional func missingAtAttributeMethod()
// CHECK-NEXT: {{^}} @objc @MainActor func mainActorMethod()
// CHECK-NEXT: {{^}} @objc @MainActor func uiActorMethod()
// CHECK-NEXT: {{^}} @objc optional func missingAtAttributeMethod()
// CHECK-NEXT: {{^[}]$}}

// CHECK: {{^}}var MAGIC_NUMBER: Int32 { get }
// CHECK: {{^}}@actorIndependent(unsafe) var MAGIC_NUMBER: Int32 { get }

// CHECK: func doSomethingConcurrently(_ block: @Sendable () -> Void)

Expand Down
5 changes: 3 additions & 2 deletions test/IDE/print_clang_objc_effectful_properties.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=EffectfulProperties -function-definitions=false -enable-experimental-concurrency > %t/EffectfulProperties.printed.txt
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -print-implicit-attrs -source-filename %s -module-to-print=EffectfulProperties -function-definitions=false -enable-experimental-concurrency > %t/EffectfulProperties.printed.txt
// RUN: %FileCheck -input-file %t/EffectfulProperties.printed.txt %s

// REQUIRES: objc_interop
Expand Down Expand Up @@ -29,8 +29,9 @@
// CHECK: func getMainDog(_ completion: @escaping @MainActor (String) -> Void)
// CHECK-NEXT: var mainDogProp: String { get async }

// CHECK: @completionHandlerAsync("regularMainDog()", completionHandleIndex: 0)
// CHECK: @completionHandlerAsync("regularMainDog()", completionHandlerIndex: 0)
// CHECK-NEXT: func regularMainDog(_ completion: @escaping @MainActor (String) -> Void)
// CHECK-NEXT: @discardableResult
// CHECK-NEXT: func regularMainDog() async -> String
// CHECK: }

Expand Down
5 changes: 5 additions & 0 deletions test/SourceKit/InterfaceGen/Inputs/gen_concurrency.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ClassWithAsyncAndHandler {
@completionHandlerAsync("foo(_:)", completionHandlerIndex: 1)
func foo(_ operation: String, completionHandler handler: @escaping (Int) -> Void) {}
func foo(_ operation: String) async -> Int { 0 }
}
5 changes: 5 additions & 0 deletions test/SourceKit/InterfaceGen/Inputs/header_concurrency.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import Foundation;

@interface ClassWithHandlerMethod
-(void)methodWithHandler:(NSString *)operation completionHandler:(void (^)(NSInteger))handler;
@end
20 changes: 20 additions & 0 deletions test/SourceKit/InterfaceGen/gen_objc_concurrency.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// REQUIRES: objc_interop
// REQUIRES: concurrency

// RUN: %empty-directory(%t)

// RUN: %sourcekitd-test -req=interface-gen %S/Inputs/gen_concurrency.swift -- %S/Inputs/gen_concurrency.swift -target %target-triple -I %t -Xfrontend -enable-experimental-concurrency | %FileCheck %s --check-prefix=SWIFT-GEN-INTERFACE

// Make sure we print @completionHandlerAsync when it was explicitly written by the user.
// SWIFT-GEN-INTERFACE-LABEL: class ClassWithAsyncAndHandler {
// SWIFT-GEN-INTERFACE: @completionHandlerAsync("foo(_:)", completionHandlerIndex: 1)
// SWIFT-GEN-INTERFACE-NEXT: internal func foo(_ operation: String, completionHandler handler: @escaping (Int) -> Void)
// SWIFT-GEN-INTERFACE: internal func foo(_ operation: String) async -> Int

// RUN: %sourcekitd-test -req=interface-gen -using-swift-args -header %S/Inputs/header_concurrency.h -- %s -Xfrontend -enable-objc-interop -Xfrontend -enable-experimental-concurrency -import-objc-header %S/Inputs/header_concurrency.h -sdk %clang-importer-sdk | %FileCheck %s --check-prefix=OBJC-GEN-INTERFACE

// But don't print @completionHandlerAsync if it was implicitly added to an imported Clang decl (rdar://76685011).
// OBJC-GEN-INTERFACE-LABEL: class ClassWithHandlerMethod {
// OBJC-GEN-INTERFACE-NOT: @completionHandlerAsync
// OBJC-GEN-INTERFACE: func method(withHandler operation: String!, completionHandler handler: ((Int) -> Void)!)
// OBJC-GEN-INTERFACE: func method(withHandler operation: String!) async -> Int