Skip to content

Commit 6fd1600

Browse files
committed
[ConstraintSystem] Diagnose conditional requirement failures via fixes
Extend existing `RequirementFailure` functionality to support conditional requirement failures. Such fixes are introduced only if the parent type requirement has been matched successfully. Resolves: rdar://problem/47871590
1 parent dfac0d8 commit 6fd1600

File tree

7 files changed

+127
-118
lines changed

7 files changed

+127
-118
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 39 additions & 21 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.getKind() == ConstraintLocator::ConditionalRequirement) {
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
}
@@ -2764,15 +2777,25 @@ 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+
(path.back().getKind() == ConstraintLocator::TypeParameterRequirement ||
2782+
path.back().getKind() == ConstraintLocator::ConditionalRequirement)) {
2783+
ConstraintLocator *reqLoc = nullptr;
2784+
if (path.back().getKind() == ConstraintLocator::ConditionalRequirement) {
2785+
// Underlying conformance requirement is itself fixed,
2786+
// this wouldn't lead to right solution.
2787+
if (hasFixFor(getConstraintLocator(anchor, path.back())))
2788+
return SolutionKind::Error;
2789+
2790+
// For conditional requirements we need complete path, which includes
2791+
// type requirement position, to be able to fetch conformance later.
2792+
reqLoc = getConstraintLocator(locator);
2793+
} else {
2794+
// Let's strip all of the unnecessary information from locator,
2795+
// diagnostics only care about anchor - to lookup type,
2796+
// and what was the requirement# which is not satisfied.
2797+
reqLoc = getConstraintLocator(anchor, path.back());
2798+
}
27762799

27772800
auto *fix = MissingConformance::create(*this, type, protocol, reqLoc);
27782801
if (!recordFix(fix))
@@ -5417,12 +5440,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix) {
54175440
// Always useful, unless duplicate of exactly the same fix and location.
54185441
// This situation might happen when the same fix kind is applicable to
54195442
// 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())
5443+
if (!hasFixFor(fix->getLocator()))
54265444
Fixes.push_back(fix);
54275445
} else {
54285446
// Only useful to record if no pre-existing fix in the subexpr tree.

lib/Sema/ConstraintSystem.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,7 @@ class ConstraintSystem {
944944
friend class SplitterStep;
945945
friend class ComponentStep;
946946
friend class TypeVariableStep;
947+
friend class RequirementFailure;
947948
friend class MissingMemberFailure;
948949

949950
class SolverScope;
@@ -1811,6 +1812,14 @@ class ConstraintSystem {
18111812
/// subsequent solution would be worse than the best known solution.
18121813
bool recordFix(ConstraintFix *fix);
18131814

1815+
/// Determine whether constraint system already has a fix recorded
1816+
/// for a particular location.
1817+
bool hasFixFor(ConstraintLocator *locator) const {
1818+
return llvm::any_of(Fixes, [&locator](const ConstraintFix *fix) {
1819+
return fix->getLocator() == locator;
1820+
});
1821+
}
1822+
18141823
/// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the
18151824
/// constraint system, return the decl that it references.
18161825
ValueDecl *findResolvedMemberRef(ConstraintLocator *locator);

test/Constraints/array_literal.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ protocol P { }
329329
struct PArray<T> { }
330330

331331
extension PArray : ExpressibleByArrayLiteral where T: P {
332+
// expected-note@-1 {{requirement from conditional conformance of 'PArray<String>' to 'ExpressibleByArrayLiteral'}}
332333
typealias ArrayLiteralElement = T
333334

334335
init(arrayLiteral elements: T...) { }
@@ -338,7 +339,7 @@ extension Int: P { }
338339

339340
func testConditional(i: Int, s: String) {
340341
let _: PArray<Int> = [i, i, i]
341-
let _: PArray<String> = [s, s, s] // expected-error{{cannot convert value of type '[String]' to specified type 'PArray<String>'}}
342+
let _: PArray<String> = [s, s, s] // expected-error{{generic struct 'PArray' requires that 'String' conform to 'P'}}
342343
}
343344

344345

0 commit comments

Comments
 (0)