@@ -384,6 +384,13 @@ namespace {
384
384
385
385
using BlockStates = BasicBlockData<LiveOutBlockState>;
386
386
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
+
387
394
// / LifetimeChecker - This is the main heavy lifting for definite
388
395
// / initialization checking of a memory object.
389
396
class LifetimeChecker {
@@ -480,6 +487,20 @@ namespace {
480
487
bool diagnoseReturnWithoutInitializingStoredProperties (
481
488
const SILInstruction *Inst, SILLocation loc, const DIMemoryUse &Use);
482
489
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
+
483
504
void handleLoadUseFailure (const DIMemoryUse &Use,
484
505
bool SuperInitDone,
485
506
bool FailedSelfUse);
@@ -866,9 +887,13 @@ void LifetimeChecker::doIt() {
866
887
867
888
void LifetimeChecker::handleLoadUse (const DIMemoryUse &Use) {
868
889
bool IsSuperInitComplete, FailedSelfUse;
890
+ ActorInitKind ActorKind = ActorInitKind::None;
869
891
// If the value is not definitively initialized, emit an error.
870
892
if (!isInitializedAtUse (Use, &IsSuperInitComplete, &FailedSelfUse))
871
893
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);
872
897
}
873
898
874
899
static void replaceValueMetatypeInstWithMetatypeArgument (
@@ -1177,6 +1202,7 @@ static FullApplySite findApply(SILInstruction *I, bool &isSelfParameter) {
1177
1202
1178
1203
void LifetimeChecker::handleInOutUse (const DIMemoryUse &Use) {
1179
1204
bool IsSuperInitDone, FailedSelfUse;
1205
+ ActorInitKind ActorKind = ActorInitKind::None;
1180
1206
1181
1207
// inout uses are generally straight-forward: the memory must be initialized
1182
1208
// before the "address" is passed as an l-value.
@@ -1195,6 +1221,10 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) {
1195
1221
return ;
1196
1222
}
1197
1223
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
+
1198
1228
// One additional check: 'let' properties may never be passed inout, because
1199
1229
// they are only allowed to have their initial value set, not a subsequent
1200
1230
// overwrite.
@@ -1357,12 +1387,19 @@ static bool isLoadForReturn(SingleValueInstruction *loadInst) {
1357
1387
}
1358
1388
1359
1389
void LifetimeChecker::handleEscapeUse (const DIMemoryUse &Use) {
1390
+
1360
1391
// The value must be fully initialized at all escape points. If not, diagnose
1361
1392
// the error.
1362
1393
bool SuperInitDone, FailedSelfUse, FullyUninitialized;
1394
+ ActorInitKind ActorKind = ActorInitKind::None;
1363
1395
1364
1396
if (isInitializedAtUse (Use, &SuperInitDone, &FailedSelfUse,
1365
1397
&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
+
1366
1403
return ;
1367
1404
}
1368
1405
@@ -1746,6 +1783,89 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties(
1746
1783
return true ;
1747
1784
}
1748
1785
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
+
1749
1869
// / Check and diagnose various failures when a load use is not fully
1750
1870
// / initialized.
1751
1871
// /
0 commit comments