Skip to content

Commit 7b05352

Browse files
committed
[Sema] Improve invalid lvalue diagnostics for UnresolvedMemberExpr
We previously were not properly handling the diagnostics for using an rvalue implicit member on the left hand side of an assignment. This adds the proper handling and extends it for member chains.
1 parent 1892356 commit 7b05352

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,11 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
13141314
if (getContextualTypePurpose(diagExpr) == CTP_Condition)
13151315
return false;
13161316

1317+
// If the failure happened at the end of an unresolved member chain, it should
1318+
// be diagnosed instead as though it happened at the last element.
1319+
if (auto chainExpr = dyn_cast<UnresolvedMemberChainResultExpr>(diagExpr))
1320+
diagExpr = chainExpr->getSubExpr();
1321+
13171322
if (auto assignExpr = dyn_cast<AssignExpr>(diagExpr)) {
13181323
// Let's check whether this is an attempt to assign
13191324
// variable or property to itself.
@@ -1437,6 +1442,11 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
14371442
}
14381443
} else if (isa<SubscriptExpr>(diagExpr)) {
14391444
subElementDiagID = diag::assignment_subscript_has_immutable_base;
1445+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(diagExpr)) {
1446+
if (UME->hasArguments())
1447+
subElementDiagID = diag::assignment_lhs_is_apply_expression;
1448+
else
1449+
subElementDiagID = diag::assignment_lhs_is_immutable_property;
14401450
} else {
14411451
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
14421452
}
@@ -1903,6 +1913,18 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const {
19031913
return resolveImmutableBase(MRE->getBase());
19041914
}
19051915

1916+
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(expr)) {
1917+
auto loc = getConstraintLocator(UME, ConstraintLocator::UnresolvedMember);
1918+
auto member = getMemberRef(loc);
1919+
1920+
// If we can resolve a member, we can determine whether it is settable in
1921+
// this context.
1922+
if (member && member->isDecl() && isImmutable(member->getDecl()))
1923+
return {expr, member};
1924+
else
1925+
return {expr, None};
1926+
}
1927+
19061928
if (auto *DRE = dyn_cast<DeclRefExpr>(expr))
19071929
return {expr,
19081930
OverloadChoice(Type(), DRE->getDecl(), FunctionRefKind::Unapplied)};

lib/Sema/CSSimplify.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4314,6 +4314,9 @@ bool ConstraintSystem::repairFailures(
43144314
locator))
43154315
break;
43164316

4317+
if (repairByTreatingRValueAsLValue(lhs, rhs))
4318+
break;
4319+
43174320
// If there is a type mismatch here it's contextual e.g.,
43184321
// `let x: E = .foo(42)`, where `.foo` is a member of `E`
43194322
// but produces an incorrect type.

test/expr/delayed-ident/member_chains.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ struct ImplicitMembers: Equatable {
1515
}
1616

1717
static var implicit = ImplicitMembers()
18+
static let implicitLet = ImplicitMembers() // expected-note2 {{change 'let' to 'var' to make it mutable}}
19+
static var implicitImmutable: ImplicitMembers { ImplicitMembers() }
1820
static func createImplicit() -> ImplicitMembers {
1921
ImplicitMembers()
2022
}
@@ -61,6 +63,7 @@ struct ImplicitMembers: Equatable {
6163
get { ImplicitMembers() }
6264
set {}
6365
}
66+
subscript(immutable arg: Void) -> ImplicitMembers { ImplicitMembers() }
6467
subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } }
6568
subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } }
6669
subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } }
@@ -205,7 +208,22 @@ func testLValues() {
205208
.implicit.anotherOptionalMutable! = local;
206209
.implicit[()] = local;
207210
.implicit[()].anotherMutable = local;
208-
.optional?[optional: ()]?.anotherOptionalMutable! = local
211+
.optional?[optional: ()]?.anotherOptionalMutable! = local;
212+
213+
.implicitLet = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}}
214+
.implicitImmutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}}
215+
.createImplicit() = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}}
216+
.implicit.another = local; // expected-error {{cannot assign to property: 'another' is a get-only property}}
217+
.implicit[immutable: ()] = local; // expected-error {{cannot assign through subscript: subscript is get-only}}
218+
.implicit.getAnother() = local; // expected-error {{expression is not assignable: function call returns immutable value}}
219+
220+
.implicitLet.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}}
221+
.implicitImmutable.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}}
222+
.createImplicit().anotherMutable = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}}
223+
.implicit.another.anotherMutable = local; // expected-error {{cannot assign to property: 'another' is a get-only property}}
224+
.implicit[immutable: ()].anotherMutable = local; // expected-error {{cannot assign to property: subscript is get-only}}
225+
.implicit.getAnother().anotherMutable = local; // expected-error {{cannot assign to property: function call returns immutable value}}
226+
209227

210228
// FIXME: These should probably be allowed
211229
//.implicit.anotherOptionalMutable = local;

0 commit comments

Comments
 (0)