From 269f30f119fa1ea70a67020ad8d672c78fdfb0d4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Feb 2024 10:51:13 -0800 Subject: [PATCH 1/2] [ClangImporter] Add a way to determine whether type is NSNotificationName --- lib/ClangImporter/ClangAdapter.cpp | 7 +++++++ lib/ClangImporter/ClangAdapter.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index 1e498b3dce0a8..04d4a0ccb4577 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -590,6 +590,13 @@ bool importer::isNSString(clang::QualType qt) { return qt.getTypePtrOrNull() && isNSString(qt.getTypePtrOrNull()); } +bool importer::isNSNotificationName(clang::QualType type) { + if (auto *typealias = type->getAs()) { + return typealias->getDecl()->getName() == "NSNotificationName"; + } + return false; +} + bool importer::isNSNotificationGlobal(const clang::NamedDecl *decl) { // Looking for: extern NSString *fooNotification; diff --git a/lib/ClangImporter/ClangAdapter.h b/lib/ClangImporter/ClangAdapter.h index 1b1552e777efb..32d85f17a2409 100644 --- a/lib/ClangImporter/ClangAdapter.h +++ b/lib/ClangImporter/ClangAdapter.h @@ -129,6 +129,9 @@ clang::TypedefNameDecl *findSwiftNewtype(const clang::NamedDecl *decl, bool isNSString(const clang::Type *); bool isNSString(clang::QualType); +/// Wehther the passed type is `NSNotificationName` typealias +bool isNSNotificationName(clang::QualType); + /// Whether the given declaration was exported from Swift. /// /// Note that this only checks the immediate declaration being passed. From 5f0a9c475b7d945a712322c5ca26bb8b858bb988 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 6 Feb 2024 11:33:09 -0800 Subject: [PATCH 2/2] [ClangImporter] Import NSNotificationName constants as `nonisolated` These constants could be used with observer APIs from a different isolation context, so it's more convenient to import them as `nonisolated` unless they are explicitly isolated to a MainActor. Resolves: rdar://114052705 --- lib/ClangImporter/ImportDecl.cpp | 17 +++++++ ...icationname_constants_as_nonisolated.swift | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/Concurrency/import_nsnotificationname_constants_as_nonisolated.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index f2da70bd37c35..8fca2a11c0061 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -8138,6 +8138,23 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { /*isUnchecked=*/true); } } + + // Special handling of `NSNotificationName` static immutable properties. + // + // These constants could be used with observer APIs from a different isolation + // context, so it's more convenient to import them as `nonisolated` unless + // they are explicitly isolated to a MainActor. + if (!seenMainActorAttr) { + auto *DC = MappedDecl->getDeclContext(); + if (DC->isTypeContext() && isa(MappedDecl)) { + auto *mappedVar = cast(MappedDecl); + if (mappedVar->isStatic() && mappedVar->isLet() && + isNSNotificationName(cast(ClangDecl)->getType())) { + MappedDecl->getAttrs().add(new (SwiftContext) NonisolatedAttr( + /*unsafe=*/false, /*implicit=*/true)); + } + } + } } static bool isUsingMacroName(clang::SourceManager &SM, diff --git a/test/Concurrency/import_nsnotificationname_constants_as_nonisolated.swift b/test/Concurrency/import_nsnotificationname_constants_as_nonisolated.swift new file mode 100644 index 0000000000000..2c6609a906bca --- /dev/null +++ b/test/Concurrency/import_nsnotificationname_constants_as_nonisolated.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t/src) +// RUN: %empty-directory(%t/sdk) +// RUN: split-file %s %t/src + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/src/main.swift \ +// RUN: -import-objc-header %t/src/Test.h \ +// RUN: -strict-concurrency=complete \ +// RUN: -disable-availability-checking \ +// RUN: -module-name main -I %t -verify + +// REQUIRES: objc_interop +// REQUIRES: concurrency + +//--- Test.h +#define SWIFT_MAIN_ACTOR __attribute__((swift_attr("@MainActor"))) + +#pragma clang assume_nonnull begin + +@import Foundation; + +SWIFT_MAIN_ACTOR +@interface Test : NSObject +@end + +extern NSNotificationName const TestDidTrigger __attribute__((swift_name("Test.didTrigger"))); + +SWIFT_MAIN_ACTOR +extern NSNotificationName const TestIsolatedTrigger __attribute__((swift_name("Test.isolatedTrigger"))); + +#pragma clang assume_nonnull end + +//--- main.swift + +func testAsync() async { + print(Test.didTrigger) // Ok (property is nonisolated) + print(Test.isolatedTrigger) + // expected-warning@-1 {{expression is 'async' but is not marked with 'await'; this is an error in Swift 6}} + // expected-note@-2 {{property access is 'async'}} +} + +@MainActor +func testMainActor() { + print(Test.didTrigger) // Ok + print(Test.isolatedTrigger) // Ok +}