swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.3k stars 10.34k forks source link

[SR-4241] Generic function with NSObject type allows unimplemented initializer to be called. #46824

Open swift-ci opened 7 years ago

swift-ci commented 7 years ago
Previous ID SR-4241
Radar None
Original Reporter calebd (JIRA User)
Type Bug
Environment Xcode 8.2.1, Swift 3.0.2
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, AcceptsInvalid | |Assignee | None | |Priority | Medium | md5: 41db62f705952a6f4afb42f3ddb6d9af

Issue Description:

This code snippet compiles successfully but causes a crash at runtime. It appears to only happen in the presence of NSObject as a pure Swift example emits the proper compile-time error.

import Foundation

class A: NSObject {
    init(string: String) {}
}

func make<T: NSObject>() -> T {
    return T()
}

let a: A = make()
AliSoftware commented 7 years ago

I've observed a similar bug here: https://github.com/AliSoftware/Reusable/pull/40

In short, in an extension with a constraint Self: UIView the compiler allows to call Self() (aka Self.init()) — which could make sense because Self inherits UIView which inherits NSObject which declares init() — but that crashes at runtime.

protocol NibOwnerLoadable { … }

public extension NibOwnerLoadable where Self: UIView {
  @discardableResult
  static func loadFromNib(owner: Self = Self()) -> Self {
    …
    return owner
  }
}

class MyCustomWidget: UIView, NibOwnerLoadable {
  init?(coder aDecoder: NSCoder) { … }
}

MyCustomWidget.loadFromNib() // This compiles but crashes at runtime because it tries to call MyCustomWidget.init() which isn't defined
swift-ci commented 7 years ago

Comment by Caleb Davenport (JIRA)

Going to start looking here (https://github.com/apple/swift/blob/master/lib/Sema/CSApply.cpp#L6273) when I get some free time.

belkadan commented 7 years ago

Hm, right. Initializers are inherited in Objective-C, but not always in Swift. That means that <T: NSObject> shouldn't be assumed to have init(), even though NSObject does.

That said, fixing this would be a source-breaking change, so we'll have to limit it to Swift 4 mode.