Skip to content

Commit 9e0dae5

Browse files
committed
Start changing over rvalue-vs-lvalue errors to be done via constraint system fixes. For this first commit, just handling inout parameter problems.
1 parent 2f1b464 commit 9e0dae5

File tree

7 files changed

+86
-40
lines changed

7 files changed

+86
-40
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ static void fixItChangeInoutArgType(const Expr * arg,
738738
.fixItReplaceChars(startLoc, endLoc, scratch);
739739
}
740740

741-
static void diagnoseSubElementFailure(Expr *destExpr,
741+
void swift::diagnoseSubElementFailure(Expr *destExpr,
742742
SourceLoc loc,
743743
ConstraintSystem &CS,
744744
Diag<StringRef> diagID,
@@ -883,7 +883,8 @@ static void diagnoseSubElementFailure(Expr *destExpr,
883883
}
884884
}
885885

886-
TC.diagnose(loc, unknownDiagID, CS.getType(destExpr))
886+
auto type = destExpr->getType() ?: CS.getType(destExpr);
887+
TC.diagnose(loc, unknownDiagID, type)
887888
.highlight(immInfo.first->getSourceRange());
888889
}
889890

@@ -6157,33 +6158,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
61576158
if (!argExpr)
61586159
return true; // already diagnosed.
61596160

6160-
// A common error is to apply an operator that only has inout forms (e.g. +=)
6161-
// to non-lvalues (e.g. a local let). Produce a nice diagnostic for this
6162-
// case.
6163-
if (calleeInfo.closeness == CC_NonLValueInOut) {
6164-
Diag<StringRef> subElementDiagID;
6165-
Diag<Type> rvalueDiagID;
6166-
Expr *diagExpr = nullptr;
6167-
6168-
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
6169-
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
6170-
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
6171-
diagExpr = argExpr;
6172-
} else if (isa<BinaryExpr>(callExpr)) {
6173-
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
6174-
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
6175-
6176-
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
6177-
diagExpr = argTuple->getElement(0);
6178-
}
6179-
6180-
if (diagExpr) {
6181-
diagnoseSubElementFailure(diagExpr, callExpr->getFn()->getLoc(), CS,
6182-
subElementDiagID, rvalueDiagID);
6183-
return true;
6184-
}
6185-
}
6186-
61876161
// Handle argument label mismatches when we have multiple candidates.
61886162
if (calleeInfo.closeness == CC_ArgumentLabelMismatch) {
61896163
auto args = decomposeArgType(CS.getType(argExpr), argLabels);

lib/Sema/CSDiag.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ namespace swift {
2626
/// UnresolvedType.
2727
Type replaceTypeParametersWithUnresolved(Type ty);
2828
Type replaceTypeVariablesWithUnresolved(Type ty);
29-
29+
30+
/// Diagnose lvalue expr error.
31+
void diagnoseSubElementFailure(Expr *destExpr, SourceLoc loc,
32+
constraints::ConstraintSystem &CS,
33+
Diag<StringRef> diagID,
34+
Diag<Type> unknownDiagID);
3035
};
3136

3237
#endif /* SWIFT_SEMA_CSDIAG_H */

lib/Sema/CSDiagnostics.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "CSDiagnostics.h"
1818
#include "ConstraintSystem.h"
19+
#include "CSDiag.h"
1920
#include "MiscDiagnostics.h"
2021
#include "swift/AST/Expr.h"
2122
#include "swift/AST/GenericSignature.h"
@@ -527,3 +528,44 @@ bool MissingOptionalUnwrapFailure::diagnose() {
527528
.fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()}, "try!");
528529
return true;
529530
}
531+
532+
bool RValueTreatedAsLValueFailure::diagnose() {
533+
if (auto callExpr = dyn_cast<ApplyExpr>(getAnchor())) {
534+
Expr *argExpr = callExpr->getArg();
535+
536+
Diag<StringRef> subElementDiagID;
537+
Diag<Type> rvalueDiagID;
538+
Expr *diagExpr = nullptr;
539+
540+
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
541+
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
542+
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
543+
diagExpr = argExpr;
544+
} else if (isa<BinaryExpr>(callExpr)) {
545+
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
546+
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
547+
auto argTuple = dyn_cast<TupleExpr>(argExpr);
548+
diagExpr = argTuple->getElement(0);
549+
} else {
550+
auto lastPathElement = getLocator()->getPath().back();
551+
assert(lastPathElement.getKind() == ConstraintLocator::PathElementKind::ApplyArgToParam);
552+
553+
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
554+
rvalueDiagID = diag::cannot_pass_rvalue_inout;
555+
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
556+
diagExpr = argTuple->getElement(lastPathElement.getValue());
557+
else if (auto parens = dyn_cast<ParenExpr>(argExpr))
558+
diagExpr = parens->getSubExpr();
559+
}
560+
561+
// FIXME: Would like for this to be unnecessary.
562+
getConstraintSystem().TC.typeCheckExpression(diagExpr, getConstraintSystem().DC);
563+
564+
diagnoseSubElementFailure(diagExpr, callExpr->getFn()->getLoc(),
565+
getConstraintSystem(),
566+
subElementDiagID, rvalueDiagID);
567+
return true;
568+
}
569+
// These fixes are only being created for matching arg types right now, so this is unreachable.
570+
return false;
571+
}

lib/Sema/CSDiagnostics.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ class MissingOptionalUnwrapFailure final : public FailureDiagnostic {
294294
bool diagnose() override;
295295
};
296296

297+
/// Diagnose errors associated with rvalues in positions
298+
/// where an lvalue is required, such as inout arguments.
299+
class RValueTreatedAsLValueFailure final : public FailureDiagnostic {
300+
301+
public:
302+
RValueTreatedAsLValueFailure(const Solution &solution, ConstraintLocator *locator)
303+
: FailureDiagnostic(nullptr, solution, locator) {}
304+
305+
bool diagnose() override;
306+
};
307+
297308
} // end namespace constraints
298309
} // end namespace swift
299310

lib/Sema/CSSimplify.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,10 +2439,15 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
24392439
ForceDowncast::create(*this, type2, getConstraintLocator(locator)));
24402440
}
24412441

2442-
// If we're converting an lvalue to an inout type, add the missing '&'.
2443-
if (type2->getRValueType()->is<InOutType>() && type1->is<LValueType>()) {
2444-
conversionsOrFixes.push_back(
2442+
if (type2->getRValueType()->is<InOutType>()) {
2443+
if (type1->is<LValueType>()) {
2444+
// If we're converting an lvalue to an inout type, add the missing '&'.
2445+
conversionsOrFixes.push_back(
24452446
AddAddressOf::create(*this, getConstraintLocator(locator)));
2447+
} else if (!isTypeVarOrMember1) {
2448+
// If we have a concrete type that's an rvalue, "fix" it.
2449+
conversionsOrFixes.push_back(FixKind::TreatRValueAsLValue);
2450+
}
24462451
}
24472452
}
24482453

@@ -4981,6 +4986,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
49814986
return result;
49824987
}
49834988

4989+
case FixKind::TreatRValueAsLValue: {
4990+
auto result = matchTypes(LValueType::get(type1), type2,
4991+
matchKind, subflags, locator);
4992+
if (result == SolutionKind::Solved)
4993+
if (recordFix(fix, locator))
4994+
return SolutionKind::Error;
4995+
4996+
return result;
4997+
}
4998+
49844999
case FixKind::ExplicitlyEscaping:
49855000
case FixKind::CoerceToCheckedCast:
49865001
case FixKind::RelabelArguments:

test/Constraints/diagnostics.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -719,14 +719,13 @@ func overloadSetResultType(_ a : Int, b : Int) -> Int {
719719
}
720720

721721
postfix operator +++
722-
postfix func +++ <T>(_: inout T) -> T { fatalError() } // expected-note {{in call to operator '+++'}}
722+
postfix func +++ <T>(_: inout T) -> T { fatalError() }
723723

724724
// <rdar://problem/21523291> compiler error message for mutating immutable field is incorrect
725725
func r21523291(_ bytes : UnsafeMutablePointer<UInt8>) {
726-
let i = 42
726+
let i = 42 // expected-note {{change 'let' to 'var' to make it mutable}}
727727

728-
// FIXME: rdar://41416382
729-
_ = bytes[i+++] // expected-error {{generic parameter 'T' could not be inferred}}
728+
_ = bytes[i+++] // expected-error {{cannot pass immutable value to mutating operator: 'i' is a 'let' constant}}
730729
}
731730

732731

test/Constraints/lvalues.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ f2(&non_settable_x) // expected-error{{cannot pass immutable value as inout argu
7676
f1(&non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
7777
// - inout assignment
7878
non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
79-
+++non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
79+
+++non_settable_x // expected-error{{cannot pass immutable value to mutating operator: 'non_settable_x' is a get-only property}}
8080

8181
// non-settable property is non-settable:
8282
z.non_settable_x = x // expected-error{{cannot assign to property: 'non_settable_x' is a get-only property}}
8383
f2(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
8484
f1(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
8585
z.non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
86-
+++z.non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
86+
+++z.non_settable_x // expected-error{{cannot pass immutable value to mutating operator: 'non_settable_x' is a get-only property}}
8787

8888
// non-settable subscript is non-settable:
8989
z[0] = 0.0 // expected-error{{cannot assign through subscript: subscript is get-only}}
@@ -97,7 +97,7 @@ fz().settable_x = x // expected-error{{cannot assign to property: 'fz' returns i
9797
f2(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
9898
f1(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
9999
fz().settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'fz' returns immutable value}}
100-
+++fz().settable_x // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
100+
+++fz().settable_x // expected-error{{cannot pass immutable value to mutating operator: 'fz' returns immutable value}}
101101

102102
// settable property of an rvalue reference type IS SETTABLE:
103103
fref().property = 0.0

0 commit comments

Comments
 (0)