Skip to content

Commit 1e1564e

Browse files
authored
Merge pull request #70555 from xedin/issue-70550
[Concurrency] `memberAccessHasSpecialPermissionInSwift5` should treat…
2 parents b691f41 + 4275bbd commit 1e1564e

File tree

4 files changed

+146
-7
lines changed

4 files changed

+146
-7
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.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,10 +1772,20 @@ static bool memberAccessHasSpecialPermissionInSwift5(
17721772

17731773
// If the context in which we consider the access matches between the
17741774
// old (escaping-use restriction) and new (flow-isolation) contexts,
1775-
// and it is a stored property, then permit it here without any warning.
1775+
// and it is a stored or init accessor property, then permit it here
1776+
// without any warning.
17761777
// Later, flow-isolation pass will check and emit a warning if needed.
1777-
if (refCxt == oldFn && isStoredProperty(member))
1778-
return true;
1778+
if (refCxt == oldFn) {
1779+
if (isStoredProperty(member))
1780+
return true;
1781+
1782+
if (auto *var = dyn_cast<VarDecl>(member)) {
1783+
// Init accessor properties are permitted to access only stored
1784+
// properties.
1785+
if (var->hasInitAccessor())
1786+
return true;
1787+
}
1788+
}
17791789

17801790
// Otherwise, it's definitely going to be illegal, so warn and permit.
17811791
auto &diags = refCxt->getASTContext().Diags;

test/Concurrency/flow_isolation.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,3 +734,86 @@ actor CheckDeinitFromActor {
734734
ns = nil // expected-warning {{cannot access property 'ns' with a non-sendable type 'NonSendableType?' from non-isolated deinit; this is an error in Swift 6}}
735735
}
736736
}
737+
738+
// https://github.com/apple/swift/issues/70550
739+
func testActorWithInitAccessorInit() {
740+
@available(SwiftStdlib 5.1, *)
741+
actor Angle {
742+
var degrees: Double
743+
var radians: Double = 0 {
744+
@storageRestrictions(initializes: degrees)
745+
init(initialValue) {
746+
degrees = initialValue * 180 / .pi
747+
}
748+
749+
get { degrees * .pi / 180 }
750+
set { degrees = newValue * 180 / .pi }
751+
}
752+
753+
init(degrees: Double) {
754+
self.degrees = degrees // Ok
755+
}
756+
757+
init(radians: Double) {
758+
self.radians = radians // Ok
759+
}
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+
}
818+
}
819+
}

0 commit comments

Comments
 (0)