@@ -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,21 @@ 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,
500
+ bool suggestConvenienceInit) const ;
501
+
502
+ void handleLoadUseFailureForActorInit (const DIMemoryUse &Use,
503
+ ActorInitKind ActorKind) const ;
504
+
483
505
void handleLoadUseFailure (const DIMemoryUse &Use,
484
506
bool SuperInitDone,
485
507
bool FailedSelfUse);
@@ -866,9 +888,13 @@ void LifetimeChecker::doIt() {
866
888
867
889
void LifetimeChecker::handleLoadUse (const DIMemoryUse &Use) {
868
890
bool IsSuperInitComplete, FailedSelfUse;
891
+ ActorInitKind ActorKind = ActorInitKind::None;
869
892
// If the value is not definitively initialized, emit an error.
870
893
if (!isInitializedAtUse (Use, &IsSuperInitComplete, &FailedSelfUse))
871
894
return handleLoadUseFailure (Use, IsSuperInitComplete, FailedSelfUse);
895
+ // Check if it involves 'self' in a restricted actor init.
896
+ if (isRestrictedActorInitSelf (Use, &ActorKind))
897
+ return handleLoadUseFailureForActorInit (Use, ActorKind);
872
898
}
873
899
874
900
static void replaceValueMetatypeInstWithMetatypeArgument (
@@ -1177,6 +1203,7 @@ static FullApplySite findApply(SILInstruction *I, bool &isSelfParameter) {
1177
1203
1178
1204
void LifetimeChecker::handleInOutUse (const DIMemoryUse &Use) {
1179
1205
bool IsSuperInitDone, FailedSelfUse;
1206
+ ActorInitKind ActorKind = ActorInitKind::None;
1180
1207
1181
1208
// inout uses are generally straight-forward: the memory must be initialized
1182
1209
// before the "address" is passed as an l-value.
@@ -1195,6 +1222,11 @@ void LifetimeChecker::handleInOutUse(const DIMemoryUse &Use) {
1195
1222
return ;
1196
1223
}
1197
1224
1225
+ // 'self' cannot be passed 'inout' from some kinds of actor initializers.
1226
+ if (isRestrictedActorInitSelf (Use, &ActorKind))
1227
+ reportIllegalUseForActorInit (Use, ActorKind, " be passed 'inout'" ,
1228
+ /* suggestConvenienceInit=*/ false );
1229
+
1198
1230
// One additional check: 'let' properties may never be passed inout, because
1199
1231
// they are only allowed to have their initial value set, not a subsequent
1200
1232
// overwrite.
@@ -1357,12 +1389,20 @@ static bool isLoadForReturn(SingleValueInstruction *loadInst) {
1357
1389
}
1358
1390
1359
1391
void LifetimeChecker::handleEscapeUse (const DIMemoryUse &Use) {
1392
+
1360
1393
// The value must be fully initialized at all escape points. If not, diagnose
1361
1394
// the error.
1362
1395
bool SuperInitDone, FailedSelfUse, FullyUninitialized;
1396
+ ActorInitKind ActorKind = ActorInitKind::None;
1363
1397
1364
1398
if (isInitializedAtUse (Use, &SuperInitDone, &FailedSelfUse,
1365
1399
&FullyUninitialized)) {
1400
+
1401
+ // no escaping uses of 'self' are allowed in restricted actor inits.
1402
+ if (isRestrictedActorInitSelf (Use, &ActorKind))
1403
+ reportIllegalUseForActorInit (Use, ActorKind, " be captured by a closure" ,
1404
+ /* suggestConvenienceInit=*/ true );
1405
+
1366
1406
return ;
1367
1407
}
1368
1408
@@ -1746,6 +1786,100 @@ bool LifetimeChecker::diagnoseReturnWithoutInitializingStoredProperties(
1746
1786
return true ;
1747
1787
}
1748
1788
1789
+ bool LifetimeChecker::isRestrictedActorInitSelf (const DIMemoryUse& Use,
1790
+ ActorInitKind *Kind) const {
1791
+
1792
+ auto result = [&](ActorInitKind k, bool isRestricted) -> bool {
1793
+ if (Kind)
1794
+ *Kind = k;
1795
+ return isRestricted;
1796
+ };
1797
+
1798
+ // Currently: being synchronous, or global-actor isolated, means the actor's
1799
+ // self is restricted within the init.
1800
+ if (auto *ctor = TheMemory.isActorInitSelf ()) {
1801
+ if (getActorIsolation (ctor).isGlobalActor ()) // global-actor isolated?
1802
+ return result (ActorInitKind::GlobalActorIsolated, true );
1803
+ else if (!ctor->hasAsync ()) // synchronous?
1804
+ return result (ActorInitKind::Plain, true );
1805
+ else
1806
+ return result (ActorInitKind::PlainAsync, false );
1807
+ }
1808
+
1809
+ return result (ActorInitKind::None, false );
1810
+ }
1811
+
1812
+ void LifetimeChecker::reportIllegalUseForActorInit (
1813
+ const DIMemoryUse &Use,
1814
+ ActorInitKind ActorKind,
1815
+ StringRef ProblemDesc,
1816
+ bool suggestConvenienceInit) const {
1817
+ switch (ActorKind) {
1818
+ case ActorInitKind::None:
1819
+ case ActorInitKind::PlainAsync:
1820
+ llvm::report_fatal_error (" this actor init is never problematic!" );
1821
+
1822
+ case ActorInitKind::Plain:
1823
+ diagnose (Module, Use.Inst ->getLoc (), diag::self_disallowed_actor_init,
1824
+ false , ProblemDesc);
1825
+ break ;
1826
+
1827
+ case ActorInitKind::GlobalActorIsolated:
1828
+ diagnose (Module, Use.Inst ->getLoc (), diag::self_disallowed_actor_init,
1829
+ true , ProblemDesc);
1830
+ break ;
1831
+ }
1832
+
1833
+ if (suggestConvenienceInit)
1834
+ diagnose (Module, Use.Inst ->getLoc (), diag::actor_convenience_init);
1835
+ }
1836
+
1837
+ void LifetimeChecker::handleLoadUseFailureForActorInit (
1838
+ const DIMemoryUse &Use,
1839
+ ActorInitKind ActorKind) const {
1840
+ assert (TheMemory.isAnyInitSelf ());
1841
+ SILInstruction *Inst = Use.Inst ;
1842
+
1843
+ // While enum instructions have no side-effects, they do wrap-up `self`
1844
+ // such that it can escape our analysis. So, enum instruction uses are only
1845
+ // acceptable if the enum is part of a specific pattern of usage that is
1846
+ // generated for a failable initializer.
1847
+ if (auto *enumInst = dyn_cast<EnumInst>(Inst)) {
1848
+ if (isFailableInitReturnUseOfEnum (enumInst))
1849
+ return ;
1850
+
1851
+ // Otherwise, if the use has no possibility of side-effects, then its OK.
1852
+ } else if (!Inst->mayHaveSideEffects ()) {
1853
+ return ;
1854
+
1855
+ // While loads can have side-effects, we are not concerned by them here.
1856
+ } else if (isa<LoadInst>(Inst)) {
1857
+ return ;
1858
+ }
1859
+
1860
+ // Everything else is disallowed!
1861
+ switch (ActorKind) {
1862
+ case ActorInitKind::None:
1863
+ case ActorInitKind::PlainAsync:
1864
+ llvm::report_fatal_error (" this actor init is never problematic!" );
1865
+
1866
+ case ActorInitKind::Plain:
1867
+ diagnose (Module, Use.Inst ->getLoc (), diag::self_use_actor_init, false );
1868
+ break ;
1869
+
1870
+ case ActorInitKind::GlobalActorIsolated:
1871
+ diagnose (Module, Use.Inst ->getLoc (), diag::self_use_actor_init, true );
1872
+ break ;
1873
+ }
1874
+
1875
+ // We cannot easily determine which argument in the call the use of 'self'
1876
+ // appears in. If we could, then we could determine whether the callee
1877
+ // is 'isolated' to that parameter, in order to avoid suggesting a convenience
1878
+ // init in those cases. Thus, the phrasing of the note should be informative.
1879
+ if (isa<ApplyInst>(Inst))
1880
+ diagnose (Module, Use.Inst ->getLoc (), diag::actor_convenience_init);
1881
+ }
1882
+
1749
1883
// / Check and diagnose various failures when a load use is not fully
1750
1884
// / initialized.
1751
1885
// /
0 commit comments