Skip to content

Commit 2a7e009

Browse files
committed
[Diagnostics] Add notes for the type_cannot_conform error that point
to the declaration that requires protocol conformance.
1 parent 173e452 commit 2a7e009

File tree

15 files changed

+82
-16
lines changed

15 files changed

+82
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,15 @@ ERROR(type_cannot_conform, none,
16641664
"%select{|protocol }0type %1 cannot conform to %2; "
16651665
"only struct/enum/class types can conform to protocols",
16661666
(bool, Type, Type))
1667+
NOTE(required_by_opaque_return,none,
1668+
"required by opaque return type of %0 %1", (DescriptiveDeclKind, DeclName))
1669+
NOTE(required_by_decl,none,
1670+
"required by %0 %1 where %2 = %3",
1671+
(DescriptiveDeclKind, DeclName, Type, Type))
1672+
NOTE(required_by_decl_ref,none,
1673+
"required by referencing %0 %1 on %2 where %3 = %4",
1674+
(DescriptiveDeclKind, DeclName, Type, Type, Type))
1675+
16671676
ERROR(protocol_does_not_conform_static,none,
16681677
"%0 cannot be used as a type conforming to protocol %1 because %1 "
16691678
"has static requirements",

lib/Sema/CSDiagnostics.cpp

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -597,17 +597,54 @@ bool MissingConformanceFailure::diagnoseAsError() {
597597

598598
bool MissingConformanceFailure::diagnoseTypeCannotConform(Expr *anchor,
599599
Type nonConformingType, Type protocolType) const {
600-
if (nonConformingType->is<AnyFunctionType>() ||
600+
if (!(nonConformingType->is<AnyFunctionType>() ||
601601
nonConformingType->is<TupleType>() ||
602602
nonConformingType->isExistentialType() ||
603-
nonConformingType->is<AnyMetatypeType>()) {
604-
emitDiagnostic(anchor->getLoc(), diag::type_cannot_conform,
605-
nonConformingType->isExistentialType(), nonConformingType,
606-
protocolType);
603+
nonConformingType->is<AnyMetatypeType>())) {
604+
return false;
605+
}
606+
607+
emitDiagnostic(anchor->getLoc(), diag::type_cannot_conform,
608+
nonConformingType->isExistentialType(), nonConformingType,
609+
protocolType);
610+
611+
if (auto *OTD = dyn_cast<OpaqueTypeDecl>(AffectedDecl)) {
612+
auto *namingDecl = OTD->getNamingDecl();
613+
if (auto *repr = namingDecl->getOpaqueResultTypeRepr()) {
614+
emitDiagnostic(repr->getLoc(), diag::required_by_opaque_return,
615+
namingDecl->getDescriptiveKind(), namingDecl->getFullName())
616+
.highlight(repr->getSourceRange());
617+
}
607618
return true;
608619
}
609620

610-
return false;
621+
auto &req = getRequirement();
622+
auto *reqDC = getRequirementDC();
623+
auto *genericCtx = getGenericContext();
624+
auto noteLocation = reqDC->getAsDecl()->getLoc();
625+
626+
if (!noteLocation.isValid())
627+
noteLocation = anchor->getLoc();
628+
629+
if (isConditional()) {
630+
emitDiagnostic(noteLocation, diag::requirement_implied_by_conditional_conformance,
631+
resolveType(Conformance->getType()),
632+
Conformance->getProtocol()->getDeclaredInterfaceType());
633+
} else if (genericCtx != reqDC && (genericCtx->isChildContextOf(reqDC) ||
634+
isStaticOrInstanceMember(AffectedDecl))) {
635+
emitDiagnostic(noteLocation, diag::required_by_decl_ref,
636+
AffectedDecl->getDescriptiveKind(),
637+
AffectedDecl->getFullName(),
638+
reqDC->getSelfNominalTypeDecl()->getDeclaredType(),
639+
req.getFirstType(), nonConformingType);
640+
} else {
641+
emitDiagnostic(noteLocation, diag::required_by_decl,
642+
AffectedDecl->getDescriptiveKind(),
643+
AffectedDecl->getFullName(), req.getFirstType(),
644+
nonConformingType);
645+
}
646+
647+
return true;
611648
}
612649

613650
Optional<Diag<Type, Type>> GenericArgumentsMismatchFailure::getDiagnosticFor(

lib/Sema/CSDiagnostics.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,10 @@ class RequirementFailure : public FailureDiagnostic {
308308
isa<BinaryExpr>(apply);
309309
}
310310

311+
/// Determine whether given declaration represents a static
312+
/// or instance property/method, excluding operators.
313+
static bool isStaticOrInstanceMember(const ValueDecl *decl);
314+
311315
private:
312316
/// Retrieve declaration associated with failing generic requirement.
313317
ValueDecl *getDeclRef() const;
@@ -317,10 +321,6 @@ class RequirementFailure : public FailureDiagnostic {
317321

318322
void emitRequirementNote(const Decl *anchor, Type lhs, Type rhs) const;
319323

320-
/// Determine whether given declaration represents a static
321-
/// or instance property/method, excluding operators.
322-
static bool isStaticOrInstanceMember(const ValueDecl *decl);
323-
324324
/// If this is a failure in conditional requirement, retrieve
325325
/// conformance information.
326326
ProtocolConformance *

test/Constraints/diagnostics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ func f3(_: @escaping (_: @escaping (Int) -> Float) -> Int) {}
2828
func f4(_ x: Int) -> Int { }
2929

3030
func f5<T : P2>(_ : T) { }
31+
// expected-note@-1 {{required by global function 'f5' where 'T' = '(Int) -> Int'}}
32+
// expected-note@-2 {{required by global function 'f5' where 'T' = '(Int, String)'}}
33+
// expected-note@-3 {{required by global function 'f5' where 'T' = 'Int.Type'}}
3134

3235
func f6<T : P, U : P>(_ t: T, _ u: U) where T.SomeType == U.SomeType {}
3336

@@ -96,6 +99,7 @@ func f7() -> (c: Int, v: A) {
9699
}
97100

98101
func f8<T:P2>(_ n: T, _ f: @escaping (T) -> T) {}
102+
// expected-note@-1 {{required by global function 'f8' where 'T' = 'Tup' (aka '(Int, Double)')}}
99103
f8(3, f4) // expected-error {{argument type 'Int' does not conform to expected type 'P2'}}
100104
typealias Tup = (Int, Double)
101105
func f9(_ x: Tup) -> Tup { return x }

test/Constraints/function_builder_diags.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ struct TupleP<U> : P {
144144

145145
@_functionBuilder
146146
struct Builder {
147-
static func buildBlock<S0, S1>(_ stmt1: S0, _ stmt2: S1)
147+
static func buildBlock<S0, S1>(_ stmt1: S0, _ stmt2: S1) // expected-note {{required by static method 'buildBlock' where 'S1' = 'Label<Any>.Type'}}
148148
-> TupleP<(S0, S1)> where S0: P, S1: P {
149149
return TupleP((stmt1, stmt2))
150150
}

test/Constraints/generics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func r22459135() {
192192
// <rdar://problem/19710848> QoI: Friendlier error message for "[] as Set"
193193
// <rdar://problem/22326930> QoI: "argument for generic parameter 'Element' could not be inferred" lacks context
194194
_ = [] as Set // expected-error {{protocol type 'Any' cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols}}
195+
// expected-note@-1 {{required by generic struct 'Set' where 'Element' = 'Any'}}
195196

196197

197198
//<rdar://problem/22509125> QoI: Error when unable to infer generic archetype lacks greatness

test/Constraints/operator.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ func rdar46459603() {
221221
// expected-note@-2 {{expected an argument list of type '(Self, Self)'}}
222222
_ = [arr.values] == [[e]]
223223
// expected-error@-1 {{protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols}}
224+
// expected-note@-2 {{requirement from conditional conformance of '[Any]' to 'Equatable'}}
224225
}
225226

226227
// SR-10843

test/Generics/conditional_conformances_literals.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extension Array: Conforms where Element: Conforms {}
1616
// expected-note@-1 5 {{requirement from conditional conformance of '[Fails]' to 'Conforms'}}
1717
extension Dictionary: Conforms where Value: Conforms {}
1818
// expected-note@-1 3 {{requirement from conditional conformance of '[Int : Fails]' to 'Conforms'}}
19+
// expected-note@-2 2 {{requirement from conditional conformance of '[Int : Conforms]' to 'Conforms'}}
1920

2021
let works = Works()
2122
let fails = Fails()

test/Generics/existential_restrictions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ protocol CP : class { }
99
}
1010

1111
func fP<T : P>(_ t: T) { }
12+
// expected-note@-1 {{required by global function 'fP' where 'T' = 'P'}}
13+
// expected-note@-2 {{required by global function 'fP' where 'T' = 'OP & P'}}
1214
func fOP<T : OP>(_ t: T) { }
15+
// expected-note@-1 {{required by global function 'fOP' where 'T' = 'OP & P'}}
1316
func fOPE(_ t: OP) { }
1417
func fSP<T : SP>(_ t: T) { }
1518
func fAO<T : AnyObject>(_ t: T) { }
@@ -73,6 +76,7 @@ protocol Mine {}
7376
class M1: Mine {}
7477
class M2: Mine {}
7578
extension Collection where Iterator.Element : Mine {
79+
// expected-note@-1 {{required by referencing instance method 'takeAll()' on 'Collection'}}
7680
func takeAll() {}
7781
}
7882

test/decl/protocol/conforms/error_self_conformance.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// RUN: %target-typecheck-verify-swift
22

33
func wantsError<T: Error>(_: T) {}
4+
// expected-note@-1 {{required by global function 'wantsError' where 'T' = 'ErrorRefinement'}}
5+
// expected-note@-2 {{required by global function 'wantsError' where 'T' = 'Error & OtherProtocol'}}
6+
// expected-note@-3 {{required by global function 'wantsError' where 'T' = 'C & Error'}}
47

58
func testSimple(error: Error) {
69
wantsError(error)

0 commit comments

Comments
 (0)