-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Diagnostics] Adding assignments directly to CS and diagnosing from there. #18950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -689,6 +689,17 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) { | |
return { expr, member }; | ||
} | ||
|
||
if (auto tupleExpr = dyn_cast<TupleExpr>(SE->getIndex())) { | ||
if (tupleExpr->getNumElements() == 1 && tupleExpr->getElementName(0).str() == "keyPath") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would at least compare Identifiers and not strings, so tupleExpr->getElementName(0) == ctx.getIdentifier("keyPath"). However I imagine this check ("is this a subscript that's really a keypath application") must appear elsewhere in the compiler. Can we extract it into a common method? |
||
auto indexType = CS.simplifyType(CS.getType(tupleExpr->getElement(0))); | ||
if (auto bgt = indexType->getAs<BoundGenericType>()) { | ||
auto decl = bgt->getDecl(); | ||
if (decl == CS.getASTContext().getKeyPathDecl()) | ||
return resolveImmutableBase(tupleExpr->getElement(0), CS); | ||
} | ||
} | ||
} | ||
|
||
// If it is settable, then the base must be the problem, recurse. | ||
return resolveImmutableBase(SE->getBase(), CS); | ||
} | ||
|
@@ -744,6 +755,9 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) { | |
if (!isa<LoadExpr>(ICE->getSubExpr())) | ||
return resolveImmutableBase(ICE->getSubExpr(), CS); | ||
|
||
if (auto *SAE = dyn_cast<SelfApplyExpr>(expr)) | ||
return resolveImmutableBase(SAE->getFn(), CS); | ||
|
||
return { expr, nullptr }; | ||
} | ||
|
||
|
@@ -840,7 +854,11 @@ void swift::diagnoseSubElementFailure(Expr *destExpr, | |
message += VD->getName().str().str(); | ||
message += "'"; | ||
|
||
if (VD->isCaptureList()) | ||
auto type = CS.simplifyType(CS.getType(immInfo.first)); | ||
auto bgt = type ? type->getAs<BoundGenericType>() : nullptr; | ||
if (bgt && bgt->getDecl() == CS.getASTContext().getKeyPathDecl()) | ||
message += " is a read-only key path"; | ||
else if (VD->isCaptureList()) | ||
message += " is an immutable capture"; | ||
else if (VD->isImplicit()) | ||
message += " is immutable"; | ||
|
@@ -898,6 +916,14 @@ void swift::diagnoseSubElementFailure(Expr *destExpr, | |
return; | ||
} | ||
|
||
// If a keypath was the problem but wasn't resolved into a vardecl | ||
// it is ambiguous or unable to be used for setting. | ||
if (auto *KPE = dyn_cast_or_null<KeyPathExpr>(immInfo.first)) { | ||
TC.diagnose(loc, diagID, "immutable key path") | ||
.highlight(KPE->getSourceRange()); | ||
return; | ||
} | ||
|
||
// If the expression is the result of a call, it is an rvalue, not a mutable | ||
// lvalue. | ||
if (auto *AE = dyn_cast<ApplyExpr>(immInfo.first)) { | ||
|
@@ -3401,44 +3427,30 @@ bool FailureDiagnosis::diagnoseContextualConversionError( | |
|
||
/// When an assignment to an expression is detected and the destination is | ||
/// invalid, emit a detailed error about the condition. | ||
void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy, | ||
bool ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy, | ||
SourceLoc equalLoc) { | ||
auto &TC = getTypeChecker(); | ||
|
||
// Diagnose obvious assignments to literals. | ||
if (isa<LiteralExpr>(dest->getValueProvidingExpr())) { | ||
TC.diagnose(equalLoc, diag::cannot_assign_to_literal); | ||
return; | ||
} | ||
|
||
// Diagnose assignments to let-properties in delegating initializers. | ||
if (auto *member = dyn_cast<UnresolvedDotExpr>(dest)) { | ||
if (auto *ctor = dyn_cast<ConstructorDecl>(DC)) { | ||
if (auto *baseRef = dyn_cast<DeclRefExpr>(member->getBase())) { | ||
if (baseRef->getDecl() == ctor->getImplicitSelfDecl() && | ||
ctor->getDelegatingOrChainedInitKind(nullptr) == | ||
ConstructorDecl::BodyInitKind::Delegating) { | ||
auto resolved = resolveImmutableBase(member, *this); | ||
assert(resolved.first == member); | ||
TC.diagnose(equalLoc, diag::assignment_let_property_delegating_init, | ||
member->getName()); | ||
if (resolved.second) { | ||
TC.diagnose(resolved.second, diag::decl_declared_here, | ||
member->getName()); | ||
} | ||
return; | ||
} | ||
} | ||
// Assignments to let-properties in delegating initializers will be caught | ||
// elsewhere now, so if we see them here, it isn't an assignment problem. | ||
if (auto *ctor = dyn_cast<ConstructorDecl>(DC)) { | ||
DeclRefExpr *baseRef = nullptr; | ||
if (auto *member = dyn_cast<UnresolvedDotExpr>(dest)) { | ||
baseRef = dyn_cast<DeclRefExpr>(member->getBase()); | ||
} else if (auto *member = dyn_cast<MemberRefExpr>(dest)) { | ||
if (auto *load = dyn_cast<LoadExpr>(member->getBase())) | ||
baseRef = dyn_cast<DeclRefExpr>(load->getSubExpr()); | ||
} | ||
if (baseRef && baseRef->getDecl() == ctor->getImplicitSelfDecl() && | ||
ctor->getDelegatingOrChainedInitKind(nullptr) == | ||
ConstructorDecl::BodyInitKind::Delegating) { | ||
return false; | ||
} | ||
} | ||
|
||
Diag<StringRef> diagID; | ||
if (isa<ApplyExpr>(dest)) | ||
if (isa<ApplyExpr>(dest) || isa<SelfApplyExpr>(dest)) | ||
diagID = diag::assignment_lhs_is_apply_expression; | ||
else if (isa<DeclRefExpr>(dest)) | ||
diagID = diag::assignment_lhs_is_immutable_variable; | ||
else if (isa<ForceValueExpr>(dest)) | ||
diagID = diag::assignment_bang_has_immutable_subcomponent; | ||
else if (isa<UnresolvedDotExpr>(dest) || isa<MemberRefExpr>(dest)) | ||
diagID = diag::assignment_lhs_is_immutable_property; | ||
else if (auto sub = dyn_cast<SubscriptExpr>(dest)) { | ||
|
@@ -3451,12 +3463,12 @@ void ConstraintSystem::diagnoseAssignmentFailure(Expr *dest, Type destTy, | |
sub->getArgumentLabels().size() == 1 && | ||
sub->getArgumentLabels().front() == TC.Context.Id_dynamicMember) | ||
diagID = diag::assignment_dynamic_property_has_immutable_base; | ||
} else { | ||
} else | ||
diagID = diag::assignment_lhs_is_immutable_variable; | ||
} | ||
|
||
diagnoseSubElementFailure(dest, equalLoc, *this, diagID, | ||
diag::assignment_lhs_not_lvalue); | ||
return true; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
|
@@ -6545,8 +6557,8 @@ bool FailureDiagnosis::visitAssignExpr(AssignExpr *assignExpr) { | |
if (diagnoseSubscriptErrors(subscriptExpr, /* inAssignmentDestination = */ true)) | ||
return true; | ||
} | ||
CS.diagnoseAssignmentFailure(destExpr, destType, assignExpr->getLoc()); | ||
return true; | ||
if (CS.diagnoseAssignmentFailure(destExpr, destType, assignExpr->getLoc())) | ||
return true; | ||
} | ||
|
||
auto *srcExpr = assignExpr->getSrc(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2745,26 +2745,30 @@ namespace { | |
auto typeVar = CS.createTypeVariable(locator); | ||
return LValueType::get(typeVar); | ||
} | ||
|
||
|
||
static Type genAssignDestType(Expr *expr, ConstraintSystem &CS) { | ||
if (auto *TE = dyn_cast<TupleExpr>(expr)) { | ||
SmallVector<TupleTypeElt, 4> destTupleTypes; | ||
for (unsigned i = 0; i != TE->getNumElements(); ++i) { | ||
Type subType = genAssignDestType(TE->getElement(i), CS); | ||
destTupleTypes.push_back(TupleTypeElt(subType, TE->getElementName(i))); | ||
} | ||
return TupleType::get(destTupleTypes, CS.getASTContext()); | ||
} else { | ||
Type destTy = CS.createTypeVariable(CS.getConstraintLocator(expr)); | ||
CS.addConstraint(ConstraintKind::Bind, CS.getType(expr), LValueType::get(destTy), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit - I think this should be reversed - |
||
CS.getConstraintLocator(expr)); | ||
return destTy; | ||
} | ||
} | ||
|
||
Type visitAssignExpr(AssignExpr *expr) { | ||
// Handle invalid code. | ||
if (!expr->getDest() || !expr->getSrc()) | ||
return Type(); | ||
|
||
// Compute the type to which the source must be converted to allow | ||
// assignment to the destination. | ||
auto destTy = CS.computeAssignDestType(expr->getDest(), expr->getLoc()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens to computeAssignDestType()? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After I kill the other caller in CSApply, which shouldn't need it any more because any conversion restrictions should already be in the CS, then it gets deleted. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sweet |
||
if (!destTy) | ||
return Type(); | ||
if (destTy->is<UnresolvedType>()) { | ||
return CS.createTypeVariable(CS.getConstraintLocator(expr)); | ||
} | ||
|
||
// The source must be convertible to the destination. | ||
CS.addConstraint(ConstraintKind::Conversion, | ||
CS.getType(expr->getSrc()), destTy, | ||
CS.getConstraintLocator(expr->getSrc())); | ||
|
||
Type destTy = genAssignDestType(expr->getDest(), CS); | ||
CS.addConstraint(ConstraintKind::Conversion, CS.getType(expr->getSrc()), destTy, | ||
CS.getConstraintLocator(expr)); | ||
return TupleType::getEmpty(CS.getASTContext()); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there no better way to check this, @jckarter ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have
SubscriptDecl
s been sufficiently parameter-list-ified that we can look at the argument labels and parameter list instead? cc @slavapestovThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but here we're looking at the arguments to the call, not the subscript declaration.