Skip to content

Commit 32119aa

Browse files
committed
Sema: Fudge backward compatibility for init special declname
In Swift 4, constructors had the same name as properties, methods and enum cases named `init`. This meant that you could use constructor syntax to call such a member, which caused some confusing behavior. Recently I added a special declname for `init` so that constructors have unique names distinct from any name you can spell, and "foo.init" syntax would look for a member with the special name rather than one named `init`. Unfortunately people actually had code where they defined members named `init` and then use them via normal member lookup syntax, like this: enum E { case `init` } let e: E = E.init So to maintain backward compatibility, hack member lookup to also find members named `init` when looking up the special declname for constructors. The workaround is only enabled in Swift 4 and 4.2 mode; in Swift 5 mode you are expected to write "foo.`init`" to access non-constructor members named `init`. Fixes <rdar://problem/38682258>.
1 parent 9795190 commit 32119aa

File tree

5 files changed

+114
-3
lines changed

5 files changed

+114
-3
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3121,9 +3121,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
31213121
// Look for members within the base.
31223122
LookupResult &lookup = lookupMember(instanceTy, memberName);
31233123

3124+
// If this is true, we're using type construction syntax (Foo()) rather
3125+
// than an explicit call to `init` (Foo.init()).
3126+
bool isImplicitInit = false;
31243127
TypeBase *favoredType = nullptr;
31253128
if (memberName.isSimpleName(DeclBaseName::createConstructor())) {
3126-
if (auto anchor = memberLocator->getAnchor()) {
3129+
SmallVector<LocatorPathElt, 2> parts;
3130+
if (auto *anchor = memberLocator->getAnchor()) {
3131+
auto path = memberLocator->getPath();
3132+
if (!path.empty())
3133+
if (path.back().getKind() == ConstraintLocator::ConstructorMember)
3134+
isImplicitInit = true;
3135+
31273136
if (auto applyExpr = dyn_cast<ApplyExpr>(anchor)) {
31283137
auto argExpr = applyExpr->getArg();
31293138
favoredType = getFavoredType(argExpr);
@@ -3338,6 +3347,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
33383347
/*isBridged=*/false,
33393348
/*isUnwrappedOptional=*/false));
33403349

3350+
// Backward compatibility hack. In Swift 4, `init` and init were
3351+
// the same name, so you could write "foo.init" to look up a
3352+
// method or property named `init`.
3353+
if (!TC.Context.isSwiftVersionAtLeast(5) &&
3354+
memberName.getBaseName() == DeclBaseName::createConstructor() &&
3355+
!isImplicitInit) {
3356+
auto &compatLookup = lookupMember(instanceTy,
3357+
TC.Context.getIdentifier("init"));
3358+
for (auto result : compatLookup)
3359+
addChoice(getOverloadChoice(result.getValueDecl(),
3360+
/*isBridged=*/false,
3361+
/*isUnwrappedOptional=*/false));
3362+
}
3363+
33413364
// If the instance type is a bridged to an Objective-C type, perform
33423365
// a lookup into that Objective-C type.
33433366
if (bridgedType) {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
3+
// https://bugs.swift.org/browse/SR-1660
4+
5+
enum DayOfTheWeek : Int {
6+
case monday = 0
7+
case `inout` = 1
8+
case `init` = 2
9+
case friday = 3
10+
case tuesday = 4
11+
}
12+
13+
let _: DayOfTheWeek = DayOfTheWeek.init
14+
15+
let _: DayOfTheWeek = DayOfTheWeek.`init`
16+
17+
func match(_ d: DayOfTheWeek) {
18+
switch d {
19+
case .monday: break
20+
case .`inout`: break
21+
case .`init`: break
22+
case .friday: break
23+
case .tuesday: break
24+
}
25+
}
26+
27+
enum Fox {
28+
case `init`(Int)
29+
30+
init() {
31+
self = .`init`(10)
32+
}
33+
}
34+
35+
let _: Fox = Fox(10)
36+
// expected-error@-1 {{argument passed to call that takes no arguments}}
37+
38+
let _: () -> Fox = Fox.init
39+
let _: (Int) -> Fox = Fox.`init`
40+
41+
func match(_ f: Fox) {
42+
switch f {
43+
case .`init`(let n): _ = n
44+
}
45+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: %target-typecheck-verify-swift -swift-version 4
2+
3+
protocol P1 {
4+
static func `init`(_: Int) // expected-note {{protocol requires function 'init' with type '(Int) -> ()'; do you want to add a stub?}}
5+
// expected-note@-1 {{did you mean 'init'?}}
6+
}
7+
8+
struct S11 : P1 {
9+
static func `init`(_: Int) {}
10+
}
11+
12+
struct S12 : P1 { // expected-error {{type 'S12' does not conform to protocol 'P1'}}
13+
init(_: Int) {}
14+
}
15+
16+
protocol P2 {
17+
init(_: Int) // expected-note {{protocol requires initializer 'init' with type 'Int'; do you want to add a stub?}}
18+
}
19+
20+
struct S21 : P2 { // expected-error {{type 'S21' does not conform to protocol 'P2'}}
21+
// expected-note@-1 {{candidate has non-matching type '()'}}
22+
static func `init`(_: Int) {}
23+
}
24+
25+
struct S22 : P2 {
26+
init(_: Int) {}
27+
}
28+
29+
_ = S11(0) // expected-error {{argument passed to call that takes no arguments}}
30+
_ = S11.init(0)
31+
_ = S11.`init`(0)
32+
33+
_ = S12(0)
34+
_ = S12.init(0)
35+
_ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}}
36+
37+
_ = S21(0) // expected-error {{argument passed to call that takes no arguments}}
38+
_ = S21.init(0)
39+
_ = S21.`init`(0)
40+
41+
_ = S22(0)
42+
_ = S22.init(0)
43+
_ = S22.`init`(0) // expected-error {{type 'S22' has no member 'init'}}

test/decl/enum/special_case_name.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
22

33
// https://bugs.swift.org/browse/SR-1660
44

test/decl/func/special_func_name.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -swift-version 5
22

33
protocol P1 {
44
static func `init`(_: Int) // expected-note {{protocol requires function 'init' with type '(Int) -> ()'; do you want to add a stub?}}

0 commit comments

Comments
 (0)