Skip to content

Commit e744f41

Browse files
authored
Merge pull request #41151 from DougGregor/preconcurrency-diagnostic-fixes-5.6
2 parents 9839378 + 4f64661 commit e744f41

File tree

5 files changed

+163
-22
lines changed

5 files changed

+163
-22
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -714,18 +714,20 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
714714
// Determine whether the type was explicitly non-Sendable.
715715
auto nominalModule = nominal->getParentModule();
716716
bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked() ||
717-
isExplicitSendableConformance() ||
718717
hasExplicitSendableConformance(nominal);
719718

720719
// Determine whether this nominal type is visible via a @preconcurrency
721720
// import.
722721
auto import = findImportFor(nominal, fromDC);
723722

724723
// When the type is explicitly non-Sendable...
724+
auto sourceFile = fromDC->getParentSourceFile();
725725
if (isExplicitlyNonSendable) {
726-
// @preconcurrency imports downgrade the diagnostic to a warning.
726+
// @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
727727
if (import && import->options.contains(ImportFlags::Preconcurrency)) {
728-
// FIXME: Note that this @preconcurrency import was "used".
728+
if (sourceFile)
729+
sourceFile->setImportUsedPreconcurrency(*import);
730+
729731
return DiagnosticBehavior::Warning;
730732
}
731733

@@ -737,13 +739,25 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
737739
// @preconcurrency suppresses the diagnostic in Swift 5.x, and
738740
// downgrades it to a warning in Swift 6 and later.
739741
if (import && import->options.contains(ImportFlags::Preconcurrency)) {
740-
// FIXME: Note that this @preconcurrency import was "used".
742+
if (sourceFile)
743+
sourceFile->setImportUsedPreconcurrency(*import);
744+
741745
return nominalModule->getASTContext().LangOpts.isSwiftVersionAtLeast(6)
742746
? DiagnosticBehavior::Warning
743747
: DiagnosticBehavior::Ignore;
744748
}
745749

746-
return defaultDiagnosticBehavior();
750+
auto defaultBehavior = defaultDiagnosticBehavior();
751+
752+
// If we are checking an implicit Sendable conformance, don't suppress
753+
// diagnostics for declarations in the same module. We want them so make
754+
// enclosing inferred types non-Sendable.
755+
if (defaultBehavior == DiagnosticBehavior::Ignore &&
756+
nominal->getParentSourceFile() &&
757+
conformanceCheck && *conformanceCheck == SendableCheck::Implicit)
758+
return DiagnosticBehavior::Warning;
759+
760+
return defaultBehavior;
747761
}
748762

749763
/// Produce a diagnostic for a single instance of a non-Sendable type where
@@ -765,14 +779,6 @@ static bool diagnoseSingleNonSendableType(
765779

766780
bool wasSuppressed = diagnose(type, behavior);
767781

768-
// If this type was imported from another module, try to find the
769-
// corresponding import.
770-
Optional<AttributedImport<swift::ImportedModule>> import;
771-
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
772-
if (nominal && nominal->getParentModule() != module) {
773-
import = findImportFor(nominal, fromContext.fromDC);
774-
}
775-
776782
if (behavior == DiagnosticBehavior::Ignore || wasSuppressed) {
777783
// Don't emit any other diagnostics.
778784
} else if (type->is<FunctionType>()) {
@@ -796,6 +802,14 @@ static bool diagnoseSingleNonSendableType(
796802
diag::non_sendable_nominal, nominal->getDescriptiveKind(),
797803
nominal->getName());
798804

805+
// This type was imported from another module; try to find the
806+
// corresponding import.
807+
Optional<AttributedImport<swift::ImportedModule>> import;
808+
SourceFile *sourceFile = fromContext.fromDC->getParentSourceFile();
809+
if (sourceFile) {
810+
import = findImportFor(nominal, fromContext.fromDC);
811+
}
812+
799813
// If we found the import that makes this nominal type visible, remark
800814
// that it can be @preconcurrency import.
801815
// Only emit this remark once per source file, because it can happen a
@@ -814,14 +828,6 @@ static bool diagnoseSingleNonSendableType(
814828
}
815829
}
816830

817-
// If we found an import that makes this nominal type visible, and that
818-
// was a @preconcurrency import, note that we have made use of the
819-
// attribute.
820-
if (import && import->options.contains(ImportFlags::Preconcurrency) &&
821-
sourceFile) {
822-
sourceFile->setImportUsedPreconcurrency(*import);
823-
}
824-
825831
return behavior == DiagnosticBehavior::Unspecified && !wasSuppressed;
826832
}
827833

@@ -3871,8 +3877,10 @@ static bool checkSendableInstanceStorage(
38713877
bool operator()(VarDecl *property, Type propertyType) {
38723878
// Classes with mutable properties are not Sendable.
38733879
if (property->supportsMutation() && isa<ClassDecl>(nominal)) {
3874-
if (check == SendableCheck::Implicit)
3880+
if (check == SendableCheck::Implicit) {
3881+
invalid = true;
38753882
return true;
3883+
}
38763884

38773885
auto behavior = SendableCheckContext(
38783886
dc, check).defaultDiagnosticBehavior();
@@ -3891,6 +3899,10 @@ static bool checkSendableInstanceStorage(
38913899
propertyType, SendableCheckContext(dc, check), property->getLoc(),
38923900
[&](Type type, DiagnosticBehavior behavior) {
38933901
if (check == SendableCheck::Implicit) {
3902+
// If we are to ignore this diagnose, just continue.
3903+
if (behavior == DiagnosticBehavior::Ignore)
3904+
return false;
3905+
38943906
invalid = true;
38953907
return true;
38963908
}
@@ -3918,6 +3930,10 @@ static bool checkSendableInstanceStorage(
39183930
elementType, SendableCheckContext(dc, check), element->getLoc(),
39193931
[&](Type type, DiagnosticBehavior behavior) {
39203932
if (check == SendableCheck::Implicit) {
3933+
// If we are to ignore this diagnose, just continue.
3934+
if (behavior == DiagnosticBehavior::Ignore)
3935+
return false;
3936+
39213937
invalid = true;
39223938
return true;
39233939
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
@preconcurrency import NonStrictModule
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note 2{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2: Sendable {
22+
var nsc: NonStrictClass // no warning; @preconcurrency suppressed it
23+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NS'}}
24+
}
25+
26+
struct MyType3 {
27+
var nsc: NonStrictClass
28+
}
29+
30+
func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3) async {
31+
Task {
32+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
33+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
34+
print(mt2)
35+
print(mt3)
36+
}
37+
}
38+
39+
extension NonStrictStruct: @unchecked Sendable { }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
@preconcurrency import NonStrictModule
9+
10+
struct MyType {
11+
var nsc: NonStrictClass
12+
}
13+
14+
func test(mt: MyType, nsc: NonStrictClass) {
15+
Task {
16+
print(mt)
17+
}
18+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
import NonStrictModule
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2 { // expected-note{{consider making struct 'MyType2' conform to the 'Sendable' protocol}}
22+
var nsc: NonStrictClass
23+
var ns: NS
24+
}
25+
26+
func testA(ns: NS, mt: MyType, mt2: MyType2) async {
27+
Task {
28+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
29+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
30+
print(mt2) // expected-warning{{capture of 'mt2' with non-sendable type 'MyType2' in a `@Sendable` closure}}
31+
}
32+
}
33+
34+
extension NonStrictStruct: @unchecked Sendable { }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
import NonStrictModule // expected-remark{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'NonStrictModule'}}
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note 2{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2: Sendable {
22+
var nsc: NonStrictClass // expected-warning{{stored property 'nsc' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NonStrictClass'}}
23+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NS'}}
24+
}
25+
26+
func testA(ns: NS, mt: MyType, mt2: MyType2) async {
27+
Task {
28+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
29+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
30+
print(mt2)
31+
}
32+
}
33+
34+
extension NonStrictStruct: @unchecked Sendable { }

0 commit comments

Comments
 (0)