Skip to content

Commit 638272d

Browse files
authored
Merge pull request #18952 from hamishknight/concrete-is-better-swift4
2 parents 27e326a + db2897f commit 638272d

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

lib/Sema/CSRanking.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,9 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
802802
bool isStdlibOptionalMPlusOperator1 = false;
803803
bool isStdlibOptionalMPlusOperator2 = false;
804804

805+
bool isVarAndNotProtocol1 = false;
806+
bool isVarAndNotProtocol2 = false;
807+
805808
auto getWeight = [&](ConstraintLocator *locator) -> unsigned {
806809
if (auto *anchor = locator->getAnchor()) {
807810
auto weight = weights.find(anchor);
@@ -1016,6 +1019,32 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
10161019
}
10171020
}
10181021

1022+
// Swift 4.1 compatibility hack: If everything else is considered equal,
1023+
// favour a property on a concrete type over a protocol property member.
1024+
//
1025+
// This hack is required due to changes in shadowing behaviour where a
1026+
// protocol property member will no longer shadow a property on a concrete
1027+
// type, which created unintentional ambiguities in 4.2. This hack ensures
1028+
// we at least keep these cases unambiguous in Swift 5 under Swift 4
1029+
// compatibility mode. Don't however apply this hack for decls found through
1030+
// dynamic lookup, as we want the user to have to disambiguate those.
1031+
//
1032+
// This is intentionally narrow in order to best preserve source
1033+
// compatibility under Swift 4 mode by ensuring we don't introduce any new
1034+
// ambiguities. This will become a more general "is more specialised" rule
1035+
// in Swift 5 mode.
1036+
if (!tc.Context.isSwiftVersionAtLeast(5) &&
1037+
choice1.getKind() != OverloadChoiceKind::DeclViaDynamic &&
1038+
choice2.getKind() != OverloadChoiceKind::DeclViaDynamic &&
1039+
isa<VarDecl>(decl1) && isa<VarDecl>(decl2)) {
1040+
auto *nominal1 = dc1->getSelfNominalTypeDecl();
1041+
auto *nominal2 = dc2->getSelfNominalTypeDecl();
1042+
if (nominal1 && nominal2 && nominal1 != nominal2) {
1043+
isVarAndNotProtocol1 = !isa<ProtocolDecl>(nominal1);
1044+
isVarAndNotProtocol2 = !isa<ProtocolDecl>(nominal2);
1045+
}
1046+
}
1047+
10191048
// FIXME: Lousy hack for ?? to prefer the catamorphism (flattening)
10201049
// over the mplus (non-flattening) overload if all else is equal.
10211050
if (decl1->getBaseName() == "??") {
@@ -1172,6 +1201,14 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
11721201
score1 += isStdlibOptionalMPlusOperator2;
11731202
}
11741203

1204+
// All other things being equal, apply the Swift 4.1 compatibility hack for
1205+
// preferring var members in concrete types over a protocol requirement
1206+
// (see the comment above for the rationale of this hack).
1207+
if (!tc.Context.isSwiftVersionAtLeast(5) && score1 == score2) {
1208+
score1 += isVarAndNotProtocol1;
1209+
score2 += isVarAndNotProtocol2;
1210+
}
1211+
11751212
// FIXME: There are type variables and overloads not common to both solutions
11761213
// that haven't been considered. They make the systems different, but don't
11771214
// affect ranking. We need to handle this.

test/Constraints/sr7425.swift

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
3+
protocol X {
4+
var foo: Int { get }
5+
var bar: Int { get }
6+
}
7+
8+
class Y {
9+
var foo: Int = 0
10+
}
11+
extension Y {
12+
var bar: Int { return foo }
13+
}
14+
15+
protocol Z : Y {
16+
var foo: Int { get }
17+
var bar: Int { get }
18+
}
19+
20+
class GenericClass<T> {
21+
var foo: T
22+
init(_ foo: T) { self.foo = foo }
23+
}
24+
extension GenericClass {
25+
var bar: T { return foo }
26+
}
27+
28+
// Make sure we keep all of the following cases unambiguous to retain source compatibility with Swift 4.1.
29+
30+
func testGenericPropertyProtocolClass<T : X & Y>(_ t: T) {
31+
_ = t.foo
32+
_ = t.bar
33+
}
34+
35+
func testExistentialPropertyProtocolClass(_ t: X & Y) {
36+
_ = t.foo
37+
_ = t.bar
38+
}
39+
40+
func testGenericPropertySubclassConstrainedProtocol<T : Z>(_ t: T) {
41+
_ = t.foo
42+
_ = t.bar
43+
}
44+
45+
func testExistentialPropertySubclassConstrainedProtocol(_ t: Z) {
46+
_ = t.foo
47+
_ = t.bar
48+
}
49+
50+
func testExistentialPropertyProtocolGenericClass(_ t: GenericClass<Int> & X) {
51+
_ = t.foo
52+
_ = t.bar
53+
}
54+
55+
func testExistentialPropertyProtocolGenericClass(_ t: GenericClass<String> & X) {
56+
_ = t.foo
57+
_ = t.bar
58+
}
59+
60+
extension X where Self : Y {
61+
func testGenericPropertyProtocolClass(_ x: Self) {
62+
_ = self.foo
63+
_ = self.bar
64+
}
65+
}
66+
67+
extension X where Self : GenericClass<Int> {
68+
func testGenericPropertyProtocolGenericClass(_ x: Self) {
69+
_ = self.foo
70+
_ = self.bar
71+
}
72+
}
73+
74+
extension X where Self : GenericClass<String> {
75+
func testGenericPropertyProtocolGenericClass(_ x: Self) {
76+
_ = self.foo
77+
_ = self.bar
78+
}
79+
}

0 commit comments

Comments
 (0)