@@ -602,14 +602,21 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
602602 if (cast<ValueDecl>(decl)->isLocalCapture ())
603603 return forUnrestricted ();
604604
605+ // 'let' declarations are immutable, so they can be accessed across
606+ // actors.
607+ bool isAccessibleAcrossActors = false ;
608+ if (auto var = dyn_cast<VarDecl>(decl)) {
609+ if (var->isLet ())
610+ isAccessibleAcrossActors = true ;
611+ }
612+
605613 // A function that provides an asynchronous context has no restrictions
606614 // on its access.
607615 //
608616 // FIXME: technically, synchronous functions are allowed to be cross-actor.
609617 // The call-sites are just conditionally async based on where they appear
610618 // (outside or inside the actor). This suggests that the implicitly-async
611619 // concept could be merged into the CrossActorSelf concept.
612- bool isAccessibleAcrossActors = false ;
613620 if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
614621 if (func->isAsyncContext ())
615622 isAccessibleAcrossActors = true ;
@@ -922,7 +929,8 @@ static bool diagnoseNonConcurrentProperty(
922929
923930// / Whether we should diagnose cases where Sendable conformances are
924931// / missing.
925- bool swift::shouldDiagnoseNonSendableViolations (const LangOptions &langOpts) {
932+ static bool shouldDiagnoseNonSendableViolations (
933+ const LangOptions &langOpts) {
926934 return langOpts.WarnConcurrency ;
927935}
928936
@@ -1426,17 +1434,11 @@ namespace {
14261434 // was it an attempt to mutate an actor instance's isolated state?
14271435 } else if (auto environment = kindOfUsage (decl, context)) {
14281436
1429- if (isa<VarDecl>(decl) && cast<VarDecl>(decl)->isLet ()) {
1430- auto diag = decl->diagnose (diag::actor_isolated_let);
1431- SourceLoc fixItLoc =
1432- decl->getAttributeInsertionLoc (/* forModifier=*/ true );
1433- if (fixItLoc.isValid ())
1434- diag.fixItInsert (fixItLoc, " nonisolated " );
1435- } else if (environment.getValue () == VarRefUseEnv::Read) {
1437+ if (environment.getValue () == VarRefUseEnv::Read)
14361438 decl->diagnose (diag::kind_declared_here, decl->getDescriptiveKind ());
1437- } else {
1439+ else
14381440 decl->diagnose (diag::actor_mutable_state, decl->getDescriptiveKind ());
1439- }
1441+
14401442 } else {
14411443 decl->diagnose (diag::kind_declared_here, decl->getDescriptiveKind ());
14421444 }
@@ -1609,6 +1611,11 @@ namespace {
16091611
16101612 // is it an access to a property?
16111613 if (isPropOrSubscript (decl)) {
1614+ // we assume let-bound properties are taken care of elsewhere,
1615+ // since they are never implicitly async.
1616+ assert (!isa<VarDecl>(decl) || cast<VarDecl>(decl)->isLet () == false
1617+ && " unexpected let-bound property; never implicitly async!" );
1618+
16121619 if (auto declRef = dyn_cast_or_null<DeclRefExpr>(context)) {
16131620 if (usageEnv (declRef) == VarRefUseEnv::Read) {
16141621
@@ -1943,6 +1950,27 @@ namespace {
19431950 bool checkKeyPathExpr (KeyPathExpr *keyPath) {
19441951 bool diagnosed = false ;
19451952
1953+ // returns None if it is not a 'let'-bound var decl. Otherwise,
1954+ // the bool indicates whether a diagnostic was emitted.
1955+ auto checkLetBoundVarDecl = [&](KeyPathExpr::Component const & component)
1956+ -> Optional<bool > {
1957+ auto decl = component.getDeclRef ().getDecl ();
1958+ if (auto varDecl = dyn_cast<VarDecl>(decl)) {
1959+ if (varDecl->isLet ()) {
1960+ auto type = component.getComponentType ();
1961+ if (shouldDiagnoseNonSendableViolations (ctx.LangOpts )
1962+ && !isSendableType (getDeclContext (), type)) {
1963+ ctx.Diags .diagnose (
1964+ component.getLoc (), diag::non_concurrent_keypath_access,
1965+ type);
1966+ return true ;
1967+ }
1968+ return false ;
1969+ }
1970+ }
1971+ return None;
1972+ };
1973+
19461974 // check the components of the keypath.
19471975 for (const auto &component : keyPath->getComponents ()) {
19481976 // The decl referred to by the path component cannot be within an actor.
@@ -1970,6 +1998,13 @@ namespace {
19701998 LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
19711999
19722000 case ActorIsolationRestriction::CrossActorSelf:
2001+ // 'let'-bound decls with this isolation are OK, just check them.
2002+ if (auto wasLetBound = checkLetBoundVarDecl (component)) {
2003+ diagnosed = wasLetBound.getValue ();
2004+ break ;
2005+ }
2006+ LLVM_FALLTHROUGH; // otherwise, it's invalid so diagnose it.
2007+
19732008 case ActorIsolationRestriction::ActorSelf: {
19742009 auto decl = concDecl.getDecl ();
19752010 ctx.Diags .diagnose (component.getLoc (),
0 commit comments