Skip to content

Commit 57122c1

Browse files
authored
Merge pull request #20407 from xedin/contextual-closure-result-mismatch
[CSDiagnostics] Diagnose contextual closure result mismatches via fixes
2 parents 2964b58 + 66a7930 commit 57122c1

File tree

11 files changed

+240
-78
lines changed

11 files changed

+240
-78
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2228,48 +2228,6 @@ static bool tryRawRepresentableFixIts(InFlightDiagnostic &diag,
22282228
return false;
22292229
}
22302230

2231-
/// Try to add a fix-it when converting between a collection and its slice type,
2232-
/// such as String <-> Substring or (eventually) Array <-> ArraySlice
2233-
static bool trySequenceSubsequenceConversionFixIts(InFlightDiagnostic &diag,
2234-
ConstraintSystem &CS,
2235-
Type fromType, Type toType,
2236-
Expr *expr) {
2237-
if (CS.TC.Context.getStdlibModule() == nullptr)
2238-
return false;
2239-
2240-
auto String = CS.TC.getStringType(CS.DC);
2241-
auto Substring = CS.TC.getSubstringType(CS.DC);
2242-
2243-
if (!String || !Substring)
2244-
return false;
2245-
2246-
/// FIXME: Remove this flag when void subscripts are implemented.
2247-
/// Make this unconditional and remove the if statement.
2248-
if (CS.TC.getLangOpts().FixStringToSubstringConversions) {
2249-
// String -> Substring conversion
2250-
// Add '[]' void subscript call to turn the whole String into a Substring
2251-
if (fromType->isEqual(String)) {
2252-
if (toType->isEqual(Substring)) {
2253-
diag.fixItInsertAfter(expr->getEndLoc (), "[]");
2254-
return true;
2255-
}
2256-
}
2257-
}
2258-
2259-
// Substring -> String conversion
2260-
// Wrap in String.init
2261-
if (fromType->isEqual(Substring)) {
2262-
if (toType->isEqual(String)) {
2263-
auto range = expr->getSourceRange();
2264-
diag.fixItInsert(range.Start, "String(");
2265-
diag.fixItInsertAfter(range.End, ")");
2266-
return true;
2267-
}
2268-
}
2269-
2270-
return false;
2271-
}
2272-
22732231
/// Attempts to add fix-its for these two mistakes:
22742232
///
22752233
/// - Passing an integer with the right type but which is getting wrapped with a
@@ -2738,10 +2696,9 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
27382696

27392697
// Try to convert between a sequence and its subsequence, notably
27402698
// String <-> Substring.
2741-
if (trySequenceSubsequenceConversionFixIts(diag, CS, exprType, contextualType,
2742-
expr)) {
2699+
if (ContextualFailure::trySequenceSubsequenceFixIts(diag, CS, exprType,
2700+
contextualType, expr))
27432701
return true;
2744-
}
27452702

27462703
// Attempt to add a fixit for the error.
27472704
switch (CTP) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,3 +1106,90 @@ Diag<StringRef> AssignmentFailure::findDeclDiagonstic(ASTContext &ctx,
11061106

11071107
return diag::assignment_lhs_is_immutable_variable;
11081108
}
1109+
1110+
bool ContextualFailure::diagnoseAsError() {
1111+
auto *anchor = getAnchor();
1112+
auto path = getLocator()->getPath();
1113+
1114+
assert(!path.empty());
1115+
1116+
if (diagnoseMissingFunctionCall())
1117+
return true;
1118+
1119+
Diag<Type, Type> diagnostic;
1120+
switch (path.back().getKind()) {
1121+
case ConstraintLocator::ClosureResult: {
1122+
diagnostic = diag::cannot_convert_closure_result;
1123+
break;
1124+
}
1125+
1126+
default:
1127+
return false;
1128+
}
1129+
1130+
auto diag = emitDiagnostic(anchor->getLoc(), diagnostic, FromType, ToType);
1131+
diag.highlight(anchor->getSourceRange());
1132+
1133+
(void)trySequenceSubsequenceFixIts(diag, getConstraintSystem(), FromType,
1134+
ToType, anchor);
1135+
return true;
1136+
}
1137+
1138+
bool ContextualFailure::diagnoseMissingFunctionCall() const {
1139+
auto &TC = getTypeChecker();
1140+
1141+
auto *srcFT = FromType->getAs<FunctionType>();
1142+
if (!srcFT || !srcFT->getParams().empty())
1143+
return false;
1144+
1145+
if (ToType->is<AnyFunctionType>() ||
1146+
!TC.isConvertibleTo(srcFT->getResult(), ToType, getDC()))
1147+
return false;
1148+
1149+
auto *anchor = getAnchor();
1150+
emitDiagnostic(anchor->getLoc(), diag::missing_nullary_call,
1151+
srcFT->getResult())
1152+
.highlight(anchor->getSourceRange())
1153+
.fixItInsertAfter(anchor->getEndLoc(), "()");
1154+
return true;
1155+
}
1156+
1157+
bool ContextualFailure::trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
1158+
ConstraintSystem &CS,
1159+
Type fromType, Type toType,
1160+
Expr *expr) {
1161+
if (!CS.TC.Context.getStdlibModule())
1162+
return false;
1163+
1164+
auto String = CS.TC.getStringType(CS.DC);
1165+
auto Substring = CS.TC.getSubstringType(CS.DC);
1166+
1167+
if (!String || !Substring)
1168+
return false;
1169+
1170+
/// FIXME: Remove this flag when void subscripts are implemented.
1171+
/// Make this unconditional and remove the if statement.
1172+
if (CS.TC.getLangOpts().FixStringToSubstringConversions) {
1173+
// String -> Substring conversion
1174+
// Add '[]' void subscript call to turn the whole String into a Substring
1175+
if (fromType->isEqual(String)) {
1176+
if (toType->isEqual(Substring)) {
1177+
diag.fixItInsertAfter(expr->getEndLoc(), "[]");
1178+
return true;
1179+
}
1180+
}
1181+
}
1182+
1183+
// Substring -> String conversion
1184+
// Wrap in String.init
1185+
if (fromType->isEqual(Substring)) {
1186+
if (toType->isEqual(String)) {
1187+
auto range = expr->getSourceRange();
1188+
diag.fixItInsert(range.Start, "String(");
1189+
diag.fixItInsertAfter(range.End, ")");
1190+
return true;
1191+
}
1192+
}
1193+
1194+
return false;
1195+
}

lib/Sema/CSDiagnostics.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,40 @@ class AssignmentFailure final : public FailureDiagnostic {
558558
}
559559
};
560560

561+
/// Intended to diagnose any possible contextual failure
562+
/// e.g. argument/parameter, closure result, conversions etc.
563+
class ContextualFailure final : public FailureDiagnostic {
564+
Type FromType, ToType;
565+
566+
public:
567+
ContextualFailure(Expr *root, ConstraintSystem &cs, Type lhs, Type rhs,
568+
ConstraintLocator *locator)
569+
: FailureDiagnostic(root, cs, locator), FromType(resolve(lhs)),
570+
ToType(resolve(rhs)) {}
571+
572+
bool diagnoseAsError() override;
573+
574+
// If we're trying to convert something of type "() -> T" to T,
575+
// then we probably meant to call the value.
576+
bool diagnoseMissingFunctionCall() const;
577+
578+
/// Try to add a fix-it when converting between a collection and its slice
579+
/// type, such as String <-> Substring or (eventually) Array <-> ArraySlice
580+
static bool trySequenceSubsequenceFixIts(InFlightDiagnostic &diag,
581+
ConstraintSystem &CS, Type fromType,
582+
Type toType, Expr *expr);
583+
584+
private:
585+
Type resolve(Type rawType) {
586+
auto type = resolveType(rawType)->getWithoutSpecifierType();
587+
if (auto *BGT = type->getAs<BoundGenericType>()) {
588+
if (BGT->hasUnresolvedType())
589+
return BGT->getDecl()->getDeclaredInterfaceType();
590+
}
591+
return type;
592+
}
593+
};
594+
561595
} // end namespace constraints
562596
} // end namespace swift
563597

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,15 @@ SkipSuperclassRequirement::create(ConstraintSystem &cs, Type lhs, Type rhs,
190190
return new (cs.getAllocator())
191191
SkipSuperclassRequirement(cs, lhs, rhs, locator);
192192
}
193+
194+
bool ContextualMismatch::diagnose(Expr *root, bool asNote) const {
195+
auto failure = ContextualFailure(root, getConstraintSystem(), getFromType(),
196+
getToType(), getLocator());
197+
return failure.diagnose(asNote);
198+
}
199+
200+
ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs,
201+
Type rhs,
202+
ConstraintLocator *locator) {
203+
return new (cs.getAllocator()) ContextualMismatch(cs, lhs, rhs, locator);
204+
}

lib/Sema/CSFix.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ enum class FixKind : uint8_t {
8080
/// and assume that types are related.
8181
SkipSuperclassRequirement,
8282

83+
/// Fix up one of the sides of conversion to make it seem
84+
/// like the types are aligned.
85+
ContextualMismatch,
8386
};
8487

8588
class ConstraintFix {
@@ -347,6 +350,35 @@ class SkipSuperclassRequirement final : public ConstraintFix {
347350
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
348351
};
349352

353+
/// For example: Sometimes type returned from the body of the
354+
/// closure doesn't match expected contextual type:
355+
///
356+
/// func foo(_: () -> Int) {}
357+
/// foo { "ultimate question" }
358+
///
359+
/// Body of the closure produces `String` type when `Int` is expected
360+
/// by the context.
361+
class ContextualMismatch : public ConstraintFix {
362+
Type LHS, RHS;
363+
364+
protected:
365+
ContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
366+
ConstraintLocator *locator)
367+
: ConstraintFix(cs, FixKind::ContextualMismatch, locator), LHS(lhs),
368+
RHS(rhs) {}
369+
370+
public:
371+
std::string getName() const override { return "fix contextual mismatch"; }
372+
373+
Type getFromType() const { return LHS; }
374+
Type getToType() const { return RHS; }
375+
376+
bool diagnose(Expr *root, bool asNote = false) const override;
377+
378+
static ContextualMismatch *create(ConstraintSystem &cs, Type lhs, Type rhs,
379+
ConstraintLocator *locator);
380+
};
381+
350382
} // end namespace constraints
351383
} // end namespace swift
352384

lib/Sema/CSSimplify.cpp

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,47 +1573,30 @@ ConstraintSystem::matchTypesBindTypeVar(
15731573
return getTypeMatchSuccess();
15741574
}
15751575

1576-
static void attemptToFixRequirementFailure(
1577-
ConstraintSystem &cs, Type type1, Type type2,
1578-
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
1579-
ConstraintLocatorBuilder locator) {
1580-
using LocatorPathEltKind = ConstraintLocator::PathElementKind;
1581-
1576+
static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
1577+
Type type2, Expr *anchor,
1578+
LocatorPathElt &req) {
15821579
// Can't fix not yet properly resolved types.
15831580
if (type1->hasTypeVariable() || type2->hasTypeVariable())
1584-
return;
1581+
return nullptr;
15851582

15861583
// If dependent members are present here it's because
15871584
// base doesn't conform to associated type's protocol.
15881585
if (type1->hasDependentMember() || type2->hasDependentMember())
1589-
return;
1590-
1591-
SmallVector<LocatorPathElt, 4> path;
1592-
auto *anchor = locator.getLocatorParts(path);
1593-
1594-
if (path.empty())
1595-
return;
1596-
1597-
auto &elt = path.back();
1598-
if (elt.getKind() != LocatorPathEltKind::TypeParameterRequirement)
1599-
return;
1586+
return nullptr;
16001587

16011588
// Build simplified locator which only contains anchor and requirement info.
16021589
ConstraintLocatorBuilder requirement(cs.getConstraintLocator(anchor));
1603-
auto *reqLoc = cs.getConstraintLocator(requirement.withPathElement(elt));
1590+
auto *reqLoc = cs.getConstraintLocator(requirement.withPathElement(req));
16041591

1605-
auto reqKind = static_cast<RequirementKind>(elt.getValue2());
1592+
auto reqKind = static_cast<RequirementKind>(req.getValue2());
16061593
switch (reqKind) {
16071594
case RequirementKind::SameType: {
1608-
auto *fix = SkipSameTypeRequirement::create(cs, type1, type2, reqLoc);
1609-
conversionsOrFixes.push_back(fix);
1610-
return;
1595+
return SkipSameTypeRequirement::create(cs, type1, type2, reqLoc);
16111596
}
16121597

16131598
case RequirementKind::Superclass: {
1614-
auto *fix = SkipSuperclassRequirement::create(cs, type1, type2, reqLoc);
1615-
conversionsOrFixes.push_back(fix);
1616-
return;
1599+
return SkipSuperclassRequirement::create(cs, type1, type2, reqLoc);
16171600
}
16181601

16191602
case RequirementKind::Conformance:
@@ -1622,6 +1605,36 @@ static void attemptToFixRequirementFailure(
16221605
}
16231606
}
16241607

1608+
static void
1609+
repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
1610+
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
1611+
ConstraintLocatorBuilder locator) {
1612+
SmallVector<LocatorPathElt, 4> path;
1613+
auto *anchor = locator.getLocatorParts(path);
1614+
1615+
if (path.empty())
1616+
return;
1617+
1618+
auto &elt = path.back();
1619+
switch (elt.getKind()) {
1620+
case ConstraintLocator::TypeParameterRequirement: {
1621+
if (auto *fix = fixRequirementFailure(cs, lhs, rhs, anchor, elt))
1622+
conversionsOrFixes.push_back(fix);
1623+
break;
1624+
}
1625+
1626+
case ConstraintLocator::ClosureResult: {
1627+
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
1628+
cs.getConstraintLocator(locator));
1629+
conversionsOrFixes.push_back(fix);
1630+
break;
1631+
}
1632+
1633+
default:
1634+
return;
1635+
}
1636+
}
1637+
16251638
ConstraintSystem::TypeMatchResult
16261639
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
16271640
TypeMatchOptions flags,
@@ -2407,9 +2420,9 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
24072420
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
24082421
}
24092422

2423+
// Attempt to repair any failures identifiable at this point.
24102424
if (attemptFixes)
2411-
attemptToFixRequirementFailure(*this, type1, type2, conversionsOrFixes,
2412-
locator);
2425+
repairFailures(*this, type1, type2, conversionsOrFixes, locator);
24132426

24142427
if (conversionsOrFixes.empty()) {
24152428
// If one of the types is a type variable or member thereof, we leave this
@@ -5047,7 +5060,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
50475060
}
50485061

50495062
case FixKind::SkipSameTypeRequirement:
5050-
case FixKind::SkipSuperclassRequirement: {
5063+
case FixKind::SkipSuperclassRequirement:
5064+
case FixKind::ContextualMismatch: {
50515065
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
50525066
}
50535067

0 commit comments

Comments
 (0)