Skip to content

Commit c95cfc6

Browse files
authored
Merge pull request #18950 from gregomni/rvalue-as-lvalue
[Diagnostics] Adding assignments directly to CS and diagnosing from there.
2 parents b43654a + 6d5a69c commit c95cfc6

17 files changed

+209
-182
lines changed

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3832,18 +3832,11 @@ namespace {
38323832
}
38333833

38343834
Expr *visitAssignExpr(AssignExpr *expr) {
3835-
// Compute the type to which the source must be converted to allow
3836-
// assignment to the destination.
3837-
//
3838-
// FIXME: This is also computed when the constraint system is set up.
3839-
auto destTy = cs.computeAssignDestType(expr->getDest(), expr->getLoc());
3840-
if (!destTy)
3841-
return nullptr;
3842-
38433835
// Convert the source to the simplified destination type.
3836+
auto destTy = simplifyType(cs.getType(expr->getDest()));
38443837
auto locator =
38453838
ConstraintLocatorBuilder(cs.getConstraintLocator(expr->getSrc()));
3846-
Expr *src = coerceToType(expr->getSrc(), destTy, locator);
3839+
Expr *src = coerceToType(expr->getSrc(), destTy->getRValueType(), locator);
38473840
if (!src)
38483841
return nullptr;
38493842

lib/Sema/CSDiag.cpp

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,17 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) {
375375
return { expr, member };
376376
}
377377

378+
if (auto tupleExpr = dyn_cast<TupleExpr>(SE->getIndex())) {
379+
if (tupleExpr->getNumElements() == 1 && tupleExpr->getElementName(0).str() == "keyPath") {
380+
auto indexType = CS.simplifyType(CS.getType(tupleExpr->getElement(0)));
381+
if (auto bgt = indexType->getAs<BoundGenericType>()) {
382+
auto decl = bgt->getDecl();
383+
if (decl == CS.getASTContext().getKeyPathDecl())
384+
return resolveImmutableBase(tupleExpr->getElement(0), CS);
385+
}
386+
}
387+
}
388+
378389
// If it is settable, then the base must be the problem, recurse.
379390
return resolveImmutableBase(SE->getBase(), CS);
380391
}
@@ -430,6 +441,9 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) {
430441
if (!isa<LoadExpr>(ICE->getSubExpr()))
431442
return resolveImmutableBase(ICE->getSubExpr(), CS);
432443

444+
if (auto *SAE = dyn_cast<SelfApplyExpr>(expr))
445+
return resolveImmutableBase(SAE->getFn(), CS);
446+
433447
return { expr, nullptr };
434448
}
435449

@@ -526,7 +540,11 @@ void swift::diagnoseSubElementFailure(Expr *destExpr,
526540
message += VD->getName().str().str();
527541
message += "'";
528542

529-
if (VD->isCaptureList())
543+
auto type = CS.simplifyType(CS.getType(immInfo.first));
544+
auto bgt = type ? type->getAs<BoundGenericType>() : nullptr;
545+
if (bgt && bgt->getDecl() == CS.getASTContext().getKeyPathDecl())
546+
message += " is a read-only key path";
547+
else if (VD->isCaptureList())
530548
message += " is an immutable capture";
531549
else if (VD->isImplicit())
532550
message += " is immutable";
@@ -584,6 +602,14 @@ void swift::diagnoseSubElementFailure(Expr *destExpr,
584602
return;
585603
}
586604

605+
// If a keypath was the problem but wasn't resolved into a vardecl
606+
// it is ambiguous or unable to be used for setting.
607+
if (auto *KPE = dyn_cast_or_null<KeyPathExpr>(immInfo.first)) {
608+
TC.diagnose(loc, diagID, "immutable key path")
609+
.highlight(KPE->getSourceRange());
610+
return;
611+
}
612+
587613
// If the expression is the result of a call, it is an rvalue, not a mutable
588614
// lvalue.
589615
if (auto *AE = dyn_cast<ApplyExpr>(immInfo.first)) {
@@ -3087,44 +3113,30 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
30873113

30883114
/// When an assignment to an expression is detected and the destination is
30893115
/// invalid, emit a detailed error about the condition.
3090-
void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy,
3116+
bool ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy,
30913117
SourceLoc equalLoc) {
30923118
auto &TC = getTypeChecker();
30933119

3094-
// Diagnose obvious assignments to literals.
3095-
if (isa<LiteralExpr>(dest->getValueProvidingExpr())) {
3096-
TC.diagnose(equalLoc, diag::cannot_assign_to_literal);
3097-
return;
3098-
}
3099-
3100-
// Diagnose assignments to let-properties in delegating initializers.
3101-
if (auto *member = dyn_cast<UnresolvedDotExpr>(dest)) {
3102-
if (auto *ctor = dyn_cast<ConstructorDecl>(DC)) {
3103-
if (auto *baseRef = dyn_cast<DeclRefExpr>(member->getBase())) {
3104-
if (baseRef->getDecl() == ctor->getImplicitSelfDecl() &&
3105-
ctor->getDelegatingOrChainedInitKind(nullptr) ==
3106-
ConstructorDecl::BodyInitKind::Delegating) {
3107-
auto resolved = resolveImmutableBase(member, *this);
3108-
assert(resolved.first == member);
3109-
TC.diagnose(equalLoc, diag::assignment_let_property_delegating_init,
3110-
member->getName());
3111-
if (resolved.second) {
3112-
TC.diagnose(resolved.second, diag::decl_declared_here,
3113-
member->getName());
3114-
}
3115-
return;
3116-
}
3117-
}
3120+
// Assignments to let-properties in delegating initializers will be caught
3121+
// elsewhere now, so if we see them here, it isn't an assignment problem.
3122+
if (auto *ctor = dyn_cast<ConstructorDecl>(DC)) {
3123+
DeclRefExpr *baseRef = nullptr;
3124+
if (auto *member = dyn_cast<UnresolvedDotExpr>(dest)) {
3125+
baseRef = dyn_cast<DeclRefExpr>(member->getBase());
3126+
} else if (auto *member = dyn_cast<MemberRefExpr>(dest)) {
3127+
if (auto *load = dyn_cast<LoadExpr>(member->getBase()))
3128+
baseRef = dyn_cast<DeclRefExpr>(load->getSubExpr());
3129+
}
3130+
if (baseRef && baseRef->getDecl() == ctor->getImplicitSelfDecl() &&
3131+
ctor->getDelegatingOrChainedInitKind(nullptr) ==
3132+
ConstructorDecl::BodyInitKind::Delegating) {
3133+
return false;
31183134
}
31193135
}
31203136

31213137
Diag<StringRef> diagID;
3122-
if (isa<ApplyExpr>(dest))
3138+
if (isa<ApplyExpr>(dest) || isa<SelfApplyExpr>(dest))
31233139
diagID = diag::assignment_lhs_is_apply_expression;
3124-
else if (isa<DeclRefExpr>(dest))
3125-
diagID = diag::assignment_lhs_is_immutable_variable;
3126-
else if (isa<ForceValueExpr>(dest))
3127-
diagID = diag::assignment_bang_has_immutable_subcomponent;
31283140
else if (isa<UnresolvedDotExpr>(dest) || isa<MemberRefExpr>(dest))
31293141
diagID = diag::assignment_lhs_is_immutable_property;
31303142
else if (auto sub = dyn_cast<SubscriptExpr>(dest)) {
@@ -3137,12 +3149,12 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy,
31373149
sub->getArgumentLabels().size() == 1 &&
31383150
sub->getArgumentLabels().front() == TC.Context.Id_dynamicMember)
31393151
diagID = diag::assignment_dynamic_property_has_immutable_base;
3140-
} else {
3152+
} else
31413153
diagID = diag::assignment_lhs_is_immutable_variable;
3142-
}
31433154

31443155
diagnoseSubElementFailure(dest, equalLoc, *this, diagID,
31453156
diag::assignment_lhs_not_lvalue);
3157+
return true;
31463158
}
31473159

31483160
//===----------------------------------------------------------------------===//
@@ -6231,8 +6243,8 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) {
62316243
if (diagnoseSubscriptErrors(subscriptExpr, /* inAssignmentDestination = */ true))
62326244
return true;
62336245
}
6234-
CS.diagnoseAssignmentFailure(destExpr, destType, assignExpr->getLoc());
6235-
return true;
6246+
if (CS.diagnoseAssignmentFailure(destExpr, destType, assignExpr->getLoc()))
6247+
return true;
62366248
}
62376249

62386250
auto *srcExpr = assignExpr->getSrc();

lib/Sema/CSDiagnostics.cpp

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,10 @@ bool MissingForcedDowncastFailure::diagnoseAsError() {
279279

280280
auto &TC = getTypeChecker();
281281

282-
auto *coerceExpr = dyn_cast<CoerceExpr>(getAnchor());
282+
auto *expr = getAnchor();
283+
if (auto *assignExpr = dyn_cast<AssignExpr>(expr))
284+
expr = assignExpr->getSrc();
285+
auto *coerceExpr = dyn_cast<CoerceExpr>(expr);
283286
if (!coerceExpr)
284287
return false;
285288

@@ -333,6 +336,8 @@ bool MissingExplicitConversionFailure::diagnoseAsError() {
333336
auto &TC = getTypeChecker();
334337

335338
auto *anchor = getAnchor();
339+
if (auto *assign = dyn_cast<AssignExpr>(anchor))
340+
anchor = assign->getSrc();
336341
if (auto *paren = dyn_cast<ParenExpr>(anchor))
337342
anchor = paren->getSubExpr();
338343

@@ -540,6 +545,10 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() {
540545
return false;
541546

542547
auto *anchor = getAnchor();
548+
549+
if (auto assignExpr = dyn_cast<AssignExpr>(anchor))
550+
anchor = assignExpr->getSrc();
551+
543552
auto *unwrapped = anchor->getValueProvidingExpr();
544553
auto type = getType(anchor)->getRValueType();
545554

@@ -554,9 +563,13 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() {
554563

555564
bool RValueTreatedAsLValueFailure::diagnoseAsError() {
556565
Diag<StringRef> subElementDiagID;
557-
Diag<Type> rvalueDiagID;
566+
Diag<Type> rvalueDiagID = diag::assignment_lhs_not_lvalue;
558567
Expr *diagExpr = getLocator()->getAnchor();
559-
SourceLoc loc;
568+
SourceLoc loc = diagExpr->getLoc();
569+
570+
if (auto assignExpr = dyn_cast<AssignExpr>(diagExpr)) {
571+
diagExpr = assignExpr->getDest();
572+
}
560573

561574
if (auto callExpr = dyn_cast<ApplyExpr>(diagExpr)) {
562575
Expr *argExpr = callExpr->getArg();
@@ -571,7 +584,7 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
571584
rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
572585
auto argTuple = dyn_cast<TupleExpr>(argExpr);
573586
diagExpr = argTuple->getElement(0);
574-
} else {
587+
} else if (getLocator()->getPath().size() > 0) {
575588
auto lastPathElement = getLocator()->getPath().back();
576589
assert(lastPathElement.getKind() ==
577590
ConstraintLocator::PathElementKind::ApplyArgToParam);
@@ -582,10 +595,11 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
582595
diagExpr = argTuple->getElement(lastPathElement.getValue());
583596
else if (auto parens = dyn_cast<ParenExpr>(argExpr))
584597
diagExpr = parens->getSubExpr();
598+
} else {
599+
subElementDiagID = diag::assignment_lhs_is_apply_expression;
585600
}
586601
} else if (auto inoutExpr = dyn_cast<InOutExpr>(diagExpr)) {
587-
Type type = getConstraintSystem().getType(inoutExpr);
588-
if (auto restriction = restrictionForType(type)) {
602+
if (auto restriction = getRestrictionForType(getType(inoutExpr))) {
589603
PointerTypeKind pointerKind;
590604
if (restriction->second == ConversionRestrictionKind::ArrayToPointer &&
591605
restriction->first->getAnyPointerElementType(pointerKind) &&
@@ -603,10 +617,42 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
603617

604618
subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
605619
rvalueDiagID = diag::cannot_pass_rvalue_inout;
606-
loc = diagExpr->getLoc();
607620
diagExpr = inoutExpr->getSubExpr();
621+
} else if (isa<DeclRefExpr>(diagExpr)) {
622+
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
623+
} else if (isa<ForceValueExpr>(diagExpr)) {
624+
subElementDiagID = diag::assignment_bang_has_immutable_subcomponent;
625+
} else if (isa<MemberRefExpr>(diagExpr)) {
626+
subElementDiagID = diag::assignment_lhs_is_immutable_property;
627+
} else if (auto member = dyn_cast<UnresolvedDotExpr>(diagExpr)) {
628+
subElementDiagID = diag::assignment_lhs_is_immutable_property;
629+
630+
if (auto *ctor = dyn_cast<ConstructorDecl>(getDC())) {
631+
if (auto *baseRef = dyn_cast<DeclRefExpr>(member->getBase())) {
632+
if (baseRef->getDecl() == ctor->getImplicitSelfDecl() &&
633+
ctor->getDelegatingOrChainedInitKind(nullptr) ==
634+
ConstructorDecl::BodyInitKind::Delegating) {
635+
emitDiagnostic(loc, diag::assignment_let_property_delegating_init,
636+
member->getName());
637+
if (auto *ref = getResolvedMemberRef(member)) {
638+
emitDiagnostic(ref, diag::decl_declared_here, member->getName());
639+
}
640+
return true;
641+
}
642+
}
643+
}
644+
} else if (auto sub = dyn_cast<SubscriptExpr>(diagExpr)) {
645+
subElementDiagID = diag::assignment_subscript_has_immutable_base;
646+
647+
// If the destination is a subscript with a 'dynamicLookup:' label and if
648+
// the tuple is implicit, then this was actually a @dynamicMemberLookup
649+
// access. Emit a more specific diagnostic.
650+
if (sub->getIndex()->isImplicit() &&
651+
sub->getArgumentLabels().size() == 1 &&
652+
sub->getArgumentLabels().front() == getTypeChecker().Context.Id_dynamicMember)
653+
subElementDiagID = diag::assignment_dynamic_property_has_immutable_base;
608654
} else {
609-
return false;
655+
subElementDiagID = diag::assignment_lhs_is_immutable_variable;
610656
}
611657

612658
diagnoseSubElementFailure(diagExpr, loc, getConstraintSystem(),

lib/Sema/CSDiagnostics.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class FailureDiagnostic {
9595
DeclContext *getDC() const { return CS.DC; }
9696

9797
Optional<std::pair<Type, ConversionRestrictionKind>>
98-
restrictionForType(Type type) const {
98+
getRestrictionForType(Type type) const {
9999
for (auto &restriction : CS.ConstraintRestrictions) {
100100
if (std::get<0>(restriction)->isEqual(type))
101101
return std::pair<Type, ConversionRestrictionKind>(
@@ -104,6 +104,11 @@ class FailureDiagnostic {
104104
return None;
105105
}
106106

107+
ValueDecl *getResolvedMemberRef(UnresolvedDotExpr *member) {
108+
auto locator = CS.getConstraintLocator(member, ConstraintLocator::Member);
109+
return CS.findResolvedMemberRef(locator);
110+
}
111+
107112
Optional<SelectedOverload>
108113
getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
109114
if (auto *overload = getResolvedOverload(locator))

lib/Sema/CSGen.cpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,26 +2745,30 @@ namespace {
27452745
auto typeVar = CS.createTypeVariable(locator);
27462746
return LValueType::get(typeVar);
27472747
}
2748-
2748+
2749+
static Type genAssignDestType(Expr *expr, ConstraintSystem &CS) {
2750+
if (auto *TE = dyn_cast<TupleExpr>(expr)) {
2751+
SmallVector<TupleTypeElt, 4> destTupleTypes;
2752+
for (unsigned i = 0; i != TE->getNumElements(); ++i) {
2753+
Type subType = genAssignDestType(TE->getElement(i), CS);
2754+
destTupleTypes.push_back(TupleTypeElt(subType, TE->getElementName(i)));
2755+
}
2756+
return TupleType::get(destTupleTypes, CS.getASTContext());
2757+
} else {
2758+
Type destTy = CS.createTypeVariable(CS.getConstraintLocator(expr));
2759+
CS.addConstraint(ConstraintKind::Bind, CS.getType(expr), LValueType::get(destTy),
2760+
CS.getConstraintLocator(expr));
2761+
return destTy;
2762+
}
2763+
}
2764+
27492765
Type visitAssignExpr(AssignExpr *expr) {
27502766
// Handle invalid code.
27512767
if (!expr->getDest() || !expr->getSrc())
27522768
return Type();
2753-
2754-
// Compute the type to which the source must be converted to allow
2755-
// assignment to the destination.
2756-
auto destTy = CS.computeAssignDestType(expr->getDest(), expr->getLoc());
2757-
if (!destTy)
2758-
return Type();
2759-
if (destTy->is<UnresolvedType>()) {
2760-
return CS.createTypeVariable(CS.getConstraintLocator(expr));
2761-
}
2762-
2763-
// The source must be convertible to the destination.
2764-
CS.addConstraint(ConstraintKind::Conversion,
2765-
CS.getType(expr->getSrc()), destTy,
2766-
CS.getConstraintLocator(expr->getSrc()));
2767-
2769+
Type destTy = genAssignDestType(expr->getDest(), CS);
2770+
CS.addConstraint(ConstraintKind::Conversion, CS.getType(expr->getSrc()), destTy,
2771+
CS.getConstraintLocator(expr));
27682772
return TupleType::getEmpty(CS.getASTContext());
27692773
}
27702774

0 commit comments

Comments
 (0)