Skip to content

Commit 73cabe3

Browse files
committed
Sema: Account for existential member access limitations in doesStorageProduceLValue
1 parent 1ce0aee commit 73cabe3

File tree

6 files changed

+354
-70
lines changed

6 files changed

+354
-70
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9876,8 +9876,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
98769876
const auto isUnsupportedExistentialMemberAccess = [&] {
98779877
// We may not be able to derive a well defined type for an existential
98789878
// member access if the member's signature references 'Self'.
9879-
if (instanceTy->isExistentialType() &&
9880-
decl->getDeclContext()->getSelfProtocolDecl()) {
9879+
if (instanceTy->isExistentialType()) {
98819880
switch (isMemberAvailableOnExistential(instanceTy, decl)) {
98829881
case ExistentialMemberAccessLimitation::Unsupported:
98839882
// TODO: Write-only accesses are not supported yet.

lib/Sema/OpenedExistentials.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,14 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
515515

516516
ExistentialMemberAccessLimitation
517517
swift::isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member) {
518+
auto *dc = member->getDeclContext();
519+
if (!dc->getSelfProtocolDecl()) {
520+
return ExistentialMemberAccessLimitation::None;
521+
}
518522

519523
auto &ctx = member->getASTContext();
520524
auto existentialSig = ctx.getOpenedExistentialSignature(baseTy);
521525

522-
auto *dc = member->getDeclContext();
523-
ASSERT(dc->getSelfProtocolDecl());
524-
525526
auto origParam = dc->getSelfInterfaceType()->castTo<GenericTypeParamType>();
526527
auto openedParam = existentialSig.SelfType->castTo<GenericTypeParamType>();
527528

lib/Sema/TypeOfReference.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -505,13 +505,33 @@ static bool doesStorageProduceLValue(
505505
if (!storage->isSetterAccessibleFrom(useDC))
506506
return false;
507507

508-
// if (baseType) {
509-
// isMemberAvailableOnExistential(baseType, storage)
510-
// }
511-
// If there is no base, or the base is an lvalue, then a reference
512-
// produces an lvalue.
513-
if (!baseType || baseType->is<LValueType>())
508+
// This path handles storage that is mutable in the given context.
509+
510+
if (!baseType) {
514511
return true;
512+
}
513+
514+
{
515+
const auto rValueInstanceTy =
516+
baseType->getRValueType()->getMetatypeInstanceType();
517+
if (rValueInstanceTy->isExistentialType()) {
518+
switch (isMemberAvailableOnExistential(rValueInstanceTy, storage)) {
519+
case ExistentialMemberAccessLimitation::Unsupported:
520+
case ExistentialMemberAccessLimitation::ReadOnly:
521+
// Never an lvalue because the current type system cannot represent the
522+
// setter's type out of context.
523+
return false;
524+
525+
case ExistentialMemberAccessLimitation::WriteOnly:
526+
case ExistentialMemberAccessLimitation::None:
527+
break;
528+
}
529+
}
530+
}
531+
532+
if (baseType->is<LValueType>()) {
533+
return true;
534+
}
515535

516536
// The base is an rvalue type. The only way an accessor can
517537
// produce an lvalue is if we have a property where both the
Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,15 @@
11
// RUN: %target-typecheck-verify-swift
22

33
// rdar://121214563
4+
// https://github.com/swiftlang/swift/issues/60619
45
// REQUIRES: rdar121214563
56

67
protocol Q {}
78

8-
protocol P {
9-
associatedtype T: Q
10-
var member: T { get set }
11-
var otherMember: any Q { get set }
12-
subscript(_: Int) -> T { get set }
13-
}
14-
159
func takesAny(x: any Q) {}
1610
func takesRValue(x: any Q) {}
1711
func takesInOut(x: inout any Q) {}
1812

19-
func f(data: inout any P) {
20-
takesAny(x: data.member)
21-
takesAny(x: data[0])
22-
23-
takesRValue(x: data.member)
24-
takesRValue(x: data[0])
25-
26-
takesInOut(x: &data.member) // expected-error {{cannot pass immutable value as inout argument: 'data' is immutable}}
27-
takesInOut(x: &data[0]) // expected-error {{cannot pass immutable value as inout argument: 'data' is immutable}}
28-
29-
takesInOut(x: &data.otherMember) // okay
30-
}
31-
3213
struct S {
3314
subscript<T: Q>(_ ct: T.Type) -> T {
3415
get { fatalError() }
@@ -41,28 +22,3 @@ func f(s: inout S, t: any Q.Type) -> (any Q) {
4122
takesRValue(x: s[t])
4223
takesInOut(x: &s[t]) // expected-error {{cannot pass immutable value as inout argument: 's' is immutable}}
4324
}
44-
45-
// https://github.com/apple/swift/issues/62219
46-
do {
47-
struct Action {
48-
var intVar: Int
49-
var strVar: String
50-
}
51-
52-
protocol TestDelegate: AnyObject {
53-
associatedtype ActionType
54-
var actions: [ActionType] { get set }
55-
}
56-
57-
class TestDelegateImpl: TestDelegate {
58-
typealias ActionType = Action
59-
var actions: [Action] = []
60-
}
61-
62-
class TestViewController {
63-
var testDelegate: (any TestDelegate)?
64-
func testFunc() {
65-
testDelegate?.actions.removeAll() // expected-error {{cannot use mutating member on immutable value: 'self' is immutable}}
66-
}
67-
}
68-
}

0 commit comments

Comments
 (0)