Skip to content

Commit fc3d975

Browse files
authored
Merge pull request #26677 from xedin/dedup-generic-requirements
[Diagnostics] Correctly identify location of requirement failure
2 parents 3ab208d + df47d1b commit fc3d975

File tree

10 files changed

+110
-42
lines changed

10 files changed

+110
-42
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,36 @@ ValueDecl *RequirementFailure::getDeclRef() const {
373373
if (isFromContextualType())
374374
return getAffectedDeclFromType(cs.getContextualType());
375375

376-
if (auto overload = getChoiceFor(getRawAnchor()))
377-
return overload->choice.getDecl();
376+
if (auto overload = getChoiceFor(getRawAnchor())) {
377+
// If there is a declaration associated with this
378+
// failure e.g. an overload choice of the call
379+
// expression, let's see whether failure is
380+
// associated with it directly or rather with
381+
// one of its parents.
382+
if (auto *decl = overload->choice.getDeclOrNull()) {
383+
auto *DC = decl->getDeclContext();
384+
385+
do {
386+
if (auto *parent = DC->getAsDecl()) {
387+
if (auto *GC = parent->getAsGenericContext()) {
388+
if (GC->getGenericSignature() != Signature)
389+
continue;
390+
391+
// If this is a signature if an extension
392+
// then it means that code has referenced
393+
// something incorrectly and diagnostic
394+
// should point to the referenced declaration.
395+
if (isa<ExtensionDecl>(parent))
396+
break;
397+
398+
return cast<ValueDecl>(parent);
399+
}
400+
}
401+
} while ((DC = DC->getParent()));
402+
403+
return decl;
404+
}
405+
}
378406

379407
return getAffectedDeclFromType(getOwnerType());
380408
}

lib/Sema/CSDiagnostics.h

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -304,31 +304,12 @@ class RequirementFailure : public FailureDiagnostic {
304304
if (isConditional())
305305
return true;
306306

307-
auto *anchor = getAnchor();
308-
// In the situations like this:
309-
//
310-
// ```swift
311-
// enum E<T: P> { case foo(T) }
312-
// let _: E = .foo(...)
313-
// ```
314-
//
315-
// `E` is going to be opened twice. First, when
316-
// it's used as a contextual type, and when `E.foo`
317-
// is found and its function type is opened.
318-
// We still want to record both fixes but should
319-
// avoid diagnosing the same problem multiple times.
320-
if (isa<UnresolvedMemberExpr>(anchor)) {
321-
auto path = getLocator()->getPath();
322-
if (path.front().getKind() != ConstraintLocator::UnresolvedMember)
323-
return false;
324-
}
325-
326307
// For static/initializer calls there is going to be
327308
// a separate fix, attached to the argument, which is
328309
// much easier to diagnose.
329310
// For operator calls we can't currently produce a good
330311
// diagnostic, so instead let's refer to expression diagnostics.
331-
return !(Apply && (isOperator(Apply) || isa<TypeExpr>(anchor)));
312+
return !(Apply && isOperator(Apply));
332313
}
333314

334315
static bool isOperator(const ApplyExpr *apply) {

lib/Sema/CSSimplify.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,8 +2502,14 @@ bool ConstraintSystem::repairFailures(
25022502
if (lhs->hasDependentMember() || rhs->hasDependentMember())
25032503
break;
25042504

2505-
if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path))
2505+
auto reqKind = static_cast<RequirementKind>(elt.getValue2());
2506+
if (hasFixedRequirement(lhs, reqKind, rhs))
2507+
return true;
2508+
2509+
if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path)) {
2510+
recordFixedRequirement(lhs, reqKind, rhs);
25062511
conversionsOrFixes.push_back(fix);
2512+
}
25072513
break;
25082514
}
25092515

@@ -3775,6 +3781,11 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
37753781
if (type->isVoid() || type->isUninhabited())
37763782
return SolutionKind::Error;
37773783

3784+
auto protocolTy = protocol->getDeclaredType();
3785+
// If this conformance has been fixed already, let's just consider this done.
3786+
if (hasFixedRequirement(type, RequirementKind::Conformance, protocolTy))
3787+
return SolutionKind::Solved;
3788+
37783789
// If this is a generic requirement let's try to record that
37793790
// conformance is missing and consider this a success, which
37803791
// makes it much easier to diagnose problems like that.
@@ -3787,10 +3798,14 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
37873798

37883799
if (path.back().isTypeParameterRequirement() ||
37893800
path.back().isConditionalRequirement()) {
3790-
if (auto *fix = fixRequirementFailure(
3791-
*this, type, protocol->getDeclaredType(), anchor, path)) {
3792-
if (!recordFix(fix))
3801+
if (auto *fix =
3802+
fixRequirementFailure(*this, type, protocolTy, anchor, path)) {
3803+
if (!recordFix(fix)) {
3804+
// Record this conformance requirement as "fixed".
3805+
recordFixedRequirement(type, RequirementKind::Conformance,
3806+
protocolTy);
37933807
return SolutionKind::Solved;
3808+
}
37943809
}
37953810
}
37963811

lib/Sema/CSSolver.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
452452
numSavedBindings = cs.solverState->savedBindings.size();
453453
numConstraintRestrictions = cs.ConstraintRestrictions.size();
454454
numFixes = cs.Fixes.size();
455+
numFixedRequirements = cs.FixedRequirements.size();
455456
numDisjunctionChoices = cs.DisjunctionChoices.size();
456457
numOpenedTypes = cs.OpenedTypes.size();
457458
numOpenedExistentialTypes = cs.OpenedExistentialTypes.size();
@@ -505,6 +506,10 @@ ConstraintSystem::SolverScope::~SolverScope() {
505506
// Remove any opened types.
506507
truncate(cs.OpenedTypes, numOpenedTypes);
507508

509+
// Remove any conformances solver had to fix along
510+
// the current path.
511+
truncate(cs.FixedRequirements, numFixedRequirements);
512+
508513
// Remove any opened existential types.
509514
truncate(cs.OpenedExistentialTypes, numOpenedExistentialTypes);
510515

lib/Sema/ConstraintSystem.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,23 @@ class ConstraintSystem {
11191119
SmallVector<std::pair<ConstraintLocator *, ArrayRef<OpenedType>>, 4>
11201120
OpenedTypes;
11211121

1122+
/// The list of all generic requirements fixed along the current
1123+
/// solver path.
1124+
using FixedRequirement = std::tuple<TypeBase *, RequirementKind, TypeBase *>;
1125+
SmallVector<FixedRequirement, 4> FixedRequirements;
1126+
1127+
bool hasFixedRequirement(Type lhs, RequirementKind kind, Type rhs) {
1128+
auto reqInfo = std::make_tuple(lhs.getPointer(), kind, rhs.getPointer());
1129+
return llvm::any_of(
1130+
FixedRequirements,
1131+
[&reqInfo](const FixedRequirement &entry) { return entry == reqInfo; });
1132+
}
1133+
1134+
void recordFixedRequirement(Type lhs, RequirementKind kind, Type rhs) {
1135+
FixedRequirements.push_back(
1136+
std::make_tuple(lhs.getPointer(), kind, rhs.getPointer()));
1137+
}
1138+
11221139
/// A mapping from constraint locators to the opened existential archetype
11231140
/// used for the 'self' of an existential type.
11241141
SmallVector<std::pair<ConstraintLocator *, OpenedArchetypeType *>, 4>
@@ -1614,6 +1631,9 @@ class ConstraintSystem {
16141631
/// The length of \c Fixes.
16151632
unsigned numFixes;
16161633

1634+
/// The length of \c FixedRequirements.
1635+
unsigned numFixedRequirements;
1636+
16171637
/// The length of \c DisjunctionChoices.
16181638
unsigned numDisjunctionChoices;
16191639

test/Constraints/generics.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,14 @@ func testFixIts() {
335335
_ = AnyClassAndProtoBound() // expected-error {{generic parameter 'Foo' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{28-28=<<#Foo: SubProto & AnyObject#>>}}
336336
_ = AnyClassAndProtoBound2() // expected-error {{generic parameter 'Foo' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{29-29=<<#Foo: SubProto & AnyObject#>>}}
337337

338-
_ = ClassAndProtoBound() // expected-error {{referencing initializer 'init()' on 'ClassAndProtoBound' requires that 'X' conform to 'SubProto'}}
338+
_ = ClassAndProtoBound() // expected-error {{generic struct 'ClassAndProtoBound' requires that 'X' conform to 'SubProto'}}
339339

340-
_ = ClassAndProtosBound()
341-
// expected-error@-1 {{referencing initializer 'init()' on 'ClassAndProtosBound' requires that 'X' conform to 'NSCopyish'}}
342-
// expected-error@-2 {{referencing initializer 'init()' on 'ClassAndProtosBound' requires that 'X' conform to 'SubProto'}}
340+
_ = ClassAndProtosBound()
341+
// expected-error@-1 {{generic struct 'ClassAndProtosBound' requires that 'X' conform to 'SubProto'}}
342+
// expected-error@-2 {{generic struct 'ClassAndProtosBound' requires that 'X' conform to 'NSCopyish'}}
343343
_ = ClassAndProtosBound2()
344-
// expected-error@-1 {{referencing initializer 'init()' on 'ClassAndProtosBound2' requires that 'X' conform to 'NSCopyish'}}
345-
// expected-error@-2 {{referencing initializer 'init()' on 'ClassAndProtosBound2' requires that 'X' conform to 'SubProto'}}
344+
// expected-error@-1 {{generic struct 'ClassAndProtosBound2' requires that 'X' conform to 'SubProto'}}
345+
// expected-error@-2 {{generic struct 'ClassAndProtosBound2' requires that 'X' conform to 'NSCopyish'}}
346346

347347
_ = Pair()
348348
// expected-error@-1 {{generic parameter 'T' could not be inferred}}
@@ -631,16 +631,16 @@ func rdar40537858() {
631631
}
632632

633633
var arr: [S] = []
634-
_ = List(arr, id: \.id) // expected-error {{referencing initializer 'init(_:id:)' on 'List' requires that 'S.Id' conform to 'Hashable'}}
634+
_ = List(arr, id: \.id) // expected-error {{generic struct 'List' requires that 'S.Id' conform to 'Hashable'}}
635635

636636
enum E<T: P> { // expected-note 2 {{where 'T' = 'S'}}
637637
case foo(T)
638638
case bar([T])
639639
}
640640

641641
var s = S(id: S.Id())
642-
let _: E = .foo(s) // expected-error {{enum case 'foo' requires that 'S' conform to 'P'}}
643-
let _: E = .bar([s]) // expected-error {{enum case 'bar' requires that 'S' conform to 'P'}}
642+
let _: E = .foo(s) // expected-error {{generic enum 'E' requires that 'S' conform to 'P'}}
643+
let _: E = .bar([s]) // expected-error {{generic enum 'E' requires that 'S' conform to 'P'}}
644644
}
645645

646646
// https://bugs.swift.org/browse/SR-8934
@@ -798,3 +798,20 @@ struct R_51413254 {
798798
self.str = try anyDict ==> Key("a") // Ok
799799
}
800800
}
801+
802+
func test_correct_identification_of_requirement_source() {
803+
struct X<T: P> { // expected-note {{where 'T' = 'Int'}}
804+
init<U: P>(_: T, _: U) {} // expected-note {{where 'U' = 'Int'}}
805+
}
806+
807+
struct A : P {
808+
static func foo(_ arg: A) -> A {
809+
return A()
810+
}
811+
}
812+
813+
_ = X(17, A())
814+
// expected-error@-1 {{generic struct 'X' requires that 'Int' conform to 'P'}}
815+
_ = X(A(), 17)
816+
// expected-error@-1 {{initializer 'init(_:_:)' requires that 'Int' conform to 'P'}}
817+
}

test/Constraints/rdar44569159.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ struct S<V> where V: P { // expected-note {{where 'V' = 'Double'}}
66
}
77

88
struct A {
9-
subscript<T>(_ v: S<T>) -> A { // expected-note {{where 'T' = 'Double'}}
9+
subscript<T>(_ v: S<T>) -> A {
1010
fatalError()
1111
}
1212
}
1313

1414
func foo(_ v: Double) {
1515
_ = A()[S(value: v)]
16-
// expected-error@-1 {{subscript 'subscript(_:)' requires that 'Double' conform to 'P'}}
17-
// expected-error@-2 {{referencing initializer 'init(value:)' on 'S' requires that 'Double' conform to 'P'}}
16+
// expected-error@-1 {{generic struct 'S' requires that 'Double' conform to 'P'}}
1817
}

test/Sema/diag_ambiguous_overloads.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class sr7440_Genre {
9494
return sr7440_Genre.fetch(genreID: iTunesGenre.genreID, name: iTunesGenre.name)
9595
// expected-error@-1 {{value of type 'sr7440_ITunesGenre' has no member 'genreID'; did you mean 'genre'?}}
9696
// expected-error@-2 {{cannot convert return expression of type '()' to return type 'sr7440_Genre'}}
97+
// expected-error@-3 {{protocol type 'Any' cannot conform to 'BinaryInteger' because only concrete types can conform to protocols}}
98+
// TODO(diagnostics): Last diagnostic should not be recorded but to be able to correctly handle it we need a notion of a "hole" in constraint system.
9799
}
98100
}
99101

test/decl/nested/type_in_type.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ func test() {
405405
// expected-error@-1 {{generic parameter 'A' could not be inferred}}
406406
_ = Claws.Fangs<NotADog>()
407407
// expected-error@-1 {{generic parameter 'A' could not be inferred}}
408-
// expected-error@-2 {{referencing initializer 'init()' on 'Claws.Fangs' requires that 'NotADog' conform to 'ExpressibleByDogLiteral'}}
408+
// expected-error@-2 {{generic struct 'Fangs' requires that 'NotADog' conform to 'ExpressibleByDogLiteral'}}
409409
// expected-note@-3 {{explicitly specify the generic arguments to fix this issue}} {{12-12=<<#A: ExpressibleByCatLiteral#>>}}
410410
}
411411

test/expr/unary/keypath/salvage-with-other-type-errors.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
// to diagnose other errors in adjacent exprs.
55

66
struct P<T: K> { }
7+
// expected-note@-1 {{arguments to generic parameter 'T' ('String' and '_') are expected to be equal}}
78

89
struct S {
9-
init<B>(_ a: P<B>) { // expected-note {{where 'B' = 'String'}}
10+
init<B>(_ a: P<B>) {
1011
fatalError()
1112
}
1213
}
@@ -17,7 +18,7 @@ func + <Object>(lhs: KeyPath<A, Object>, rhs: String) -> P<Object> {
1718
fatalError()
1819
}
1920

20-
// expected-error@+1{{}}
21+
// expected-error@+1{{type 'String' does not conform to protocol 'K'}}
2122
func + (lhs: KeyPath<A, String>, rhs: String) -> P<String> {
2223
fatalError()
2324
}
@@ -27,7 +28,7 @@ struct A {
2728
}
2829

2930
extension A: K {
30-
static let j = S(\A.id + "id") // expected-error {{initializer 'init(_:)' requires that 'String' conform to 'K'}}
31+
static let j = S(\A.id + "id") // expected-error {{cannot convert value of type 'P<String>' to expected argument type 'P<_>'}}
3132
}
3233

3334
// SR-5034

0 commit comments

Comments
 (0)