Skip to content

Commit 5d68053

Browse files
authored
Merge pull request #64015 from tshortli/unavailable-actor
AST: Emit correct synthesized availability attributes for `unownedExecutor` property
2 parents 95b8a48 + 9c76e0d commit 5d68053

File tree

7 files changed

+166
-16
lines changed

7 files changed

+166
-16
lines changed

include/swift/AST/Availability.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ class AvailabilityContext {
334334

335335
class AvailabilityInference {
336336
public:
337+
/// Returns the decl that should be considered the parent decl of the given
338+
/// decl when looking for inherited availability annotations.
339+
static const Decl *parentDeclForInferredAvailability(const Decl *D);
340+
337341
/// Infers the common availability required to access an array of
338342
/// declarations and adds attributes reflecting that availability
339343
/// to ToDecl.

lib/AST/Availability.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,22 @@ static AvailableAttr *createAvailableAttr(PlatformKind Platform,
102102
StringRef Rename,
103103
ValueDecl *RenameDecl,
104104
ASTContext &Context) {
105-
106105
llvm::VersionTuple Introduced =
107106
Inferred.Introduced.value_or(llvm::VersionTuple());
108107
llvm::VersionTuple Deprecated =
109108
Inferred.Deprecated.value_or(llvm::VersionTuple());
110109
llvm::VersionTuple Obsoleted =
111110
Inferred.Obsoleted.value_or(llvm::VersionTuple());
112111

112+
// If a decl is unavailable then it cannot have any introduced, deprecated, or
113+
// obsoleted version.
114+
if (Inferred.PlatformAgnostic ==
115+
PlatformAgnosticAvailabilityKind::Unavailable) {
116+
Introduced = llvm::VersionTuple();
117+
Deprecated = llvm::VersionTuple();
118+
Obsoleted = llvm::VersionTuple();
119+
}
120+
113121
return new (Context)
114122
AvailableAttr(SourceLoc(), SourceRange(), Platform,
115123
Message, Rename, RenameDecl,
@@ -165,7 +173,8 @@ void AvailabilityInference::applyInferredAvailableAttrs(
165173

166174
/// Returns the decl that should be considered the parent decl of the given decl
167175
/// when looking for inherited availability annotations.
168-
static Decl *parentDeclForAvailability(const Decl *D) {
176+
const Decl *
177+
AvailabilityInference::parentDeclForInferredAvailability(const Decl *D) {
169178
if (auto *AD = dyn_cast<AccessorDecl>(D))
170179
return AD->getStorage();
171180

@@ -230,7 +239,8 @@ SemanticAvailableRangeAttrRequest::evaluate(Evaluator &evaluator,
230239
decl, decl->getASTContext()))
231240
return std::make_pair(attr, decl);
232241

233-
if (auto *parent = parentDeclForAvailability(decl))
242+
if (auto *parent =
243+
AvailabilityInference::parentDeclForInferredAvailability(decl))
234244
return parent->getSemanticAvailableRangeAttr();
235245

236246
return None;
@@ -262,7 +272,8 @@ SemanticUnavailableAttrRequest::evaluate(Evaluator &evaluator,
262272
if (auto attr = decl->getAttrs().getUnavailable(decl->getASTContext()))
263273
return std::make_pair(attr, decl);
264274

265-
if (auto *parent = parentDeclForAvailability(decl))
275+
if (auto *parent =
276+
AvailabilityInference::parentDeclForInferredAvailability(decl))
266277
return parent->getSemanticUnavailableAttr();
267278

268279
return None;

lib/AST/Decl.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,12 @@ unsigned Decl::getAttachedMacroDiscriminator(
477477
}
478478

479479
const Decl *Decl::getInnermostDeclWithAvailability() const {
480-
if (auto attrAndDecl = getSemanticAvailableRangeAttr())
481-
return attrAndDecl.value().second;
480+
if (getAttrs().hasAttribute<AvailableAttr>())
481+
return this;
482+
483+
if (auto parent =
484+
AvailabilityInference::parentDeclForInferredAvailability(this))
485+
return parent->getInnermostDeclWithAvailability();
482486

483487
return nullptr;
484488
}

test/Concurrency/concurrency_availability.swift

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// RUN: %target-swift-frontend -parse-stdlib -target x86_64-apple-macosx12 -typecheck %s -DTARGET_MACOS_12
44
// REQUIRES: OS=macosx
55

6+
import _Concurrency
7+
68
func f() async { } // expected-error{{concurrency is only available in}}
79
// expected-note@-1{{add @available}}
810

@@ -13,19 +15,41 @@ actor A { } // expected-error{{concurrency is only available in}}
1315
public func swift_deletedAsyncMethodError() async {
1416
}
1517

16-
// Ensure that our synthesis of the actor's unownedExecutor does not cause
17-
// availability errors.
1818
@available(macOS 12.0, *)
1919
struct S {
20-
actor A {
20+
// Ensure that our synthesis of the actor's unownedExecutor does not cause
21+
// availability errors.
22+
actor NestedActor {
23+
}
24+
25+
// The synthesized unownedExecutor inside this actor should inherit the
26+
// un-availability of UnavailableActor.
27+
@available(macOS, unavailable)
28+
actor UnavailableActor {
2129
}
2230
}
2331

32+
#if TARGET_MACOS_12
2433
// The synthesized unownedExecutor inside this extension on S should inherit
2534
// availability from S to avoid availability errors.
26-
#if TARGET_MACOS_12
2735
extension S {
28-
actor A2 {
36+
actor ExtensionNestedActor {
2937
}
3038
}
3139
#endif
40+
41+
// Make sure that the conformances to Actor are actually being synthesized
42+
// since otherwise this test isn't actually testing what it is designed to test.
43+
@available(macOS 10.15, *)
44+
func takesExecutor(_ e: UnownedSerialExecutor) { }
45+
46+
@available(macOS 12.0, *)
47+
func testNestedActorConformance(_ a: S.NestedActor) {
48+
takesExecutor(a.unownedExecutor)
49+
}
50+
51+
#if TARGET_MACOS_12
52+
func testExtensionNestedActorConformance(_ a: S.ExtensionNestedActor) {
53+
takesExecutor(a.unownedExecutor)
54+
}
55+
#endif
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library -target %target-swift-abi-5.3-triple
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -module-name Library
4+
// RUN: %FileCheck %s < %t/Library.swiftinterface
5+
6+
// REQUIRES: VENDOR=apple
7+
8+
// CHECK: #if compiler(>=5.3) && $Actors
9+
// CHECK-NEXT: public actor ActorWithImplicitAvailability {
10+
public actor ActorWithImplicitAvailability {
11+
// CHECK: @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
12+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
13+
// CHECK-NEXT: get
14+
// CHECK-NEXT: }
15+
}
16+
// CHECK: #endif
17+
18+
// CHECK: #if compiler(>=5.3) && $Actors
19+
// CHECK-NEXT: @available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *)
20+
// CHECK-NEXT: public actor ActorWithExplicitAvailability {
21+
@available(SwiftStdlib 5.2, *)
22+
public actor ActorWithExplicitAvailability {
23+
// CHECK: @available(iOS 13.4, tvOS 13.4, watchOS 6.2, macOS 10.15.4, *)
24+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
25+
// CHECK-NEXT: get
26+
// CHECK-NEXT: }
27+
}
28+
// CHECK: #endif
29+
30+
// CHECK: #if compiler(>=5.3) && $Actors
31+
// CHECK-NEXT: @_hasMissingDesignatedInitializers @available(macOS, unavailable)
32+
// CHECK-NEXT: public actor UnavailableActor {
33+
@available(macOS, unavailable)
34+
public actor UnavailableActor {
35+
// CHECK: @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
36+
// CHECK-NEXT: @available(macOS, unavailable)
37+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
38+
// CHECK-NEXT: get
39+
// CHECK-NEXT: }
40+
}
41+
// CHECK: #endif
42+
43+
// CHECK: @available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *)
44+
// CHECK-NEXT: public enum Enum {
45+
@available(SwiftStdlib 5.2, *)
46+
public enum Enum {
47+
// CHECK: #if compiler(>=5.3) && $Actors
48+
// CHECK-NEXT: @_hasMissingDesignatedInitializers public actor NestedActor {
49+
public actor NestedActor {
50+
// CHECK: @available(iOS 13.4, tvOS 13.4, watchOS 6.2, macOS 10.15.4, *)
51+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
52+
// CHECK-NEXT: get
53+
// CHECK-NEXT: }
54+
}
55+
// CHECK: #endif
56+
}
57+
58+
// CHECK: extension Library.Enum {
59+
extension Enum {
60+
// CHECK: #if compiler(>=5.3) && $Actors
61+
// CHECK-NEXT: @_hasMissingDesignatedInitializers public actor ExtensionNestedActor {
62+
public actor ExtensionNestedActor {
63+
// CHECK: @available(iOS 13.4, tvOS 13.4, watchOS 6.2, macOS 10.15.4, *)
64+
// CHECK-NEXT: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
65+
// CHECK-NEXT: get
66+
// CHECK-NEXT: }
67+
}
68+
}

test/ModuleInterface/actor_protocol.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -disable-availability-checking -module-name Library
3-
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -disable-availability-checking -module-name Library
4-
// RUN: %FileCheck --check-prefix CHECK-EXTENSION %s <%t/Library.swiftinterface
2+
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -module-name Library
54
// RUN: %FileCheck --check-prefix CHECK %s <%t/Library.swiftinterface
65
// REQUIRES: concurrency
76

@@ -10,7 +9,7 @@
109
/// and not via some extension. The requirement is due to the unique
1110
/// optimizations applied to the implementation of actors.
1211

13-
// CHECK-EXTENSION-NOT: extension {{.+}} : _Concurrency.Actor
12+
// CHECK-NOT: extension {{.+}} : _Concurrency.Actor
1413

1514
// CHECK: public actor PlainActorClass {
1615
@available(SwiftStdlib 5.1, *)

test/lit.cfg

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,15 +426,55 @@ swift_version = lit_config.params.get('swift-version',
426426
lit_config.note('Compiling with -swift-version ' + swift_version)
427427
config.swift_test_options = '-swift-version ' + swift_version
428428

429-
# Load availability macros for known stdlib releases.
429+
# Loads availability macros for known stdlib releases.
430430
def load_availability_macros():
431431
path = os.path.join(os.path.dirname(__file__), "../utils/availability-macros.def")
432432
lines = open(path, 'r').read().splitlines()
433433
pattern = re.compile(r"\s*(#.*)?")
434434
return filter(lambda l: pattern.fullmatch(l) is None, lines)
435+
436+
# Returns a tuple with the Swift ABI version (e.g. '5.0') and the corresponding
437+
# target triple (e.g. 'arm64-apple-ios12.2')
438+
def availability_macro_to_swift_abi_target_triple(macro):
439+
# Use a regex and split to pull out the components of an availability macro
440+
# that is formatted like this:
441+
# SwiftStdlib 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2
442+
matches = re.search(r'^[\s]*SwiftStdlib[\s]+([0-9\.]+)[\s]*:[\s]*(.+)', macro)
443+
stdlib_vers = matches.group(1)
444+
vers_by_platform = {}
445+
for platform_vers in matches.group(2).split(', '):
446+
components = platform_vers.split(' ')
447+
vers_by_platform[components[0]] = components[1]
448+
449+
platform = {
450+
'macosx': 'macOS',
451+
'ios': 'iOS',
452+
'maccatalyst': 'iOS',
453+
'tvos': 'tvOS',
454+
'watchos': 'watchOS'
455+
}.get(run_os)
456+
457+
if platform is None:
458+
return stdlib_vers, config.variant_triple
459+
460+
os_vers = vers_by_platform.get(platform)
461+
if os_vers is None:
462+
return stdlib_vers, config.variant_triple
463+
464+
if run_os == 'maccatalyst':
465+
return stdlib_vers, '%s-%s-ios%s-macabi' % (run_cpu, run_vendor, os_vers)
466+
467+
return stdlib_vers, '%s-%s-%s%s%s' % (run_cpu, run_vendor, run_os,
468+
os_vers, run_environment)
469+
470+
# Add availability macros to the default frontend/driver flags and create target
471+
# triple substitutions for each stdlib version.
435472
for macro in load_availability_macros():
436473
config.swift_frontend_test_options += " -define-availability '{0}'".format(macro)
437474
config.swift_driver_test_options += " -Xfrontend -define-availability -Xfrontend '{0}'".format(macro)
475+
(stdlib_vers, triple) = availability_macro_to_swift_abi_target_triple(macro)
476+
config.substitutions.append(('%target-swift-abi-{}-triple'.format(stdlib_vers), triple))
477+
438478

439479
differentiable_programming = lit_config.params.get('differentiable_programming', None)
440480
if differentiable_programming is not None:

0 commit comments

Comments
 (0)