Skip to content

Commit 3c3ca0a

Browse files
authored
Merge pull request #73763 from DougGregor/concurrency-diag-usability-6.0
[6.0] Collected usability improvements for concurrency-related diagnostics
2 parents 1d2a23b + c514ca2 commit 3c3ca0a

32 files changed

+293
-138
lines changed

include/swift/AST/ASTContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1238,7 +1238,8 @@ class ASTContext final {
12381238
DeclContext *dc,
12391239
ProtocolConformanceState state,
12401240
bool isUnchecked,
1241-
bool isPreconcurrency);
1241+
bool isPreconcurrency,
1242+
SourceLoc preconcurrencyLoc = SourceLoc());
12421243

12431244
/// Produce a self-conformance for the given protocol.
12441245
SelfProtocolConformance *

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,6 +2644,8 @@ WARNING(add_predates_concurrency_import,none,
26442644
"%select{| as warnings}0", (bool, Identifier))
26452645
WARNING(remove_predates_concurrency_import,none,
26462646
"'@preconcurrency' attribute on module %0 has no effect", (Identifier))
2647+
NOTE(add_preconcurrency_to_conformance,none,
2648+
"add '@preconcurrency' to the %0 conformance to defer isolation checking to run time", (DeclName))
26472649
WARNING(remove_public_import,none,
26482650
"public import of %0 was not used in public declarations or inlinable code",
26492651
(const ModuleDecl *))
@@ -5571,16 +5573,17 @@ ERROR(shared_mutable_state_access,none,
55715573
ERROR(shared_mutable_state_decl,none,
55725574
"%kind0 is not concurrency-safe because it is non-isolated global shared "
55735575
"mutable state", (const ValueDecl *))
5574-
NOTE(shared_mutable_state_decl_note,none,
5575-
"isolate %0 to a global actor, or convert it to a 'let' constant and "
5576-
"conform it to 'Sendable'", (const ValueDecl *))
55775576
ERROR(shared_immutable_state_decl,none,
55785577
"%kind1 is not concurrency-safe because non-'Sendable' type %0 may have "
55795578
"shared mutable state",
55805579
(Type, const ValueDecl *))
5581-
NOTE(shared_immutable_state_decl_note,none,
5582-
"isolate %0 to a global actor, or conform %1 to 'Sendable'",
5583-
(const ValueDecl *, Type))
5580+
NOTE(shared_state_make_immutable,none,
5581+
"convert %0 to a 'let' constant to make 'Sendable' shared state immutable",
5582+
(const ValueDecl *))
5583+
NOTE(shared_state_main_actor_node,none,
5584+
"annotate %0 with '@MainActor' if property should only be accessed from the main actor", (const ValueDecl *))
5585+
NOTE(shared_state_nonisolated_unsafe,none,
5586+
"disable concurrency-safety checks if accesses are protected by an external synchronization mechanism", (const ValueDecl *))
55845587
ERROR(actor_isolated_witness,none,
55855588
"%select{|distributed }0%1 %kind2 cannot be used to satisfy %3 protocol "
55865589
"requirement",

include/swift/AST/ProtocolConformance.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
530530
/// The location of this protocol conformance in the source.
531531
SourceLoc Loc;
532532

533+
/// The location of the protocol name within the conformance.
534+
SourceLoc ProtocolNameLoc;
535+
536+
/// The location of the `@preconcurrency` attribute, if any.
537+
SourceLoc PreconcurrencyLoc;
538+
533539
/// The declaration context containing the ExtensionDecl or
534540
/// NominalTypeDecl that declared the conformance.
535541
DeclContext *Context;
@@ -562,12 +568,17 @@ class NormalProtocolConformance : public RootProtocolConformance,
562568
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
563569
SourceLoc loc, DeclContext *dc,
564570
ProtocolConformanceState state, bool isUnchecked,
565-
bool isPreconcurrency)
571+
bool isPreconcurrency,
572+
SourceLoc preconcurrencyLoc)
566573
: RootProtocolConformance(ProtocolConformanceKind::Normal,
567574
conformingType),
568-
Protocol(protocol), Loc(loc), Context(dc) {
575+
Protocol(protocol), Loc(extractNearestSourceLoc(dc)),
576+
ProtocolNameLoc(loc), PreconcurrencyLoc(preconcurrencyLoc),
577+
Context(dc) {
569578
assert(!conformingType->hasArchetype() &&
570579
"ProtocolConformances should store interface types");
580+
assert((preconcurrencyLoc.isInvalid() || isPreconcurrency) &&
581+
"Cannot have a @preconcurrency location without isPreconcurrency");
571582
setState(state);
572583
Bits.NormalProtocolConformance.IsInvalid = false;
573584
Bits.NormalProtocolConformance.IsUnchecked = isUnchecked;
@@ -580,9 +591,12 @@ class NormalProtocolConformance : public RootProtocolConformance,
580591
/// Get the protocol being conformed to.
581592
ProtocolDecl *getProtocol() const { return Protocol; }
582593

583-
/// Retrieve the location of this
594+
/// Retrieve the location of this conformance.
584595
SourceLoc getLoc() const { return Loc; }
585596

597+
/// Retrieve the name of the protocol location.
598+
SourceLoc getProtocolNameLoc() const { return ProtocolNameLoc; }
599+
586600
/// Get the declaration context that contains the conforming extension or
587601
/// nominal type declaration.
588602
DeclContext *getDeclContext() const { return Context; }
@@ -629,6 +643,10 @@ class NormalProtocolConformance : public RootProtocolConformance,
629643
return Bits.NormalProtocolConformance.IsPreconcurrency;
630644
}
631645

646+
/// Retrieve the location of `@preconcurrency`, if there is one and it is
647+
/// known.
648+
SourceLoc getPreconcurrencyLoc() const { return PreconcurrencyLoc; }
649+
632650
/// Determine whether we've lazily computed the associated conformance array
633651
/// already.
634652
bool hasComputedAssociatedConformances() const {

include/swift/Sema/Concurrency.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
#ifndef SWIFT_SEMA_CONCURRENCY_H
2121
#define SWIFT_SEMA_CONCURRENCY_H
2222

23+
#include <optional>
24+
2325
namespace swift {
2426

2527
class DeclContext;
2628
class SourceFile;
2729
class NominalTypeDecl;
2830
class VarDecl;
2931

32+
enum class DiagnosticBehavior: uint8_t;
33+
3034
/// If any of the imports in this source file was @preconcurrency but there were
3135
/// no diagnostics downgraded or suppressed due to that @preconcurrency, suggest
3236
/// that the attribute be removed.
@@ -44,6 +48,13 @@ bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
4448
bool diagnoseNonSendableFromDeinit(
4549
SourceLoc refLoc, VarDecl *var, DeclContext *dc);
4650

51+
/// Determinate the appropriate diagnostic behavior when referencing
52+
/// the given nominal type from the given declaration context.
53+
std::optional<DiagnosticBehavior>
54+
getConcurrencyDiagnosticBehaviorLimit(NominalTypeDecl *nominal,
55+
const DeclContext *fromDC,
56+
bool ignoreExplicitConformance = false);
57+
4758
} // namespace swift
4859

4960
#endif

lib/AST/ASTContext.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,7 +2592,8 @@ ASTContext::getNormalConformance(Type conformingType,
25922592
DeclContext *dc,
25932593
ProtocolConformanceState state,
25942594
bool isUnchecked,
2595-
bool isPreconcurrency) {
2595+
bool isPreconcurrency,
2596+
SourceLoc preconcurrencyLoc) {
25962597
assert(dc->isTypeContext());
25972598

25982599
llvm::FoldingSetNodeID id;
@@ -2608,7 +2609,7 @@ ASTContext::getNormalConformance(Type conformingType,
26082609
// Build a new normal protocol conformance.
26092610
auto result = new (*this, AllocationArena::Permanent)
26102611
NormalProtocolConformance(conformingType, protocol, loc, dc, state,
2611-
isUnchecked, isPreconcurrency);
2612+
isUnchecked, isPreconcurrency, preconcurrencyLoc);
26122613
normalConformances.InsertNode(result, insertPos);
26132614

26142615
return result;

lib/AST/ConformanceLookupTable.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -969,10 +969,11 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
969969
assert(!isa<ProtocolDecl>(conformingDC->getSelfNominalTypeDecl()));
970970
Type conformingType = conformingDC->getSelfInterfaceType();
971971

972-
SourceLoc conformanceLoc
973-
= conformingNominal == conformingDC
974-
? conformingNominal->getLoc()
975-
: cast<ExtensionDecl>(conformingDC)->getLoc();
972+
SourceLoc conformanceLoc =
973+
entry->getLoc().isValid() ? entry->getLoc()
974+
: (conformingNominal == conformingDC
975+
? conformingNominal->getLoc()
976+
: cast<ExtensionDecl>(conformingDC)->getLoc());
976977

977978
NormalProtocolConformance *implyingConf = nullptr;
978979
if (entry->Source.getKind() == ConformanceEntryKind::Implied) {
@@ -989,7 +990,8 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
989990
conformingType, protocol, conformanceLoc, conformingDC,
990991
ProtocolConformanceState::Incomplete,
991992
entry->Source.getUncheckedLoc().isValid(),
992-
entry->Source.getPreconcurrencyLoc().isValid());
993+
entry->Source.getPreconcurrencyLoc().isValid(),
994+
entry->Source.getPreconcurrencyLoc());
993995
// Invalid code may cause the getConformance call below to loop, so break
994996
// the infinite recursion by setting this eagerly to shortcircuit with the
995997
// early return at the start of this function.

lib/AST/TypeRepr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ SourceLoc TypeRepr::findAttrLoc(TypeAttrKind kind) const {
147147
for (auto attr : attrTypeRepr->getAttrs()) {
148148
if (auto typeAttr = attr.dyn_cast<TypeAttribute*>())
149149
if (typeAttr->getKind() == kind)
150-
return typeAttr->getAttrLoc();
150+
return typeAttr->getStartLoc();
151151
}
152152

153153
typeRepr = attrTypeRepr->getTypeRepr();

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,7 @@ getDiagnosticBehaviorLimitForValue(SILValue value) {
6969
return {};
7070

7171
auto *fromDC = declRef.getInnermostDeclContext();
72-
auto attributedImport = nom->findImport(fromDC);
73-
if (!attributedImport ||
74-
!attributedImport->options.contains(ImportFlags::Preconcurrency))
75-
return {};
76-
77-
if (auto *sourceFile = fromDC->getParentSourceFile())
78-
sourceFile->setImportUsedPreconcurrency(*attributedImport);
79-
80-
if (hasExplicitSendableConformance(nom))
81-
return DiagnosticBehavior::Warning;
82-
83-
return attributedImport->module.importedModule->isConcurrencyChecked()
84-
? DiagnosticBehavior::Warning
85-
: DiagnosticBehavior::Ignore;
72+
return getConcurrencyDiagnosticBehaviorLimit(nom, fromDC);
8673
}
8774

8875
static std::optional<SILDeclRef> getDeclRefForCallee(SILInstruction *inst) {

lib/Sema/MiscDiagnostics.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3374,6 +3374,28 @@ class ReturnTypePlaceholderReplacer : public ASTWalker {
33743374

33753375
} // end anonymous namespace
33763376

3377+
SourceLoc swift::getFixItLocForVarToLet(VarDecl *var) {
3378+
// Try to find the location of the 'var' so we can produce a fixit. If
3379+
// this is a simple PatternBinding, use its location.
3380+
if (auto *PBD = var->getParentPatternBinding()) {
3381+
if (PBD->getSingleVar() == var)
3382+
return PBD->getLoc();
3383+
} else if (auto *pattern = var->getParentPattern()) {
3384+
BindingPattern *foundVP = nullptr;
3385+
pattern->forEachNode([&](Pattern *P) {
3386+
if (auto *VP = dyn_cast<BindingPattern>(P))
3387+
if (VP->getSingleVar() == var)
3388+
foundVP = VP;
3389+
});
3390+
3391+
if (foundVP && foundVP->getIntroducer() != VarDecl::Introducer::Let) {
3392+
return foundVP->getLoc();
3393+
}
3394+
}
3395+
3396+
return SourceLoc();
3397+
}
3398+
33773399
// After we have scanned the entire region, diagnose variables that could be
33783400
// declared with a narrower usage kind.
33793401
VarDeclUsageChecker::~VarDeclUsageChecker() {
@@ -3593,25 +3615,7 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
35933615
// Don't warn if we have something like "let (x,y) = ..." and 'y' was
35943616
// never mutated, but 'x' was.
35953617
!isVarDeclPartOfPBDThatHadSomeMutation(var)) {
3596-
SourceLoc FixItLoc;
3597-
3598-
// Try to find the location of the 'var' so we can produce a fixit. If
3599-
// this is a simple PatternBinding, use its location.
3600-
if (auto *PBD = var->getParentPatternBinding()) {
3601-
if (PBD->getSingleVar() == var)
3602-
FixItLoc = PBD->getLoc();
3603-
} else if (auto *pattern = var->getParentPattern()) {
3604-
BindingPattern *foundVP = nullptr;
3605-
pattern->forEachNode([&](Pattern *P) {
3606-
if (auto *VP = dyn_cast<BindingPattern>(P))
3607-
if (VP->getSingleVar() == var)
3608-
foundVP = VP;
3609-
});
3610-
3611-
if (foundVP && foundVP->getIntroducer() != VarDecl::Introducer::Let) {
3612-
FixItLoc = foundVP->getLoc();
3613-
}
3614-
}
3618+
SourceLoc FixItLoc = getFixItLocForVarToLet(var);
36153619

36163620
// If this is a parameter explicitly marked 'var', remove it.
36173621
if (FixItLoc.isInvalid()) {

lib/Sema/MiscDiagnostics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ namespace swift {
7070
AccessLevel desiredAccess, bool isForSetter = false,
7171
bool shouldUseDefaultAccess = false);
7272

73+
/// Compute the location of the 'var' keyword for a 'var'-to-'let' Fix-It.
74+
SourceLoc getFixItLocForVarToLet(VarDecl *var);
75+
7376
/// Describes the context of a parameter, for use in diagnosing argument
7477
/// label problems.
7578
enum class ParameterContext : unsigned {

0 commit comments

Comments
 (0)