Skip to content

Commit f698c3f

Browse files
authored
Merge pull request #41352 from DougGregor/public-frozen-sendable-preconcurrency-fix-5.6
Limit inference of Sendable for public, frozen types relying on preconcurrency
2 parents df1ede9 + 6cb0b72 commit f698c3f

File tree

4 files changed

+70
-23
lines changed

4 files changed

+70
-23
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ bool SendableCheckContext::isExplicitSendableConformance() const {
641641

642642
case SendableCheck::ImpliedByStandardProtocol:
643643
case SendableCheck::Implicit:
644+
case SendableCheck::ImplicitForExternallyVisible:
644645
return false;
645646
}
646647
}
@@ -754,7 +755,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
754755
// enclosing inferred types non-Sendable.
755756
if (defaultBehavior == DiagnosticBehavior::Ignore &&
756757
nominal->getParentSourceFile() &&
757-
conformanceCheck && *conformanceCheck == SendableCheck::Implicit)
758+
conformanceCheck && isImplicitSendableCheck(*conformanceCheck))
758759
return DiagnosticBehavior::Warning;
759760

760761
return defaultBehavior;
@@ -3877,7 +3878,7 @@ static bool checkSendableInstanceStorage(
38773878
bool operator()(VarDecl *property, Type propertyType) {
38783879
// Classes with mutable properties are not Sendable.
38793880
if (property->supportsMutation() && isa<ClassDecl>(nominal)) {
3880-
if (check == SendableCheck::Implicit) {
3881+
if (isImplicitSendableCheck(check)) {
38813882
invalid = true;
38823883
return true;
38833884
}
@@ -3898,8 +3899,14 @@ static bool checkSendableInstanceStorage(
38983899
diagnoseNonSendableTypes(
38993900
propertyType, SendableCheckContext(dc, check), property->getLoc(),
39003901
[&](Type type, DiagnosticBehavior behavior) {
3901-
if (check == SendableCheck::Implicit) {
3902-
// If we are to ignore this diagnose, just continue.
3902+
if (isImplicitSendableCheck(check)) {
3903+
// If this is for an externally-visible conformance, fail.
3904+
if (check == SendableCheck::ImplicitForExternallyVisible) {
3905+
invalid = true;
3906+
return true;
3907+
}
3908+
3909+
// If we are to ignore this diagnostic, just continue.
39033910
if (behavior == DiagnosticBehavior::Ignore)
39043911
return false;
39053912

@@ -3917,7 +3924,7 @@ static bool checkSendableInstanceStorage(
39173924

39183925
if (invalid) {
39193926
// For implicit checks, bail out early if anything failed.
3920-
if (check == SendableCheck::Implicit)
3927+
if (isImplicitSendableCheck(check))
39213928
return true;
39223929
}
39233930

@@ -3929,8 +3936,14 @@ static bool checkSendableInstanceStorage(
39293936
diagnoseNonSendableTypes(
39303937
elementType, SendableCheckContext(dc, check), element->getLoc(),
39313938
[&](Type type, DiagnosticBehavior behavior) {
3932-
if (check == SendableCheck::Implicit) {
3933-
// If we are to ignore this diagnose, just continue.
3939+
if (isImplicitSendableCheck(check)) {
3940+
// If this is for an externally-visible conformance, fail.
3941+
if (check == SendableCheck::ImplicitForExternallyVisible) {
3942+
invalid = true;
3943+
return true;
3944+
}
3945+
3946+
// If we are to ignore this diagnostic, just continue.
39343947
if (behavior == DiagnosticBehavior::Ignore)
39353948
return false;
39363949

@@ -3948,7 +3961,7 @@ static bool checkSendableInstanceStorage(
39483961

39493962
if (invalid) {
39503963
// For implicit checks, bail out early if anything failed.
3951-
if (check == SendableCheck::Implicit)
3964+
if (isImplicitSendableCheck(check))
39523965
return true;
39533966
}
39543967

@@ -4177,21 +4190,27 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
41774190
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
41784191
return nullptr;
41794192

4180-
// Public, non-frozen structs and enums defined in Swift don't get implicit
4181-
// Sendable conformances.
4182-
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
4183-
nominal->getFormalAccessScope(
4184-
/*useDC=*/nullptr,
4185-
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
4186-
!(nominal->hasClangNode() ||
4187-
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
4188-
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
4193+
SendableCheck check;
4194+
4195+
// Okay to infer Sendable conformance for non-public types or when
4196+
// specifically requested.
4197+
if (nominal->getASTContext().LangOpts.EnableInferPublicSendable ||
4198+
!nominal->getFormalAccessScope(
4199+
/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true)
4200+
.isPublic()) {
4201+
check = SendableCheck::Implicit;
4202+
} else if (nominal->hasClangNode() ||
4203+
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
4204+
nominal->getAttrs().hasAttribute<FrozenAttr>()) {
4205+
// @_frozen public types can also infer Sendable, but be more careful here.
4206+
check = SendableCheck::ImplicitForExternallyVisible;
4207+
} else {
4208+
// No inference.
41894209
return nullptr;
41904210
}
41914211

41924212
// Check the instance storage for Sendable conformance.
4193-
if (checkSendableInstanceStorage(
4194-
nominal, nominal, SendableCheck::Implicit))
4213+
if (checkSendableInstanceStorage(nominal, nominal, check))
41954214
return nullptr;
41964215

41974216
return formConformance(nullptr);

lib/Sema/TypeCheckConcurrency.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,25 @@ enum class SendableCheck {
265265

266266
/// Implicit conformance to Sendable.
267267
Implicit,
268+
269+
/// Implicit conformance to Sendable that would be externally-visible, i.e.,
270+
/// for a public @_frozen type.
271+
ImplicitForExternallyVisible,
268272
};
269273

274+
/// Whether this sendable check is implicit.
275+
static inline bool isImplicitSendableCheck(SendableCheck check) {
276+
switch (check) {
277+
case SendableCheck::Explicit:
278+
case SendableCheck::ImpliedByStandardProtocol:
279+
return false;
280+
281+
case SendableCheck::Implicit:
282+
case SendableCheck::ImplicitForExternallyVisible:
283+
return true;
284+
}
285+
}
286+
270287
/// Describes the context in which a \c Sendable check occurs.
271288
struct SendableCheckContext {
272289
const DeclContext * const fromDC;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class NotSendable { }

test/ModuleInterface/actor_isolation.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency %s
2+
// RUN: %target-swift-frontend -emit-module -o %t/Preconcurrency.swiftmodule -module-name Preconcurrency %S/Inputs/preconcurrency.swift
3+
4+
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency -I %t %s
35
// RUN: %FileCheck %s < %t/Test.swiftinterface
46
// RUN: %FileCheck %s -check-prefix SYNTHESIZED < %t/Test.swiftinterface
5-
// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/Test.swiftinterface
7+
// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/Test.swiftinterface -I %t
68

7-
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency
9+
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency -I %t
810
// RUN: %FileCheck %s < %t/TestFromModule.swiftinterface
9-
// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/TestFromModule.swiftinterface
11+
// RUN: %target-swift-frontend -typecheck-module-from-interface -module-name Test %t/TestFromModule.swiftinterface -I %t
1012

1113
// REQUIRES: concurrency
14+
import Preconcurrency
1215

1316
// CHECK: public actor SomeActor
1417

@@ -97,6 +100,13 @@ public class C8 : P2 {
97100
public func method() {}
98101
}
99102

103+
// CHECK-NOT: StructWithImplicitlyNonSendable{{.*}}Sendable
104+
@available(SwiftStdlib 5.1, *)
105+
@_frozen
106+
public struct StructWithImplicitlyNonSendable {
107+
var ns: NotSendable? = nil
108+
}
109+
100110
// FIXME: Work around a bug where module printing depends on the "synthesized"
101111
// bit in conformances which is not serialized and not present in the textual
102112
// form.

0 commit comments

Comments
 (0)