Skip to content

Commit 85cced7

Browse files
committed
[Concurrency] Diagnose concurrency violations in derived conformances at valid
source locations.
1 parent df858a5 commit 85cced7

File tree

4 files changed

+92
-27
lines changed

4 files changed

+92
-27
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5559,6 +5559,9 @@ ERROR(isolated_parameter_combined_nonisolated,none,
55595559
"%0 with 'isolated' parameter cannot be 'nonisolated'",
55605560
(DescriptiveDeclKind))
55615561

5562+
NOTE(in_derived_conformance, none,
5563+
"in derived conformance to %0",
5564+
(Type))
55625565
WARNING(non_sendable_param_type,none,
55635566
"non-sendable type %0 %select{passed in call to %3 %kind2|"
55645567
"exiting %3 context in call to non-isolated %kind2|"

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,11 +1002,13 @@ void swift::diagnoseUnnecessaryPreconcurrencyImports(SourceFile &sf) {
10021002
/// Produce a diagnostic for a single instance of a non-Sendable type where
10031003
/// a Sendable type is required.
10041004
static bool diagnoseSingleNonSendableType(
1005-
Type type, SendableCheckContext fromContext, SourceLoc loc,
1005+
Type type, SendableCheckContext fromContext,
1006+
Type inDerivedConformance, SourceLoc loc,
10061007
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose) {
10071008

10081009
auto module = fromContext.fromDC->getParentModule();
10091010
auto nominal = type->getAnyNominal();
1011+
auto &ctx = module->getASTContext();
10101012

10111013
return diagnoseSendabilityErrorBasedOn(nominal, fromContext,
10121014
[&](DiagnosticBehavior behavior) {
@@ -1017,9 +1019,13 @@ static bool diagnoseSingleNonSendableType(
10171019
if (wasSuppressed || behavior == DiagnosticBehavior::Ignore)
10181020
return true;
10191021

1022+
if (inDerivedConformance) {
1023+
ctx.Diags.diagnose(loc, diag::in_derived_conformance,
1024+
inDerivedConformance);
1025+
}
1026+
10201027
if (type->is<FunctionType>()) {
1021-
module->getASTContext().Diags
1022-
.diagnose(loc, diag::nonsendable_function_type);
1028+
ctx.Diags.diagnose(loc, diag::nonsendable_function_type);
10231029
} else if (nominal && nominal->getParentModule() == module) {
10241030
// If the nominal type is in the current module, suggest adding
10251031
// `Sendable` if it might make sense. Otherwise, just complain.
@@ -1052,7 +1058,8 @@ static bool diagnoseSingleNonSendableType(
10521058
}
10531059

10541060
bool swift::diagnoseNonSendableTypes(
1055-
Type type, SendableCheckContext fromContext, SourceLoc loc,
1061+
Type type, SendableCheckContext fromContext,
1062+
Type inDerivedConformance, SourceLoc loc,
10561063
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose) {
10571064
auto module = fromContext.fromDC->getParentModule();
10581065

@@ -1064,15 +1071,17 @@ bool swift::diagnoseNonSendableTypes(
10641071
// FIXME: More detail for unavailable conformances.
10651072
auto conformance = TypeChecker::conformsToProtocol(type, proto, module);
10661073
if (conformance.isInvalid() || conformance.hasUnavailableConformance()) {
1067-
return diagnoseSingleNonSendableType(type, fromContext, loc, diagnose);
1074+
return diagnoseSingleNonSendableType(
1075+
type, fromContext, inDerivedConformance, loc, diagnose);
10681076
}
10691077

10701078
// Walk the conformance, diagnosing any missing Sendable conformances.
10711079
bool anyMissing = false;
10721080
conformance.forEachMissingConformance(module,
10731081
[&](BuiltinProtocolConformance *missing) {
10741082
if (diagnoseSingleNonSendableType(
1075-
missing->getType(), fromContext, loc, diagnose)) {
1083+
missing->getType(), fromContext,
1084+
inDerivedConformance, loc, diagnose)) {
10761085
anyMissing = true;
10771086
}
10781087

@@ -1095,11 +1104,27 @@ bool swift::diagnoseNonSendableTypesInReference(
10951104
return swift::getActorIsolation(declRef.getDecl());
10961105
};
10971106

1107+
// If the violation is in the implementation of a derived conformance,
1108+
// point to the location of the parent type instead.
1109+
Type derivedConformanceType;
1110+
if (refLoc.isInvalid()) {
1111+
auto *decl = fromDC->getAsDecl();
1112+
if (decl && decl->isImplicit()) {
1113+
if (auto *implements = decl->getAttrs().getAttribute<ImplementsAttr>()) {
1114+
auto *parentDC = decl->getDeclContext();
1115+
refLoc = parentDC->getAsDecl()->getLoc();
1116+
derivedConformanceType =
1117+
implements->getProtocol(parentDC)->getDeclaredInterfaceType();
1118+
}
1119+
}
1120+
}
1121+
10981122
// Check the 'self' argument.
10991123
if (base) {
11001124
if (diagnoseNonSendableTypes(
11011125
base->getType(),
1102-
fromDC, base->getStartLoc(),
1126+
fromDC, derivedConformanceType,
1127+
base->getStartLoc(),
11031128
diag::non_sendable_param_type,
11041129
(unsigned)refKind, declRef.getDecl(),
11051130
getActorIsolation()))
@@ -1114,7 +1139,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11141139
for (auto param : *function->getParameters()) {
11151140
Type paramType = param->getInterfaceType().subst(subs);
11161141
if (diagnoseNonSendableTypes(
1117-
paramType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1142+
paramType, fromDC, derivedConformanceType,
1143+
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
11181144
diag::non_sendable_param_type,
11191145
(unsigned)refKind, function, getActorIsolation()))
11201146
return true;
@@ -1127,7 +1153,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11271153
// only check results if funcCheckKind specifies so
11281154
Type resultType = func->getResultInterfaceType().subst(subs);
11291155
if (diagnoseNonSendableTypes(
1130-
resultType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1156+
resultType, fromDC, derivedConformanceType,
1157+
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
11311158
diag::non_sendable_result_type,
11321159
(unsigned)refKind, func, getActorIsolation()))
11331160
return true;
@@ -1142,7 +1169,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11421169
? var->getTypeInContext()
11431170
: var->getValueInterfaceType().subst(subs);
11441171
if (diagnoseNonSendableTypes(
1145-
propertyType, fromDC, refLoc,
1172+
propertyType, fromDC,
1173+
derivedConformanceType, refLoc,
11461174
diag::non_sendable_property_type,
11471175
var,
11481176
var->isLocalCapture(),
@@ -1157,7 +1185,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11571185
// Check params of this subscript override for sendability
11581186
Type paramType = param->getInterfaceType().subst(subs);
11591187
if (diagnoseNonSendableTypes(
1160-
paramType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1188+
paramType, fromDC, derivedConformanceType,
1189+
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
11611190
diag::non_sendable_param_type,
11621191
(unsigned)refKind, subscript, getActorIsolation()))
11631192
return true;
@@ -1168,7 +1197,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11681197
// Check the element type of a subscript.
11691198
Type resultType = subscript->getElementInterfaceType().subst(subs);
11701199
if (diagnoseNonSendableTypes(
1171-
resultType, fromDC, refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
1200+
resultType, fromDC, derivedConformanceType,
1201+
refLoc, diagnoseLoc.isInvalid() ? refLoc : diagnoseLoc,
11721202
diag::non_sendable_result_type,
11731203
(unsigned)refKind, subscript, getActorIsolation()))
11741204
return true;
@@ -1183,7 +1213,8 @@ bool swift::diagnoseNonSendableTypesInReference(
11831213
void swift::diagnoseMissingSendableConformance(
11841214
SourceLoc loc, Type type, const DeclContext *fromDC) {
11851215
diagnoseNonSendableTypes(
1186-
type, fromDC, loc, diag::non_sendable_type);
1216+
type, fromDC, /*inDerivedConformance*/Type(),
1217+
loc, diag::non_sendable_type);
11871218
}
11881219

11891220
namespace {
@@ -1935,7 +1966,8 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19351966
auto *base = selfApply->getBase();
19361967
if (diagnoseNonSendableTypes(
19371968
base->getType(),
1938-
declContext, base->getStartLoc(),
1969+
declContext, /*inDerivedConformance*/Type(),
1970+
base->getStartLoc(),
19391971
diag::non_sendable_call_argument,
19401972
isolationCrossing.value().exitsIsolation(),
19411973
isolationCrossing.value().getDiagnoseIsolation()))
@@ -1976,7 +2008,8 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19762008

19772009
if (diagnoseNonSendableTypes(
19782010
argType ? argType : param.getParameterType(),
1979-
declContext, argLoc, diag::non_sendable_call_argument,
2011+
declContext, /*inDerivedConformance*/Type(),
2012+
argLoc, diag::non_sendable_call_argument,
19802013
isolationCrossing.value().exitsIsolation(),
19812014
isolationCrossing.value().getDiagnoseIsolation()))
19822015
return true;
@@ -2418,19 +2451,24 @@ namespace {
24182451
// into async let to SIL level region based isolation.
24192452
if (!ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation)) {
24202453
diagnoseNonSendableTypes(
2421-
type, getDeclContext(), capture.getLoc(),
2454+
type, getDeclContext(),
2455+
/*inDerivedConformance*/Type(), capture.getLoc(),
24222456
diag::implicit_async_let_non_sendable_capture,
24232457
decl->getName());
24242458
}
24252459
} else {
24262460
// Fallback to a generic implicit capture missing sendable
24272461
// conformance diagnostic.
2428-
diagnoseNonSendableTypes(type, getDeclContext(), capture.getLoc(),
2462+
diagnoseNonSendableTypes(type, getDeclContext(),
2463+
/*inDerivedConformance*/Type(),
2464+
capture.getLoc(),
24292465
diag::implicit_non_sendable_capture,
24302466
decl->getName());
24312467
}
24322468
} else {
2433-
diagnoseNonSendableTypes(type, getDeclContext(), capture.getLoc(),
2469+
diagnoseNonSendableTypes(type, getDeclContext(),
2470+
/*inDerivedConformance*/Type(),
2471+
capture.getLoc(),
24342472
diag::non_sendable_capture, decl->getName(),
24352473
/*closure=*/closure != nullptr);
24362474
}
@@ -3341,7 +3379,9 @@ namespace {
33413379

33423380
// Check for sendability of the result type.
33433381
if (diagnoseNonSendableTypes(
3344-
fnType->getResult(), getDeclContext(), apply->getLoc(),
3382+
fnType->getResult(), getDeclContext(),
3383+
/*inDerivedConformance*/Type(),
3384+
apply->getLoc(),
33453385
diag::non_sendable_call_result_type,
33463386
apply->isImplicitlyAsync().has_value(),
33473387
*unsatisfiedIsolation))
@@ -3483,6 +3523,7 @@ namespace {
34833523
llvm::None)) {
34843524
if (diagnoseNonSendableTypes(
34853525
component.getComponentType(), getDeclContext(),
3526+
/*inDerivedConformance*/Type(),
34863527
component.getLoc(),
34873528
diag::non_sendable_keypath_access)) {
34883529
diagnosed = true;
@@ -3516,6 +3557,7 @@ namespace {
35163557
auto type = getType(arg.getExpr());
35173558
if (type && shouldDiagnoseExistingDataRaces(getDeclContext()) &&
35183559
diagnoseNonSendableTypes(type, getDeclContext(),
3560+
/*inDerivedConformance*/Type(),
35193561
component.getLoc(),
35203562
diag::non_sendable_keypath_capture))
35213563
diagnosed = true;
@@ -5163,7 +5205,8 @@ static bool checkSendableInstanceStorage(
51635205

51645206
// Check that the property type is Sendable.
51655207
diagnoseNonSendableTypes(
5166-
propertyType, SendableCheckContext(dc, check), property->getLoc(),
5208+
propertyType, SendableCheckContext(dc, check),
5209+
/*inDerivedConformance*/Type(), property->getLoc(),
51675210
[&](Type type, DiagnosticBehavior behavior) {
51685211
if (isImplicitSendableCheck(check)) {
51695212
// If this is for an externally-visible conformance, fail.
@@ -5199,7 +5242,8 @@ static bool checkSendableInstanceStorage(
51995242
/// Handle an enum associated value.
52005243
bool operator()(EnumElementDecl *element, Type elementType) override {
52015244
diagnoseNonSendableTypes(
5202-
elementType, SendableCheckContext(dc, check), element->getLoc(),
5245+
elementType, SendableCheckContext(dc, check),
5246+
/*inDerivedConformance*/Type(), element->getLoc(),
52035247
[&](Type type, DiagnosticBehavior behavior) {
52045248
if (isImplicitSendableCheck(check)) {
52055249
// If this is for an externally-visible conformance, fail.

lib/Sema/TypeCheckConcurrency.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ struct SendableCheckContext {
393393
/// \returns \c true if any errors were produced, \c false if no diagnostics or
394394
/// only warnings and notes were produced.
395395
bool diagnoseNonSendableTypes(
396-
Type type, SendableCheckContext fromContext, SourceLoc loc,
396+
Type type, SendableCheckContext fromContext,
397+
Type inDerivedConformance, SourceLoc loc,
397398
llvm::function_ref<bool(Type, DiagnosticBehavior)> diagnose);
398399

399400
namespace detail {
@@ -416,14 +417,15 @@ namespace detail {
416417
template<typename ...DiagArgs>
417418
bool diagnoseNonSendableTypes(
418419
Type type, SendableCheckContext fromContext,
420+
Type derivedConformance,
419421
SourceLoc typeLoc, SourceLoc diagnoseLoc,
420422
Diag<Type, DiagArgs...> diag,
421423
typename detail::Identity<DiagArgs>::type ...diagArgs) {
422424

423425
ASTContext &ctx = fromContext.fromDC->getASTContext();
424426
return diagnoseNonSendableTypes(
425-
type, fromContext, typeLoc, [&](Type specificType,
426-
DiagnosticBehavior behavior) {
427+
type, fromContext, derivedConformance, typeLoc,
428+
[&](Type specificType, DiagnosticBehavior behavior) {
427429

428430
if (behavior != DiagnosticBehavior::Ignore) {
429431
ctx.Diags.diagnose(diagnoseLoc, diag, type, diagArgs...)
@@ -441,12 +443,14 @@ bool diagnoseNonSendableTypes(
441443
/// only warnings and notes were produced.
442444
template<typename ...DiagArgs>
443445
bool diagnoseNonSendableTypes(
444-
Type type, SendableCheckContext fromContext, SourceLoc loc,
446+
Type type, SendableCheckContext fromContext,
447+
Type derivedConformance, SourceLoc loc,
445448
Diag<Type, DiagArgs...> diag,
446449
typename detail::Identity<DiagArgs>::type ...diagArgs) {
447450

448-
return diagnoseNonSendableTypes(type, fromContext, loc, loc, diag,
449-
std::forward<decltype(diagArgs)>(diagArgs)...);
451+
return diagnoseNonSendableTypes(
452+
type, fromContext, derivedConformance, loc, loc, diag,
453+
std::forward<decltype(diagArgs)>(diagArgs)...);
450454
}
451455

452456
/// Diagnose this sendability error with behavior based on the import of

test/Concurrency/sendable_checking.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,17 @@ func testPointersAreNotSendable() {
344344
testSendable(rawMutableBuffer.makeIterator()) // expected-warning {{conformance of 'UnsafeRawBufferPointer.Iterator' to 'Sendable' is unavailable}}
345345
}
346346
}
347+
348+
@available(*, unavailable)
349+
extension SynthesizedConformances.NotSendable: Sendable {}
350+
351+
enum SynthesizedConformances {
352+
// expected-note@+1 2 {{consider making struct 'NotSendable' conform to the 'Sendable' protocol}}
353+
struct NotSendable: Equatable {}
354+
355+
// expected-warning@+2 2{{non-sendable type 'SynthesizedConformances.NotSendable' in asynchronous access to main actor-isolated property 'x' cannot cross actor boundary}}
356+
// expected-note@+1 2 {{in derived conformance to 'Equatable'}}
357+
@MainActor struct Isolated: Equatable {
358+
let x: NotSendable
359+
}
360+
}

0 commit comments

Comments
 (0)