Skip to content

Commit 32eacc5

Browse files
authored
Merge pull request swiftlang#18608 from gregomni/rvalue-as-lvalue
[ConstraintSystem] New FailureDiagnostic for rvalues that should be lvalues
2 parents 29c8329 + aeb9627 commit 32eacc5

File tree

14 files changed

+122
-46
lines changed

14 files changed

+122
-46
lines changed

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7764,6 +7764,8 @@ bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
77647764
llvm::SmallDenseMap<Expr *, SmallVector<const ConstraintFix *, 4>>
77657765
fixesPerExpr;
77667766

7767+
applySolution(solution);
7768+
77677769
for (auto *fix : solution.Fixes)
77687770
fixesPerExpr[fix->getAnchor()].push_back(fix);
77697771

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.simplifyType(CS.getType(destExpr));
887+
TC.diagnose(loc, unknownDiagID, type)
887888
.highlight(immInfo.first->getSourceRange());
888889
}
889890

@@ -6153,33 +6154,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
61536154
if (!argExpr)
61546155
return true; // already diagnosed.
61556156

6156-
// A common error is to apply an operator that only has inout forms (e.g. +=)
6157-
// to non-lvalues (e.g. a local let). Produce a nice diagnostic for this
6158-
// case.
6159-
if (calleeInfo.closeness == CC_NonLValueInOut) {
6160-
Diag<StringRef> subElementDiagID;
6161-
Diag<Type> rvalueDiagID;
6162-
Expr *diagExpr = nullptr;
6163-
6164-
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
6165-
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
6166-
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
6167-
diagExpr = argExpr;
6168-
} else if (isa<BinaryExpr>(callExpr)) {
6169-
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
6170-
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
6171-
6172-
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
6173-
diagExpr = argTuple->getElement(0);
6174-
}
6175-
6176-
if (diagExpr) {
6177-
diagnoseSubElementFailure(diagExpr, callExpr->getFn()->getLoc(), CS,
6178-
subElementDiagID, rvalueDiagID);
6179-
return true;
6180-
}
6181-
}
6182-
61836157
// Handle argument label mismatches when we have multiple candidates.
61846158
if (calleeInfo.closeness == CC_ArgumentLabelMismatch) {
61856159
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"
@@ -532,3 +533,44 @@ bool MissingOptionalUnwrapFailure::diagnose() {
532533
.fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()}, "try!");
533534
return true;
534535
}
536+
537+
bool RValueTreatedAsLValueFailure::diagnose() {
538+
Diag<StringRef> subElementDiagID;
539+
Diag<Type> rvalueDiagID;
540+
Expr *diagExpr = getLocator()->getAnchor();
541+
SourceLoc loc;
542+
543+
auto callExpr = dyn_cast<ApplyExpr>(diagExpr);
544+
if (!callExpr)
545+
return false; // currently only creating these for args, so should be
546+
// unreachable
547+
548+
Expr *argExpr = callExpr->getArg();
549+
loc = callExpr->getFn()->getLoc();
550+
551+
if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
552+
subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
553+
rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
554+
diagExpr = argExpr;
555+
} else if (isa<BinaryExpr>(callExpr)) {
556+
subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
557+
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
558+
auto argTuple = dyn_cast<TupleExpr>(argExpr);
559+
diagExpr = argTuple->getElement(0);
560+
} else {
561+
auto lastPathElement = getLocator()->getPath().back();
562+
assert(lastPathElement.getKind() ==
563+
ConstraintLocator::PathElementKind::ApplyArgToParam);
564+
565+
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
566+
rvalueDiagID = diag::cannot_pass_rvalue_inout;
567+
if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
568+
diagExpr = argTuple->getElement(lastPathElement.getValue());
569+
else if (auto parens = dyn_cast<ParenExpr>(argExpr))
570+
diagExpr = parens->getSubExpr();
571+
}
572+
573+
diagnoseSubElementFailure(diagExpr, loc, getConstraintSystem(),
574+
subElementDiagID, rvalueDiagID);
575+
return true;
576+
}

lib/Sema/CSDiagnostics.h

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

348+
/// Diagnose errors associated with rvalues in positions
349+
/// where an lvalue is required, such as inout arguments.
350+
class RValueTreatedAsLValueFailure final : public FailureDiagnostic {
351+
352+
public:
353+
RValueTreatedAsLValueFailure(const Solution &solution, ConstraintLocator *locator)
354+
: FailureDiagnostic(nullptr, solution, locator) {}
355+
356+
bool diagnose() override;
357+
};
358+
348359
} // end namespace constraints
349360
} // end namespace swift
350361

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ AddAddressOf *AddAddressOf::create(ConstraintSystem &cs,
104104
return new (cs.getAllocator()) AddAddressOf(locator);
105105
}
106106

107+
bool TreatRValueAsLValue::diagnose(Expr *root, const Solution &solution) const {
108+
RValueTreatedAsLValueFailure failure(solution, getLocator());
109+
return failure.diagnose();
110+
}
111+
112+
TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs,
113+
ConstraintLocator *locator) {
114+
return new (cs.getAllocator()) TreatRValueAsLValue(locator);
115+
}
116+
117+
107118
bool CoerceToCheckedCast::diagnose(Expr *root, const Solution &solution) const {
108119
MissingForcedDowncastFailure failure(root, solution, getLocator());
109120
return failure.diagnose();

lib/Sema/CSFix.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ enum class FixKind : uint8_t {
6868

6969
/// Add a new conformance to the type to satisfy a requirement.
7070
AddConformance,
71+
72+
/// Treat rvalue as lvalue
73+
TreatRValueAsLValue,
7174
};
7275

7376
class ConstraintFix {
@@ -168,6 +171,20 @@ class AddAddressOf final : public ConstraintFix {
168171
static AddAddressOf *create(ConstraintSystem &cs, ConstraintLocator *locator);
169172
};
170173

174+
// Treat rvalue as if it was an lvalue
175+
class TreatRValueAsLValue final : public ConstraintFix {
176+
TreatRValueAsLValue(ConstraintLocator *locator)
177+
: ConstraintFix(FixKind::TreatRValueAsLValue, locator) {}
178+
179+
public:
180+
std::string getName() const override { return "treat rvalue as lvalue"; }
181+
182+
bool diagnose(Expr *root, const Solution &solution) const override;
183+
184+
static TreatRValueAsLValue *create(ConstraintSystem &cs, ConstraintLocator *locator);
185+
};
186+
187+
171188
/// Replace a coercion ('as') with a forced checked cast ('as!').
172189
class CoerceToCheckedCast final : public ConstraintFix {
173190
CoerceToCheckedCast(ConstraintLocator *locator)

lib/Sema/CSSimplify.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,10 +2439,16 @@ 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(
2450+
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
2451+
}
24462452
}
24472453
}
24482454

@@ -4980,6 +4986,16 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
49804986
return result;
49814987
}
49824988

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))
4994+
return SolutionKind::Error;
4995+
4996+
return result;
4997+
}
4998+
49834999
case FixKind::ExplicitlyEscaping:
49845000
case FixKind::CoerceToCheckedCast:
49855001
case FixKind::RelabelArguments:

lib/Sema/ConstraintLocator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ class ConstraintLocator : public llvm::FoldingSetNode {
343343
return PathElement(NamedTupleElement, position);
344344
}
345345

346-
/// Retrieve a patch element for an argument/parameter comparison in a
346+
/// Retrieve a path element for an argument/parameter comparison in a
347347
/// function application.
348348
static PathElement getApplyArgToParam(unsigned argIdx, unsigned paramIdx) {
349349
return PathElement(ApplyArgToParam, argIdx, paramIdx);

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

0 commit comments

Comments
 (0)