Skip to content

Commit ada6ab5

Browse files
committed
[ConstraintSystem] Use missing conformance fix to diagnose contextual failures
Extend use of `missing protocol conformance` fix to cover contextual failures, such as: - Assignment mismatches, where destination requires source to conform to certain protocol (or protocol composition); - Incorrect returns where returned type doesn't conform to the protocol specified in the signature.
1 parent ace86e8 commit ada6ab5

File tree

6 files changed

+154
-33
lines changed

6 files changed

+154
-33
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,7 +2052,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20522052
// system had a contextual type specified, we use it - it will have a purpose
20532053
// indicator which allows us to give a very "to the point" diagnostic.
20542054
Diag<Type, Type> diagID;
2055-
Diag<Type, Type> diagIDProtocol;
20562055
Diag<Type> nilDiag;
20572056
std::function<void(void)> nilFollowup;
20582057

@@ -2069,7 +2068,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20692068
"contextual type");
20702069
case CTP_Initialization:
20712070
diagID = diag::cannot_convert_initializer_value;
2072-
diagIDProtocol = diag::cannot_convert_initializer_value_protocol;
20732071
nilDiag = diag::cannot_convert_initializer_value_nil;
20742072
nilFollowup = [this] {
20752073
TypeRepr *patternTR = CS.getContextualTypeLoc().getTypeRepr();
@@ -2095,7 +2093,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20952093
}
20962094

20972095
diagID = diag::cannot_convert_to_return_type;
2098-
diagIDProtocol = diag::cannot_convert_to_return_type_protocol;
20992096
nilDiag = diag::cannot_convert_to_return_type_nil;
21002097
break;
21012098
case CTP_ThrowStmt: {
@@ -2143,12 +2140,10 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21432140

21442141
case CTP_EnumCaseRawValue:
21452142
diagID = diag::cannot_convert_raw_initializer_value;
2146-
diagIDProtocol = diag::cannot_convert_raw_initializer_value;
21472143
nilDiag = diag::cannot_convert_raw_initializer_value_nil;
21482144
break;
21492145
case CTP_DefaultParameter:
21502146
diagID = diag::cannot_convert_default_arg_value;
2151-
diagIDProtocol = diag::cannot_convert_default_arg_value_protocol;
21522147
nilDiag = diag::cannot_convert_default_arg_value_nil;
21532148
break;
21542149

@@ -2168,42 +2163,34 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21682163
return true;
21692164
case CTP_YieldByValue:
21702165
diagID = diag::cannot_convert_yield_value;
2171-
diagIDProtocol = diag::cannot_convert_yield_value_protocol;
21722166
nilDiag = diag::cannot_convert_yield_value_nil;
21732167
break;
21742168
case CTP_CallArgument:
21752169
diagID = diag::cannot_convert_argument_value;
2176-
diagIDProtocol = diag::cannot_convert_argument_value_protocol;
21772170
nilDiag = diag::cannot_convert_argument_value_nil;
21782171
break;
21792172
case CTP_ClosureResult:
21802173
diagID = diag::cannot_convert_closure_result;
2181-
diagIDProtocol = diag::cannot_convert_closure_result_protocol;
21822174
nilDiag = diag::cannot_convert_closure_result_nil;
21832175
break;
21842176
case CTP_ArrayElement:
21852177
diagID = diag::cannot_convert_array_element;
2186-
diagIDProtocol = diag::cannot_convert_array_element_protocol;
21872178
nilDiag = diag::cannot_convert_array_element_nil;
21882179
break;
21892180
case CTP_DictionaryKey:
21902181
diagID = diag::cannot_convert_dict_key;
2191-
diagIDProtocol = diag::cannot_convert_dict_key_protocol;
21922182
nilDiag = diag::cannot_convert_dict_key_nil;
21932183
break;
21942184
case CTP_DictionaryValue:
21952185
diagID = diag::cannot_convert_dict_value;
2196-
diagIDProtocol = diag::cannot_convert_dict_value_protocol;
21972186
nilDiag = diag::cannot_convert_dict_value_nil;
21982187
break;
21992188
case CTP_CoerceOperand:
22002189
diagID = diag::cannot_convert_coerce;
2201-
diagIDProtocol = diag::cannot_convert_coerce_protocol;
22022190
nilDiag = diag::cannot_convert_coerce_nil;
22032191
break;
22042192
case CTP_AssignSource:
22052193
diagID = diag::cannot_convert_assign;
2206-
diagIDProtocol = diag::cannot_convert_assign_protocol;
22072194
nilDiag = diag::cannot_convert_assign_nil;
22082195
break;
22092196
}
@@ -2316,9 +2303,13 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
23162303
// When complaining about conversion to a protocol type, complain about
23172304
// conformance instead of "conversion".
23182305
if (contextualType->is<ProtocolType>() ||
2319-
contextualType->is<ProtocolCompositionType>())
2320-
diagID = diagIDProtocol;
2321-
2306+
contextualType->is<ProtocolCompositionType>()) {
2307+
MissingContextualConformanceFailure failure(
2308+
expr, CS, CTP, exprType, contextualType,
2309+
CS.getConstraintLocator(expr, ConstraintLocator::ContextualType));
2310+
return failure.diagnoseAsError();
2311+
}
2312+
23222313
// Try to simplify irrelevant details of function types. For example, if
23232314
// someone passes a "() -> Float" function to a "() throws -> Int"
23242315
// parameter, then uttering the "throws" may confuse them into thinking that

lib/Sema/CSDiagnostics.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2812,3 +2812,65 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
28122812
eltType, contextualType, anchor);
28132813
return true;
28142814
}
2815+
2816+
bool MissingContextualConformanceFailure::diagnoseAsError() {
2817+
auto *anchor = getAnchor();
2818+
auto path = getLocator()->getPath();
2819+
2820+
Optional<Diag<Type, Type>> diagnostic;
2821+
if (path.empty()) {
2822+
assert(isa<AssignExpr>(anchor));
2823+
diagnostic = getDiagnosticFor(CTP_AssignSource);
2824+
} else {
2825+
const auto &last = path.back();
2826+
switch (last.getKind()) {
2827+
case ConstraintLocator::ContextualType:
2828+
assert(Context != CTP_Unused);
2829+
diagnostic = getDiagnosticFor(Context);
2830+
break;
2831+
}
2832+
}
2833+
2834+
emitDiagnostic(anchor->getLoc(), *diagnostic, getFromType(), getToType());
2835+
return true;
2836+
}
2837+
2838+
Optional<Diag<Type, Type>>
2839+
MissingContextualConformanceFailure::getDiagnosticFor(
2840+
ContextualTypePurpose context) {
2841+
switch (context) {
2842+
case CTP_Initialization:
2843+
return diag::cannot_convert_initializer_value_protocol;
2844+
case CTP_ReturnStmt:
2845+
case CTP_ReturnSingleExpr:
2846+
return diag::cannot_convert_to_return_type_protocol;
2847+
case CTP_EnumCaseRawValue:
2848+
return diag::cannot_convert_raw_initializer_value;
2849+
case CTP_DefaultParameter:
2850+
return diag::cannot_convert_default_arg_value_protocol;
2851+
case CTP_YieldByValue:
2852+
return diag::cannot_convert_yield_value_protocol;
2853+
case CTP_CallArgument:
2854+
return diag::cannot_convert_argument_value_protocol;
2855+
case CTP_ClosureResult:
2856+
return diag::cannot_convert_closure_result_protocol;
2857+
case CTP_ArrayElement:
2858+
return diag::cannot_convert_array_element_protocol;
2859+
case CTP_DictionaryKey:
2860+
return diag::cannot_convert_dict_key_protocol;
2861+
case CTP_DictionaryValue:
2862+
return diag::cannot_convert_dict_value_protocol;
2863+
case CTP_CoerceOperand:
2864+
return diag::cannot_convert_coerce_protocol;
2865+
case CTP_AssignSource:
2866+
return diag::cannot_convert_assign_protocol;
2867+
2868+
case CTP_ThrowStmt:
2869+
case CTP_Unused:
2870+
case CTP_CannotFail:
2871+
case CTP_YieldByReference:
2872+
case CTP_CalleeResult:
2873+
break;
2874+
}
2875+
return None;
2876+
}

lib/Sema/CSDiagnostics.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,27 @@ class CollectionElementContextualFailure final : public ContextualFailure {
12031203
bool diagnoseAsError() override;
12041204
};
12051205

1206+
class MissingContextualConformanceFailure final : public ContextualFailure {
1207+
ContextualTypePurpose Context;
1208+
1209+
public:
1210+
MissingContextualConformanceFailure(Expr *root, ConstraintSystem &cs,
1211+
ContextualTypePurpose context, Type type,
1212+
Type protocolType,
1213+
ConstraintLocator *locator)
1214+
: ContextualFailure(root, cs, type, protocolType, locator),
1215+
Context(context) {
1216+
assert(protocolType->is<ProtocolType>() ||
1217+
protocolType->is<ProtocolCompositionType>());
1218+
}
1219+
1220+
bool diagnoseAsError() override;
1221+
1222+
private:
1223+
static Optional<Diag<Type, Type>>
1224+
getDiagnosticFor(ContextualTypePurpose purpose);
1225+
};
1226+
12061227
} // end namespace constraints
12071228
} // end namespace swift
12081229

lib/Sema/CSFix.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,35 @@ RelabelArguments::create(ConstraintSystem &cs,
160160
}
161161

162162
bool MissingConformance::diagnose(Expr *root, bool asNote) const {
163-
MissingConformanceFailure failure(root, getConstraintSystem(), getLocator(),
164-
{NonConformingType, ProtocolType});
163+
auto &cs = getConstraintSystem();
164+
auto *locator = getLocator();
165+
166+
if (IsContextual) {
167+
auto context = cs.getContextualTypePurpose();
168+
MissingContextualConformanceFailure failure(
169+
root, cs, context, NonConformingType, ProtocolType, locator);
170+
return failure.diagnose(asNote);
171+
}
172+
173+
MissingConformanceFailure failure(
174+
root, cs, locator, std::make_pair(NonConformingType, ProtocolType));
165175
return failure.diagnose(asNote);
166176
}
167177

168-
MissingConformance *MissingConformance::create(ConstraintSystem &cs, Type type,
169-
Type protocolType,
170-
ConstraintLocator *locator) {
171-
return new (cs.getAllocator())
172-
MissingConformance(cs, type, protocolType, locator);
178+
MissingConformance *
179+
MissingConformance::forContextual(ConstraintSystem &cs, Type type,
180+
Type protocolType,
181+
ConstraintLocator *locator) {
182+
return new (cs.getAllocator()) MissingConformance(
183+
cs, /*isContextual=*/true, type, protocolType, locator);
184+
}
185+
186+
MissingConformance *
187+
MissingConformance::forRequirement(ConstraintSystem &cs, Type type,
188+
Type protocolType,
189+
ConstraintLocator *locator) {
190+
return new (cs.getAllocator()) MissingConformance(
191+
cs, /*isContextual=*/false, type, protocolType, locator);
173192
}
174193

175194
bool SkipSameTypeRequirement::diagnose(Expr *root, bool asNote) const {

lib/Sema/CSFix.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,19 @@ class RelabelArguments final
363363

364364
/// Add a new conformance to the type to satisfy a requirement.
365365
class MissingConformance final : public ConstraintFix {
366+
// Determines whether given protocol type comes from the context e.g.
367+
// assignment destination or argument comparison.
368+
bool IsContextual;
369+
366370
Type NonConformingType;
367371
// This could either be a protocol or protocol composition.
368372
Type ProtocolType;
369373

370-
MissingConformance(ConstraintSystem &cs, Type type, Type protocolType,
371-
ConstraintLocator *locator)
374+
MissingConformance(ConstraintSystem &cs, bool isContextual, Type type,
375+
Type protocolType, ConstraintLocator *locator)
372376
: ConstraintFix(cs, FixKind::AddConformance, locator),
373-
NonConformingType(type), ProtocolType(protocolType) {}
377+
IsContextual(isContextual), NonConformingType(type),
378+
ProtocolType(protocolType) {}
374379

375380
public:
376381
std::string getName() const override {
@@ -379,9 +384,13 @@ class MissingConformance final : public ConstraintFix {
379384

380385
bool diagnose(Expr *root, bool asNote = false) const override;
381386

382-
static MissingConformance *create(ConstraintSystem &cs, Type type,
383-
Type protocolType,
384-
ConstraintLocator *locator);
387+
static MissingConformance *forRequirement(ConstraintSystem &cs, Type type,
388+
Type protocolType,
389+
ConstraintLocator *locator);
390+
391+
static MissingConformance *forContextual(ConstraintSystem &cs, Type type,
392+
Type protocolType,
393+
ConstraintLocator *locator);
385394

386395
Type getNonConformingType() { return NonConformingType; }
387396

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,19 @@ bool ConstraintSystem::repairFailures(
20392039
return false;
20402040
};
20412041

2042+
auto repairByAddingConformance = [&](Type lhs, Type rhs) -> bool {
2043+
if (lhs->isTypeVariableOrMember())
2044+
return false;
2045+
2046+
if (rhs->isAny() ||
2047+
!(rhs->is<ProtocolType>() || rhs->is<ProtocolCompositionType>()))
2048+
return false;
2049+
2050+
conversionsOrFixes.push_back(MissingConformance::forContextual(
2051+
*this, lhs, rhs, getConstraintLocator(locator)));
2052+
return true;
2053+
};
2054+
20422055
if (path.empty()) {
20432056
if (!anchor)
20442057
return false;
@@ -2058,6 +2071,9 @@ bool ConstraintSystem::repairFailures(
20582071
if (repairByInsertingExplicitCall(lhs, rhs))
20592072
return true;
20602073

2074+
if (repairByAddingConformance(lhs, rhs))
2075+
return true;
2076+
20612077
if (isa<InOutExpr>(AE->getSrc())) {
20622078
conversionsOrFixes.push_back(
20632079
RemoveAddressOf::create(*this, getConstraintLocator(locator)));
@@ -2149,6 +2165,9 @@ bool ConstraintSystem::repairFailures(
21492165
if (repairByInsertingExplicitCall(lhs, rhs))
21502166
return true;
21512167

2168+
if (repairByAddingConformance(lhs, rhs))
2169+
return true;
2170+
21522171
// If both types are key path, the only differences
21532172
// between them are mutability and/or root, value type mismatch.
21542173
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
@@ -3352,9 +3371,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
33523371
return SolutionKind::Error;
33533372
}
33543373

3355-
auto *fix =
3356-
MissingConformance::create(*this, type, protocol->getDeclaredType(),
3357-
getConstraintLocator(locator));
3374+
auto *fix = MissingConformance::forRequirement(
3375+
*this, type, protocol->getDeclaredType(),
3376+
getConstraintLocator(locator));
33583377
if (!recordFix(fix))
33593378
return SolutionKind::Solved;
33603379
}
@@ -6588,6 +6607,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65886607

65896608
case FixKind::InsertCall:
65906609
case FixKind::RemoveReturn:
6610+
case FixKind::AddConformance:
65916611
case FixKind::RemoveAddressOf:
65926612
case FixKind::SkipSameTypeRequirement:
65936613
case FixKind::SkipSuperclassRequirement:
@@ -6600,7 +6620,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
66006620
case FixKind::ExplicitlyEscaping:
66016621
case FixKind::CoerceToCheckedCast:
66026622
case FixKind::RelabelArguments:
6603-
case FixKind::AddConformance:
66046623
case FixKind::RemoveUnwrap:
66056624
case FixKind::DefineMemberBasedOnUse:
66066625
case FixKind::AllowTypeOrInstanceMember:

0 commit comments

Comments
 (0)