Skip to content

Commit 4275bbd

Browse files
committed
[SILOptimizer] FlowIsolation: Teach analysis to recognize use of init accessor properties
Init accessor properties should be handled specifically because they do not reference underlying storage directly via `ref_element_addr` instructions when 'self' is fully initialized.
1 parent e4d2d51 commit 4275bbd

File tree

3 files changed

+108
-4
lines changed

3 files changed

+108
-4
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,10 @@ NOTE(nonisolated_blame, none, "after %1%2 %3, "
674674
ERROR(non_sendable_from_deinit,none,
675675
"cannot access %1 %2 with a non-sendable type %0 from non-isolated deinit",
676676
(Type, DescriptiveDeclKind, DeclName))
677+
ERROR(isolated_property_mutation_in_nonisolated_context,none,
678+
"actor-isolated %kind0 can not be %select{referenced|mutated}1 "
679+
"from a non-isolated context",
680+
(const ValueDecl *, bool))
677681

678682
// Yield usage errors
679683
ERROR(return_before_yield, none, "accessor must yield before returning",())

lib/SILOptimizer/Mandatory/FlowIsolation.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,22 @@ void Info::diagnoseAll(AnalysisInfo &info, bool forDeinit,
457457
// If the illegal use is a call to a defer, then recursively diagnose
458458
// all of the defer's uses, if this is the first time encountering it.
459459
if (auto *callee = getCallee(use)) {
460-
assert(info.haveDeferInfo(callee) && "non-defer call as a property use?");
461-
auto &defer = info.getOrCreateDeferInfo(callee);
462-
if (defer.setNonisolatedStart()) {
463-
defer.diagnoseEntireFunction(blame);
460+
if (info.haveDeferInfo(callee)) {
461+
auto &defer = info.getOrCreateDeferInfo(callee);
462+
if (defer.setNonisolatedStart()) {
463+
defer.diagnoseEntireFunction(blame);
464+
}
465+
continue;
464466
}
467+
468+
// Init accessor `setter` use.
469+
auto *accessor =
470+
cast<AccessorDecl>(callee->getLocation().getAsDeclContext());
471+
auto illegalLoc = use->getDebugLocation().getLocation();
472+
diag.diagnose(illegalLoc.getSourceLoc(),
473+
diag::isolated_property_mutation_in_nonisolated_context,
474+
accessor->getStorage(), accessor->isSetter())
475+
.warnUntilSwiftVersion(6);
465476
continue;
466477
}
467478

@@ -631,6 +642,37 @@ void AnalysisInfo::analyze(const SILArgument *selfParam) {
631642
}
632643
}
633644
}
645+
646+
// Detect and handle use of init accessor properties.
647+
if (callee->hasLocation()) {
648+
auto loc = callee->getLocation();
649+
if (auto *accessor =
650+
dyn_cast_or_null<AccessorDecl>(loc.getAsDeclContext())) {
651+
auto *storage = accessor->getStorage();
652+
653+
// Note 'nonisolated' property use.
654+
if (storage->getAttrs().hasAttribute<NonisolatedAttr>()) {
655+
markNonIsolated(user);
656+
continue;
657+
}
658+
659+
// Init accessor is used exclusively for initialization
660+
// of properties while 'self' is not fully initialized.
661+
if (accessor->isInitAccessor()) {
662+
markNonIsolated(user);
663+
continue;
664+
}
665+
666+
// Otherwise if this is an init accessor property, it's either
667+
// a call to a getter or a setter and should be treated like
668+
// an isolated computed property reference.
669+
670+
if (storage->hasInitAccessor()) {
671+
markPropertyUse(user);
672+
continue;
673+
}
674+
}
675+
}
634676
}
635677

636678
// For all other call-sites, uses of `self` are nonisolated.

test/Concurrency/flow_isolation.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,5 +757,63 @@ func testActorWithInitAccessorInit() {
757757
init(radians: Double) {
758758
self.radians = radians // Ok
759759
}
760+
761+
init(value: Double) {
762+
let escapingSelf: (Angle) -> Void = { _ in }
763+
764+
// degrees are initialized here via default value associated with radians
765+
766+
escapingSelf(self)
767+
768+
self.radians = 0
769+
// expected-warning@-1 {{actor-isolated property 'radians' can not be mutated from a non-isolated context; this is an error in Swift 6}}
770+
}
771+
}
772+
773+
@available(SwiftStdlib 5.1, *)
774+
actor EscapeBeforeFullInit {
775+
var _a: Int // expected-note {{'self._a' not initialized}}
776+
777+
var a: Int {
778+
@storageRestrictions(initializes: _a)
779+
init {
780+
_a = newValue
781+
}
782+
783+
get { _a }
784+
set { _a = newValue }
785+
}
786+
787+
init(v: Int) {
788+
let escapingSelf: (EscapeBeforeFullInit) -> Void = { _ in }
789+
790+
escapingSelf(self) // expected-error {{'self' used before all stored properties are initialized}}
791+
// expected-note@-1 {{after this closure involving 'self', only non-isolated properties of 'self' can be accessed from this init}}
792+
793+
self.a = v
794+
// expected-warning@-1 {{cannot access property '_a' here in non-isolated initializer; this is an error in Swift 6}}
795+
}
796+
}
797+
798+
@available(SwiftStdlib 5.1, *)
799+
actor NonisolatedAccessors {
800+
nonisolated var a: Int = 0 {
801+
init {
802+
}
803+
804+
get { 0 }
805+
set {}
806+
}
807+
808+
init(value: Int) {
809+
let escapingSelf: (NonisolatedAccessors) -> Void = { _ in }
810+
811+
// a is initialized here via default value
812+
813+
escapingSelf(self)
814+
815+
self.a = value // Ok (nonisolated)
816+
print(a) // Ok (nonisolated)
817+
}
760818
}
761819
}

0 commit comments

Comments
 (0)