Skip to content

Commit c1af52c

Browse files
authored
Merge pull request #70419 from angela-laar/group-actor-isolation-errors
Group actor isolation errors
2 parents 4eddc1d + aed7edf commit c1af52c

File tree

6 files changed

+324
-68
lines changed

6 files changed

+324
-68
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5269,11 +5269,10 @@ NOTE(note_add_async_and_throws_to_decl,none,
52695269
NOTE(note_add_distributed_to_decl,none,
52705270
"add 'distributed' to %0 to make this %kindonly0 satisfy the protocol requirement",
52715271
(const ValueDecl *))
5272-
NOTE(note_add_globalactor_to_function,none,
5272+
ERROR(add_globalactor_to_function,none,
52735273
"add '@%0' to make %kind1 part of global actor %2",
52745274
(StringRef, const ValueDecl *, Type))
52755275
FIXIT(insert_globalactor_attr, "@%0 ", (Type))
5276-
52775276
ERROR(main_function_must_be_mainActor,none,
52785277
"main() must be '@MainActor'", ())
52795278

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ EXPERIMENTAL_FEATURE(BitwiseCopyable, true)
278278
/// Enables the FixedArray data type.
279279
EXPERIMENTAL_FEATURE(FixedArrays, true)
280280

281+
// Group Main Actor Isolation Errors by Scope
282+
EXPERIMENTAL_FEATURE(GroupActorErrors, true)
283+
281284
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
282285
#undef EXPERIMENTAL_FEATURE
283286
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3440,6 +3440,10 @@ static bool usesFeatureDeprecateApplicationMain(Decl *decl) {
34403440
return false;
34413441
}
34423442

3443+
static bool usesFeatureGroupActorErrors(Decl *decl) {
3444+
return false;
3445+
}
3446+
34433447
static bool usesFeatureImportObjcForwardDeclarations(Decl *decl) {
34443448
ClangNode clangNode = decl->getClangNode();
34453449
if (!clangNode)

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 185 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,39 +1921,6 @@ static FuncDecl *findAnnotatableFunction(DeclContext *dc) {
19211921
return fn;
19221922
}
19231923

1924-
/// Note when the enclosing context could be put on a global actor.
1925-
// FIXME: This should handle closures too.
1926-
static void noteGlobalActorOnContext(DeclContext *dc, Type globalActor) {
1927-
// If we are in a synchronous function on the global actor,
1928-
// suggest annotating with the global actor itself.
1929-
if (auto fn = findAnnotatableFunction(dc)) {
1930-
// Suppress this for accessories because you can't change the
1931-
// actor isolation of an individual accessor. Arguably we could
1932-
// add this to the entire storage declaration, though.
1933-
// Suppress this for async functions out of caution; but don't
1934-
// suppress it if we looked through a defer.
1935-
if (!isa<AccessorDecl>(fn) &&
1936-
(!fn->isAsyncContext() || fn != dc)) {
1937-
switch (getActorIsolation(fn)) {
1938-
case ActorIsolation::ActorInstance:
1939-
case ActorIsolation::GlobalActor:
1940-
case ActorIsolation::GlobalActorUnsafe:
1941-
case ActorIsolation::Nonisolated:
1942-
case ActorIsolation::NonisolatedUnsafe:
1943-
return;
1944-
1945-
case ActorIsolation::Unspecified:
1946-
fn->diagnose(diag::note_add_globalactor_to_function,
1947-
globalActor->getWithoutParens().getString(),
1948-
fn, globalActor)
1949-
.fixItInsert(fn->getAttributeInsertionLoc(false),
1950-
diag::insert_globalactor_attr, globalActor);
1951-
return;
1952-
}
1953-
}
1954-
}
1955-
}
1956-
19571924
bool swift::diagnoseApplyArgSendability(ApplyExpr *apply, const DeclContext *declContext) {
19581925
auto isolationCrossing = apply->getIsolationCrossing();
19591926
if (!isolationCrossing.has_value())
@@ -2036,6 +2003,16 @@ namespace {
20362003
/// an expression or function.
20372004
llvm::SmallDenseMap<const DeclContext *, ActorIsolation> requiredIsolation;
20382005

2006+
using ActorRefKindPair = std::pair<ReferencedActor::Kind, ActorIsolation>;
2007+
2008+
using IsolationPair = std::pair<ActorIsolation, ActorIsolation>;
2009+
2010+
using DiagnosticList = std::vector<IsolationError>;
2011+
2012+
llvm::DenseMap<ActorRefKindPair, DiagnosticList> refErrors;
2013+
2014+
llvm::DenseMap<IsolationPair, DiagnosticList> applyErrors;
2015+
20392016
/// Keeps track of the capture context of variables that have been
20402017
/// explicitly captured in closures.
20412018
llvm::SmallDenseMap<VarDecl *, TinyPtrVector<const DeclContext *>>
@@ -2054,6 +2031,99 @@ namespace {
20542031
return applyStack.back().dyn_cast<ApplyExpr *>();
20552032
}
20562033

2034+
/// Note when the enclosing context could be put on a global actor.
2035+
// FIXME: This should handle closures too.
2036+
static bool missingGlobalActorOnContext(DeclContext *dc, Type globalActor, DiagnosticBehavior behavior) {
2037+
// If we are in a synchronous function on the global actor,
2038+
// suggest annotating with the global actor itself.
2039+
if (auto fn = findAnnotatableFunction(dc)) {
2040+
// Suppress this for accessors because you can't change the
2041+
// actor isolation of an individual accessor. Arguably we could
2042+
// add this to the entire storage declaration, though.
2043+
// Suppress this for async functions out of caution; but don't
2044+
// suppress it if we looked through a defer.
2045+
if (!isa<AccessorDecl>(fn) &&
2046+
(!fn->isAsyncContext() || fn != dc)) {
2047+
switch (getActorIsolation(fn)) {
2048+
case ActorIsolation::ActorInstance:
2049+
case ActorIsolation::GlobalActor:
2050+
case ActorIsolation::GlobalActorUnsafe:
2051+
case ActorIsolation::Nonisolated:
2052+
case ActorIsolation::NonisolatedUnsafe:
2053+
return false;
2054+
2055+
case ActorIsolation::Unspecified:
2056+
fn->diagnose(diag::add_globalactor_to_function,
2057+
globalActor->getWithoutParens().getString(),
2058+
fn, globalActor)
2059+
.limitBehavior(behavior)
2060+
.fixItInsert(fn->getAttributeInsertionLoc(false),
2061+
diag::insert_globalactor_attr, globalActor);
2062+
return true;
2063+
}
2064+
}
2065+
}
2066+
return false;
2067+
}
2068+
2069+
public:
2070+
bool diagnoseIsolationErrors() {
2071+
bool diagnosedError = false;
2072+
2073+
for (auto list : refErrors) {
2074+
ActorRefKindPair key = list.getFirst();
2075+
DiagnosticList errors = list.getSecond();
2076+
ActorIsolation isolation = key.second;
2077+
2078+
auto behavior = DiagnosticBehavior::Error;
2079+
2080+
// Add Fix-it for missing @SomeActor annotation
2081+
if (isolation.isGlobalActor()) {
2082+
if (missingGlobalActorOnContext(
2083+
const_cast<DeclContext *>(getDeclContext()),
2084+
isolation.getGlobalActor(), behavior) &&
2085+
errors.size() > 1) {
2086+
behavior= DiagnosticBehavior::Note;
2087+
}
2088+
}
2089+
2090+
for (IsolationError error : errors) {
2091+
// Diagnose actor_isolated_non_self_reference as note
2092+
// if fix-it provided in missingGlobalActorOnContext
2093+
ctx.Diags.diagnose(error.loc, error.diag)
2094+
.limitBehavior(behavior);
2095+
}
2096+
}
2097+
2098+
for (auto list : applyErrors) {
2099+
IsolationPair key = list.getFirst();
2100+
DiagnosticList errors = list.getSecond();
2101+
ActorIsolation isolation = key.first;
2102+
2103+
auto behavior = DiagnosticBehavior::Error;
2104+
2105+
// Add Fix-it for missing @SomeActor annotation
2106+
if (isolation.isGlobalActor()) {
2107+
if (missingGlobalActorOnContext(
2108+
const_cast<DeclContext *>(getDeclContext()),
2109+
isolation.getGlobalActor(), behavior) &&
2110+
errors.size() > 1) {
2111+
behavior= DiagnosticBehavior::Note;
2112+
}
2113+
}
2114+
2115+
for (IsolationError error : errors) {
2116+
// Diagnose actor_isolated_call as note if
2117+
// fix-it provided in missingGlobalActorOnContext
2118+
ctx.Diags.diagnose(error.loc, error.diag)
2119+
.limitBehavior(behavior);
2120+
}
2121+
}
2122+
2123+
return diagnosedError;
2124+
}
2125+
2126+
private:
20572127
const PatternBindingDecl *getTopPatternBindingDecl() const {
20582128
return patternBindingStack.empty() ? nullptr : patternBindingStack.back();
20592129
}
@@ -3185,27 +3255,58 @@ namespace {
31853255
// If we need to mark the call as implicitly asynchronous, make sure
31863256
// we're in an asynchronous context.
31873257
if (requiresAsync && !getDeclContext()->isAsyncContext()) {
3188-
if (calleeDecl) {
3189-
auto preconcurrency = getContextIsolation().preconcurrency() ||
3190-
calleeDecl->preconcurrency();
3191-
ctx.Diags.diagnose(
3192-
apply->getLoc(), diag::actor_isolated_call_decl,
3193-
*unsatisfiedIsolation,
3194-
calleeDecl,
3195-
getContextIsolation())
3196-
.warnUntilSwiftVersionIf(preconcurrency, 6);
3197-
calleeDecl->diagnose(diag::actor_isolated_sync_func, calleeDecl);
3258+
3259+
if (ctx.LangOpts.hasFeature(Feature::GroupActorErrors)) {
3260+
3261+
IsolationError mismatch([calleeDecl, apply, unsatisfiedIsolation, getContextIsolation]() {
3262+
if (calleeDecl) {
3263+
return IsolationError(
3264+
apply->getLoc(),
3265+
Diagnostic(diag::actor_isolated_call_decl,
3266+
*unsatisfiedIsolation,
3267+
calleeDecl,
3268+
getContextIsolation()));
3269+
} else {
3270+
return IsolationError(
3271+
apply->getLoc(),
3272+
Diagnostic(diag::actor_isolated_call,
3273+
*unsatisfiedIsolation,
3274+
getContextIsolation()));
3275+
}
3276+
}());
3277+
3278+
auto iter = applyErrors.find(std::make_pair(*unsatisfiedIsolation, getContextIsolation()));
3279+
if (iter != applyErrors.end()){
3280+
iter->second.push_back((mismatch));
3281+
} else {
3282+
DiagnosticList list;
3283+
list.push_back((mismatch));
3284+
auto keyPair = std::make_pair(*unsatisfiedIsolation, getContextIsolation());
3285+
applyErrors.insert(std::make_pair(keyPair, list));
3286+
}
31983287
} else {
3199-
ctx.Diags.diagnose(
3200-
apply->getLoc(), diag::actor_isolated_call, *unsatisfiedIsolation,
3201-
getContextIsolation())
3288+
if (calleeDecl) {
3289+
auto preconcurrency = getContextIsolation().preconcurrency() ||
3290+
calleeDecl->preconcurrency();
3291+
ctx.Diags.diagnose(
3292+
apply->getLoc(), diag::actor_isolated_call_decl,
3293+
*unsatisfiedIsolation,
3294+
calleeDecl,
3295+
getContextIsolation())
3296+
.warnUntilSwiftVersionIf(preconcurrency, 6);
3297+
calleeDecl->diagnose(diag::actor_isolated_sync_func, calleeDecl);
3298+
} else {
3299+
ctx.Diags.diagnose(
3300+
apply->getLoc(), diag::actor_isolated_call, *unsatisfiedIsolation,
3301+
getContextIsolation())
32023302
.warnUntilSwiftVersionIf(getContextIsolation().preconcurrency(), 6);
3203-
}
3303+
}
32043304

3205-
if (unsatisfiedIsolation->isGlobalActor()) {
3206-
noteGlobalActorOnContext(
3207-
const_cast<DeclContext *>(getDeclContext()),
3208-
unsatisfiedIsolation->getGlobalActor());
3305+
if (unsatisfiedIsolation->isGlobalActor()) {
3306+
missingGlobalActorOnContext(
3307+
const_cast<DeclContext *>(getDeclContext()),
3308+
unsatisfiedIsolation->getGlobalActor(), DiagnosticBehavior::Note);
3309+
}
32093310
}
32103311

32113312
return true;
@@ -3574,22 +3675,38 @@ namespace {
35743675
bool preconcurrencyContext =
35753676
result.options.contains(ActorReferenceResult::Flags::Preconcurrency);
35763677

3577-
ctx.Diags.diagnose(
3578-
loc, diag::actor_isolated_non_self_reference,
3579-
decl,
3580-
useKind,
3581-
refKind + 1, refGlobalActor,
3582-
result.isolation)
3583-
.warnUntilSwiftVersionIf(preconcurrencyContext, 6);
3584-
3585-
noteIsolatedActorMember(decl, context);
3586-
3587-
if (result.isolation.isGlobalActor()) {
3588-
noteGlobalActorOnContext(
3589-
const_cast<DeclContext *>(getDeclContext()),
3590-
result.isolation.getGlobalActor());
3591-
}
3678+
if (ctx.LangOpts.hasFeature(Feature::GroupActorErrors)) {
3679+
IsolationError mismatch = IsolationError(loc, Diagnostic(diag::actor_isolated_non_self_reference,
3680+
decl,
3681+
useKind,
3682+
refKind + 1, refGlobalActor,
3683+
result.isolation));
35923684

3685+
auto iter = refErrors.find(std::make_pair(refKind,result.isolation));
3686+
if (iter != refErrors.end()){
3687+
iter->second.push_back(mismatch);
3688+
} else {
3689+
DiagnosticList list;
3690+
list.push_back(mismatch);
3691+
auto keyPair = std::make_pair(refKind,result.isolation);
3692+
refErrors.insert(std::make_pair(keyPair, list));
3693+
}
3694+
} else {
3695+
ctx.Diags.diagnose(
3696+
loc, diag::actor_isolated_non_self_reference,
3697+
decl,
3698+
useKind,
3699+
refKind + 1, refGlobalActor,
3700+
result.isolation)
3701+
.warnUntilSwiftVersionIf(preconcurrencyContext, 6);
3702+
3703+
noteIsolatedActorMember(decl, context);
3704+
if (result.isolation.isGlobalActor()) {
3705+
missingGlobalActorOnContext(
3706+
const_cast<DeclContext *>(getDeclContext()),
3707+
result.isolation.getGlobalActor(), DiagnosticBehavior::Note);
3708+
}
3709+
}
35933710
return true;
35943711
}
35953712
}
@@ -3731,9 +3848,11 @@ void swift::checkFunctionActorIsolation(AbstractFunctionDecl *decl) {
37313848
if (decl->getAttrs().hasAttribute<LLDBDebuggerFunctionAttr>())
37323849
return;
37333850

3851+
auto &ctx = decl->getASTContext();
37343852
ActorIsolationChecker checker(decl);
37353853
if (auto body = decl->getBody()) {
37363854
body->walk(checker);
3855+
if(ctx.LangOpts.hasFeature(Feature::GroupActorErrors)){ checker.diagnoseIsolationErrors(); }
37373856
}
37383857
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
37393858
if (auto superInit = ctor->getSuperInitCall())

0 commit comments

Comments
 (0)