Skip to content

Commit a9c2055

Browse files
authored
Merge pull request #39937 from hborla/opaque-type-fixit
[Diagnostics] Add a fix-it to insert 'some' when associated type inference failed because existential types can't conform to protocols.
2 parents 2f6b5b9 + ba2187a commit a9c2055

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,34 @@ bool AssociatedTypeInference::diagnoseNoSolutions(
17761776
if ((!failed.TypeWitness->getAnyNominal() ||
17771777
failed.TypeWitness->isExistentialType()) &&
17781778
failed.Result.isConformanceRequirement()) {
1779+
Type resultType;
1780+
SourceRange typeRange;
1781+
if (auto *var = dyn_cast<VarDecl>(failed.Witness)) {
1782+
resultType = var->getValueInterfaceType();
1783+
typeRange = var->getTypeSourceRangeForDiagnostics();
1784+
} else if (auto *func = dyn_cast<FuncDecl>(failed.Witness)) {
1785+
resultType = func->getResultInterfaceType();
1786+
typeRange = func->getResultTypeSourceRange();
1787+
} else if (auto *subscript = dyn_cast<SubscriptDecl>(failed.Witness)) {
1788+
resultType = subscript->getElementInterfaceType();
1789+
typeRange = subscript->getElementTypeSourceRange();
1790+
}
1791+
1792+
// If the type witness was inferred from an existential
1793+
// result type, suggest an opaque result type instead,
1794+
// which can conform to protocols.
1795+
if (failed.TypeWitness->isExistentialType() &&
1796+
resultType && resultType->isEqual(failed.TypeWitness) &&
1797+
typeRange.isValid()) {
1798+
diags.diagnose(typeRange.Start,
1799+
diag::suggest_opaque_type_witness,
1800+
assocType->getName(), failed.TypeWitness,
1801+
failed.Result.getRequirement())
1802+
.highlight(typeRange)
1803+
.fixItInsert(typeRange.Start, "some ");
1804+
continue;
1805+
}
1806+
17791807
diags.diagnose(failed.Witness,
17801808
diag::associated_type_witness_conform_impossible,
17811809
assocType->getName(), failed.TypeWitness,

test/decl/protocol/req/missing_conformance.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,25 @@ 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 }}
120+
}
121+
122+
protocol P14 {
123+
associatedtype A : P11 // expected-note {{unable to infer associated type 'A' for protocol 'P14'}}
124+
subscript(i: Int) -> A { get }
125+
}
126+
struct S5: P14 { // expected-error {{type 'S5' does not conform to protocol 'P14'}}
127+
subscript(i: Int) -> P11 { return i }
128+
// 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?}}{{24-24=some }}
111129
}
112130

113131
// SR-12759

0 commit comments

Comments
 (0)