Skip to content

Commit 4943e14

Browse files
committed
[Frontend] Add experimental flag for @preconcurrency conformances feature
1 parent 233d279 commit 4943e14

File tree

7 files changed

+101
-42
lines changed

7 files changed

+101
-42
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5630,6 +5630,8 @@ ERROR(preconcurrency_not_inheritance_clause,none,
56305630
"'preconcurrency' attribute only applies in inheritance clauses", ())
56315631
ERROR(preconcurrency_not_existential,none,
56325632
"'preconcurrency' attribute cannot apply to non-protocol type %0", (Type))
5633+
ERROR(preconcurrency_attr_disabled,none,
5634+
"attribute requires '-enable-experimental-feature PreconcurrencyConformances'", ())
56335635

56345636
ERROR(redundant_any_in_existential,none,
56355637
"redundant 'any' in type %0",

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ EXPERIMENTAL_FEATURE(GroupActorErrors, true)
270270
// Allow for the 'transferring' keyword to be applied to arguments and results.
271271
EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
272272

273+
// Enable `@preconcurrency` attribute on protocol conformances.
274+
EXPERIMENTAL_FEATURE(PreconcurrencyConformances, false)
275+
273276
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
274277
#undef EXPERIMENTAL_FEATURE
275278
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3908,6 +3908,30 @@ static bool usesFeatureBitwiseCopyable(Decl *decl) { return false; }
39083908

39093909
static bool usesFeatureTransferringArgsAndResults(Decl *decl) { return false; }
39103910

3911+
static bool usesFeaturePreconcurrencyConformances(Decl *decl) {
3912+
auto usesPreconcurrencyConformance = [&](const InheritedTypes &inherited) {
3913+
return llvm::any_of(
3914+
inherited.getEntries(),
3915+
[](const InheritedEntry &entry) { return entry.isPreconcurrency; });
3916+
};
3917+
3918+
if (auto *T = dyn_cast<TypeDecl>(decl))
3919+
return usesPreconcurrencyConformance(T->getInherited());
3920+
3921+
if (auto *E = dyn_cast<ExtensionDecl>(decl)) {
3922+
// If type has `@preconcurrency` conformance(s) all of its
3923+
// extensions have to be guarded by the flag too.
3924+
if (auto *T = dyn_cast<TypeDecl>(E->getExtendedNominal())) {
3925+
if (usesPreconcurrencyConformance(T->getInherited()))
3926+
return true;
3927+
}
3928+
3929+
return usesPreconcurrencyConformance(E->getInherited());
3930+
}
3931+
3932+
return false;
3933+
}
3934+
39113935
/// Suppress the printing of a particular feature.
39123936
static void suppressingFeature(PrintOptions &options, Feature feature,
39133937
llvm::function_ref<void()> action) {

lib/Sema/TypeCheckType.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,22 +3251,30 @@ TypeResolver::resolveAttributedType(TypeAttributes &attrs, TypeRepr *repr,
32513251
}
32523252

32533253
if (attrs.has(TAK_preconcurrency)) {
3254-
ty = resolveType(repr, options);
3255-
if (!ty || ty->hasError()) return ty;
3254+
auto &ctx = getASTContext();
3255+
if (ctx.LangOpts.hasFeature(Feature::PreconcurrencyConformances)) {
3256+
ty = resolveType(repr, options);
3257+
if (!ty || ty->hasError())
3258+
return ty;
32563259

3257-
if (!options.is(TypeResolverContext::Inherited) ||
3258-
getDeclContext()->getSelfProtocolDecl()) {
3259-
diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency),
3260-
diag::preconcurrency_not_inheritance_clause);
3261-
ty = ErrorType::get(getASTContext());
3262-
} else if (!ty->isConstraintType()) {
3260+
if (!options.is(TypeResolverContext::Inherited) ||
3261+
getDeclContext()->getSelfProtocolDecl()) {
3262+
diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency),
3263+
diag::preconcurrency_not_inheritance_clause);
3264+
ty = ErrorType::get(getASTContext());
3265+
} else if (!ty->isConstraintType()) {
3266+
diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency),
3267+
diag::preconcurrency_not_existential, ty);
3268+
ty = ErrorType::get(getASTContext());
3269+
}
3270+
3271+
// Nothing to record in the type. Just clear the attribute.
3272+
attrs.clearAttribute(TAK_preconcurrency);
3273+
} else {
32633274
diagnoseInvalid(repr, attrs.getLoc(TAK_preconcurrency),
3264-
diag::preconcurrency_not_existential, ty);
3275+
diag::preconcurrency_attr_disabled);
32653276
ty = ErrorType::get(getASTContext());
32663277
}
3267-
3268-
// Nothing to record in the type. Just clear the attribute.
3269-
attrs.clearAttribute(TAK_preconcurrency);
32703278
}
32713279

32723280
if (attrs.has(TAK_retroactive)) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-frontend -disable-availability-checking %s -emit-sil -o /dev/null -verify -enable-experimental-feature PreconcurrencyConformances -verify-additional-prefix minimal-targeted-
2+
// RUN: %target-swift-frontend -disable-availability-checking %s -emit-sil -o /dev/null -verify -enable-experimental-feature PreconcurrencyConformances -strict-concurrency=targeted -verify-additional-prefix minimal-targeted-
3+
// RUN: %target-swift-frontend -disable-availability-checking %s -emit-sil -o /dev/null -verify -enable-experimental-feature PreconcurrencyConformances -strict-concurrency=complete -verify-additional-prefix complete-tns-
4+
5+
// REQUIRES: concurrency
6+
// REQUIRES: asserts
7+
8+
protocol Q {
9+
}
10+
11+
do {
12+
class K {}
13+
14+
struct A : @preconcurrency Q {} // Ok
15+
struct B : @preconcurrency K {
16+
// expected-error@-1 {{'preconcurrency' attribute cannot apply to non-protocol type 'K'}}
17+
var x: @preconcurrency Int
18+
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
19+
}
20+
21+
typealias T = @preconcurrency Q
22+
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
23+
24+
func test(_: @preconcurrency K) {}
25+
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
26+
}
27+
28+
protocol InvalidUseOfPreconcurrencyAttr : @preconcurrency Q {
29+
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
30+
}
31+
32+
struct TestPreconcurrencyAttr {}
33+
extension TestPreconcurrencyAttr : @preconcurrency Q { // Ok
34+
}

test/Concurrency/predates_concurrency.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -249,28 +249,3 @@ extension MainActorPreconcurrency: NotIsolated {
249249
}
250250
}
251251
}
252-
253-
do {
254-
class K {}
255-
256-
struct A : @preconcurrency Q {} // Ok
257-
struct B : @preconcurrency K {
258-
// expected-error@-1 {{'preconcurrency' attribute cannot apply to non-protocol type 'K'}}
259-
var x: @preconcurrency Int
260-
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
261-
}
262-
263-
typealias T = @preconcurrency Q
264-
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
265-
266-
func test(_: @preconcurrency K) {}
267-
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
268-
}
269-
270-
protocol InvalidUseOfPreconcurrencyAttr : @preconcurrency Q {
271-
// expected-error@-1 {{'preconcurrency' attribute only applies in inheritance clauses}}
272-
}
273-
274-
struct TestPreconcurrencyAttr {}
275-
extension TestPreconcurrencyAttr : @preconcurrency Q { // Ok
276-
}

test/ModuleInterface/preconcurrency_conformances.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@
1111
// RUN: %target-swift-frontend -emit-module %t/src/Client.swift \
1212
// RUN: -module-name Client -I %t -swift-version 5 -enable-library-evolution \
1313
// RUN: -emit-module-path %t/Client.swiftmodule \
14-
// RUN: -emit-module-interface-path %t/Client.swiftinterface
14+
// RUN: -emit-module-interface-path %t/Client.swiftinterface \
15+
// RUN: -enable-experimental-feature PreconcurrencyConformances
1516

1617
// RUN: %FileCheck %s < %t/Client.swiftinterface
1718

18-
// RUN: %target-swift-emit-module-interface(%t/Client.swiftinterface) -I %t %s -module-name Client
19-
// RUN: %target-swift-typecheck-module-from-interface(%t/Client.swiftinterface) -I %t -module-name Client
19+
// RUN: %target-swift-emit-module-interface(%t/Client.swiftinterface) -I %t %s -module-name Client \
20+
// RUN: -enable-experimental-feature PreconcurrencyConformances
21+
22+
// RUN: %target-swift-typecheck-module-from-interface(%t/Client.swiftinterface) -I %t -module-name Client \
23+
// RUN: -enable-experimental-feature PreconcurrencyConformances
24+
25+
// REQUIRES: asserts
26+
// REQUIRES: concurrency
2027

2128
//--- PublicModule.swift
2229
public protocol P {
@@ -26,7 +33,8 @@ public protocol P {
2633
//--- Client.swift
2734
import PublicModule
2835

29-
// CHECK: @_Concurrency.MainActor public struct GlobalActorTest : @preconcurrency PublicModule.P
36+
// CHECK: #if compiler(>=5.3) && $PreconcurrencyConformances
37+
// CHECK-NEXT: @_Concurrency.MainActor public struct GlobalActorTest : @preconcurrency PublicModule.P
3038
@MainActor
3139
public struct GlobalActorTest : @preconcurrency P {
3240
public func test() -> Int { 0 }
@@ -36,9 +44,14 @@ public struct GlobalActorTest : @preconcurrency P {
3644
public class ExtTest {
3745
}
3846

39-
// CHECK: extension Client.ExtTest : @preconcurrency PublicModule.P
47+
// CHECK: #if compiler(>=5.3) && $PreconcurrencyConformances
48+
// CHECK-NEXT: extension Client.ExtTest : @preconcurrency PublicModule.P
4049
extension ExtTest : @preconcurrency P {
4150
public func test() -> Int { 1 }
4251
}
4352

53+
// CHECK: #if compiler(>=5.3) && $PreconcurrencyConformances
54+
// CHECK-NEXT: extension Client.GlobalActorTest : Swift.Sendable {}
55+
// CHECK-NEXT: #endif
56+
4457
// TODO: 'actor' cannot be tested until @preconcurrency conformances are implemented.

0 commit comments

Comments
 (0)