-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
[SR-8814] GenericSignatureBuilder should re-introduce conformance requirements when adding a superclass requirement #51322
Comments
Can you share the project? If not publicly here, possibly just with Apple via https://bugreport.apple.com? @eeckstein, does this look familiar? |
No. |
Comment by Lukas Foldyna (JIRA) import UIKit
protocol TableRowID {
init?(rawValue: Int)
var rawValue: Int { get }
var order: Int { get }
}
extension TableRowID {
var order: Int {
return rawValue
}
}
protocol TableRow {
static var identifiers: [String] { get }
var identifier: String { get }
var height: CGFloat { get }
var estimatedHeight: CGFloat { get }
}
extension TableRow {
var identifier: String {
return Self.identifiers.first ?? ""
}
var estimatedHeight: CGFloat {
return height
}
}
protocol InputViewCell {
var id: TableRowID { get }
var inputResponder: UIResponder? { get }
func setup(row: TableRow, id: TableRowID, delegate: Any)
}
protocol TableSectioning: MutableCollection {
associatedtype ID: TableRowID & Hashable
var rows: [ID: TableRow] { get }
func row(at index: Int) -> TableRow?
}
struct TableSection<ID>: TableSectioning where ID: TableRowID & Hashable {
typealias DictionaryType = [ID: TableRow]
internal(set) var rows: DictionaryType
internal var orderIndex: [Int: ID] = [:]
init(rows: DictionaryType = [:]) {
self.rows = rows
rows.keys.forEach { orderIndex[$0.order] = $0 }
}
func row(at index: Int) -> TableRow? {
guard let id = orderIndex[index] else { return nil }
return rows[id]
}
// MARK: - Collection
typealias Index = DictionaryType.Index
typealias Element = DictionaryType.Element
var startIndex: Index { return rows.startIndex }
var endIndex: Index { return rows.endIndex }
subscript(index: Index) -> DictionaryType.Iterator.Element {
get {
return rows[index]
}
set {
rows[newValue.key] = newValue.value
orderIndex[newValue.key.order] = newValue.key
}
}
public subscript(key: DictionaryType.Key) -> DictionaryType.Value? {
get {
return rows[key]
}
set {
rows[key] = newValue
orderIndex[key.order] = key
}
}
func index(after i: DictionaryType.Index) -> DictionaryType.Index {
return rows.index(after: i)
}
// MARK: - Sequence
typealias Iterator = AnyIterator<(key: ID, value: TableRow)>
func makeIterator() -> Iterator {
var iterator = rows.makeIterator()
return AnyIterator {
return iterator.next()
}
}
}
protocol TableViewModel {
associatedtype Sectioning: TableSectioning
var sections: [Sectioning] { get }
}
extension TableViewModel {
func tableRow(for indexPath: IndexPath) -> TableRow? {
return sections[indexPath.section].row(at: indexPath.row)
}
}
protocol InputRow: class, TableRow {
typealias InputValue = AnyObject
var value: InputValue? { get set }
var isValid: Bool { get }
}
protocol TextInputRow: InputRow {
var title: String { get set }
var text: String? { get set }
}
extension TextInputRow {
var value: Self.InputValue? {
get {
return text as Self.InputValue
}
set {
text = newValue as? String
}
}
var isTextInLimit: Bool {
return true
}
}
protocol BaseFormViewModel: TableViewModel where ID: TableRowID & Hashable {
associatedtype ID
typealias Values = [ID: InputRow.InputValue]
func id(at indexPath: IndexPath) -> ID?
func indexPath(with id: ID) -> IndexPath?
func inputValue(with id: ID) -> InputRow.InputValue?
var indexPathForFirstInputRow: IndexPath? { get }
}
extension BaseFormViewModel {
func inputRow(with id: Self.Sectioning.ID) -> InputRow? {
for section in sections {
guard let row = section.rows[id] as? InputRow else { continue }
return row
}
return nil
}
func inputValue(with id: Self.Sectioning.ID) -> InputRow.InputValue? {
return inputRow(with: id)?.value
}
var indexPathForFirstInputRow: IndexPath? {
var sectionIndex = 0
for section in sections {
for index in 0..<section.count {
guard let _ = section.row(at: index) as? TextInputRow else { continue }
return IndexPath(row: index, section: sectionIndex)
}
sectionIndex += 1
}
return nil
}
}
class DefaultFormViewModel<I>: BaseFormViewModel where I: TableRowID & Hashable {
typealias ID = I
typealias Sectioning = TableSection<ID>
internal(set) var sections: [Sectioning] = [] {
didSet {
setupIndex()
}
}
private(set) var inputIndex: [ID: IndexPath] = [:]
private(set) var pathIndex: [IndexPath: ID] = [:]
func setupIndex() {
inputIndex.removeAll()
pathIndex.removeAll()
var sectionIndex = 0
for section in sections {
for index in 0..<section.count {
guard let key = section.orderIndex[index] else { continue }
let indexPath = IndexPath(row: index, section: sectionIndex)
inputIndex[key] = indexPath
pathIndex[indexPath] = key
}
sectionIndex += 1
}
}
func inputValue(with id: ID) -> InputRow.InputValue? {
return inputRow(with: id)?.value
}
func id(at indexPath: IndexPath) -> ID? {
return pathIndex[indexPath]
}
func indexPath(with id: ID) -> IndexPath? {
return inputIndex[id]
}
}
class BaseFormVC<VM: BaseFormViewModel>: UIViewController, UITextViewDelegate, UITextFieldDelegate {
private var formSentIndicator: Bool = false
weak var tableView: UITableView!
// MARK: - Model
internal var viewModel: VM?
// MARK: - First responder
internal func makeFirstInputFirstResponder() {
guard let indexPath = viewModel?.indexPathForFirstInputRow,
let cell = tableView.cellForRow(at: indexPath) else { return }
if let cell = cell as? InputViewCell {
cell.inputResponder?.becomeFirstResponder()
}
}
internal func makeInputFirstResponder(with id: VM.ID) {
guard !formSentIndicator,
let indexPath = viewModel?.indexPath(with: id) else { return }
if let cell = tableView.cellForRow(at: indexPath) as? InputViewCell {
cell.inputResponder?.becomeFirstResponder()
} else {
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
if let cell = self.tableView.cellForRow(at: indexPath) as? InputViewCell {
cell.inputResponder?.becomeFirstResponder()
}
}
}
}
}
struct Payment {
var amount: Double
var message: String
}
protocol NewPaymentVM: BaseFormViewModel {
associatedtype Value = Payment
var payment: Value { get }
}
protocol PaymentController where Self: UIViewController {
var payment: Payment? { get set }
}
class NewPaymentVC<RPVM>: BaseFormVC<RPVM>, PaymentController where RPVM: NewPaymentVM {
var payment: Payment? = nil {
didSet {
setupViewModel()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
makeFirstInputFirstResponder()
}
internal func setupViewModel() {
}
}
enum NewDirectDebitInputType: Int, TableRowID {
case amount
case message
}
class NewDirectDebitPaymentVM: DefaultFormViewModel<NewDirectDebitInputType>, NewPaymentVM {
var payment: Payment
init(payment: Payment) {
self.payment = payment
super.init()
}
}
class NewDirectDebitPaymentVC: NewPaymentVC<NewDirectDebitPaymentVM> {
override func setupViewModel() {
guard let payment = payment else { return }
let viewModel = NewDirectDebitPaymentVM(payment: payment)
self.viewModel = viewModel
}
internal override func makeFirstInputFirstResponder() {
if let limit = viewModel?.inputValue(with: .amount) as? Double, limit > 0 {
makeInputFirstResponder(with: .message)
} else {
super.makeFirstInputFirstResponder()
}
}
} |
Thanks! I can reproduce the crash. |
@swift-ci create |
Reduced test case:
If you run it with -emit-silgen you'll see that the 'T : Hashable' requirement is incorrectly dropped from the protocol witness thunk's generic signature. |
So I made a bit of progress, but it’s still a mystery. My latest test case requires this setup:
Here is the bad case I’ve been looking at:
Here, T and U.T are in the same equivalence class. The conformance U : P has two sources:
The second one responds with true to isDerivedRequirement(), so it becomes the best one, so it gets dropped. Now this version of foo() works — but for the wrong reason:
When we add same-type requirements between a type parameter and a concrete type, we make sure to concretize any nested types in concretizeNestedTypeFromConcreteParent(). However, we don’t do anything of the sort when adding a superclass constraint. So in the above case, T and U.T are different equivalence classes. T : Q does not have a derived source, so we retain it in the minimized signature. However U.T is redundant for the same reason as above. I suspect not concretizing nested types that already exist when a superclass constraint is added is causing us other problems elsewhere, too. |
Comment by Jonathan (JIRA) @belkadan I don't know enough about the compiler to know if this is actually a duplicate of https://bugs.swift.org/browse/SR-10182, but SR-10182 didn't cause a crash in Swift 4/Xcode 10, only in Xcode 10.2, unlike this ticket, so not sure it should be marked as a duplicate? Also it is still causing a segmentation fault in the Xcode 11 beta. |
My repro from SR-11913 (which Slava duped here): public protocol Something {
associatedtype McGuffin: BinaryInteger
func whatever() throws -> [String]
}
extension Something {
func whatever() throws -> [String] { [] }
}
class Thing<McGuffin: BinaryInteger>: Something {} Still happens in 11.3 |
Environment
Xcode (10A255), macOS 10.13.6 (17G2307)
Additional Detail from JIRA
md5: 3ceba1c7e1776ae34423d56b0b58a883
is duplicated by:
relates to:
Issue Description:
Our app is failing to archive in new Xcode 10 and Swift 4. (-0)
The text was updated successfully, but these errors were encountered: