Skip to content

Commit cbd2916

Browse files
authored
Merge pull request #36045 from xedin/handle-non-ambiguous-fixes-better
[CSFix] Handle some cases where the same fix appears in multiple solutions
2 parents 9552442 + 2274e17 commit cbd2916

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

include/swift/Sema/CSFix.h

Lines changed: 24 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,
@@ -1386,11 +1394,19 @@ class MoveOutOfOrderArgument final : public ConstraintFix {
13861394

13871395
bool diagnose(const Solution &solution, bool asNote = false) const override;
13881396

1397+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
1398+
1399+
bool isEqual(const ConstraintFix *other) const;
1400+
13891401
static MoveOutOfOrderArgument *create(ConstraintSystem &cs,
13901402
unsigned argIdx,
13911403
unsigned prevArgIdx,
13921404
ArrayRef<ParamBinding> bindings,
13931405
ConstraintLocator *locator);
1406+
1407+
static bool classof(const ConstraintFix *fix) {
1408+
return fix->getKind() == FixKind::MoveOutOfOrderArgument;
1409+
}
13941410
};
13951411

13961412
class AllowInaccessibleMember final : public AllowInvalidMemberRef {
@@ -1512,10 +1528,18 @@ class AllowInvalidRefInKeyPath final : public ConstraintFix {
15121528

15131529
bool diagnose(const Solution &solution, bool asNote = false) const override;
15141530

1531+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
1532+
15151533
/// Determine whether give reference requires a fix and produce one.
15161534
static AllowInvalidRefInKeyPath *
15171535
forRef(ConstraintSystem &cs, ValueDecl *member, ConstraintLocator *locator);
15181536

1537+
bool isEqual(const ConstraintFix *other) const;
1538+
1539+
static bool classof(const ConstraintFix *fix) {
1540+
return fix->getKind() == FixKind::AllowInvalidRefInKeyPath;
1541+
}
1542+
15191543
private:
15201544
static AllowInvalidRefInKeyPath *create(ConstraintSystem &cs, RefKind kind,
15211545
ValueDecl *member,

lib/Sema/CSFix.cpp

Lines changed: 74 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,
@@ -837,6 +865,29 @@ bool MoveOutOfOrderArgument::diagnose(const Solution &solution,
837865
return failure.diagnose(asNote);
838866
}
839867

868+
bool MoveOutOfOrderArgument::diagnoseForAmbiguity(
869+
CommonFixesArray commonFixes) const {
870+
auto *primaryFix =
871+
commonFixes.front().second->getAs<MoveOutOfOrderArgument>();
872+
assert(primaryFix);
873+
874+
if (llvm::all_of(
875+
commonFixes,
876+
[&primaryFix](
877+
const std::pair<const Solution *, const ConstraintFix *> &entry) {
878+
return primaryFix->isEqual(entry.second);
879+
}))
880+
return diagnose(*commonFixes.front().first);
881+
882+
return false;
883+
}
884+
885+
bool MoveOutOfOrderArgument::isEqual(const ConstraintFix *other) const {
886+
auto OoOFix = other->getAs<MoveOutOfOrderArgument>();
887+
return OoOFix ? ArgIdx == OoOFix->ArgIdx && PrevArgIdx == OoOFix->PrevArgIdx
888+
: false;
889+
}
890+
840891
MoveOutOfOrderArgument *MoveOutOfOrderArgument::create(
841892
ConstraintSystem &cs, unsigned argIdx, unsigned prevArgIdx,
842893
ArrayRef<ParamBinding> bindings, ConstraintLocator *locator) {
@@ -925,6 +976,29 @@ bool AllowInvalidRefInKeyPath::diagnose(const Solution &solution,
925976
llvm_unreachable("covered switch");
926977
}
927978

979+
bool AllowInvalidRefInKeyPath::diagnoseForAmbiguity(
980+
CommonFixesArray commonFixes) const {
981+
auto *primaryFix =
982+
commonFixes.front().second->getAs<AllowInvalidRefInKeyPath>();
983+
assert(primaryFix);
984+
985+
if (llvm::all_of(
986+
commonFixes,
987+
[&primaryFix](
988+
const std::pair<const Solution *, const ConstraintFix *> &entry) {
989+
return primaryFix->isEqual(entry.second);
990+
})) {
991+
return diagnose(*commonFixes.front().first);
992+
}
993+
994+
return false;
995+
}
996+
997+
bool AllowInvalidRefInKeyPath::isEqual(const ConstraintFix *other) const {
998+
auto *refFix = other->getAs<AllowInvalidRefInKeyPath>();
999+
return refFix ? Kind == refFix->Kind && Member == refFix->Member : false;
1000+
}
1001+
9281002
AllowInvalidRefInKeyPath *
9291003
AllowInvalidRefInKeyPath::forRef(ConstraintSystem &cs, ValueDecl *member,
9301004
ConstraintLocator *locator) {
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+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
import Foundation
8+
9+
struct ContentView: View {
10+
@State private var date = Date()
11+
12+
var body: some View {
13+
Group {
14+
DatePicker("Enter a date", selection: $date, displayedComponents: .date, in: Date())
15+
// expected-error@-1 {{argument 'in' must precede argument 'displayedComponents'}} {{78-90=}} {{52-52=in: Date(), }}
16+
DatePicker("Enter a date", selection: $date, displayedComponents: .date, in: Date() ... Date().addingTimeInterval(100))
17+
// expected-error@-1 {{argument 'in' must precede argument 'displayedComponents'}} {{78-125=}} {{52-52=in: Date() ... Date().addingTimeInterval(100), }}
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)