@@ -384,6 +384,13 @@ namespace {
384384
385385 using BlockStates = BasicBlockData<LiveOutBlockState>;
386386
387+ enum class ActorInitKind {
388+ None, // not an actor init
389+ Plain, // synchronous, not isolated to global-actor
390+ PlainAsync, // asynchronous, not isolated to global-actor
391+ GlobalActorIsolated // isolated to global-actor (sync or async).
392+ };
393+
387394 // / LifetimeChecker - This is the main heavy lifting for definite
388395 // / initialization checking of a memory object.
389396 class LifetimeChecker {
@@ -480,6 +487,20 @@ namespace {
480487 bool diagnoseReturnWithoutInitializingStoredProperties (
481488 const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use);
482489
490+ // / Returns true iff the use involves 'self' in a restricted kind of
491+ // / actor initializer. If a non-null Kind pointer was passed in,
492+ // / then the specific kind of restricted actor initializer will be
493+ // / written out. Otherwise, the None initializer kind will be written out.
494+ bool isRestrictedActorInitSelf (const DIMemoryUse& Use,
495+ ActorInitKind *Kind = nullptr ) const ;
496+
497+ void reportIllegalUseForActorInit (const DIMemoryUse &Use,
498+ ActorInitKind ActorKind,
499+ StringRef ProblemDesc) const ;
500+
501+ void handleLoadUseFailureForActorInit (const DIMemoryUse &Use,
502+ ActorInitKind ActorKind) const ;
503+
483504 void handleLoadUseFailure (const DIMemoryUse &Use,
484505 bool SuperInitDone,
485506 bool FailedSelfUse);
@@ -866,9 +887,13 @@ void LifetimeChecker::doIt() {
866887
867888void LifetimeChecker::handleLoadUse (const DIMemoryUse &Use) {
868889 bool IsSuperInitComplete, FailedSelfUse;
890+ ActorInitKind ActorKind = ActorInitKind::None;
869891 // If the value is not definitively initialized, emit an error.
870892 if (!isInitializedAtUse (Use, &IsSuperInitComplete, &FailedSelfUse))
871893 return handleLoadUseFailure (Use, IsSuperInitComplete, FailedSelfUse);
894+ // Check if it involves 'self' in a restricted actor init.
895+ if (isRestrictedActorInitSelf (Use, &ActorKind))
896+ return handleLoadUseFailureForActorInit (Use, ActorKind);
872897}
873898
874899static void replaceValueMetatypeInstWithMetatypeArgument (
@@ -1177,6 +1202,7 @@ static FullApplySite findApply(SILInstruction *I, bool &isSelfParameter) {
11771202
11781203void LifetimeChecker::handleInOutUse (const DIMemoryUse &Use) {
11791204 bool IsSuperInitDone, FailedSelfUse;
1205+ ActorInitKind ActorKind = ActorInitKind::None;
11801206
11811207 // inout uses are generally straight-forward: the memory must be initialized
11821208 // before the "address" is passed as an l-value.
@@ -1195,6 +1221,10 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) {
11951221 return ;
11961222 }
11971223
1224+ // 'self' cannot be passed 'inout' from some kinds of actor initializers.
1225+ if (isRestrictedActorInitSelf (Use, &ActorKind))
1226+ reportIllegalUseForActorInit (Use, ActorKind, " be passed 'inout'" );
1227+
11981228 // One additional check: 'let' properties may never be passed inout, because
11991229 // they are only allowed to have their initial value set, not a subsequent
12001230 // overwrite.
@@ -1357,12 +1387,19 @@ static bool isLoadForReturn(SingleValueInstruction *loadInst) {
13571387}
13581388
13591389void LifetimeChecker::handleEscapeUse (const DIMemoryUse &Use) {
1390+
13601391 // The value must be fully initialized at all escape points. If not, diagnose
13611392 // the error.
13621393 bool SuperInitDone, FailedSelfUse, FullyUninitialized;
1394+ ActorInitKind ActorKind = ActorInitKind::None;
13631395
13641396 if (isInitializedAtUse (Use, &SuperInitDone, &FailedSelfUse,
13651397 &FullyUninitialized)) {
1398+
1399+ // no escaping uses of 'self' are allowed in restricted actor inits.
1400+ if (isRestrictedActorInitSelf (Use, &ActorKind))
1401+ reportIllegalUseForActorInit (Use, ActorKind, " be captured by a closure" );
1402+
13661403 return ;
13671404 }
13681405
@@ -1746,6 +1783,89 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties(
17461783 return true ;
17471784}
17481785
1786+ bool LifetimeChecker::isRestrictedActorInitSelf (const DIMemoryUse& Use,
1787+ ActorInitKind *Kind) const {
1788+
1789+ auto result = [&](ActorInitKind k, bool isRestricted) -> bool {
1790+ if (Kind)
1791+ *Kind = k;
1792+ return isRestricted;
1793+ };
1794+
1795+ // Currently: being synchronous, or global-actor isolated, means the actor's
1796+ // self is restricted within the init.
1797+ if (auto *ctor = TheMemory.isActorInitSelf ()) {
1798+ if (getActorIsolation (ctor).isGlobalActor ()) // global-actor isolated?
1799+ return result (ActorInitKind::GlobalActorIsolated, true );
1800+ else if (!ctor->hasAsync ()) // synchronous?
1801+ return result (ActorInitKind::Plain, true );
1802+ else
1803+ return result (ActorInitKind::PlainAsync, false );
1804+ }
1805+
1806+ return result (ActorInitKind::None, false );
1807+ }
1808+
1809+ void LifetimeChecker::reportIllegalUseForActorInit (
1810+ const DIMemoryUse &Use,
1811+ ActorInitKind ActorKind,
1812+ StringRef ProblemDesc) const {
1813+ switch (ActorKind) {
1814+ case ActorInitKind::None:
1815+ case ActorInitKind::PlainAsync:
1816+ llvm::report_fatal_error (" this actor init is never problematic!" );
1817+
1818+ case ActorInitKind::Plain:
1819+ diagnose (Module, Use.Inst ->getLoc (), diag::self_disallowed_actor_init,
1820+ false , ProblemDesc);
1821+ break ;
1822+
1823+ case ActorInitKind::GlobalActorIsolated:
1824+ diagnose (Module, Use.Inst ->getLoc (), diag::self_disallowed_actor_init,
1825+ true , ProblemDesc);
1826+ break ;
1827+ }
1828+ }
1829+
1830+ void LifetimeChecker::handleLoadUseFailureForActorInit (
1831+ const DIMemoryUse &Use,
1832+ ActorInitKind ActorKind) const {
1833+ assert (TheMemory.isAnyInitSelf ());
1834+ SILInstruction *Inst = Use.Inst ;
1835+
1836+ // While enum instructions have no side-effects, they do wrap-up `self`
1837+ // such that it can escape our analysis. So, enum instruction uses are only
1838+ // acceptable if the enum is part of a specific pattern of usage that is
1839+ // generated for a failable initializer.
1840+ if (auto *enumInst = dyn_cast<EnumInst>(Inst)) {
1841+ if (isFailableInitReturnUseOfEnum (enumInst))
1842+ return ;
1843+
1844+ // Otherwise, if the use has no possibility of side-effects, then its OK.
1845+ } else if (!Inst->mayHaveSideEffects ()) {
1846+ return ;
1847+
1848+ // While loads can have side-effects, we are not concerned by them here.
1849+ } else if (isa<LoadInst>(Inst)) {
1850+ return ;
1851+ }
1852+
1853+ // Everything else is disallowed!
1854+ switch (ActorKind) {
1855+ case ActorInitKind::None:
1856+ case ActorInitKind::PlainAsync:
1857+ llvm::report_fatal_error (" this actor init is never problematic!" );
1858+
1859+ case ActorInitKind::Plain:
1860+ diagnose (Module, Use.Inst ->getLoc (), diag::self_use_actor_init, false );
1861+ break ;
1862+
1863+ case ActorInitKind::GlobalActorIsolated:
1864+ diagnose (Module, Use.Inst ->getLoc (), diag::self_use_actor_init, true );
1865+ break ;
1866+ }
1867+ }
1868+
17491869// / Check and diagnose various failures when a load use is not fully
17501870// / initialized.
17511871// /
0 commit comments