Skip to content

Commit 0ef32ac

Browse files
committed
Diagnose references to nonisolated(unsafe) declarations in strict-concurrency code
A nonisolated(unsafe) declaration clearly indicates that the declaration itself is unsafe, so it doesn't need to be diagnosted. Instead, diagnose any reference to such a declaration that occurs when strict concurrency is enabled. Make this a collatable unsafe use.
1 parent 056441c commit 0ef32ac

File tree

7 files changed

+66
-35
lines changed

7 files changed

+66
-35
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8084,6 +8084,9 @@ NOTE(note_reference_to_unsafe_decl,none,
80848084
NOTE(note_reference_to_unsafe_typed_decl,none,
80858085
"%select{reference|call}0 to %kind1 involves unsafe type %2",
80868086
(bool, const ValueDecl *, Type))
8087+
NOTE(note_reference_to_nonisolated_unsafe,none,
8088+
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
8089+
(const ValueDecl *))
80878090

80888091
GROUPED_WARNING(override_safe_withunsafe,Unsafe,none,
80898092
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
@@ -8097,8 +8100,9 @@ GROUPED_WARNING(unchecked_conformance_is_unsafe,Unsafe,none,
80978100
"@unchecked conformance involves unsafe code", ())
80988101
GROUPED_WARNING(unowned_unsafe_is_unsafe,Unsafe,none,
80998102
"unowned(unsafe) involves unsafe code", ())
8100-
GROUPED_WARNING(nonisolated_unsafe_is_unsafe,Unsafe,none,
8101-
"nonisolated(unsafe) involves unsafe code", ())
8103+
GROUPED_WARNING(reference_to_nonisolated_unsafe,Unsafe,none,
8104+
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
8105+
(const ValueDecl *))
81028106
GROUPED_WARNING(reference_to_unsafe_decl,Unsafe,none,
81038107
"%select{reference|call}0 to unsafe %kindbase1",
81048108
(bool, const ValueDecl *))

lib/Sema/TypeCheckAttr.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7201,15 +7201,6 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
72017201
// that do not have storage.
72027202
auto dc = D->getDeclContext();
72037203

7204-
// nonisolated(unsafe) is unsafe, but only under strict concurrency.
7205-
if (attr->isUnsafe() &&
7206-
Ctx.LangOpts.hasFeature(Feature::WarnUnsafe) &&
7207-
Ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete &&
7208-
!D->allowsUnsafe()) {
7209-
diagnoseUnsafeUse(
7210-
UnsafeUse::forNonisolatedUnsafe(D, attr->getLocation(), dc));
7211-
}
7212-
72137204
if (auto var = dyn_cast<VarDecl>(D)) {
72147205
// stored properties have limitations as to when they can be nonisolated.
72157206
auto type = var->getTypeInContext();

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4184,6 +4184,20 @@ diagnoseDeclAsyncAvailability(const ValueDecl *D, SourceRange R,
41844184
return true;
41854185
}
41864186

4187+
/// Determine whether a reference to the given variable is treated as
4188+
/// nonisolated(unsafe).
4189+
static bool isReferenceToNonisolatedUnsafe(
4190+
ValueDecl *decl,
4191+
DeclContext *fromDC
4192+
) {
4193+
auto isolation = getActorIsolationForReference(decl, fromDC);
4194+
if (!isolation.isNonisolated())
4195+
return false;
4196+
4197+
auto attr = decl->getAttrs().getAttribute<NonisolatedAttr>();
4198+
return attr && attr->isUnsafe();
4199+
}
4200+
41874201
/// Diagnose uses of unsafe declarations.
41884202
static void
41894203
diagnoseDeclUnsafe(const ValueDecl *D, SourceRange R,
@@ -4192,16 +4206,29 @@ diagnoseDeclUnsafe(const ValueDecl *D, SourceRange R,
41924206
if (!ctx.LangOpts.hasFeature(Feature::WarnUnsafe))
41934207
return;
41944208

4195-
if (!D->isUnsafe())
4196-
return;
4197-
41984209
if (Where.getAvailability().allowsUnsafe())
41994210
return;
42004211

42014212
SourceLoc diagLoc = call ? call->getLoc() : R.Start;
4202-
diagnoseUnsafeUse(
4203-
UnsafeUse::forReferenceToUnsafe(
4204-
D, call != nullptr, Where.getDeclContext(), Type(), diagLoc));
4213+
if (D->isUnsafe()) {
4214+
diagnoseUnsafeUse(
4215+
UnsafeUse::forReferenceToUnsafe(
4216+
D, call != nullptr, Where.getDeclContext(), Type(), diagLoc));
4217+
return;
4218+
}
4219+
4220+
if (auto valueDecl = dyn_cast<ValueDecl>(D)) {
4221+
// Use of a nonisolated(unsafe) declaration is unsafe, but is only
4222+
// diagnosed as such under strict concurrency.
4223+
if (ctx.LangOpts.StrictConcurrencyLevel == StrictConcurrency::Complete &&
4224+
isReferenceToNonisolatedUnsafe(const_cast<ValueDecl *>(valueDecl),
4225+
Where.getDeclContext())) {
4226+
diagnoseUnsafeUse(
4227+
UnsafeUse::forNonisolatedUnsafe(
4228+
valueDecl, R.Start, Where.getDeclContext()));
4229+
return;
4230+
}
4231+
}
42054232
}
42064233

42074234
/// Diagnose uses of unavailable declarations. Returns true if a diagnostic

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,9 +1645,6 @@ ReferencedActor ReferencedActor::forGlobalActor(VarDecl *actor,
16451645
return ReferencedActor(actor, isPotentiallyIsolated, kind, globalActor);
16461646
}
16471647

1648-
static ActorIsolation getActorIsolationForReference(
1649-
ValueDecl *decl, const DeclContext *fromDC);
1650-
16511648
bool ReferencedActor::isKnownToBeLocal() const {
16521649
switch (kind) {
16531650
case GlobalActor:
@@ -7161,7 +7158,7 @@ bool swift::isPotentiallyIsolatedActor(
71617158

71627159
/// Determine the actor isolation used when we are referencing the given
71637160
/// declaration.
7164-
static ActorIsolation getActorIsolationForReference(ValueDecl *decl,
7161+
ActorIsolation swift::getActorIsolationForReference(ValueDecl *decl,
71657162
const DeclContext *fromDC) {
71667163
auto declIsolation = getActorIsolation(decl);
71677164

lib/Sema/TypeCheckConcurrency.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,11 @@ checkGlobalActorAttributes(SourceLoc loc, DeclContext *dc,
564564
/// Get the explicit global actor specified for a closure.
565565
Type getExplicitGlobalActor(ClosureExpr *closure);
566566

567+
/// Determine the actor isolation used when we are referencing the given
568+
/// declaration.
569+
ActorIsolation getActorIsolationForReference(ValueDecl *decl,
570+
const DeclContext *fromDC);
571+
567572
/// Adjust the type of the variable for concurrency.
568573
Type adjustVarTypeForConcurrency(
569574
Type type, VarDecl *var, DeclContext *dc,

lib/Sema/TypeCheckUnsafe.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ static bool isUnsafeUseInDefinition(const UnsafeUse &use) {
4040
case UnsafeUse::TypeWitness:
4141
case UnsafeUse::UnsafeConformance:
4242
case UnsafeUse::UnownedUnsafe:
43-
case UnsafeUse::NonisolatedUnsafe:
4443
// Never part of the definition. These are always part of the interface.
4544
return false;
4645

4746
case UnsafeUse::ReferenceToUnsafe:
4847
case UnsafeUse::CallToUnsafe:
48+
case UnsafeUse::NonisolatedUnsafe:
4949
return enclosingContextForUnsafe(use).second;
5050
}
5151
}
@@ -99,7 +99,8 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use, bool asNote) {
9999
// It will be diagnosed later, along with all other unsafe uses within this
100100
// same declaration.
101101
if (use.getKind() == UnsafeUse::ReferenceToUnsafe ||
102-
use.getKind() == UnsafeUse::CallToUnsafe) {
102+
use.getKind() == UnsafeUse::CallToUnsafe ||
103+
use.getKind() == UnsafeUse::NonisolatedUnsafe) {
103104
auto [enclosingDecl, _] = enclosingContextForUnsafe(
104105
use.getLocation(), use.getDeclContext());
105106
if (enclosingDecl) {
@@ -172,10 +173,16 @@ void swift::diagnoseUnsafeUse(const UnsafeUse &use, bool asNote) {
172173
case UnsafeUse::NonisolatedUnsafe: {
173174
auto decl = use.getDecl();
174175
ASTContext &ctx = decl->getASTContext();
175-
ctx.Diags.diagnose(use.getLocation(), diag::nonisolated_unsafe_is_unsafe);
176-
if (auto var = dyn_cast<VarDecl>(decl)) {
177-
var->diagnose(diag::make_enclosing_context_unsafe, var)
178-
.fixItInsert(var->getAttributeInsertionLoc(false), "@unsafe ");
176+
ctx.Diags.diagnose(
177+
use.getLocation(),
178+
asNote ? diag::note_reference_to_nonisolated_unsafe
179+
: diag::note_reference_to_nonisolated_unsafe,
180+
cast<ValueDecl>(decl));
181+
if (!asNote) {
182+
if (auto var = dyn_cast<VarDecl>(decl)) {
183+
var->diagnose(diag::make_enclosing_context_unsafe, var)
184+
.fixItInsert(var->getAttributeInsertionLoc(false), "@unsafe ");
185+
}
179186
}
180187
return;
181188
}

test/Unsafe/unsafe_concurrency.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -enable-experimental-feature AllowUnsafeAttribute -enable-experimental-feature WarnUnsafe -enable-experimental-feature StrictConcurrency
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-feature WarnUnsafe -enable-experimental-feature StrictConcurrency
22

33
// REQUIRES: concurrency
44
// REQUIRES: swift_feature_AllowUnsafeAttribute
@@ -11,15 +11,15 @@ class C: @unchecked Sendable {
1111
var counter: Int = 0
1212
}
1313

14+
nonisolated(unsafe) var globalCounter = 0
15+
1416
@available(SwiftStdlib 5.1, *)
15-
func f() async {
16-
// expected-warning@+2{{nonisolated(unsafe) involves unsafe code}}
17-
// expected-note@+1{{make var 'counter' @unsafe to indicate that its use is not memory-safe}}
17+
func f() async { // expected-warning{{global function 'f' involves unsafe code; use '@safe(unchecked)' to assert that the code is memory-safe}}
1818
nonisolated(unsafe) var counter = 0
1919
Task.detached {
20-
counter += 1
20+
counter += 1 // expected-note{{reference to nonisolated(unsafe) var 'counter' is unsafe in concurrently-executing code}}
2121
}
22-
counter += 1
23-
print(counter)
22+
counter += 1 // expected-note{{reference to nonisolated(unsafe) var 'counter' is unsafe in concurrently-executing code}}
23+
print(counter) // expected-note{{reference to nonisolated(unsafe) var 'counter' is unsafe in concurrently-executing code}}
24+
print(globalCounter) // expected-note{{reference to nonisolated(unsafe) var 'globalCounter' is unsafe in concurrently-executing code}}
2425
}
25-

0 commit comments

Comments
 (0)