Skip to content

Commit 913f371

Browse files
authored
Merge pull request #18484 from xedin/diag-req-failures-via-fixes
[ConstraintSystem] Diagnose missing conformance requirements via "fixes"
2 parents 1d47dc9 + e4da7a1 commit 913f371

25 files changed

+248
-87
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,8 +1512,8 @@ ERROR(use_of_equal_instead_of_equality,none,
15121512

15131513

15141514
ERROR(protocol_does_not_conform_objc,none,
1515-
"using %0 as a concrete type conforming to protocol %1 is not supported",
1516-
(Type, Type))
1515+
"protocol type %0 cannot conform to %1 because only concrete "
1516+
"types can conform to protocols", (Type, Type))
15171517
ERROR(protocol_does_not_conform_static,none,
15181518
"%0 cannot be used as a type conforming to protocol %1 because %1 "
15191519
"has static requirements",

lib/Sema/CSApply.cpp

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7906,7 +7906,7 @@ bool ConstraintSystem::applySolutionFixes(Expr *E, const Solution &solution) {
79067906

79077907
bool diagnosed = false;
79087908
for (auto &fix : fixes->second)
7909-
diagnosed |= applySolutionFix(expr, solution, fix);
7909+
diagnosed |= applySolutionFix(E, solution, fix);
79107910
return diagnosed;
79117911
};
79127912

@@ -7950,7 +7950,8 @@ bool ConstraintSystem::applySolutionFix(
79507950
if (!resolved || !resolved->getAnchor() ||
79517951
(!resolved->getPath().empty() &&
79527952
fix.first.getKind() != FixKind::ExplicitlyEscaping &&
7953-
fix.first.getKind() != FixKind::ExplicitlyEscapingToAny))
7953+
fix.first.getKind() != FixKind::ExplicitlyEscapingToAny &&
7954+
fix.first.getKind() != FixKind::AddConformance))
79547955
return false;
79557956

79567957
Expr *affected = resolved->getAnchor();
@@ -8128,6 +8129,95 @@ bool ConstraintSystem::applySolutionFix(
81288129
fix.first.getArgumentLabels(*this),
81298130
isa<SubscriptExpr>(call->getFn()));
81308131
}
8132+
8133+
case FixKind::AddConformance: {
8134+
auto getMissingConformance = [&](ConstraintLocator *locator) {
8135+
auto *anchor = locator->getAnchor();
8136+
auto &requirement = locator->getPath().back();
8137+
auto result = MissingConformances.find({anchor, requirement.getValue()});
8138+
assert(result != MissingConformances.end());
8139+
return result->getSecond();
8140+
};
8141+
8142+
auto conformance = getMissingConformance(locator);
8143+
8144+
auto *anchor = locator->getAnchor();
8145+
auto owner = solution.simplifyType(getType(anchor))->getRValueInstanceType();
8146+
8147+
auto type = conformance.first;
8148+
auto protocolType = conformance.second->getDeclaredType();
8149+
8150+
// Find `ApplyExpr` based on a function expression attached to it.
8151+
auto findApplyExpr = [](Expr *parent, Expr *fnExpr) -> ApplyExpr * {
8152+
ApplyExpr *applyExpr = nullptr;
8153+
parent->forEachChildExpr([&applyExpr, &fnExpr](Expr *subExpr) -> Expr * {
8154+
auto *AE = dyn_cast<ApplyExpr>(subExpr);
8155+
if (!AE || AE->getFn() != fnExpr)
8156+
return subExpr;
8157+
8158+
applyExpr = AE;
8159+
return nullptr;
8160+
});
8161+
return applyExpr;
8162+
};
8163+
8164+
auto getArgumentAt = [](ApplyExpr *AE, unsigned index) -> Expr * {
8165+
assert(AE);
8166+
8167+
auto *arg = AE->getArg();
8168+
if (auto *TE = dyn_cast<TupleExpr>(arg))
8169+
return TE->getElement(index);
8170+
8171+
assert(index == 0);
8172+
if (auto *PE = dyn_cast<ParenExpr>(arg))
8173+
return PE->getSubExpr();
8174+
8175+
return arg;
8176+
};
8177+
8178+
auto *applyExpr = findApplyExpr(expr, anchor);
8179+
8180+
Optional<unsigned> atParameterPos;
8181+
// Sometimes fix is recorded by type-checking sub-expression
8182+
// during normal diagnostics, in such case call expression
8183+
// is unavailable.
8184+
if (applyExpr) {
8185+
// If this is a static, initializer or operator call,
8186+
// let's not try to diagnose it here, but refer to expression
8187+
// diagnostics.
8188+
if (isa<BinaryExpr>(applyExpr) || isa<TypeExpr>(anchor))
8189+
return false;
8190+
8191+
if (auto *fnType = owner->getAs<AnyFunctionType>()) {
8192+
auto parameters = fnType->getParams();
8193+
for (auto index : indices(parameters)) {
8194+
if (parameters[index].getType()->isEqual(type)) {
8195+
atParameterPos = index;
8196+
break;
8197+
}
8198+
}
8199+
}
8200+
}
8201+
8202+
if (type->isExistentialType()) {
8203+
auto diagnostic = diag::protocol_does_not_conform_objc;
8204+
if (type->isObjCExistentialType())
8205+
diagnostic = diag::protocol_does_not_conform_static;
8206+
8207+
TC.diagnose(anchor->getLoc(), diagnostic, type, protocolType);
8208+
} else if (atParameterPos) {
8209+
// Requirement comes from one of the parameter types,
8210+
// let's try to point diagnostic to the argument expression.
8211+
auto *argExpr = getArgumentAt(applyExpr, *atParameterPos);
8212+
TC.diagnose(argExpr->getLoc(),
8213+
diag::cannot_convert_argument_value_protocol, type,
8214+
protocolType);
8215+
} else {
8216+
TC.diagnose(anchor->getLoc(), diag::type_does_not_conform_owner, owner,
8217+
type, protocolType);
8218+
}
8219+
return true;
8220+
}
81318221
}
81328222

81338223
// FIXME: It would be really nice to emit a follow-up note showing where

lib/Sema/CSSimplify.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,28 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
27662766
}
27672767
return result;
27682768
}
2769+
2770+
// If this is a generic requirement let's try to record that
2771+
// conformance is missing and consider this a success, which
2772+
// makes it much easier to diagnose problems like that.
2773+
{
2774+
SmallVector<LocatorPathElt, 4> path;
2775+
auto *anchor = locator.getLocatorParts(path);
2776+
2777+
if (!path.empty() && path.back().getKind() ==
2778+
ConstraintLocator::PathElementKind::TypeParameterRequirement) {
2779+
auto typeRequirement = path.back();
2780+
std::pair<Expr *, unsigned> reqLoc = {anchor, typeRequirement.getValue()};
2781+
MissingConformances[reqLoc] = {type.getPointer(), protocol};
2782+
// Let's strip all of the unnecessary information from locator,
2783+
// diagnostics only care about anchor - to lookup type,
2784+
// and what was the requirement# which is not satisfied.
2785+
ConstraintLocatorBuilder requirement(getConstraintLocator(anchor));
2786+
if (!recordFix({FixKind::AddConformance},
2787+
requirement.withPathElement(typeRequirement)))
2788+
return SolutionKind::Solved;
2789+
}
2790+
}
27692791

27702792
// There's nothing more we can do; fail.
27712793
return SolutionKind::Error;
@@ -5004,6 +5026,7 @@ ConstraintSystem::simplifyFixConstraint(Fix fix, Type type1, Type type2,
50045026
case FixKind::ExplicitlyEscapingToAny:
50055027
case FixKind::CoerceToCheckedCast:
50065028
case FixKind::RelabelArguments:
5029+
case FixKind::AddConformance:
50075030
llvm_unreachable("handled elsewhere");
50085031
}
50095032

lib/Sema/Constraint.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@ StringRef Fix::getName(FixKind kind) {
542542
return "fix: add @escaping";
543543
case FixKind::RelabelArguments:
544544
return "fix: re-label argument(s)";
545+
case FixKind::AddConformance:
546+
return "fix: add missing protocol conformance";
545547
}
546548

547549
llvm_unreachable("Unhandled FixKind in switch.");

lib/Sema/Constraint.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ enum class FixKind : uint8_t {
253253
/// Arguments have labeling failures - missing/extraneous or incorrect
254254
/// labels attached to the, fix it by suggesting proper labels.
255255
RelabelArguments,
256+
257+
/// Add a new conformance to the type to satisfy a requirement.
258+
AddConformance,
256259
};
257260

258261
/// Describes a fix that can be applied to a constraint before visiting it.

lib/Sema/ConstraintSystem.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,10 @@ struct SelectedOverload {
437437
enum ScoreKind {
438438
// These values are used as indices into a Score value.
439439

440-
/// A reference to an @unavailable declaration.
441-
SK_Unavailable,
442440
/// A fix needs to be applied to the source.
443441
SK_Fix,
442+
/// A reference to an @unavailable declaration.
443+
SK_Unavailable,
444444
/// An implicit force of an implicitly unwrapped optional value.
445445
SK_ForceUnchecked,
446446
/// A user-defined conversion.
@@ -1009,6 +1009,12 @@ class ConstraintSystem {
10091009
/// Argument labels fixed by the constraint solver.
10101010
SmallVector<std::vector<Identifier>, 4> FixedArgLabels;
10111011

1012+
/// Conformances which solver "fixed" to help with
1013+
/// diagnosing problems related to generic requirements.
1014+
llvm::SmallDenseMap<std::pair<Expr *, unsigned>,
1015+
std::pair<TypeBase *, ProtocolDecl *>>
1016+
MissingConformances;
1017+
10121018
/// \brief The set of remembered disjunction choices used to reach
10131019
/// the current constraint system.
10141020
SmallVector<std::pair<ConstraintLocator*, unsigned>, 32>

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,6 @@ extension Node {
709709
return item.key == key
710710
// expected-error@-1 {{binary operator '==' cannot be applied to operands of type '_' and 'Self.K'}}
711711
// expected-note@-2 {{overloads for '==' exist with these partially matching parameter lists:}}
712-
})
712+
})!
713713
}
714714
}

test/Constraints/conditionally_defined_types.swift

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,18 @@ let _ = Conforms<X>.Decl3.self
7575
let _ = Conforms<X>.Decl4<X>.self
7676
let _ = Conforms<X>.Decl5<X>.self
7777

78-
let _ = Conforms<Y>.TypeAlias1.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
79-
let _ = Conforms<Y>.TypeAlias2.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
78+
let _ = Conforms<Y>.TypeAlias1.self // expected-error {{'Conforms<Y>.TypeAlias1' (aka 'Y') requires that 'Y' conform to 'P'}}
79+
let _ = Conforms<Y>.TypeAlias2.self // expected-error {{'Conforms<Y>.TypeAlias2' (aka 'Y') requires that 'Y' conform to 'P'}}
8080
let _ = Conforms<Y>.TypeAlias3<X>.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
81-
let _ = Conforms<Y>.Decl1.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
82-
let _ = Conforms<Y>.Decl2.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
83-
let _ = Conforms<Y>.Decl3.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
81+
let _ = Conforms<Y>.Decl1.self // expected-error {{'Conforms<Y>.Decl1' requires that 'Y' conform to 'P'}}
82+
let _ = Conforms<Y>.Decl2.self // expected-error {{'Conforms<Y>.Decl2' requires that 'Y' conform to 'P'}}
83+
let _ = Conforms<Y>.Decl3.self // expected-error {{'Conforms<Y>.Decl3' requires that 'Y' conform to 'P'}}
8484
let _ = Conforms<Y>.Decl4<X>.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
8585
let _ = Conforms<Y>.Decl5<X>.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
8686

8787
extension Conforms: AssociatedType where T: P {}
8888

89-
let _ = Conforms<Y>.T.self // expected-error {{type 'Y' does not conform to protocol 'P'}}
89+
let _ = Conforms<Y>.T.self // expected-error {{'Conforms<Y>.T' (aka 'Y') requires that 'Y' conform to 'P'}}
9090

9191
let _ = Conforms<X>.T.self
9292

@@ -196,20 +196,35 @@ let _ = Conforms<X>.Decl4<Z1>.Decl5<X>.self
196196

197197
// Two different forms of badness, corresponding to the two requirements:
198198

199-
let _ = Conforms<X>.Decl4<Y>.TypeAlias1.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
200-
let _ = Conforms<X>.Decl4<Y>.TypeAlias2.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
199+
let _ = Conforms<X>.Decl4<Y>.TypeAlias1.self
200+
// expected-error@-1 {{'Conforms<X>.Decl4<Y>.TypeAlias1' (aka 'X') requires that 'Y.T' conform to 'P'}}
201+
// expected-error@-2 {{'Conforms<X>.Decl4<Y>.TypeAlias1' (aka 'X') requires that 'Y' conform to 'AssociatedType'}}
202+
203+
let _ = Conforms<X>.Decl4<Y>.TypeAlias2.self
204+
// expected-error@-1 {{'Conforms<X>.Decl4<Y>.TypeAlias2' (aka 'Y') requires that 'Y.T' conform to 'P'}}
205+
// expected-error@-2 {{'Conforms<X>.Decl4<Y>.TypeAlias2' (aka 'Y') requires that 'Y' conform to 'AssociatedType'}}
206+
201207
let _ = Conforms<X>.Decl4<Y>.TypeAlias3<X>.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
202-
let _ = Conforms<X>.Decl4<Y>.Decl1.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
203-
let _ = Conforms<X>.Decl4<Y>.Decl2.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
204-
let _ = Conforms<X>.Decl4<Y>.Decl3.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
208+
let _ = Conforms<X>.Decl4<Y>.Decl1.self
209+
// expected-error@-1 {{'Conforms<X>.Decl4<Y>.Decl1' requires that 'Y.T' conform to 'P'}}
210+
// expected-error@-2 {{'Conforms<X>.Decl4<Y>.Decl1' requires that 'Y' conform to 'AssociatedType'}}
211+
212+
let _ = Conforms<X>.Decl4<Y>.Decl2.self
213+
// expected-error@-1 {{'Conforms<X>.Decl4<Y>.Decl2' requires that 'Y.T' conform to 'P'}}
214+
// expected-error@-2 {{'Conforms<X>.Decl4<Y>.Decl2' requires that 'Y' conform to 'AssociatedType'}}
215+
216+
let _ = Conforms<X>.Decl4<Y>.Decl3.self
217+
// expected-error@-1 {{'Conforms<X>.Decl4<Y>.Decl3' requires that 'Y.T' conform to 'P'}}
218+
// expected-error@-2 {{'Conforms<X>.Decl4<Y>.Decl3' requires that 'Y' conform to 'AssociatedType'}}
219+
205220
let _ = Conforms<X>.Decl4<Y>.Decl4<X>.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
206221
let _ = Conforms<X>.Decl4<Y>.Decl5<X>.self // expected-error {{type 'Y' does not conform to protocol 'AssociatedType'}}
207222

208-
let _ = Conforms<X>.Decl4<Z2>.TypeAlias1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
209-
let _ = Conforms<X>.Decl4<Z2>.TypeAlias2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
223+
let _ = Conforms<X>.Decl4<Z2>.TypeAlias1.self // expected-error {{'Conforms<X>.Decl4<Z2>.TypeAlias1' (aka 'X') requires that 'Z2.T' (aka 'Y') conform to 'P'}}
224+
let _ = Conforms<X>.Decl4<Z2>.TypeAlias2.self // expected-error {{'Conforms<X>.Decl4<Z2>.TypeAlias2' (aka 'Y') requires that 'Z2.T' (aka 'Y') conform to 'P'}}
210225
let _ = Conforms<X>.Decl4<Z2>.TypeAlias3<X>.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
211-
let _ = Conforms<X>.Decl4<Z2>.Decl1.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
212-
let _ = Conforms<X>.Decl4<Z2>.Decl2.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
213-
let _ = Conforms<X>.Decl4<Z2>.Decl3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
226+
let _ = Conforms<X>.Decl4<Z2>.Decl1.self // expected-error {{'Conforms<X>.Decl4<Z2>.Decl1' requires that 'Z2.T' (aka 'Y') conform to 'P'}}
227+
let _ = Conforms<X>.Decl4<Z2>.Decl2.self // expected-error {{'Conforms<X>.Decl4<Z2>.Decl2' requires that 'Z2.T' (aka 'Y') conform to 'P'}}
228+
let _ = Conforms<X>.Decl4<Z2>.Decl3.self // expected-error {{'Conforms<X>.Decl4<Z2>.Decl3' requires that 'Z2.T' (aka 'Y') conform to 'P'}}
214229
let _ = Conforms<X>.Decl4<Z2>.Decl4<X>.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}
215230
let _ = Conforms<X>.Decl4<Z2>.Decl5<X>.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}}

test/Constraints/diagnostics.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ func f7() -> (c: Int, v: A) {
9494
}
9595

9696
func f8<T:P2>(_ n: T, _ f: @escaping (T) -> T) {}
97-
f8(3, f4) // expected-error {{in argument type '(Int) -> Int', 'Int' does not conform to expected type 'P2'}}
97+
f8(3, f4) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}}
9898
typealias Tup = (Int, Double)
9999
func f9(_ x: Tup) -> Tup { return x }
100-
f8((1,2.0), f9) // expected-error {{in argument type '(Tup) -> Tup' (aka '((Int, Double)) -> (Int, Double)'), 'Tup' (aka '(Int, Double)') does not conform to expected type 'P2'}}
100+
f8((1,2.0), f9) // expected-error {{'(Tup, @escaping (Tup) -> Tup) -> ()' (aka '((Int, Double), @escaping ((Int, Double)) -> (Int, Double)) -> ()') requires that '(_, _)' conform to 'P2'}}
101101

102102
// <rdar://problem/19658691> QoI: Incorrect diagnostic for calling nonexistent members on literals
103103
1.doesntExist(0) // expected-error {{value of type 'Int' has no member 'doesntExist'}}
@@ -657,8 +657,7 @@ example21890157.property = "confusing" // expected-error {{value of optional ty
657657

658658
struct UnaryOp {}
659659

660-
_ = -UnaryOp() // expected-error {{unary operator '-' cannot be applied to an operand of type 'UnaryOp'}}
661-
// expected-note @-1 {{overloads for '-' exist with these partially matching parameter lists: (Float), (Double)}}
660+
_ = -UnaryOp() // expected-error {{argument type 'UnaryOp' does not conform to expected type 'SignedNumeric'}}
662661

663662

664663
// <rdar://problem/23433271> Swift compiler segfault in failure diagnosis
@@ -1190,10 +1189,9 @@ func rdar17170728() {
11901189
// https://bugs.swift.org/browse/SR-5934 - failure to emit diagnostic for bad
11911190
// generic constraints
11921191
func elephant<T, U>(_: T) where T : Collection, T.Element == U, T.Element : Hashable {}
1193-
// expected-note@-1 {{in call to function 'elephant'}}
11941192

11951193
func platypus<T>(a: [T]) {
1196-
_ = elephant(a) // expected-error {{generic parameter 'U' could not be inferred}}
1194+
_ = elephant(a) // expected-error {{'([T]) -> ()' requires that 'T' conform to 'Hashable'}}
11971195
}
11981196

11991197
// Another case of the above.

0 commit comments

Comments
 (0)