Skip to content

Commit 0b2ce5c

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. (cherry picked from commit ada6ab5)
1 parent 9b209f4 commit 0b2ce5c

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
@@ -2128,7 +2128,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21282128
// system had a contextual type specified, we use it - it will have a purpose
21292129
// indicator which allows us to give a very "to the point" diagnostic.
21302130
Diag<Type, Type> diagID;
2131-
Diag<Type, Type> diagIDProtocol;
21322131
Diag<Type> nilDiag;
21332132
std::function<void(void)> nilFollowup;
21342133

@@ -2145,7 +2144,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21452144
"contextual type");
21462145
case CTP_Initialization:
21472146
diagID = diag::cannot_convert_initializer_value;
2148-
diagIDProtocol = diag::cannot_convert_initializer_value_protocol;
21492147
nilDiag = diag::cannot_convert_initializer_value_nil;
21502148
nilFollowup = [this] {
21512149
TypeRepr *patternTR = CS.getContextualTypeLoc().getTypeRepr();
@@ -2171,7 +2169,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21712169
}
21722170

21732171
diagID = diag::cannot_convert_to_return_type;
2174-
diagIDProtocol = diag::cannot_convert_to_return_type_protocol;
21752172
nilDiag = diag::cannot_convert_to_return_type_nil;
21762173
break;
21772174
case CTP_ThrowStmt: {
@@ -2219,12 +2216,10 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
22192216

22202217
case CTP_EnumCaseRawValue:
22212218
diagID = diag::cannot_convert_raw_initializer_value;
2222-
diagIDProtocol = diag::cannot_convert_raw_initializer_value;
22232219
nilDiag = diag::cannot_convert_raw_initializer_value_nil;
22242220
break;
22252221
case CTP_DefaultParameter:
22262222
diagID = diag::cannot_convert_default_arg_value;
2227-
diagIDProtocol = diag::cannot_convert_default_arg_value_protocol;
22282223
nilDiag = diag::cannot_convert_default_arg_value_nil;
22292224
break;
22302225

@@ -2244,42 +2239,34 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
22442239
return true;
22452240
case CTP_YieldByValue:
22462241
diagID = diag::cannot_convert_yield_value;
2247-
diagIDProtocol = diag::cannot_convert_yield_value_protocol;
22482242
nilDiag = diag::cannot_convert_yield_value_nil;
22492243
break;
22502244
case CTP_CallArgument:
22512245
diagID = diag::cannot_convert_argument_value;
2252-
diagIDProtocol = diag::cannot_convert_argument_value_protocol;
22532246
nilDiag = diag::cannot_convert_argument_value_nil;
22542247
break;
22552248
case CTP_ClosureResult:
22562249
diagID = diag::cannot_convert_closure_result;
2257-
diagIDProtocol = diag::cannot_convert_closure_result_protocol;
22582250
nilDiag = diag::cannot_convert_closure_result_nil;
22592251
break;
22602252
case CTP_ArrayElement:
22612253
diagID = diag::cannot_convert_array_element;
2262-
diagIDProtocol = diag::cannot_convert_array_element_protocol;
22632254
nilDiag = diag::cannot_convert_array_element_nil;
22642255
break;
22652256
case CTP_DictionaryKey:
22662257
diagID = diag::cannot_convert_dict_key;
2267-
diagIDProtocol = diag::cannot_convert_dict_key_protocol;
22682258
nilDiag = diag::cannot_convert_dict_key_nil;
22692259
break;
22702260
case CTP_DictionaryValue:
22712261
diagID = diag::cannot_convert_dict_value;
2272-
diagIDProtocol = diag::cannot_convert_dict_value_protocol;
22732262
nilDiag = diag::cannot_convert_dict_value_nil;
22742263
break;
22752264
case CTP_CoerceOperand:
22762265
diagID = diag::cannot_convert_coerce;
2277-
diagIDProtocol = diag::cannot_convert_coerce_protocol;
22782266
nilDiag = diag::cannot_convert_coerce_nil;
22792267
break;
22802268
case CTP_AssignSource:
22812269
diagID = diag::cannot_convert_assign;
2282-
diagIDProtocol = diag::cannot_convert_assign_protocol;
22832270
nilDiag = diag::cannot_convert_assign_nil;
22842271
break;
22852272
}
@@ -2392,9 +2379,13 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
23922379
// When complaining about conversion to a protocol type, complain about
23932380
// conformance instead of "conversion".
23942381
if (contextualType->is<ProtocolType>() ||
2395-
contextualType->is<ProtocolCompositionType>())
2396-
diagID = diagIDProtocol;
2397-
2382+
contextualType->is<ProtocolCompositionType>()) {
2383+
MissingContextualConformanceFailure failure(
2384+
expr, CS, CTP, exprType, contextualType,
2385+
CS.getConstraintLocator(expr, ConstraintLocator::ContextualType));
2386+
return failure.diagnoseAsError();
2387+
}
2388+
23982389
// Try to simplify irrelevant details of function types. For example, if
23992390
// someone passes a "() -> Float" function to a "() throws -> Int"
24002391
// 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
@@ -2815,3 +2815,65 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
28152815
eltType, contextualType, anchor);
28162816
return true;
28172817
}
2818+
2819+
bool MissingContextualConformanceFailure::diagnoseAsError() {
2820+
auto *anchor = getAnchor();
2821+
auto path = getLocator()->getPath();
2822+
2823+
Optional<Diag<Type, Type>> diagnostic;
2824+
if (path.empty()) {
2825+
assert(isa<AssignExpr>(anchor));
2826+
diagnostic = getDiagnosticFor(CTP_AssignSource);
2827+
} else {
2828+
const auto &last = path.back();
2829+
switch (last.getKind()) {
2830+
case ConstraintLocator::ContextualType:
2831+
assert(Context != CTP_Unused);
2832+
diagnostic = getDiagnosticFor(Context);
2833+
break;
2834+
}
2835+
}
2836+
2837+
emitDiagnostic(anchor->getLoc(), *diagnostic, getFromType(), getToType());
2838+
return true;
2839+
}
2840+
2841+
Optional<Diag<Type, Type>>
2842+
MissingContextualConformanceFailure::getDiagnosticFor(
2843+
ContextualTypePurpose context) {
2844+
switch (context) {
2845+
case CTP_Initialization:
2846+
return diag::cannot_convert_initializer_value_protocol;
2847+
case CTP_ReturnStmt:
2848+
case CTP_ReturnSingleExpr:
2849+
return diag::cannot_convert_to_return_type_protocol;
2850+
case CTP_EnumCaseRawValue:
2851+
return diag::cannot_convert_raw_initializer_value;
2852+
case CTP_DefaultParameter:
2853+
return diag::cannot_convert_default_arg_value_protocol;
2854+
case CTP_YieldByValue:
2855+
return diag::cannot_convert_yield_value_protocol;
2856+
case CTP_CallArgument:
2857+
return diag::cannot_convert_argument_value_protocol;
2858+
case CTP_ClosureResult:
2859+
return diag::cannot_convert_closure_result_protocol;
2860+
case CTP_ArrayElement:
2861+
return diag::cannot_convert_array_element_protocol;
2862+
case CTP_DictionaryKey:
2863+
return diag::cannot_convert_dict_key_protocol;
2864+
case CTP_DictionaryValue:
2865+
return diag::cannot_convert_dict_value_protocol;
2866+
case CTP_CoerceOperand:
2867+
return diag::cannot_convert_coerce_protocol;
2868+
case CTP_AssignSource:
2869+
return diag::cannot_convert_assign_protocol;
2870+
2871+
case CTP_ThrowStmt:
2872+
case CTP_Unused:
2873+
case CTP_CannotFail:
2874+
case CTP_YieldByReference:
2875+
case CTP_CalleeResult:
2876+
break;
2877+
}
2878+
return None;
2879+
}

lib/Sema/CSDiagnostics.h

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

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

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
@@ -2003,6 +2003,19 @@ bool ConstraintSystem::repairFailures(
20032003
return false;
20042004
};
20052005

2006+
auto repairByAddingConformance = [&](Type lhs, Type rhs) -> bool {
2007+
if (lhs->isTypeVariableOrMember())
2008+
return false;
2009+
2010+
if (rhs->isAny() ||
2011+
!(rhs->is<ProtocolType>() || rhs->is<ProtocolCompositionType>()))
2012+
return false;
2013+
2014+
conversionsOrFixes.push_back(MissingConformance::forContextual(
2015+
*this, lhs, rhs, getConstraintLocator(locator)));
2016+
return true;
2017+
};
2018+
20062019
if (path.empty()) {
20072020
if (!anchor)
20082021
return false;
@@ -2022,6 +2035,9 @@ bool ConstraintSystem::repairFailures(
20222035
if (repairByInsertingExplicitCall(lhs, rhs))
20232036
return true;
20242037

2038+
if (repairByAddingConformance(lhs, rhs))
2039+
return true;
2040+
20252041
if (isa<InOutExpr>(AE->getSrc())) {
20262042
conversionsOrFixes.push_back(
20272043
RemoveAddressOf::create(*this, getConstraintLocator(locator)));
@@ -2113,6 +2129,9 @@ bool ConstraintSystem::repairFailures(
21132129
if (repairByInsertingExplicitCall(lhs, rhs))
21142130
return true;
21152131

2132+
if (repairByAddingConformance(lhs, rhs))
2133+
return true;
2134+
21162135
// If both types are key path, the only differences
21172136
// between them are mutability and/or root, value type mismatch.
21182137
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
@@ -3335,9 +3354,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
33353354
return SolutionKind::Error;
33363355
}
33373356

3338-
auto *fix =
3339-
MissingConformance::create(*this, type, protocol->getDeclaredType(),
3340-
getConstraintLocator(locator));
3357+
auto *fix = MissingConformance::forRequirement(
3358+
*this, type, protocol->getDeclaredType(),
3359+
getConstraintLocator(locator));
33413360
if (!recordFix(fix))
33423361
return SolutionKind::Solved;
33433362
}
@@ -6552,6 +6571,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65526571

65536572
case FixKind::InsertCall:
65546573
case FixKind::RemoveReturn:
6574+
case FixKind::AddConformance:
65556575
case FixKind::RemoveAddressOf:
65566576
case FixKind::SkipSameTypeRequirement:
65576577
case FixKind::SkipSuperclassRequirement:
@@ -6564,7 +6584,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65646584
case FixKind::ExplicitlyEscaping:
65656585
case FixKind::CoerceToCheckedCast:
65666586
case FixKind::RelabelArguments:
6567-
case FixKind::AddConformance:
65686587
case FixKind::RemoveUnwrap:
65696588
case FixKind::DefineMemberBasedOnUse:
65706589
case FixKind::AllowTypeOrInstanceMember:

0 commit comments

Comments
 (0)