Skip to content

Commit c75178e

Browse files
committed
[Diagnostics] Add a fix-it to insert 'some' when associated type inference
failed because existential types can't conform to protocols.
1 parent 8a3cae5 commit c75178e

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,11 @@ NOTE(associated_type_witness_conform_impossible,none,
22632263
"candidate can not infer %0 = %1 because %1 "
22642264
"is not a nominal type and so can't conform to %2",
22652265
(Identifier, Type, Type))
2266+
NOTE(suggest_opaque_type_witness,none,
2267+
"cannot infer %0 = %1 because %1 as a type cannot "
2268+
"conform to protocols; did you mean to use an opaque "
2269+
"result type?",
2270+
(Identifier, Type, Type))
22662271
NOTE(associated_type_witness_inherit_impossible,none,
22672272
"candidate can not infer %0 = %1 because %1 "
22682273
"is not a class type and so can't inherit from %2",

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,31 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
17601760
if ((!failed.TypeWitness->getAnyNominal() ||
17611761
failed.TypeWitness->isExistentialType()) &&
17621762
failed.Result.isConformanceRequirement()) {
1763+
Type resultType;
1764+
SourceRange typeRange;
1765+
if (auto *var = dyn_cast<VarDecl>(failed.Witness)) {
1766+
resultType = var->getValueInterfaceType();
1767+
typeRange = var->getTypeSourceRangeForDiagnostics();
1768+
} else if (auto *func = dyn_cast<FuncDecl>(failed.Witness)) {
1769+
resultType = func->getResultInterfaceType();
1770+
typeRange = func->getResultTypeSourceRange();
1771+
}
1772+
1773+
// If the type witness was inferred from an existential
1774+
// result type, suggest an opaque result type instead,
1775+
// which can conform to protocols.
1776+
if (failed.TypeWitness->isExistentialType() &&
1777+
resultType && resultType->isEqual(failed.TypeWitness) &&
1778+
typeRange.isValid()) {
1779+
diags.diagnose(typeRange.Start,
1780+
diag::suggest_opaque_type_witness,
1781+
assocType->getName(), failed.TypeWitness,
1782+
failed.Result.getRequirement())
1783+
.highlight(typeRange)
1784+
.fixItInsert(typeRange.Start, "some ");
1785+
continue;
1786+
}
1787+
17631788
diags.diagnose(failed.Witness,
17641789
diag::associated_type_witness_conform_impossible,
17651790
assocType->getName(), failed.TypeWitness,

test/decl/protocol/req/missing_conformance.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,16 @@ protocol P12 {
107107
extension Int : P11 {}
108108
struct S3 : P12 { // expected-error {{type 'S3' does not conform to protocol 'P12'}}
109109
func bar() -> P11 { return 0 }
110-
// expected-note@-1 {{candidate can not infer 'A' = 'P11' because 'P11' is not a nominal type and so can't conform to 'P11'}}
110+
// expected-note@-1 {{cannot infer 'A' = 'P11' because 'P11' as a type cannot conform to protocols; did you mean to use an opaque result type?}}{{19-19=some }}
111+
}
112+
113+
protocol P13 {
114+
associatedtype A : P11 // expected-note {{unable to infer associated type 'A' for protocol 'P13'}}
115+
var bar: A { get }
116+
}
117+
struct S4: P13 { // expected-error {{type 'S4' does not conform to protocol 'P13'}}
118+
var bar: P11 { return 0 }
119+
// expected-note@-1 {{cannot infer 'A' = 'P11' because 'P11' as a type cannot conform to protocols; did you mean to use an opaque result type?}}{{12-12=some }}
111120
}
112121

113122
// SR-12759

0 commit comments

Comments
 (0)