Skip to content

Sema: Fudge backward compatibility for init special declname #17227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/FrontendTool/ReferenceDependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,16 @@ bool swift::emitReferenceDependencies(DiagnosticEngine &diags, SourceFile *SF,
llvm::array_pod_sort(sortedMembers.begin(), sortedMembers.end(),
[](const TableEntryTy *lhs,
const TableEntryTy *rhs) -> int {
if (lhs->first.first == rhs->first.first)
return lhs->first.second.compare(rhs->first.second);
if (auto cmp = lhs->first.first->getName().compare(rhs->first.first->getName()))
return cmp;

if (lhs->first.first->getName() != rhs->first.first->getName())
return lhs->first.first->getName().compare(rhs->first.first->getName());
if (auto cmp = lhs->first.second.compare(rhs->first.second))
return cmp;

// We can have two entries with the same member name if one of them
// was the special 'init' name and the other is the plain 'init' token.
if (lhs->second != rhs->second)
return lhs->second ? -1 : 1;

// Break type name ties by mangled name.
auto lhsMangledName = mangleTypeAsContext(lhs->first.first);
Expand Down
25 changes: 24 additions & 1 deletion lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3123,9 +3123,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
// Look for members within the base.
LookupResult &lookup = lookupMember(instanceTy, memberName);

// If this is true, we're using type construction syntax (Foo()) rather
// than an explicit call to `init` (Foo.init()).
bool isImplicitInit = false;
TypeBase *favoredType = nullptr;
if (memberName.isSimpleName(DeclBaseName::createConstructor())) {
if (auto anchor = memberLocator->getAnchor()) {
SmallVector<LocatorPathElt, 2> parts;
if (auto *anchor = memberLocator->getAnchor()) {
auto path = memberLocator->getPath();
if (!path.empty())
if (path.back().getKind() == ConstraintLocator::ConstructorMember)
isImplicitInit = true;

if (auto applyExpr = dyn_cast<ApplyExpr>(anchor)) {
auto argExpr = applyExpr->getArg();
favoredType = getFavoredType(argExpr);
Expand Down Expand Up @@ -3340,6 +3349,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
/*isBridged=*/false,
/*isUnwrappedOptional=*/false));

// Backward compatibility hack. In Swift 4, `init` and init were
// the same name, so you could write "foo.init" to look up a
// method or property named `init`.
if (!TC.Context.isSwiftVersionAtLeast(5) &&
memberName.getBaseName() == DeclBaseName::createConstructor() &&
!isImplicitInit) {
auto &compatLookup = lookupMember(instanceTy,
TC.Context.getIdentifier("init"));
for (auto result : compatLookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*isBridged=*/false,
/*isUnwrappedOptional=*/false));
}

// If the instance type is a bridged to an Objective-C type, perform
// a lookup into that Objective-C type.
if (bridgedType) {
Expand Down
45 changes: 45 additions & 0 deletions test/Compatibility/special_case_name.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %target-typecheck-verify-swift -swift-version 4

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

enum DayOfTheWeek : Int {
case monday = 0
case `inout` = 1
case `init` = 2
case friday = 3
case tuesday = 4
}

let _: DayOfTheWeek = DayOfTheWeek.init

let _: DayOfTheWeek = DayOfTheWeek.`init`

func match(_ d: DayOfTheWeek) {
switch d {
case .monday: break
case .`inout`: break
case .`init`: break
case .friday: break
case .tuesday: break
}
}

enum Fox {
case `init`(Int)

init() {
self = .`init`(10)
}
}

let _: Fox = Fox(10)
// expected-error@-1 {{argument passed to call that takes no arguments}}

let _: () -> Fox = Fox.init
let _: (Int) -> Fox = Fox.`init`

func match(_ f: Fox) {
switch f {
case .`init`(let n): _ = n
}
}
43 changes: 43 additions & 0 deletions test/Compatibility/special_func_name.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %target-typecheck-verify-swift -swift-version 4

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

struct S11 : P1 {
static func `init`(_: Int) {}
}

struct S12 : P1 { // expected-error {{type 'S12' does not conform to protocol 'P1'}}
init(_: Int) {}
}

protocol P2 {
init(_: Int) // expected-note {{protocol requires initializer 'init' with type 'Int'; do you want to add a stub?}}
}

struct S21 : P2 { // expected-error {{type 'S21' does not conform to protocol 'P2'}}
// expected-note@-1 {{candidate has non-matching type '()'}}
static func `init`(_: Int) {}
}

struct S22 : P2 {
init(_: Int) {}
}

_ = S11(0) // expected-error {{argument passed to call that takes no arguments}}
_ = S11.init(0)
_ = S11.`init`(0)

_ = S12(0)
_ = S12.init(0)
_ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}}

_ = S21(0) // expected-error {{argument passed to call that takes no arguments}}
_ = S21.init(0)
_ = S21.`init`(0)

_ = S22(0)
_ = S22.init(0)
_ = S22.`init`(0) // expected-error {{type 'S22' has no member 'init'}}
2 changes: 1 addition & 1 deletion test/decl/enum/special_case_name.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift
// RUN: %target-typecheck-verify-swift -swift-version 5

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

Expand Down
2 changes: 1 addition & 1 deletion test/decl/func/special_func_name.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift
// RUN: %target-typecheck-verify-swift -swift-version 5

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