Skip to content

Commit 1b397ed

Browse files
committed
[ConstraintSystem] Be more principled about recording r-value -> l-value fix
Instead of recording `TreatRValueAsLValue` fix directly inside `matchTypes`, let's move towards recording it specifically for each possible case in `repairFailures` which makes it a lot easier to determine what other fixes could be applied (if any).
1 parent 45a3a53 commit 1b397ed

File tree

3 files changed

+86
-47
lines changed

3 files changed

+86
-47
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,7 +2291,8 @@ repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType,
22912291
/// \return true if at least some of the failures has been repaired
22922292
/// successfully, which allows type matcher to continue.
22932293
bool ConstraintSystem::repairFailures(
2294-
Type lhs, Type rhs, SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
2294+
Type lhs, Type rhs, ConstraintKind matchKind,
2295+
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
22952296
ConstraintLocatorBuilder locator) {
22962297
SmallVector<LocatorPathElt, 4> path;
22972298
auto *anchor = locator.getLocatorParts(path);
@@ -2375,10 +2376,43 @@ bool ConstraintSystem::repairFailures(
23752376
return true;
23762377
};
23772378

2379+
auto repairByTreatingRValueAsLValue = [&](Type lhs, Type rhs) -> bool {
2380+
if (!lhs->is<LValueType>() &&
2381+
(rhs->is<LValueType>() || rhs->is<InOutType>())) {
2382+
// Conversion from l-value to inout in an operator argument
2383+
// position (which doesn't require explicit `&`) decays into
2384+
// a `Bind` of involved object types, same goes for explicit
2385+
// `&` conversion from l-value to inout type.
2386+
auto kind = (isa<InOutExpr>(anchor) ||
2387+
(rhs->is<InOutType>() &&
2388+
matchKind == ConstraintKind::OperatorArgumentConversion))
2389+
? ConstraintKind::Bind
2390+
: matchKind;
2391+
2392+
auto result = matchTypes(lhs, rhs->getWithoutSpecifierType(), kind,
2393+
TMF_ApplyingFix, locator);
2394+
2395+
if (result.isSuccess()) {
2396+
conversionsOrFixes.push_back(
2397+
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
2398+
return true;
2399+
}
2400+
}
2401+
2402+
return false;
2403+
};
2404+
23782405
if (path.empty()) {
23792406
if (!anchor)
23802407
return false;
23812408

2409+
// This could be:
2410+
// - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`.
2411+
// - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are
2412+
// immutable or a subscript e.g. `foo["bar"]! = 42`.
2413+
if (repairByTreatingRValueAsLValue(lhs, rhs))
2414+
return true;
2415+
23822416
// If method reference forms a value type of the key path,
23832417
// there is going to be a constraint to match result of the
23842418
// member lookup to the generic parameter `V` of *KeyPath<R, V>
@@ -2453,6 +2487,18 @@ bool ConstraintSystem::repairFailures(
24532487
if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
24542488
break;
24552489

2490+
// Argument is a r-value but parameter expects an l-value e.g.
2491+
//
2492+
// func foo(_ x: inout Int) {}
2493+
// let x: Int = 42
2494+
// foo(x) // `x` can't be converted to `inout Int`.
2495+
//
2496+
// This has to happen before checking for optionality mismatch
2497+
// because otherwise `Int? arg conv inout Int` is going to get
2498+
// fixed as 2 fixes - "force unwrap" + r-value -> l-value mismatch.
2499+
if (repairByTreatingRValueAsLValue(lhs, rhs))
2500+
break;
2501+
24562502
if (lhs->getOptionalObjectType() && !rhs->getOptionalObjectType()) {
24572503
conversionsOrFixes.push_back(
24582504
ForceOptional::create(*this, lhs, lhs->getOptionalObjectType(), loc));
@@ -2477,6 +2523,7 @@ bool ConstraintSystem::repairFailures(
24772523
},
24782524
rhs)) {
24792525
conversionsOrFixes.push_back(fix);
2526+
break;
24802527
}
24812528

24822529
// If argument in l-value type and parameter is `inout` or a pointer,
@@ -2488,25 +2535,28 @@ bool ConstraintSystem::repairFailures(
24882535
ConstraintKind::ArgumentConversion,
24892536
TypeMatchFlags::TMF_ApplyingFix, locator);
24902537

2491-
if (result.isSuccess())
2538+
if (result.isSuccess()) {
24922539
conversionsOrFixes.push_back(AddAddressOf::create(
24932540
*this, lhs, rhs, getConstraintLocator(locator)));
2541+
break;
2542+
}
24942543
}
24952544

24962545
// If the argument is inout and the parameter is not inout or a pointer,
24972546
// suggest removing the &.
24982547
if (lhs->is<InOutType>() && !rhs->is<InOutType>()) {
24992548
auto objectType = rhs->lookThroughAllOptionalTypes();
2500-
if (objectType->getAnyPointerElementType())
2501-
break;
2502-
2503-
auto result = matchTypes(lhs->getInOutObjectType(), rhs,
2504-
ConstraintKind::ArgumentConversion,
2505-
TypeMatchFlags::TMF_ApplyingFix, locator);
2506-
2507-
if (result.isSuccess())
2508-
conversionsOrFixes.push_back(RemoveAddressOf::create(*this, lhs,
2509-
rhs, getConstraintLocator(locator)));
2549+
if (!objectType->getAnyPointerElementType()) {
2550+
auto result = matchTypes(lhs->getInOutObjectType(), rhs,
2551+
ConstraintKind::ArgumentConversion,
2552+
TypeMatchFlags::TMF_ApplyingFix, locator);
2553+
2554+
if (result.isSuccess()) {
2555+
conversionsOrFixes.push_back(RemoveAddressOf::create(
2556+
*this, lhs, rhs, getConstraintLocator(locator)));
2557+
break;
2558+
}
2559+
}
25102560
}
25112561

25122562
break;
@@ -2672,7 +2722,15 @@ bool ConstraintSystem::repairFailures(
26722722
break;
26732723
}
26742724

2675-
case ConstraintLocator::FunctionResult: {
2725+
case ConstraintLocator::Member:
2726+
case ConstraintLocator::FunctionResult:
2727+
case ConstraintLocator::DynamicLookupResult: {
2728+
// Most likely this is an attempt to use get-only subscript as mutating,
2729+
// or assign a value of a result of function/member ref e.g. `foo() = 42`
2730+
// or `foo.bar = 42`, or `foo.bar()! = 42`.
2731+
if (repairByTreatingRValueAsLValue(rhs, lhs))
2732+
break;
2733+
26762734
// `apply argument` -> `arg/param compare` ->
26772735
// `@autoclosure result` -> `function result`
26782736
if (path.size() > 3) {
@@ -2727,6 +2785,13 @@ bool ConstraintSystem::repairFailures(
27272785
break;
27282786
}
27292787

2788+
case ConstraintLocator::SubscriptMember: {
2789+
if (repairByTreatingRValueAsLValue(lhs, rhs))
2790+
break;
2791+
2792+
break;
2793+
}
2794+
27302795
default:
27312796
break;
27322797
}
@@ -3517,25 +3582,11 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
35173582
getConstraintLocator(locator)));
35183583
}
35193584
}
3520-
3521-
if (!type1->is<LValueType>() && type2->is<InOutType>()) {
3522-
// If we have a concrete type that's an rvalue, "fix" it.
3523-
conversionsOrFixes.push_back(
3524-
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
3525-
}
3526-
}
3527-
3528-
if (attemptFixes && type2->is<LValueType>()) {
3529-
conversionsOrFixes.push_back(
3530-
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
3531-
} else if (attemptFixes && kind == ConstraintKind::Bind && type1->is<LValueType>()) {
3532-
conversionsOrFixes.push_back(
3533-
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
35343585
}
35353586

35363587
// Attempt to repair any failures identifiable at this point.
35373588
if (attemptFixes) {
3538-
if (repairFailures(type1, type2, conversionsOrFixes, locator)) {
3589+
if (repairFailures(type1, type2, kind, conversionsOrFixes, locator)) {
35393590
if (conversionsOrFixes.empty())
35403591
return getTypeMatchSuccess();
35413592
}
@@ -5055,6 +5106,7 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
50555106
// overload here, that would help if such subscript has
50565107
// not been provided.
50575108
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
5109+
return TreatRValueAsLValue::create(cs, cs.getConstraintLocator(locator));
50585110
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
50595111
break;
50605112
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
@@ -7238,20 +7290,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
72387290
return result;
72397291
}
72407292

7241-
case FixKind::TreatRValueAsLValue: {
7242-
if (type2->is<LValueType>() || type2->is<InOutType>())
7243-
type1 = LValueType::get(type1);
7244-
else
7245-
type2 = LValueType::get(type2);
7246-
auto result = matchTypes(type1, type2,
7247-
matchKind, subflags, locator);
7248-
if (result == SolutionKind::Solved)
7249-
if (recordFix(fix))
7250-
return SolutionKind::Error;
7251-
7252-
return result;
7253-
}
7254-
72557293
case FixKind::AutoClosureForwarding: {
72567294
if (recordFix(fix))
72577295
return SolutionKind::Error;
@@ -7301,6 +7339,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
73017339
case FixKind::RemoveReturn:
73027340
case FixKind::AddConformance:
73037341
case FixKind::RemoveAddressOf:
7342+
case FixKind::TreatRValueAsLValue:
73047343
case FixKind::SkipSameTypeRequirement:
73057344
case FixKind::SkipSuperclassRequirement:
73067345
case FixKind::AddMissingArguments:

lib/Sema/ConstraintSystem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2765,7 +2765,7 @@ class ConstraintSystem {
27652765
/// Attempt to repair typing failures and record fixes if needed.
27662766
/// \return true if at least some of the failures has been repaired
27672767
/// successfully, which allows type matcher to continue.
2768-
bool repairFailures(Type lhs, Type rhs,
2768+
bool repairFailures(Type lhs, Type rhs, ConstraintKind matchKind,
27692769
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
27702770
ConstraintLocatorBuilder locator);
27712771

test/stmt/statements.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ func funcdecl5(_ a: Int, y: Int) {
3939
x = y
4040
(x) = y
4141

42-
1 = x // expected-error {{cannot assign to a literal value}}
43-
(1) = x // expected-error {{cannot assign to a literal value}}
44-
"string" = "other" // expected-error {{cannot assign to a literal value}}
42+
1 = x // expected-error {{cannot assign to value: literals are not mutable}}
43+
(1) = x // expected-error {{cannot assign to value: literals are not mutable}}
44+
"string" = "other" // expected-error {{cannot assign to value: literals are not mutable}}
4545
[1, 1, 1, 1] = [1, 1] // expected-error {{cannot assign to immutable expression of type '[Int]}}
4646
1.0 = x // expected-error {{cannot assign to a literal value}}
47-
nil = 1 // expected-error {{cannot assign to a literal value}}
47+
nil = 1 // expected-error {{cannot assign to value: literals are not mutable}}
4848

4949
(x:1).x = 1 // expected-error {{cannot assign to immutable expression of type 'Int'}}
5050
var tup : (x:Int, y:Int)

0 commit comments

Comments
 (0)