Skip to content

Commit dfac0d8

Browse files
committed
[CSDiagnostics] Augment RequirementFailure to support conditional requirements
1 parent f2abfc5 commit dfac0d8

File tree

2 files changed

+94
-15
lines changed

2 files changed

+94
-15
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "swift/AST/GenericSignature.h"
2525
#include "swift/AST/ParameterList.h"
2626
#include "swift/AST/Pattern.h"
27+
#include "swift/AST/ProtocolConformance.h"
28+
#include "swift/AST/ProtocolConformanceRef.h"
2729
#include "swift/AST/Types.h"
2830
#include "swift/Basic/SourceLoc.h"
2931
#include "swift/Parse/Lexer.h"
@@ -88,19 +90,54 @@ Type RequirementFailure::getOwnerType() const {
8890
}
8991

9092
const GenericContext *RequirementFailure::getGenericContext() const {
93+
// For conditional requirements, this is easy because all
94+
// required information is contained in the conformance
95+
// reference.
96+
if (isConditional()) {
97+
auto *conformance = getConformanceRef().getConcrete();
98+
auto *DC = conformance->getDeclContext();
99+
return DC->getAsDecl()->getAsGenericContext();
100+
}
101+
91102
if (auto *genericCtx = AffectedDecl->getAsGenericContext())
92103
return genericCtx;
93104
return AffectedDecl->getDeclContext()->getAsDecl()->getAsGenericContext();
94105
}
95106

96107
const Requirement &RequirementFailure::getRequirement() const {
108+
// If this is a conditional requirement failure we need to
109+
// fetch conformance from constraint system associated with
110+
// type requirement this conditional conformance belongs to.
111+
if (isConditional()) {
112+
auto conformanceRef = getConformanceRef();
113+
return conformanceRef.getConditionalRequirements()[getRequirementIndex()];
114+
}
115+
97116
return getGenericContext()->getGenericRequirements()[getRequirementIndex()];
98117
}
99118

119+
ProtocolConformanceRef RequirementFailure::getConformanceRef() const {
120+
assert(isConditional());
121+
122+
auto &cs = getConstraintSystem();
123+
auto *locator = getLocator();
124+
125+
auto *typeReqLoc =
126+
cs.getConstraintLocator(getRawAnchor(), locator->getPath().drop_back(),
127+
/*summaryFlags=*/0);
128+
129+
auto conformance = llvm::find_if(
130+
cs.CheckedConformances,
131+
[&](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
132+
&conformance) { return conformance.first == typeReqLoc; });
133+
assert(conformance != cs.CheckedConformances.end());
134+
return conformance->second;
135+
}
136+
100137
ValueDecl *RequirementFailure::getDeclRef() const {
101138
auto &cs = getConstraintSystem();
102139

103-
auto *anchor = getAnchor();
140+
auto *anchor = getRawAnchor();
104141
auto *locator = cs.getConstraintLocator(anchor);
105142
if (auto *AE = dyn_cast<CallExpr>(anchor)) {
106143
assert(isa<TypeExpr>(AE->getFn()));
@@ -142,6 +179,13 @@ ValueDecl *RequirementFailure::getDeclRef() const {
142179
}
143180

144181
const DeclContext *RequirementFailure::getRequirementDC() const {
182+
// In case of conditional requirement failure, we don't
183+
// have to guess where the it comes from.
184+
if (isConditional()) {
185+
auto *conformance = getConformanceRef().getConcrete();
186+
return conformance->getDeclContext();
187+
}
188+
145189
const auto &req = getRequirement();
146190
auto *DC = AffectedDecl->getDeclContext();
147191

@@ -159,23 +203,26 @@ bool RequirementFailure::diagnoseAsError() {
159203
if (!canDiagnoseFailure())
160204
return false;
161205

162-
auto *anchor = getAnchor();
206+
auto *anchor = getRawAnchor();
163207
const auto *reqDC = getRequirementDC();
164208
auto *genericCtx = getGenericContext();
165209

210+
auto lhs = resolveType(getLHS());
211+
auto rhs = resolveType(getRHS());
212+
166213
if (reqDC != genericCtx) {
167214
auto *NTD = reqDC->getSelfNominalTypeDecl();
168215
emitDiagnostic(anchor->getLoc(), getDiagnosticInRereference(),
169216
AffectedDecl->getDescriptiveKind(),
170-
AffectedDecl->getFullName(), NTD->getDeclaredType(),
171-
getLHS(), getRHS());
217+
AffectedDecl->getFullName(), NTD->getDeclaredType(), lhs,
218+
rhs);
172219
} else {
173220
emitDiagnostic(anchor->getLoc(), getDiagnosticOnDecl(),
174221
AffectedDecl->getDescriptiveKind(),
175-
AffectedDecl->getFullName(), getLHS(), getRHS());
222+
AffectedDecl->getFullName(), lhs, rhs);
176223
}
177224

178-
emitRequirementNote(reqDC->getAsDecl());
225+
emitRequirementNote(reqDC->getAsDecl(), lhs, rhs);
179226
return true;
180227
}
181228

@@ -188,23 +235,32 @@ bool RequirementFailure::diagnoseAsNote() {
188235
return true;
189236
}
190237

191-
void RequirementFailure::emitRequirementNote(const Decl *anchor) const {
238+
void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs,
239+
Type rhs) const {
192240
auto &req = getRequirement();
193241

194-
if (getRHS()->isEqual(req.getSecondType())) {
242+
if (isConditional()) {
243+
auto *conformance = getConformanceRef().getConcrete();
244+
emitDiagnostic(anchor, diag::requirement_implied_by_conditional_conformance,
245+
resolveType(conformance->getType()),
246+
conformance->getProtocol()->getDeclaredInterfaceType());
247+
return;
248+
}
249+
250+
if (rhs->isEqual(req.getSecondType())) {
195251
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
196-
req.getFirstType(), getLHS());
252+
req.getFirstType(), lhs);
197253
return;
198254
}
199255

200-
if (getLHS()->isEqual(req.getFirstType())) {
256+
if (lhs->isEqual(req.getFirstType())) {
201257
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
202-
req.getSecondType(), getRHS());
258+
req.getSecondType(), rhs);
203259
return;
204260
}
205261

206262
emitDiagnostic(anchor, diag::where_requirement_failure_both_subst,
207-
req.getFirstType(), getLHS(), req.getSecondType(), getRHS());
263+
req.getFirstType(), lhs, req.getSecondType(), rhs);
208264
}
209265

210266
bool MissingConformanceFailure::diagnoseAsError() {

lib/Sema/CSDiagnostics.h

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ class RequirementFailure : public FailureDiagnostic {
172172
/// to diagnose failures related to arguments.
173173
const ApplyExpr *Apply = nullptr;
174174

175+
/// If this failure associated with one of the conditional requirements.
176+
bool IsConditional = false;
177+
175178
public:
176179
RequirementFailure(ConstraintSystem &cs, Expr *expr, RequirementKind kind,
177180
ConstraintLocator *locator)
@@ -183,9 +186,12 @@ class RequirementFailure : public FailureDiagnostic {
183186
assert(!path.empty());
184187

185188
auto &last = path.back();
186-
assert(last.getKind() == ConstraintLocator::TypeParameterRequirement);
189+
assert(last.getKind() == ConstraintLocator::TypeParameterRequirement ||
190+
last.getKind() == ConstraintLocator::ConditionalRequirement);
187191
assert(static_cast<RequirementKind>(last.getValue2()) == kind);
188192

193+
IsConditional = last.getKind() == ConstraintLocator::ConditionalRequirement;
194+
189195
// It's possible sometimes not to have no base expression.
190196
if (!expr)
191197
return;
@@ -199,7 +205,10 @@ class RequirementFailure : public FailureDiagnostic {
199205
assert(!path.empty());
200206

201207
auto &requirementLoc = path.back();
202-
assert(requirementLoc.getKind() == PathEltKind::TypeParameterRequirement);
208+
assert(requirementLoc.getKind() ==
209+
ConstraintLocator::TypeParameterRequirement ||
210+
requirementLoc.getKind() ==
211+
ConstraintLocator::ConditionalRequirement);
203212
return requirementLoc.getValue();
204213
}
205214

@@ -219,6 +228,13 @@ class RequirementFailure : public FailureDiagnostic {
219228
bool diagnoseAsNote() override;
220229

221230
protected:
231+
/// Determine whether this is a conditional requirement failure.
232+
bool isConditional() const { return IsConditional; }
233+
234+
/// If this is a failure in condition requirement, retrieve
235+
/// conformance information.
236+
ProtocolConformanceRef getConformanceRef() const;
237+
222238
/// Retrieve declaration contextual where current
223239
/// requirement has been introduced.
224240
const DeclContext *getRequirementDC() const;
@@ -230,6 +246,13 @@ class RequirementFailure : public FailureDiagnostic {
230246
/// Determine whether it would be possible to diagnose
231247
/// current requirement failure.
232248
bool canDiagnoseFailure() const {
249+
// If this is a conditional requirement failure,
250+
// we have a lot more information compared to
251+
// type requirement case, because we know that
252+
// underlying conformance requirement matched.
253+
if (isConditional())
254+
return true;
255+
233256
// For static/initializer calls there is going to be
234257
// a separate fix, attached to the argument, which is
235258
// much easier to diagnose.
@@ -247,7 +270,7 @@ class RequirementFailure : public FailureDiagnostic {
247270
/// Retrieve declaration associated with failing generic requirement.
248271
ValueDecl *getDeclRef() const;
249272

250-
void emitRequirementNote(const Decl *anchor) const;
273+
void emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const;
251274
};
252275

253276
/// Diagnostics for failed conformance checks originating from

0 commit comments

Comments
 (0)