Skip to content

Commit 9402455

Browse files
authored
Merge pull request #70750 from hborla/5.10-suppress-async-iterator-warning
[5.10][Concurrency] Suppress diagnostics about non-`Sendable` async iterator arguments in implicit calls to `next()`.
2 parents d7d6188 + e696624 commit 9402455

File tree

9 files changed

+80
-14
lines changed

9 files changed

+80
-14
lines changed

include/swift/AST/Expr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,10 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
448448
return const_cast<Expr *>(this)->getValueProvidingExpr();
449449
}
450450

451+
/// Find the original expression value, looking through various
452+
/// implicit conversions.
453+
const Expr *findOriginalValue() const;
454+
451455
/// Find the original type of a value, looking through various implicit
452456
/// conversions.
453457
Type findOriginalType() const;

lib/AST/Expr.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2803,7 +2803,7 @@ FrontendStatsTracer::getTraceFormatter<const Expr *>() {
28032803
return &TF;
28042804
}
28052805

2806-
Type Expr::findOriginalType() const {
2806+
const Expr *Expr::findOriginalValue() const {
28072807
auto *expr = this;
28082808
do {
28092809
expr = expr->getSemanticsProvidingExpr();
@@ -2826,5 +2826,10 @@ Type Expr::findOriginalType() const {
28262826
break;
28272827
} while (true);
28282828

2829+
return expr;
2830+
}
2831+
2832+
Type Expr::findOriginalType() const {
2833+
auto *expr = findOriginalValue();
28292834
return expr->getType()->getRValueType();
28302835
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,7 @@ bool ActorIsolation::requiresSubstitution() const {
16241624
switch (kind) {
16251625
case ActorInstance:
16261626
case Nonisolated:
1627+
case NonisolatedUnsafe:
16271628
case Unspecified:
16281629
return false;
16291630

@@ -1638,6 +1639,7 @@ ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const {
16381639
switch (kind) {
16391640
case ActorInstance:
16401641
case Nonisolated:
1642+
case NonisolatedUnsafe:
16411643
case Unspecified:
16421644
return *this;
16431645

lib/ASTGen/Sources/ASTGen/SourceFile.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ extension Parser.ExperimentalFeatures {
3636
}
3737
}
3838
mapFeature(.ThenStatements, to: .thenStatements)
39-
mapFeature(.GlobalConcurrency, to: .globalConcurrency)
4039
}
4140
}
4241

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5344,8 +5344,7 @@ bool Parser::isStartOfSwiftDecl(bool allowPoundIfAttributes,
53445344
}
53455345

53465346
// If this is 'nonisolated', check to see if it is valid.
5347-
if (Context.LangOpts.hasFeature(Feature::GlobalConcurrency) &&
5348-
Tok.isContextualKeyword("nonisolated") && Tok2.is(tok::l_paren) &&
5347+
if (Tok.isContextualKeyword("nonisolated") && Tok2.is(tok::l_paren) &&
53495348
isParenthesizedNonisolated(*this)) {
53505349
BacktrackingScope backtrack(*this);
53515350
consumeToken(tok::identifier);

lib/Sema/CSGen.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4413,6 +4413,19 @@ generateForEachStmtConstraints(ConstraintSystem &cs,
44134413
sequenceExpr->getStartLoc(), ctx.getIdentifier(name), dc);
44144414
makeIteratorVar->setImplicit();
44154415

4416+
// FIXME: Apply `nonisolated(unsafe)` to async iterators.
4417+
//
4418+
// Async iterators are not `Sendable`; they're only meant to be used from
4419+
// the isolation domain that creates them. But the `next()` method runs on
4420+
// the generic executor, so calling it from an actor-isolated context passes
4421+
// non-`Sendable` state across the isolation boundary. `next()` should
4422+
// inherit the isolation of the caller, but for now, use the opt out.
4423+
if (isAsync) {
4424+
auto *nonisolated = new (ctx)
4425+
NonisolatedAttr(/*unsafe=*/true, /*implicit=*/true);
4426+
makeIteratorVar->getAttrs().add(nonisolated);
4427+
}
4428+
44164429
// First, let's form a call from sequence to `.makeIterator()` and save
44174430
// that in a special variable which is going to be used by SILGen.
44184431
{

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6596,10 +6596,9 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
65966596
if (auto var = dyn_cast<VarDecl>(D)) {
65976597
// stored properties have limitations as to when they can be nonisolated.
65986598
if (var->hasStorage()) {
6599-
const bool isUnsafeGlobal = attr->isUnsafe() && var->isGlobalStorage();
6600-
6601-
// 'nonisolated' can not be applied to mutable stored properties.
6602-
if (var->supportsMutation() && !isUnsafeGlobal) {
6599+
// 'nonisolated' can not be applied to mutable stored properties unless
6600+
// qualified as 'unsafe'.
6601+
if (var->supportsMutation() && !attr->isUnsafe()) {
66036602
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage);
66046603
return;
66056604
}
@@ -6641,8 +6640,9 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
66416640
return;
66426641
}
66436642

6644-
// nonisolated can not be applied to local properties.
6645-
if (dc->isLocalContext()) {
6643+
// nonisolated can not be applied to local properties unless qualified as
6644+
// 'unsafe'.
6645+
if (dc->isLocalContext() && !attr->isUnsafe()) {
66466646
diagnoseAndRemoveAttr(attr, diag::nonisolated_local_var);
66476647
return;
66486648
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,15 @@ static void noteGlobalActorOnContext(DeclContext *dc, Type globalActor) {
19471947
}
19481948
}
19491949

1950+
static bool shouldCheckSendable(Expr *expr) {
1951+
if (auto *declRef = dyn_cast<DeclRefExpr>(expr->findOriginalValue())) {
1952+
auto isolation = getActorIsolation(declRef->getDecl());
1953+
return isolation != ActorIsolation::NonisolatedUnsafe;
1954+
}
1955+
1956+
return true;
1957+
}
1958+
19501959
bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *declContext) {
19511960
auto isolationCrossing = apply->getIsolationCrossing();
19521961
if (!isolationCrossing.has_value())
@@ -1959,7 +1968,9 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19591968
// Check the 'self' argument.
19601969
if (auto *selfApply = dyn_cast<SelfApplyExpr>(apply->getFn())) {
19611970
auto *base = selfApply->getBase();
1962-
if (diagnoseNonSendableTypes(
1971+
1972+
if (shouldCheckSendable(base) &&
1973+
diagnoseNonSendableTypes(
19631974
base->getType(),
19641975
declContext, base->getStartLoc(),
19651976
diag::non_sendable_call_argument,
@@ -1979,6 +1990,7 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19791990
// Dig out the location of the argument.
19801991
SourceLoc argLoc = apply->getLoc();
19811992
Type argType;
1993+
bool checkSendable = true;
19821994
if (auto argList = apply->getArgs()) {
19831995
auto arg = argList->get(paramIdx);
19841996
if (arg.getStartLoc().isValid())
@@ -1987,11 +1999,13 @@ bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *dec
19871999
// Determine the type of the argument, ignoring any implicit
19882000
// conversions that could have stripped sendability.
19892001
if (Expr *argExpr = arg.getExpr()) {
1990-
argType = argExpr->findOriginalType();
2002+
checkSendable = shouldCheckSendable(argExpr);
2003+
argType = argExpr->findOriginalType();
19912004
}
19922005
}
19932006

1994-
if (diagnoseNonSendableTypes(
2007+
if (checkSendable &&
2008+
diagnoseNonSendableTypes(
19952009
argType ? argType : param.getParameterType(),
19962010
declContext, argLoc, diag::non_sendable_call_argument,
19972011
isolationCrossing.value().exitsIsolation(),
@@ -2812,6 +2826,11 @@ namespace {
28122826
if (getActorIsolation(value).isActorIsolated())
28132827
return false;
28142828

2829+
if (auto attr = value->getAttrs().getAttribute<NonisolatedAttr>();
2830+
attr && attr->isUnsafe()) {
2831+
return false;
2832+
}
2833+
28152834
ctx.Diags.diagnose(loc, diag::shared_mutable_state_access, value);
28162835
value->diagnose(diag::kind_declared_here, value->getDescriptiveKind());
28172836
return true;
@@ -3275,6 +3294,11 @@ namespace {
32753294
}
32763295
}
32773296

3297+
if (auto attr = var->getAttrs().getAttribute<NonisolatedAttr>();
3298+
attr && attr->isUnsafe()) {
3299+
return false;
3300+
}
3301+
32783302
// Otherwise, we have concurrent access. Complain.
32793303
bool preconcurrencyContext =
32803304
getActorIsolationOfContext(

test/Concurrency/experimental_feature_strictconcurrency.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency -enable-experimental-feature GlobalConcurrency -emit-sil -o /dev/null -verify %s
22
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -emit-sil -o /dev/null -verify %s
3-
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -emit-sil -o /dev/null -verify -enable-experimental-feature SendNonSendable %s
43

54
// REQUIRES: concurrency
65
// REQUIRES: asserts
@@ -75,3 +74,24 @@ func f() {
7574
print(TestStatics.immutableInferredSendable)
7675
print(TestStatics.mutable) // expected-warning{{reference to static property 'mutable' is not concurrency-safe because it involves shared mutable state}}
7776
}
77+
78+
func testLocalNonisolatedUnsafe() async {
79+
nonisolated(unsafe) var value = 1
80+
let task = Task {
81+
value = 2
82+
return value
83+
}
84+
print(await task.value)
85+
}
86+
87+
@MainActor
88+
func iterate(stream: AsyncStream<Int>) async {
89+
nonisolated(unsafe) var it = stream.makeAsyncIterator()
90+
while let element = await it.next() {
91+
print(element)
92+
}
93+
94+
for await x in stream {
95+
print(x)
96+
}
97+
}

0 commit comments

Comments
 (0)