Skip to content

Commit 1ea3ebc

Browse files
authored
Merge pull request #18780 from xedin/refactor-decl-req-diags-into-method
[Diagnostics] NFC: Move logic common to all requirement failures into…
2 parents 8802568 + 6756c57 commit 1ea3ebc

File tree

3 files changed

+125
-60
lines changed

3 files changed

+125
-60
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1529,8 +1529,10 @@ ERROR(type_does_not_conform_in_decl_ref,none,
15291529
ERROR(type_does_not_conform_decl_owner,none,
15301530
"%0 %1 requires that %2 conform to %3",
15311531
(DescriptiveDeclKind, DeclName, Type, Type))
1532-
NOTE(where_type_does_not_conform_type,none,
1532+
NOTE(where_requirement_failure_one_subst,none,
15331533
"where %0 = %1", (Type, Type))
1534+
NOTE(where_requirement_failure_both_subst,none,
1535+
"where %0 = %1, %2 = %3", (Type, Type, Type, Type))
15341536
NOTE(requirement_implied_by_conditional_conformance,none,
15351537
"requirement from conditional conformance of %0 to %1", (Type, Type))
15361538
NOTE(candidate_types_equal_requirement,none,

lib/Sema/CSDiagnostics.cpp

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/GenericSignature.h"
2222
#include "swift/AST/Types.h"
2323
#include "llvm/ADT/ArrayRef.h"
24+
#include "llvm/ADT/SmallString.h"
2425

2526
using namespace swift;
2627
using namespace constraints;
@@ -122,27 +123,59 @@ const DeclContext *RequirementFailure::getRequirementDC() const {
122123
return AffectedDecl->getAsGenericContext();
123124
}
124125

126+
bool RequirementFailure::diagnose() {
127+
if (!canDiagnoseFailure())
128+
return false;
129+
130+
auto *anchor = getAnchor();
131+
const auto *reqDC = getRequirementDC();
132+
auto *genericCtx = AffectedDecl->getAsGenericContext();
133+
134+
if (reqDC != genericCtx) {
135+
auto *NTD = reqDC->getAsNominalTypeOrNominalTypeExtensionContext();
136+
emitDiagnostic(anchor->getLoc(), getDiagnosticInRereference(),
137+
AffectedDecl->getDescriptiveKind(),
138+
AffectedDecl->getFullName(), NTD->getDeclaredType(),
139+
getLHS(), getRHS());
140+
} else {
141+
emitDiagnostic(anchor->getLoc(), getDiagnosticOnDecl(),
142+
AffectedDecl->getDescriptiveKind(),
143+
AffectedDecl->getFullName(), getLHS(), getRHS());
144+
}
145+
146+
emitRequirementNote(reqDC->getAsDeclOrDeclExtensionContext());
147+
return true;
148+
}
149+
150+
void RequirementFailure::emitRequirementNote(const Decl *anchor) const {
151+
auto &req = getRequirement();
152+
153+
if (getRHS()->isEqual(req.getSecondType())) {
154+
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
155+
req.getFirstType(), getLHS());
156+
return;
157+
}
158+
159+
if (getLHS()->isEqual(req.getFirstType())) {
160+
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
161+
req.getSecondType(), getRHS());
162+
return;
163+
}
164+
165+
emitDiagnostic(anchor, diag::where_requirement_failure_both_subst,
166+
req.getFirstType(), getLHS(), req.getSecondType(), getRHS());
167+
}
168+
125169
bool MissingConformanceFailure::diagnose() {
170+
if (!canDiagnoseFailure())
171+
return false;
172+
126173
auto *anchor = getAnchor();
127174
auto ownerType = getOwnerType();
128-
auto type = getNonConformingType();
129-
auto protocolType = getProtocolType();
130-
131-
// Find `ApplyExpr` based on a function expression attached to it.
132-
auto findApplyExpr = [](Expr *parent, Expr *fnExpr) -> ApplyExpr * {
133-
ApplyExpr *applyExpr = nullptr;
134-
parent->forEachChildExpr([&applyExpr, &fnExpr](Expr *subExpr) -> Expr * {
135-
auto *AE = dyn_cast<ApplyExpr>(subExpr);
136-
if (!AE || AE->getFn() != fnExpr)
137-
return subExpr;
138-
139-
applyExpr = AE;
140-
return nullptr;
141-
});
142-
return applyExpr;
143-
};
175+
auto nonConformingType = getLHS();
176+
auto protocolType = getRHS();
144177

145-
auto getArgumentAt = [](ApplyExpr *AE, unsigned index) -> Expr * {
178+
auto getArgumentAt = [](const ApplyExpr *AE, unsigned index) -> Expr * {
146179
assert(AE);
147180

148181
auto *arg = AE->getArg();
@@ -156,66 +189,45 @@ bool MissingConformanceFailure::diagnose() {
156189
return arg;
157190
};
158191

159-
auto *applyExpr = findApplyExpr(getParentExpr(), anchor);
160-
161192
Optional<unsigned> atParameterPos;
162193
// Sometimes fix is recorded by type-checking sub-expression
163194
// during normal diagnostics, in such case call expression
164195
// is unavailable.
165-
if (applyExpr) {
166-
// If this is a static, initializer or operator call,
167-
// let's not try to diagnose it here, but refer to expression
168-
// diagnostics.
169-
if (isa<PrefixUnaryExpr>(applyExpr) || isa<PostfixUnaryExpr>(applyExpr) ||
170-
isa<BinaryExpr>(applyExpr) || isa<TypeExpr>(anchor))
171-
return false;
172-
196+
if (Apply) {
173197
if (auto *fnType = ownerType->getAs<AnyFunctionType>()) {
174198
auto parameters = fnType->getParams();
175199
for (auto index : indices(parameters)) {
176-
if (parameters[index].getType()->isEqual(type)) {
200+
if (parameters[index].getType()->isEqual(nonConformingType)) {
177201
atParameterPos = index;
178202
break;
179203
}
180204
}
181205
}
182206
}
183207

184-
if (type->isExistentialType()) {
208+
if (nonConformingType->isExistentialType()) {
185209
auto diagnostic = diag::protocol_does_not_conform_objc;
186-
if (type->isObjCExistentialType())
210+
if (nonConformingType->isObjCExistentialType())
187211
diagnostic = diag::protocol_does_not_conform_static;
188212

189-
emitDiagnostic(anchor->getLoc(), diagnostic, type, protocolType);
190-
} else if (atParameterPos) {
213+
emitDiagnostic(anchor->getLoc(), diagnostic, nonConformingType,
214+
protocolType);
215+
return true;
216+
}
217+
218+
if (atParameterPos) {
191219
// Requirement comes from one of the parameter types,
192220
// let's try to point diagnostic to the argument expression.
193-
auto *argExpr = getArgumentAt(applyExpr, *atParameterPos);
221+
auto *argExpr = getArgumentAt(Apply, *atParameterPos);
194222
emitDiagnostic(argExpr->getLoc(),
195-
diag::cannot_convert_argument_value_protocol, type,
196-
protocolType);
197-
} else {
198-
const auto &req = getRequirement();
199-
auto *genericCtx = AffectedDecl->getAsGenericContext();
200-
const auto *reqDC = getRequirementDC();
201-
202-
if (reqDC != genericCtx) {
203-
auto *NTD = reqDC->getAsNominalTypeOrNominalTypeExtensionContext();
204-
emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_in_decl_ref,
205-
AffectedDecl->getDescriptiveKind(),
206-
AffectedDecl->getFullName(), NTD->getDeclaredType(), type,
207-
protocolType);
208-
} else {
209-
emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_decl_owner,
210-
AffectedDecl->getDescriptiveKind(),
211-
AffectedDecl->getFullName(), type, protocolType);
212-
}
213-
214-
emitDiagnostic(reqDC->getAsDeclOrDeclExtensionContext(),
215-
diag::where_type_does_not_conform_type, req.getFirstType(),
216-
type);
223+
diag::cannot_convert_argument_value_protocol,
224+
nonConformingType, protocolType);
225+
return true;
217226
}
218-
return true;
227+
228+
// If none of the special cases could be diagnosed,
229+
// let's fallback to the most general diagnostic.
230+
return RequirementFailure::diagnose();
219231
}
220232

221233
bool LabelingFailure::diagnose() {

lib/Sema/CSDiagnostics.h

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "Constraint.h"
2020
#include "ConstraintSystem.h"
2121
#include "OverloadChoice.h"
22+
#include "swift/AST/Decl.h"
2223
#include "swift/AST/Expr.h"
2324
#include "swift/AST/Types.h"
2425
#include "llvm/ADT/ArrayRef.h"
@@ -110,15 +111,30 @@ class FailureDiagnostic {
110111
/// failures, provides common information like failed requirement,
111112
/// declaration where such requirement comes from, etc.
112113
class RequirementFailure : public FailureDiagnostic {
114+
protected:
113115
using PathEltKind = ConstraintLocator::PathElementKind;
116+
using DiagOnDecl = Diag<DescriptiveDeclKind, DeclName, Type, Type>;
117+
using DiagInReference = Diag<DescriptiveDeclKind, DeclName, Type, Type, Type>;
114118

115-
protected:
116119
const ValueDecl *AffectedDecl;
120+
/// If possible, find application expression associated
121+
/// with current generic requirement failure, that helps
122+
/// to diagnose failures related to arguments.
123+
const ApplyExpr *Apply = nullptr;
117124

118125
public:
119126
RequirementFailure(Expr *expr, const Solution &solution,
120127
ConstraintLocator *locator)
121128
: FailureDiagnostic(expr, solution, locator), AffectedDecl(getDeclRef()) {
129+
auto *anchor = getAnchor();
130+
expr->forEachChildExpr([&](Expr *subExpr) -> Expr * {
131+
auto *AE = dyn_cast<ApplyExpr>(subExpr);
132+
if (!AE || AE->getFn() != anchor)
133+
return subExpr;
134+
135+
Apply = AE;
136+
return nullptr;
137+
});
122138
}
123139

124140
unsigned getRequirementIndex() const {
@@ -136,14 +152,40 @@ class RequirementFailure : public FailureDiagnostic {
136152
/// Generic requirement associated with the failure.
137153
const Requirement &getRequirement() const;
138154

155+
virtual Type getLHS() const = 0;
156+
virtual Type getRHS() const = 0;
157+
158+
bool diagnose() override;
159+
139160
protected:
140161
/// Retrieve declaration contextual where current
141162
/// requirement has been introduced.
142163
const DeclContext *getRequirementDC() const;
143164

165+
virtual DiagOnDecl getDiagnosticOnDecl() const = 0;
166+
virtual DiagInReference getDiagnosticInRereference() const = 0;
167+
168+
/// Determine whether it would be possible to diagnose
169+
/// current requirement failure.
170+
bool canDiagnoseFailure() const {
171+
// For static/initializer calls there is going to be
172+
// a separate fix, attached to the argument, which is
173+
// much easier to diagnose.
174+
// For operator calls we can't currently produce a good
175+
// diagnostic, so instead let's refer to expression diagnostics.
176+
return !(Apply && (isOperator(Apply) || isa<TypeExpr>(getAnchor())));
177+
}
178+
179+
static bool isOperator(const ApplyExpr *apply) {
180+
return isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
181+
isa<BinaryExpr>(apply);
182+
}
183+
144184
private:
145185
/// Retrieve declaration associated with failing generic requirement.
146186
ValueDecl *getDeclRef() const;
187+
188+
void emitRequirementNote(const Decl *anchor) const;
147189
};
148190

149191
/// Diagnostics for failed conformance checks originating from
@@ -169,10 +211,19 @@ class MissingConformanceFailure final : public RequirementFailure {
169211
private:
170212
/// The type which was expected, by one of the generic requirements,
171213
/// to conform to associated protocol.
172-
Type getNonConformingType() const { return NonConformingType; }
214+
Type getLHS() const override { return NonConformingType; }
173215

174216
/// The protocol generic requirement expected associated type to conform to.
175-
Type getProtocolType() const { return Protocol->getDeclaredType(); }
217+
Type getRHS() const override { return Protocol->getDeclaredType(); }
218+
219+
protected:
220+
DiagOnDecl getDiagnosticOnDecl() const override {
221+
return diag::type_does_not_conform_decl_owner;
222+
}
223+
224+
DiagInReference getDiagnosticInRereference() const override {
225+
return diag::type_does_not_conform_in_decl_ref;
226+
}
176227
};
177228

178229
/// Diagnose errors associated with missing, extraneous

0 commit comments

Comments
 (0)