Skip to content

Commit a7524e5

Browse files
committed
[CSFix] Allow diagnosing missing conformance in ambiguity cases
It's possible that different overload choices could have the same conformance requirement, so diagnostics should be able to emit an error in situations where multiple solutions point to the missing conformance at the same location (both in AST and requirement list). Resolves: rdar://74447308
1 parent cd724d4 commit a7524e5

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

include/swift/Sema/CSFix.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ class MissingConformance final : public ConstraintFix {
478478

479479
bool diagnose(const Solution &solution, bool asNote = false) const override;
480480

481+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
482+
481483
static MissingConformance *forRequirement(ConstraintSystem &cs, Type type,
482484
Type protocolType,
483485
ConstraintLocator *locator);
@@ -489,6 +491,12 @@ class MissingConformance final : public ConstraintFix {
489491
Type getNonConformingType() { return NonConformingType; }
490492

491493
Type getProtocolType() { return ProtocolType; }
494+
495+
bool isEqual(const ConstraintFix *other) const;
496+
497+
static bool classof(const ConstraintFix *fix) {
498+
return fix->getKind() == FixKind::AddConformance;
499+
}
492500
};
493501

494502
/// Skip same-type generic requirement constraint,

lib/Sema/CSFix.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,34 @@ bool MissingConformance::diagnose(const Solution &solution, bool asNote) const {
248248
return failure.diagnose(asNote);
249249
}
250250

251+
bool MissingConformance::diagnoseForAmbiguity(
252+
CommonFixesArray commonFixes) const {
253+
auto *primaryFix = commonFixes.front().second->getAs<MissingConformance>();
254+
assert(primaryFix);
255+
256+
if (llvm::all_of(
257+
commonFixes,
258+
[&primaryFix](
259+
const std::pair<const Solution *, const ConstraintFix *> &entry) {
260+
return primaryFix->isEqual(entry.second);
261+
}))
262+
return diagnose(*commonFixes.front().first);
263+
264+
// If the location is the same but there are different requirements
265+
// involved let's not attempt to diagnose that as an ambiguity.
266+
return false;
267+
}
268+
269+
bool MissingConformance::isEqual(const ConstraintFix *other) const {
270+
auto *conformanceFix = other->getAs<MissingConformance>();
271+
if (!conformanceFix)
272+
return false;
273+
274+
return IsContextual == conformanceFix->IsContextual &&
275+
NonConformingType->isEqual(conformanceFix->NonConformingType) &&
276+
ProtocolType->isEqual(conformanceFix->ProtocolType);
277+
}
278+
251279
MissingConformance *
252280
MissingConformance::forContextual(ConstraintSystem &cs, Type type,
253281
Type protocolType,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-typecheck-verify-swift -target x86_64-apple-macosx10.15 -swift-version 5
2+
3+
// REQUIRES: objc_interop
4+
// REQUIRES: OS=macosx
5+
6+
import SwiftUI
7+
8+
struct Item {
9+
let id = UUID()
10+
let text = ""
11+
}
12+
13+
class ItemList: ObservableObject {
14+
@Published var items = [Item]()
15+
}
16+
17+
struct ContentView: View {
18+
@StateObject var list = ItemList()
19+
20+
var body: some View {
21+
List {
22+
ForEach(list.items) { item in
23+
// expected-error@-1 {{referencing initializer 'init(_:content:)' on 'ForEach' requires that 'Item' conform to 'Identifiable'}}
24+
Text(item.text)
25+
}
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)