Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5869,8 +5869,8 @@ ERROR(non_sendable_keypath_capture,none,
(Type))
ERROR(non_concurrent_type_member,none,
"%select{stored property %2|associated value %2}1 of "
"'Sendable'-conforming %kind3 has non-Sendable type %0",
(Type, bool, DeclName, const ValueDecl *))
"'Sendable'-conforming %kind3 %select{contains|has}4 non-Sendable type %0",
(Type, bool, DeclName, const ValueDecl *, bool))
ERROR(concurrent_value_class_mutable_property,none,
"stored property %0 of 'Sendable'-conforming %kind1 is mutable",
(DeclName, const ValueDecl *))
Expand Down
91 changes: 35 additions & 56 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ bool SendableCheckContext::warnInMinimalChecking() const {
case SendableCheck::Explicit:
return true;

case SendableCheck::ImpliedByStandardProtocol:
case SendableCheck::ImpliedByPreconcurrencyProtocol:
case SendableCheck::Implicit:
case SendableCheck::ImplicitForExternallyVisible:
return false;
Expand Down Expand Up @@ -6768,11 +6768,16 @@ static bool checkSendableInstanceStorage(
if (property->supportsMutation() && isolation.isUnspecified()) {
auto behavior =
SendableCheckContext(dc, check).defaultDiagnosticBehavior();
// If Sendable came from a `@preconcurrency` protocol the error
// should be downgraded even with strict concurrency checking to
// allow clients time to address the new requirement.
auto preconcurrency =
check == SendableCheck::ImpliedByPreconcurrencyProtocol;
if (behavior != DiagnosticBehavior::Ignore) {
property
->diagnose(diag::concurrent_value_class_mutable_property,
property->getName(), nominal)
.limitBehaviorUntilSwiftVersion(behavior, 6);
.limitBehaviorWithPreconcurrency(behavior, preconcurrency);
}
invalid = invalid || (behavior == DiagnosticBehavior::Unspecified);
return true;
Expand All @@ -6783,57 +6788,22 @@ static bool checkSendableInstanceStorage(
}
}

// Check that the property type is Sendable.
SendableCheckContext context(dc, check);
diagnoseNonSendableTypes(
propertyType, context,
/*inDerivedConformance*/Type(), property->getLoc(),
[&](Type type, DiagnosticBehavior behavior) {
auto preconcurrency = context.preconcurrencyBehavior(type);
if (isImplicitSendableCheck(check)) {
// If this is for an externally-visible conformance, fail.
if (check == SendableCheck::ImplicitForExternallyVisible) {
invalid = true;
return true;
}

// If we are to ignore this diagnostic, just continue.
if (behavior == DiagnosticBehavior::Ignore ||
preconcurrency == DiagnosticBehavior::Ignore)
return true;

invalid = true;
return true;
}

if (preconcurrency)
behavior = preconcurrency.value();

property
->diagnose(diag::non_concurrent_type_member, propertyType,
false, property->getName(), nominal)
.limitBehaviorWithPreconcurrency(behavior,
preconcurrency.has_value());
return false;
});

if (invalid) {
// For implicit checks, bail out early if anything failed.
if (isImplicitSendableCheck(check))
return true;
}

return false;
return checkSendabilityOfMemberType(property, propertyType);
}

/// Handle an enum associated value.
bool operator()(EnumElementDecl *element, Type elementType) override {
SendableCheckContext context (dc, check);
return checkSendabilityOfMemberType(element, elementType);
}

private:
bool checkSendabilityOfMemberType(ValueDecl *member, Type memberType) {
SendableCheckContext context(dc, check);
diagnoseNonSendableTypes(
elementType, context,
/*inDerivedConformance*/Type(), element->getLoc(),
memberType, context,
/*inDerivedConformance*/ Type(), member->getLoc(),
[&](Type type, DiagnosticBehavior behavior) {
auto preconcurrency = context.preconcurrencyBehavior(type);
auto preconcurrencyBehavior = context.preconcurrencyBehavior(type);
if (isImplicitSendableCheck(check)) {
// If this is for an externally-visible conformance, fail.
if (check == SendableCheck::ImplicitForExternallyVisible) {
Expand All @@ -6843,21 +6813,29 @@ static bool checkSendableInstanceStorage(

// If we are to ignore this diagnostic, just continue.
if (behavior == DiagnosticBehavior::Ignore ||
preconcurrency == DiagnosticBehavior::Ignore)
preconcurrencyBehavior == DiagnosticBehavior::Ignore)
return true;

invalid = true;
return true;
}

if (preconcurrency)
behavior = preconcurrency.value();

element
->diagnose(diag::non_concurrent_type_member, type, true,
element->getName(), nominal)
.limitBehaviorWithPreconcurrency(behavior,
preconcurrency.has_value());
// If Sendable came from a `@preconcurrency` protocol the error
// should be downgraded even with strict concurrency checking to
// allow clients time to address the new requirement.
bool fromPreconcurrencyConformance =
check == SendableCheck::ImpliedByPreconcurrencyProtocol;

if (preconcurrencyBehavior)
behavior = preconcurrencyBehavior.value();

member
->diagnose(diag::non_concurrent_type_member, type,
isa<EnumElementDecl>(member), member->getName(),
nominal, type->isEqual(memberType))
.limitBehaviorWithPreconcurrency(
behavior, fromPreconcurrencyConformance ||
preconcurrencyBehavior.has_value());
return false;
});

Expand All @@ -6869,6 +6847,7 @@ static bool checkSendableInstanceStorage(

return false;
}

} visitor(nominal, dc, check);

return visitor.visit(nominal, dc) || visitor.invalid;
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ enum class SendableCheck {

/// Sendable conformance was implied by a protocol that inherits from
/// Sendable and also predates concurrency.
ImpliedByStandardProtocol,
ImpliedByPreconcurrencyProtocol,

/// Implicit conformance to Sendable.
Implicit,
Expand All @@ -366,7 +366,7 @@ enum class SendableCheck {
static inline bool isImplicitSendableCheck(SendableCheck check) {
switch (check) {
case SendableCheck::Explicit:
case SendableCheck::ImpliedByStandardProtocol:
case SendableCheck::ImpliedByPreconcurrencyProtocol:
return false;

case SendableCheck::Implicit:
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6681,7 +6681,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
if (!hasDeprecatedUnsafeSendable && SendableConformance) {
SendableCheck check = SendableCheck::Explicit;
if (sendableConformancePreconcurrency)
check = SendableCheck::ImpliedByStandardProtocol;
check = SendableCheck::ImpliedByPreconcurrencyProtocol;
else if (SendableConformance->getSourceKind() ==
ConformanceEntryKind::Synthesized)
check = SendableCheck::Implicit;
Expand Down
2 changes: 1 addition & 1 deletion test/Concurrency/concurrent_value_checking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ enum E2<T> {
extension E2: Sendable where T: Sendable { }

final class C1: Sendable {
let nc: NotConcurrent? = nil // expected-warning{{stored property 'nc' of 'Sendable'-conforming class 'C1' has non-Sendable type 'NotConcurrent?'}}
let nc: NotConcurrent? = nil // expected-warning{{stored property 'nc' of 'Sendable'-conforming class 'C1' contains non-Sendable type 'NotConcurrent'}}
var x: Int = 0 // expected-warning{{stored property 'x' of 'Sendable'-conforming class 'C1' is mutable}}
let i: Int = 0
}
Expand Down
20 changes: 17 additions & 3 deletions test/Concurrency/predates_concurrency_swift6.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@ func testCallsWithAsync() async {
@preconcurrency protocol P: Sendable { }
protocol Q: P { }

class NS { } // expected-note 3{{class 'NS' does not conform to the 'Sendable' protocol}}
class NS { } // expected-note 5{{class 'NS' does not conform to the 'Sendable' protocol}}

struct S1: P {
var ns: NS // expected-error{{stored property 'ns' of 'Sendable'-conforming struct 'S1' has non-Sendable type 'NS'}}
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'S1' has non-Sendable type 'NS'}}
}

struct S2: Q {
var ns: NS // expected-error{{stored property 'ns' of 'Sendable'-conforming struct 'S2' has non-Sendable type 'NS'}}
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'S2' has non-Sendable type 'NS'}}
}

struct S3: Q, Sendable {
Expand Down Expand Up @@ -309,3 +309,17 @@ do {
// If destination is @preconcurrency the Sendable conformance error should be downgraded
d = data // expected-warning {{type 'Any' does not conform to the 'Sendable' protocol}}
}

do {
final class Mutating: P {
var state: Int = 0 // expected-warning {{stored property 'state' of 'Sendable'-conforming class 'Mutating' is mutable}}
}

struct StructWithInit: P {
let prop = NS() // expected-warning {{stored property 'prop' of 'Sendable'-conforming struct 'StructWithInit' has non-Sendable type 'NS'}}
}

enum E : P {
case test(NS) // expected-warning {{associated value 'test' of 'Sendable'-conforming enum 'E' has non-Sendable type 'NS'}}
}
}
4 changes: 2 additions & 2 deletions test/Concurrency/sendable_metatype_typecheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ class Holder: @unchecked Sendable {
}

enum E: Sendable {
case q(Q.Type, Int) // expected-warning{{associated value 'q' of 'Sendable'-conforming enum 'E' has non-Sendable type 'any Q.Type'}}
case q(Q.Type, Int) // expected-warning{{associated value 'q' of 'Sendable'-conforming enum 'E' contains non-Sendable type 'any Q.Type'}}
}

struct S: Sendable {
var tuple: ([Q.Type], Int) // expected-warning{{stored property 'tuple' of 'Sendable'-conforming struct 'S' has non-Sendable type '([any Q.Type], Int)'}}
var tuple: ([Q.Type], Int) // expected-warning{{stored property 'tuple' of 'Sendable'-conforming struct 'S' contains non-Sendable type 'any Q.Type'}}
}

extension Q {
Expand Down
2 changes: 1 addition & 1 deletion test/type/explicit_existential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ protocol HasAssoc {

do {
enum MyError: Error {
case bad(Any) // expected-swift-6-error {{associated value 'bad' of 'Sendable'-conforming enum 'MyError' has non-Sendable type 'Any'}}
case bad(Any) // expected-swift-6-warning {{associated value 'bad' of 'Sendable'-conforming enum 'MyError' has non-Sendable type 'Any'}}
}

func checkIt(_ js: Any) throws {
Expand Down