Skip to content

Commit 683dcfc

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 092b1c0 commit 683dcfc

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
@@ -8081,6 +8081,9 @@ NOTE(note_reference_to_unsafe_decl,none,
80818081
NOTE(note_reference_to_unsafe_typed_decl,none,
80828082
"%select{reference|call}0 to %kind1 involves unsafe type %2",
80838083
(bool, const ValueDecl *, Type))
8084+
NOTE(note_reference_to_nonisolated_unsafe,none,
8085+
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
8086+
(const ValueDecl *))
80848087

80858088
GROUPED_WARNING(override_safe_withunsafe,Unsafe,none,
80868089
"override of safe %0 with unsafe %0", (DescriptiveDeclKind))
@@ -8094,8 +8097,9 @@ GROUPED_WARNING(unchecked_conformance_is_unsafe,Unsafe,none,
80948097
"@unchecked conformance involves unsafe code", ())
80958098
GROUPED_WARNING(unowned_unsafe_is_unsafe,Unsafe,none,
80968099
"unowned(unsafe) involves unsafe code", ())
8097-
GROUPED_WARNING(nonisolated_unsafe_is_unsafe,Unsafe,none,
8098-
"nonisolated(unsafe) involves unsafe code", ())
8100+
GROUPED_WARNING(reference_to_nonisolated_unsafe,Unsafe,none,
8101+
"reference to nonisolated(unsafe) %kind0 is unsafe in concurrently-executing code",
8102+
(const ValueDecl *))
80998103
GROUPED_WARNING(reference_to_unsafe_decl,Unsafe,none,
81008104
"%select{reference|call}0 to unsafe %kindbase1",
81018105
(bool, const ValueDecl *))

lib/Sema/TypeCheckAttr.cpp

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

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

lib/Sema/TypeCheckAvailability.cpp

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

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

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

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

42084235
/// 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:
@@ -7159,7 +7156,7 @@ bool swift::isPotentiallyIsolatedActor(
71597156

71607157
/// Determine the actor isolation used when we are referencing the given
71617158
/// declaration.
7162-
static ActorIsolation getActorIsolationForReference(ValueDecl *decl,
7159+
ActorIsolation swift::getActorIsolationForReference(ValueDecl *decl,
71637160
const DeclContext *fromDC) {
71647161
auto declIsolation = getActorIsolation(decl);
71657162

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)