Skip to content

Commit 577e629

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 a23bc3e commit 577e629

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
@@ -2053,7 +2053,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20532053
// system had a contextual type specified, we use it - it will have a purpose
20542054
// indicator which allows us to give a very "to the point" diagnostic.
20552055
Diag<Type, Type> diagID;
2056-
Diag<Type, Type> diagIDProtocol;
20572056
Diag<Type> nilDiag;
20582057
std::function<void(void)> nilFollowup;
20592058

@@ -2070,7 +2069,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20702069
"contextual type");
20712070
case CTP_Initialization:
20722071
diagID = diag::cannot_convert_initializer_value;
2073-
diagIDProtocol = diag::cannot_convert_initializer_value_protocol;
20742072
nilDiag = diag::cannot_convert_initializer_value_nil;
20752073
nilFollowup = [this] {
20762074
TypeRepr *patternTR = CS.getContextualTypeLoc().getTypeRepr();
@@ -2096,7 +2094,6 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
20962094
}
20972095

20982096
diagID = diag::cannot_convert_to_return_type;
2099-
diagIDProtocol = diag::cannot_convert_to_return_type_protocol;
21002097
nilDiag = diag::cannot_convert_to_return_type_nil;
21012098
break;
21022099
case CTP_ThrowStmt: {
@@ -2144,12 +2141,10 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21442141

21452142
case CTP_EnumCaseRawValue:
21462143
diagID = diag::cannot_convert_raw_initializer_value;
2147-
diagIDProtocol = diag::cannot_convert_raw_initializer_value;
21482144
nilDiag = diag::cannot_convert_raw_initializer_value_nil;
21492145
break;
21502146
case CTP_DefaultParameter:
21512147
diagID = diag::cannot_convert_default_arg_value;
2152-
diagIDProtocol = diag::cannot_convert_default_arg_value_protocol;
21532148
nilDiag = diag::cannot_convert_default_arg_value_nil;
21542149
break;
21552150

@@ -2169,42 +2164,34 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
21692164
return true;
21702165
case CTP_YieldByValue:
21712166
diagID = diag::cannot_convert_yield_value;
2172-
diagIDProtocol = diag::cannot_convert_yield_value_protocol;
21732167
nilDiag = diag::cannot_convert_yield_value_nil;
21742168
break;
21752169
case CTP_CallArgument:
21762170
diagID = diag::cannot_convert_argument_value;
2177-
diagIDProtocol = diag::cannot_convert_argument_value_protocol;
21782171
nilDiag = diag::cannot_convert_argument_value_nil;
21792172
break;
21802173
case CTP_ClosureResult:
21812174
diagID = diag::cannot_convert_closure_result;
2182-
diagIDProtocol = diag::cannot_convert_closure_result_protocol;
21832175
nilDiag = diag::cannot_convert_closure_result_nil;
21842176
break;
21852177
case CTP_ArrayElement:
21862178
diagID = diag::cannot_convert_array_element;
2187-
diagIDProtocol = diag::cannot_convert_array_element_protocol;
21882179
nilDiag = diag::cannot_convert_array_element_nil;
21892180
break;
21902181
case CTP_DictionaryKey:
21912182
diagID = diag::cannot_convert_dict_key;
2192-
diagIDProtocol = diag::cannot_convert_dict_key_protocol;
21932183
nilDiag = diag::cannot_convert_dict_key_nil;
21942184
break;
21952185
case CTP_DictionaryValue:
21962186
diagID = diag::cannot_convert_dict_value;
2197-
diagIDProtocol = diag::cannot_convert_dict_value_protocol;
21982187
nilDiag = diag::cannot_convert_dict_value_nil;
21992188
break;
22002189
case CTP_CoerceOperand:
22012190
diagID = diag::cannot_convert_coerce;
2202-
diagIDProtocol = diag::cannot_convert_coerce_protocol;
22032191
nilDiag = diag::cannot_convert_coerce_nil;
22042192
break;
22052193
case CTP_AssignSource:
22062194
diagID = diag::cannot_convert_assign;
2207-
diagIDProtocol = diag::cannot_convert_assign_protocol;
22082195
nilDiag = diag::cannot_convert_assign_nil;
22092196
break;
22102197
}
@@ -2317,9 +2304,13 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
23172304
// When complaining about conversion to a protocol type, complain about
23182305
// conformance instead of "conversion".
23192306
if (contextualType->is<ProtocolType>() ||
2320-
contextualType->is<ProtocolCompositionType>())
2321-
diagID = diagIDProtocol;
2322-
2307+
contextualType->is<ProtocolCompositionType>()) {
2308+
MissingContextualConformanceFailure failure(
2309+
expr, CS, CTP, exprType, contextualType,
2310+
CS.getConstraintLocator(expr, ConstraintLocator::ContextualType));
2311+
return failure.diagnoseAsError();
2312+
}
2313+
23232314
// Try to simplify irrelevant details of function types. For example, if
23242315
// someone passes a "() -> Float" function to a "() throws -> Int"
23252316
// 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
@@ -2887,3 +2887,65 @@ bool CollectionElementContextualFailure::diagnoseAsError() {
28872887
eltType, contextualType, anchor);
28882888
return true;
28892889
}
2890+
2891+
bool MissingContextualConformanceFailure::diagnoseAsError() {
2892+
auto *anchor = getAnchor();
2893+
auto path = getLocator()->getPath();
2894+
2895+
Optional<Diag<Type, Type>> diagnostic;
2896+
if (path.empty()) {
2897+
assert(isa<AssignExpr>(anchor));
2898+
diagnostic = getDiagnosticFor(CTP_AssignSource);
2899+
} else {
2900+
const auto &last = path.back();
2901+
switch (last.getKind()) {
2902+
case ConstraintLocator::ContextualType:
2903+
assert(Context != CTP_Unused);
2904+
diagnostic = getDiagnosticFor(Context);
2905+
break;
2906+
}
2907+
}
2908+
2909+
emitDiagnostic(anchor->getLoc(), *diagnostic, getFromType(), getToType());
2910+
return true;
2911+
}
2912+
2913+
Optional<Diag<Type, Type>>
2914+
MissingContextualConformanceFailure::getDiagnosticFor(
2915+
ContextualTypePurpose context) {
2916+
switch (context) {
2917+
case CTP_Initialization:
2918+
return diag::cannot_convert_initializer_value_protocol;
2919+
case CTP_ReturnStmt:
2920+
case CTP_ReturnSingleExpr:
2921+
return diag::cannot_convert_to_return_type_protocol;
2922+
case CTP_EnumCaseRawValue:
2923+
return diag::cannot_convert_raw_initializer_value;
2924+
case CTP_DefaultParameter:
2925+
return diag::cannot_convert_default_arg_value_protocol;
2926+
case CTP_YieldByValue:
2927+
return diag::cannot_convert_yield_value_protocol;
2928+
case CTP_CallArgument:
2929+
return diag::cannot_convert_argument_value_protocol;
2930+
case CTP_ClosureResult:
2931+
return diag::cannot_convert_closure_result_protocol;
2932+
case CTP_ArrayElement:
2933+
return diag::cannot_convert_array_element_protocol;
2934+
case CTP_DictionaryKey:
2935+
return diag::cannot_convert_dict_key_protocol;
2936+
case CTP_DictionaryValue:
2937+
return diag::cannot_convert_dict_value_protocol;
2938+
case CTP_CoerceOperand:
2939+
return diag::cannot_convert_coerce_protocol;
2940+
case CTP_AssignSource:
2941+
return diag::cannot_convert_assign_protocol;
2942+
2943+
case CTP_ThrowStmt:
2944+
case CTP_Unused:
2945+
case CTP_CannotFail:
2946+
case CTP_YieldByReference:
2947+
case CTP_CalleeResult:
2948+
break;
2949+
}
2950+
return None;
2951+
}

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
@@ -2040,6 +2040,19 @@ bool ConstraintSystem::repairFailures(
20402040
return false;
20412041
};
20422042

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

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

2169+
if (repairByAddingConformance(lhs, rhs))
2170+
return true;
2171+
21532172
// If both types are key path, the only differences
21542173
// between them are mutability and/or root, value type mismatch.
21552174
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
@@ -3347,9 +3366,9 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
33473366
return SolutionKind::Error;
33483367
}
33493368

3350-
auto *fix =
3351-
MissingConformance::create(*this, type, protocol->getDeclaredType(),
3352-
getConstraintLocator(locator));
3369+
auto *fix = MissingConformance::forRequirement(
3370+
*this, type, protocol->getDeclaredType(),
3371+
getConstraintLocator(locator));
33533372
if (!recordFix(fix))
33543373
return SolutionKind::Solved;
33553374
}
@@ -6585,6 +6604,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65856604

65866605
case FixKind::InsertCall:
65876606
case FixKind::RemoveReturn:
6607+
case FixKind::AddConformance:
65886608
case FixKind::RemoveAddressOf:
65896609
case FixKind::SkipSameTypeRequirement:
65906610
case FixKind::SkipSuperclassRequirement:
@@ -6597,7 +6617,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
65976617
case FixKind::ExplicitlyEscaping:
65986618
case FixKind::CoerceToCheckedCast:
65996619
case FixKind::RelabelArguments:
6600-
case FixKind::AddConformance:
66016620
case FixKind::RemoveUnwrap:
66026621
case FixKind::DefineMemberBasedOnUse:
66036622
case FixKind::AllowTypeOrInstanceMember:

0 commit comments

Comments
 (0)