Skip to content

Commit 98e95bd

Browse files
authored
Merge pull request #18488 from hamishknight/swift4-cross-module-var-overload
[NameLookup] Swift 4 compatibility hack for cross-module var overloads in generic type extensions
2 parents 46a542e + 1e764cc commit 98e95bd

File tree

7 files changed

+235
-0
lines changed

7 files changed

+235
-0
lines changed

lib/AST/NameLookup.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,25 @@ static void recordShadowedDeclsAfterSignatureMatch(
172172
for (unsigned firstIdx : indices(decls)) {
173173
auto firstDecl = decls[firstIdx];
174174
auto firstModule = firstDecl->getModuleContext();
175+
auto firstSig = firstDecl->getOverloadSignature();
175176
for (unsigned secondIdx : range(firstIdx + 1, decls.size())) {
176177
// Determine whether one module takes precedence over another.
177178
auto secondDecl = decls[secondIdx];
178179
auto secondModule = secondDecl->getModuleContext();
179180

181+
// Swift 4 compatibility hack: Don't shadow properties defined in
182+
// extensions of generic types with properties defined elsewhere.
183+
// This is due to the fact that in Swift 4, we only gave custom overload
184+
// types to properties in extensions of generic types, otherwise we
185+
// used the null type.
186+
if (!ctx.isSwiftVersionAtLeast(5)) {
187+
auto secondSig = secondDecl->getOverloadSignature();
188+
if (firstSig.IsVariable && secondSig.IsVariable)
189+
if (firstSig.InExtensionOfGenericType !=
190+
secondSig.InExtensionOfGenericType)
191+
continue;
192+
}
193+
180194
// If one declaration is in a protocol or extension thereof and the
181195
// other is not, prefer the one that is not.
182196
if ((bool)firstDecl->getDeclContext()->getSelfProtocolDecl() !=

test/Constraints/dynamic_lookup.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,19 @@ func dynamicInitCrash(ao: AnyObject.Type) {
340340
let sdk = ao.init(blahblah: ())
341341
// expected-error@-1 {{incorrect argument label in call (have 'blahblah:', expected 'toMemory:')}}
342342
}
343+
344+
// Test that we correctly diagnose ambiguity for different typed properties available
345+
// through dynamic lookup.
346+
@objc protocol P3 {
347+
var i: UInt { get } // expected-note {{found this candidate}}
348+
var j: Int { get }
349+
}
350+
351+
class C1 {
352+
@objc var i: Int { return 0 } // expected-note {{found this candidate}}
353+
@objc var j: Int { return 0 }
354+
}
355+
356+
_ = (C1() as AnyObject).i // expected-error {{ambiguous use of 'i'}}
357+
_ = (C1() as AnyObject).j // okay
358+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
public struct HasFooGeneric<T> {
3+
public var foo: Int = 0
4+
}
5+
6+
extension HasFooGeneric {
7+
public var bar: Int { return 0 }
8+
}
9+
10+
public class HasFooNonGeneric {
11+
public var foo: Int = 0
12+
}
13+
14+
extension HasFooNonGeneric {
15+
public var bar: Int { return 0 }
16+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -o %t -module-name=library %S/Inputs/complete_import_overloads.swift
3+
4+
// RUN: %target-swift-ide-test -code-completion -swift-version 4 -source-filename %s -code-completion-token=SELF_DOT_1 -I %t | %FileCheck %s -check-prefix=SWIFT4_SELF_DOT_1
5+
// RUN: %target-swift-ide-test -code-completion -swift-version 4 -source-filename %s -code-completion-token=SELF_DOT_2 -I %t | %FileCheck %s -check-prefix=SWIFT4_SELF_DOT_2
6+
7+
// RUN: %target-swift-ide-test -code-completion -swift-version 5 -source-filename %s -code-completion-token=SELF_DOT_1 -I %t | %FileCheck %s -check-prefix=SWIFT5_SELF_DOT_1
8+
// RUN: %target-swift-ide-test -code-completion -swift-version 5 -source-filename %s -code-completion-token=SELF_DOT_2 -I %t | %FileCheck %s -check-prefix=SWIFT5_SELF_DOT_2
9+
10+
import library
11+
12+
// Ensure we maintain compatibility with Swift 4's overload signature rules.
13+
// Variables defined in extensions of generic types had different overload
14+
// signatures to other variables, so allow overloading in such cases (SR-7341).
15+
extension HasFooGeneric {
16+
var foo: String { return "" } // foo isn't defined in a generic extension in the other module, so allow overloading in Swift 4 mode.
17+
var bar: String { return "" } // bar is defined in a generic extension in the other module, so `bar: String` always shadows it.
18+
func baz() {
19+
self.#^SELF_DOT_1^#
20+
}
21+
}
22+
23+
// SWIFT4_SELF_DOT_1: Begin completions
24+
// SWIFT4_SELF_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
25+
// SWIFT4_SELF_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: foo[#String#]; name=foo
26+
// SWIFT4_SELF_DOT_1-NOT: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar
27+
// SWIFT4_SELF_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: bar[#String#]; name=bar
28+
// SWIFT4_SELF_DOT_1: End completions
29+
30+
// But in Swift 5 mode, properties from this module currently always shadow
31+
// properties from the other module – therefore meaning that the properties from
32+
// the other module never show up in the overload set.
33+
// FIX-ME: It seems reasonable for both to show up in the overload set.
34+
// SWIFT5_SELF_DOT_1: Begin completions
35+
// SWIFT5_SELF_DOT_1-NOT: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
36+
// SWIFT5_SELF_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: foo[#String#]; name=foo
37+
// SWIFT5_SELF_DOT_1-NOT: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar
38+
// SWIFT5_SELF_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: bar[#String#]; name=bar
39+
// SWIFT5_SELF_DOT_1: End completions
40+
41+
// For non-generic types, the variable overload signature was always the
42+
// null type, so `foo/bar: String` shadows `foo/bar: Int`.
43+
extension HasFooNonGeneric {
44+
var foo: String { return "" }
45+
var bar: String { return "" }
46+
func baz() {
47+
self.#^SELF_DOT_2^#
48+
}
49+
}
50+
51+
// SWIFT4_SELF_DOT_2: Begin completions
52+
// SWIFT4_SELF_DOT_2-NOT: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
53+
// SWIFT4_SELF_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: foo[#String#]; name=foo
54+
// SWIFT4_SELF_DOT_2-NOT: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar
55+
// SWIFT4_SELF_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: bar[#String#]; name=bar
56+
// SWIFT4_SELF_DOT_2: End completions
57+
58+
// Again, in Swift 5 mode, we currently consistently shadow the properties from
59+
// the other module.
60+
// FIX-ME: It seems reasonable to not shadow them.
61+
// SWIFT5_SELF_DOT_2: Begin completions
62+
// SWIFT5_SELF_DOT_2-NOT: Decl[InstanceVar]/CurrNominal: foo[#Int#]; name=foo
63+
// SWIFT5_SELF_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: foo[#String#]; name=foo
64+
// SWIFT5_SELF_DOT_2-NOT: Decl[InstanceVar]/CurrNominal: bar[#Int#]; name=bar
65+
// SWIFT5_SELF_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: bar[#String#]; name=bar
66+
// SWIFT5_SELF_DOT_2: End completions

test/NameBinding/Inputs/overload_vars.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,19 @@ public protocol HasFoo {
1414
public protocol HasBar {
1515
var bar: Int { get }
1616
}
17+
18+
public class HasFooGeneric<T> {
19+
public var foo: Int = 0
20+
}
21+
22+
extension HasFooGeneric {
23+
public var bar: Int { return 0 }
24+
}
25+
26+
public class HasFooNonGeneric {
27+
public var foo: Int = 0
28+
}
29+
30+
extension HasFooNonGeneric {
31+
public var bar: Int { return 0 }
32+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/overload_vars.swift
3+
// RUN: %target-typecheck-verify-swift -swift-version 4 -I %t
4+
5+
import overload_vars
6+
7+
func useString(_ str: String) {}
8+
9+
// Ensure we maintain compatibility with Swift 4's overload signature rules.
10+
// Variables defined in extensions of generic types had different overload
11+
// signatures to other variables, so allow overloading in such cases (SR-7341).
12+
extension HasFooGeneric {
13+
var foo: String { return "" } // `foo` isn't defined in a generic extension in the other module, so allow overloading in Swift 4 mode.
14+
var bar: String { return "" } // `bar` is defined in a generic extension in the other module, so `bar: String` always shadows it.
15+
16+
func baz() {
17+
let x1: Int = foo // Make sure `foo: Int` is in the overload set.
18+
_ = x1
19+
20+
let x2: String = foo // Make sure `foo: String` is in the overload set.
21+
_ = x2
22+
23+
let y1 = bar // No ambiguity error.
24+
useString(y1) // Make sure we resolved to `bar: String`.
25+
26+
// Make sure `bar: Int` is not in the overload set.
27+
let y2: Int = bar // expected-error {{cannot convert}}
28+
_ = y2
29+
}
30+
}
31+
32+
// But for non-generic types, the variable overload signature was always the
33+
// null type, so `foo/bar: String` shadows `foo/bar: Int`.
34+
extension HasFooNonGeneric {
35+
var foo: String { return "" }
36+
var bar: String { return "" }
37+
38+
func baz() {
39+
let x1 = foo // No ambiguity error.
40+
useString(x1) // Make sure we resolved to `foo: String`.
41+
42+
// Make sure `foo: Int` is not in the overload set.
43+
let x2: Int = foo // expected-error {{cannot convert}}
44+
_ = x2
45+
46+
let y1 = bar // No ambiguity error.
47+
useString(y1) // Make sure we resolved to `bar: String`.
48+
49+
// Make sure `bar: Int` is not in the overload set.
50+
let y2: Int = bar // expected-error {{cannot convert}}
51+
_ = y2
52+
}
53+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/overload_vars.swift
3+
// RUN: %target-typecheck-verify-swift -swift-version 5 -I %t
4+
5+
import overload_vars
6+
7+
func useString(_ str: String) {}
8+
9+
// In Swift 5, properties from this module currently always shadow properties
10+
// from the other module – therefore meaning that the properties from the other
11+
// module never show up in the overload set.
12+
// FIX-ME: It seems reasonable for both to show up in the overload set.
13+
14+
extension HasFooGeneric {
15+
var foo: String { return "" }
16+
var bar: String { return "" }
17+
18+
func baz() {
19+
let x1 = foo // No ambiguity error.
20+
useString(x1) // Make sure we resolved to `foo: String`.
21+
22+
// Make sure `foo: Int` is not in the overload set.
23+
let x2: Int = foo // expected-error {{cannot convert}}
24+
_ = x2
25+
26+
let y1 = bar // No ambiguity error.
27+
useString(y1) // Make sure we resolved to `bar: String`.
28+
29+
// Make sure `bar: Int` is not in the overload set.
30+
let y2: Int = bar // expected-error {{cannot convert}}
31+
_ = y2
32+
}
33+
}
34+
35+
extension HasFooNonGeneric {
36+
var foo: String { return "" }
37+
var bar: String { return "" }
38+
39+
func baz() {
40+
let x1 = foo // No ambiguity error.
41+
useString(x1) // Make sure we resolved to `foo: String`.
42+
43+
// Make sure `foo: Int` is not in the overload set.
44+
let x2: Int = foo // expected-error {{cannot convert}}
45+
_ = x2
46+
47+
let y1 = bar // No ambiguity error.
48+
useString(y1) // Make sure we resolved to `bar: String`.
49+
50+
// Make sure `bar: Int` is not in the overload set.
51+
let y2: Int = bar // expected-error {{cannot convert}}
52+
_ = y2
53+
}
54+
}

0 commit comments

Comments
 (0)