Skip to content

Commit 5467232

Browse files
authored
Merge pull request #24873 from xedin/diag-missing-contextual-conformances-fixed
[ConstraintSystem] Use `missing conformance` fix to diagnose contextual failures
2 parents 460709f + e76ae9c commit 5467232

File tree

15 files changed

+222
-51
lines changed

15 files changed

+222
-51
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ ERROR(cannot_convert_sequence_element_value,none,
452452
"cannot convert sequence element type %0 to expected type %1",
453453
(Type, Type))
454454
ERROR(cannot_convert_sequence_element_protocol,none,
455-
"sequence element type %0 does not conform to expected type %1",
455+
"sequence element type %0 does not conform to expected protocol %1",
456456
(Type, Type))
457457

458458
ERROR(throws_functiontype_mismatch,none,

lib/Sema/CSDiag.cpp

Lines changed: 7 additions & 17 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
}
@@ -2315,10 +2302,13 @@ bool FailureDiagnosis::diagnoseContextualConversionError(
23152302

23162303
// When complaining about conversion to a protocol type, complain about
23172304
// conformance instead of "conversion".
2318-
if (contextualType->is<ProtocolType>() ||
2319-
contextualType->is<ProtocolCompositionType>())
2320-
diagID = diagIDProtocol;
2321-
2305+
if (contextualType->isExistentialType()) {
2306+
MissingContextualConformanceFailure failure(
2307+
expr, CS, CTP, exprType, contextualType,
2308+
CS.getConstraintLocator(expr, ConstraintLocator::ContextualType));
2309+
return failure.diagnoseAsError();
2310+
}
2311+
23222312
// Try to simplify irrelevant details of function types. For example, if
23232313
// someone passes a "() -> Float" function to a "() throws -> Int"
23242314
// parameter, then uttering the "throws" may confuse them into thinking that

lib/Sema/CSDiagnostics.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2812,3 +2812,88 @@ 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+
case ConstraintLocator::SequenceElementType: {
2833+
diagnostic = diag::cannot_convert_sequence_element_protocol;
2834+
break;
2835+
}
2836+
2837+
default:
2838+
break;
2839+
}
2840+
}
2841+
2842+
if (!diagnostic)
2843+
return false;
2844+
2845+
auto srcType = getFromType();
2846+
auto dstType = getToType();
2847+
2848+
emitDiagnostic(anchor->getLoc(), *diagnostic, srcType, dstType);
2849+
2850+
if (isa<InOutExpr>(anchor))
2851+
return true;
2852+
2853+
if (srcType->isAny() && dstType->isAnyObject()) {
2854+
emitDiagnostic(anchor->getLoc(), diag::any_as_anyobject_fixit)
2855+
.fixItInsertAfter(anchor->getEndLoc(), " as AnyObject");
2856+
}
2857+
2858+
return true;
2859+
}
2860+
2861+
Optional<Diag<Type, Type>>
2862+
MissingContextualConformanceFailure::getDiagnosticFor(
2863+
ContextualTypePurpose context) {
2864+
switch (context) {
2865+
case CTP_Initialization:
2866+
return diag::cannot_convert_initializer_value_protocol;
2867+
case CTP_ReturnStmt:
2868+
case CTP_ReturnSingleExpr:
2869+
return diag::cannot_convert_to_return_type_protocol;
2870+
case CTP_EnumCaseRawValue:
2871+
return diag::cannot_convert_raw_initializer_value;
2872+
case CTP_DefaultParameter:
2873+
return diag::cannot_convert_default_arg_value_protocol;
2874+
case CTP_YieldByValue:
2875+
return diag::cannot_convert_yield_value_protocol;
2876+
case CTP_CallArgument:
2877+
return diag::cannot_convert_argument_value_protocol;
2878+
case CTP_ClosureResult:
2879+
return diag::cannot_convert_closure_result_protocol;
2880+
case CTP_ArrayElement:
2881+
return diag::cannot_convert_array_element_protocol;
2882+
case CTP_DictionaryKey:
2883+
return diag::cannot_convert_dict_key_protocol;
2884+
case CTP_DictionaryValue:
2885+
return diag::cannot_convert_dict_value_protocol;
2886+
case CTP_CoerceOperand:
2887+
return diag::cannot_convert_coerce_protocol;
2888+
case CTP_AssignSource:
2889+
return diag::cannot_convert_assign_protocol;
2890+
2891+
case CTP_ThrowStmt:
2892+
case CTP_Unused:
2893+
case CTP_CannotFail:
2894+
case CTP_YieldByReference:
2895+
case CTP_CalleeResult:
2896+
break;
2897+
}
2898+
return None;
2899+
}

lib/Sema/CSDiagnostics.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,15 @@ class RequirementFailure : public FailureDiagnostic {
324324
/// ```
325325
class MissingConformanceFailure final : public RequirementFailure {
326326
Type NonConformingType;
327-
ProtocolDecl *Protocol;
327+
Type ProtocolType;
328328

329329
public:
330330
MissingConformanceFailure(Expr *expr, ConstraintSystem &cs,
331331
ConstraintLocator *locator,
332-
std::pair<Type, ProtocolDecl *> conformance)
332+
std::pair<Type, Type> conformance)
333333
: RequirementFailure(cs, expr, RequirementKind::Conformance, locator),
334-
NonConformingType(conformance.first), Protocol(conformance.second) {}
334+
NonConformingType(conformance.first), ProtocolType(conformance.second) {
335+
}
335336

336337
bool diagnoseAsError() override;
337338

@@ -341,7 +342,7 @@ class MissingConformanceFailure final : public RequirementFailure {
341342
Type getLHS() const override { return NonConformingType; }
342343

343344
/// The protocol generic requirement expected associated type to conform to.
344-
Type getRHS() const override { return Protocol->getDeclaredType(); }
345+
Type getRHS() const override { return ProtocolType; }
345346

346347
protected:
347348
DiagOnDecl getDiagnosticOnDecl() const override {
@@ -1202,6 +1203,27 @@ class CollectionElementContextualFailure final : public ContextualFailure {
12021203
bool diagnoseAsError() override;
12031204
};
12041205

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+
12051227
} // end namespace constraints
12061228
} // end namespace swift
12071229

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, Protocol});
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-
ProtocolDecl *protocol,
170-
ConstraintLocator *locator) {
171-
return new (cs.getAllocator())
172-
MissingConformance(cs, type, protocol, 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: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,13 +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;
367-
ProtocolDecl *Protocol;
371+
// This could either be a protocol or protocol composition.
372+
Type ProtocolType;
368373

369-
MissingConformance(ConstraintSystem &cs, Type type, ProtocolDecl *protocol,
370-
ConstraintLocator *locator)
374+
MissingConformance(ConstraintSystem &cs, bool isContextual, Type type,
375+
Type protocolType, ConstraintLocator *locator)
371376
: ConstraintFix(cs, FixKind::AddConformance, locator),
372-
NonConformingType(type), Protocol(protocol) {}
377+
IsContextual(isContextual), NonConformingType(type),
378+
ProtocolType(protocolType) {}
373379

374380
public:
375381
std::string getName() const override {
@@ -378,13 +384,17 @@ class MissingConformance final : public ConstraintFix {
378384

379385
bool diagnose(Expr *root, bool asNote = false) const override;
380386

381-
static MissingConformance *create(ConstraintSystem &cs, Type type,
382-
ProtocolDecl *protocol,
383-
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);
384394

385395
Type getNonConformingType() { return NonConformingType; }
386396

387-
ProtocolDecl *getProtocol() { return Protocol; }
397+
Type getProtocolType() { return ProtocolType; }
388398
};
389399

390400
/// Skip same-type generic requirement constraint,

0 commit comments

Comments
 (0)