Skip to content

Commit 75b5824

Browse files
authored
Merge pull request #22480 from xedin/rdar-47871590
[ConstraintSystem] Diagnose conditional requirement failures via fixes
2 parents 05033cd + 42e0f21 commit 75b5824

11 files changed

+267
-158
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 71 additions & 13 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"
@@ -94,13 +96,39 @@ const GenericContext *RequirementFailure::getGenericContext() const {
9496
}
9597

9698
const Requirement &RequirementFailure::getRequirement() const {
99+
// If this is a conditional requirement failure we need to
100+
// fetch conformance from constraint system associated with
101+
// type requirement this conditional conformance belongs to.
102+
if (isConditional()) {
103+
auto conformanceRef = getConformanceRef();
104+
return conformanceRef.getConditionalRequirements()[getRequirementIndex()];
105+
}
106+
97107
return getGenericContext()->getGenericRequirements()[getRequirementIndex()];
98108
}
99109

110+
ProtocolConformanceRef RequirementFailure::getConformanceRef() const {
111+
assert(isConditional());
112+
113+
auto &cs = getConstraintSystem();
114+
auto *locator = getLocator();
115+
116+
auto *typeReqLoc =
117+
cs.getConstraintLocator(getRawAnchor(), locator->getPath().drop_back(),
118+
/*summaryFlags=*/0);
119+
120+
auto conformance = llvm::find_if(
121+
cs.CheckedConformances,
122+
[&](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
123+
&conformance) { return conformance.first == typeReqLoc; });
124+
assert(conformance != cs.CheckedConformances.end());
125+
return conformance->second;
126+
}
127+
100128
ValueDecl *RequirementFailure::getDeclRef() const {
101129
auto &cs = getConstraintSystem();
102130

103-
auto *anchor = getAnchor();
131+
auto *anchor = getRawAnchor();
104132
auto *locator = cs.getConstraintLocator(anchor);
105133
if (auto *AE = dyn_cast<CallExpr>(anchor)) {
106134
assert(isa<TypeExpr>(AE->getFn()));
@@ -142,6 +170,13 @@ ValueDecl *RequirementFailure::getDeclRef() const {
142170
}
143171

144172
const DeclContext *RequirementFailure::getRequirementDC() const {
173+
// In case of conditional requirement failure, we don't
174+
// have to guess where the it comes from.
175+
if (isConditional()) {
176+
auto *conformance = getConformanceRef().getConcrete();
177+
return conformance->getDeclContext();
178+
}
179+
145180
const auto &req = getRequirement();
146181
auto *DC = AffectedDecl->getDeclContext();
147182

@@ -155,27 +190,41 @@ const DeclContext *RequirementFailure::getRequirementDC() const {
155190
return AffectedDecl->getAsGenericContext();
156191
}
157192

193+
bool RequirementFailure::isStaticOrInstanceMember(const ValueDecl *decl) {
194+
if (decl->isInstanceMember())
195+
return true;
196+
197+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(decl))
198+
return AFD->isStatic() && !AFD->isOperator();
199+
200+
return decl->isStatic();
201+
}
202+
158203
bool RequirementFailure::diagnoseAsError() {
159204
if (!canDiagnoseFailure())
160205
return false;
161206

162-
auto *anchor = getAnchor();
207+
auto *anchor = getRawAnchor();
163208
const auto *reqDC = getRequirementDC();
164209
auto *genericCtx = getGenericContext();
165210

166-
if (reqDC != genericCtx) {
211+
auto lhs = resolveType(getLHS());
212+
auto rhs = resolveType(getRHS());
213+
214+
if (genericCtx != reqDC && (genericCtx->isChildContextOf(reqDC) ||
215+
isStaticOrInstanceMember(AffectedDecl))) {
167216
auto *NTD = reqDC->getSelfNominalTypeDecl();
168217
emitDiagnostic(anchor->getLoc(), getDiagnosticInRereference(),
169218
AffectedDecl->getDescriptiveKind(),
170-
AffectedDecl->getFullName(), NTD->getDeclaredType(),
171-
getLHS(), getRHS());
219+
AffectedDecl->getFullName(), NTD->getDeclaredType(), lhs,
220+
rhs);
172221
} else {
173222
emitDiagnostic(anchor->getLoc(), getDiagnosticOnDecl(),
174223
AffectedDecl->getDescriptiveKind(),
175-
AffectedDecl->getFullName(), getLHS(), getRHS());
224+
AffectedDecl->getFullName(), lhs, rhs);
176225
}
177226

178-
emitRequirementNote(reqDC->getAsDecl());
227+
emitRequirementNote(reqDC->getAsDecl(), lhs, rhs);
179228
return true;
180229
}
181230

@@ -188,23 +237,32 @@ bool RequirementFailure::diagnoseAsNote() {
188237
return true;
189238
}
190239

191-
void RequirementFailure::emitRequirementNote(const Decl *anchor) const {
240+
void RequirementFailure::emitRequirementNote(const Decl *anchor, Type lhs,
241+
Type rhs) const {
192242
auto &req = getRequirement();
193243

194-
if (getRHS()->isEqual(req.getSecondType())) {
244+
if (isConditional()) {
245+
auto *conformance = getConformanceRef().getConcrete();
246+
emitDiagnostic(anchor, diag::requirement_implied_by_conditional_conformance,
247+
resolveType(conformance->getType()),
248+
conformance->getProtocol()->getDeclaredInterfaceType());
249+
return;
250+
}
251+
252+
if (rhs->isEqual(req.getSecondType())) {
195253
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
196-
req.getFirstType(), getLHS());
254+
req.getFirstType(), lhs);
197255
return;
198256
}
199257

200-
if (getLHS()->isEqual(req.getFirstType())) {
258+
if (lhs->isEqual(req.getFirstType())) {
201259
emitDiagnostic(anchor, diag::where_requirement_failure_one_subst,
202-
req.getSecondType(), getRHS());
260+
req.getSecondType(), rhs);
203261
return;
204262
}
205263

206264
emitDiagnostic(anchor, diag::where_requirement_failure_both_subst,
207-
req.getFirstType(), getLHS(), req.getSecondType(), getRHS());
265+
req.getFirstType(), lhs, req.getSecondType(), rhs);
208266
}
209267

210268
bool MissingConformanceFailure::diagnoseAsError() {

lib/Sema/CSDiagnostics.h

Lines changed: 28 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.isTypeParameterRequirement() ||
190+
last.isConditionalRequirement());
187191
assert(static_cast<RequirementKind>(last.getValue2()) == kind);
188192

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

201207
auto &requirementLoc = path.back();
202-
assert(requirementLoc.getKind() == PathEltKind::TypeParameterRequirement);
208+
assert(requirementLoc.isTypeParameterRequirement() ||
209+
requirementLoc.isConditionalRequirement());
203210
return requirementLoc.getValue();
204211
}
205212

@@ -219,6 +226,13 @@ class RequirementFailure : public FailureDiagnostic {
219226
bool diagnoseAsNote() override;
220227

221228
protected:
229+
/// Determine whether this is a conditional requirement failure.
230+
bool isConditional() const { return IsConditional; }
231+
232+
/// If this is a failure in condition requirement, retrieve
233+
/// conformance information.
234+
ProtocolConformanceRef getConformanceRef() const;
235+
222236
/// Retrieve declaration contextual where current
223237
/// requirement has been introduced.
224238
const DeclContext *getRequirementDC() const;
@@ -230,6 +244,13 @@ class RequirementFailure : public FailureDiagnostic {
230244
/// Determine whether it would be possible to diagnose
231245
/// current requirement failure.
232246
bool canDiagnoseFailure() const {
247+
// If this is a conditional requirement failure,
248+
// we have a lot more information compared to
249+
// type requirement case, because we know that
250+
// underlying conformance requirement matched.
251+
if (isConditional())
252+
return true;
253+
233254
// For static/initializer calls there is going to be
234255
// a separate fix, attached to the argument, which is
235256
// much easier to diagnose.
@@ -247,7 +268,11 @@ class RequirementFailure : public FailureDiagnostic {
247268
/// Retrieve declaration associated with failing generic requirement.
248269
ValueDecl *getDeclRef() const;
249270

250-
void emitRequirementNote(const Decl *anchor) const;
271+
void emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const;
272+
273+
/// Determine whether given declaration represents a static
274+
/// or instance property/method, excluding operators.
275+
static bool isStaticOrInstanceMember(const ValueDecl *decl);
251276
};
252277

253278
/// Diagnostics for failed conformance checks originating from

lib/Sema/CSSimplify.cpp

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,7 +1602,7 @@ ConstraintSystem::matchTypesBindTypeVar(
16021602

16031603
static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
16041604
Type type2, Expr *anchor,
1605-
LocatorPathElt &req) {
1605+
ArrayRef<LocatorPathElt> path) {
16061606
// Can't fix not yet properly resolved types.
16071607
if (type1->hasTypeVariable() || type2->hasTypeVariable())
16081608
return nullptr;
@@ -1612,9 +1612,21 @@ static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
16121612
if (type1->hasDependentMember() || type2->hasDependentMember())
16131613
return nullptr;
16141614

1615-
// Build simplified locator which only contains anchor and requirement info.
1616-
ConstraintLocatorBuilder requirement(cs.getConstraintLocator(anchor));
1617-
auto *reqLoc = cs.getConstraintLocator(requirement.withPathElement(req));
1615+
auto req = path.back();
1616+
1617+
ConstraintLocator *reqLoc = nullptr;
1618+
if (req.isConditionalRequirement()) {
1619+
// If underlaying conformance requirement has been fixed as
1620+
// we there is no reason to fix up conditional requirements.
1621+
if (cs.hasFixFor(cs.getConstraintLocator(anchor, req)))
1622+
return nullptr;
1623+
1624+
// For conditional requirements we need a full path.
1625+
reqLoc = cs.getConstraintLocator(anchor, path, /*summaryFlags=*/0);
1626+
} else {
1627+
// Build simplified locator which only contains anchor and requirement info.
1628+
reqLoc = cs.getConstraintLocator(anchor, req);
1629+
}
16181630

16191631
auto reqKind = static_cast<RequirementKind>(req.getValue2());
16201632
switch (reqKind) {
@@ -1644,8 +1656,9 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
16441656

16451657
auto &elt = path.back();
16461658
switch (elt.getKind()) {
1647-
case ConstraintLocator::TypeParameterRequirement: {
1648-
if (auto *fix = fixRequirementFailure(cs, lhs, rhs, anchor, elt))
1659+
case ConstraintLocator::TypeParameterRequirement:
1660+
case ConstraintLocator::ConditionalRequirement: {
1661+
if (auto *fix = fixRequirementFailure(cs, lhs, rhs, anchor, path))
16491662
conversionsOrFixes.push_back(fix);
16501663
break;
16511664
}
@@ -2691,10 +2704,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
26912704
if (conformance.isConcrete()) {
26922705
unsigned index = 0;
26932706
for (const auto &req : conformance.getConditionalRequirements()) {
2694-
addConstraint(
2695-
req,
2696-
locator.withPathElement(
2697-
LocatorPathElt::getConditionalRequirementComponent(index++)));
2707+
addConstraint(req,
2708+
locator.withPathElement(
2709+
LocatorPathElt::getConditionalRequirementComponent(
2710+
index++, req.getKind())));
26982711
}
26992712
}
27002713

@@ -2764,15 +2777,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
27642777
SmallVector<LocatorPathElt, 4> path;
27652778
auto *anchor = locator.getLocatorParts(path);
27662779

2767-
if (!path.empty() && path.back().getKind() ==
2768-
ConstraintLocator::PathElementKind::TypeParameterRequirement) {
2769-
auto typeRequirement = path.back();
2770-
// Let's strip all of the unnecessary information from locator,
2771-
// diagnostics only care about anchor - to lookup type,
2772-
// and what was the requirement# which is not satisfied.
2773-
ConstraintLocatorBuilder requirement(getConstraintLocator(anchor));
2774-
auto *reqLoc =
2775-
getConstraintLocator(requirement.withPathElement(typeRequirement));
2780+
if (path.empty())
2781+
return SolutionKind::Error;
2782+
2783+
if (path.back().isTypeParameterRequirement() ||
2784+
path.back().isConditionalRequirement()) {
2785+
ConstraintLocator *reqLoc = nullptr;
2786+
if (path.back().isConditionalRequirement()) {
2787+
// Underlying conformance requirement is itself fixed,
2788+
// this wouldn't lead to right solution.
2789+
if (hasFixFor(getConstraintLocator(anchor, path.back())))
2790+
return SolutionKind::Error;
2791+
2792+
// For conditional requirements we need complete path, which includes
2793+
// type requirement position, to be able to fetch conformance later.
2794+
reqLoc = getConstraintLocator(locator);
2795+
} else {
2796+
// Let's strip all of the unnecessary information from locator,
2797+
// diagnostics only care about anchor - to lookup type,
2798+
// and what was the requirement# which is not satisfied.
2799+
reqLoc = getConstraintLocator(anchor, path.back());
2800+
}
27762801

27772802
auto *fix = MissingConformance::create(*this, type, protocol, reqLoc);
27782803
if (!recordFix(fix))
@@ -5417,12 +5442,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix) {
54175442
// Always useful, unless duplicate of exactly the same fix and location.
54185443
// This situation might happen when the same fix kind is applicable to
54195444
// different overload choices.
5420-
auto *loc = fix->getLocator();
5421-
auto existingFix = llvm::find_if(Fixes, [&](const ConstraintFix *e) {
5422-
// If we already have a fix like this recorded, let's not do it again,
5423-
return e->getKind() == fix->getKind() && e->getLocator() == loc;
5424-
});
5425-
if (existingFix == Fixes.end())
5445+
if (!hasFixFor(fix->getLocator()))
54265446
Fixes.push_back(fix);
54275447
} else {
54285448
// Only useful to record if no pre-existing fix in the subexpr tree.

lib/Sema/ConstraintLocator.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,25 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
106106
}
107107
}
108108

109+
auto dumpReqKind = [&out](RequirementKind kind) {
110+
out << " (";
111+
switch (kind) {
112+
case RequirementKind::Conformance:
113+
out << "conformance";
114+
break;
115+
case RequirementKind::Superclass:
116+
out << "superclass";
117+
break;
118+
case RequirementKind::SameType:
119+
out << "same-type";
120+
break;
121+
case RequirementKind::Layout:
122+
out << "layout";
123+
break;
124+
}
125+
out << ")";
126+
};
127+
109128
for (auto elt : getPath()) {
110129
out << " -> ";
111130
switch (elt.getKind()) {
@@ -222,26 +241,12 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) {
222241

223242
case ConditionalRequirement:
224243
out << "conditional requirement #" << llvm::utostr(elt.getValue());
244+
dumpReqKind(static_cast<RequirementKind>(elt.getValue2()));
225245
break;
226246

227247
case TypeParameterRequirement: {
228-
out << "type parameter requirement #" << llvm::utostr(elt.getValue())
229-
<< " (";
230-
switch (static_cast<RequirementKind>(elt.getValue2())) {
231-
case RequirementKind::Conformance:
232-
out << "conformance";
233-
break;
234-
case RequirementKind::Superclass:
235-
out << "superclass";
236-
break;
237-
case RequirementKind::SameType:
238-
out << "same-type";
239-
break;
240-
case RequirementKind::Layout:
241-
out << "layout";
242-
break;
243-
}
244-
out << ")";
248+
out << "type parameter requirement #" << llvm::utostr(elt.getValue());
249+
dumpReqKind(static_cast<RequirementKind>(elt.getValue2()));
245250
break;
246251
}
247252

0 commit comments

Comments
 (0)