swiftlang / swift

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

[SR-15033] Swift Compiler Error: Abort trap 6 (maybe due to SE-0293) #57360

Open mattyoung opened 2 years ago

mattyoung commented 2 years ago
Previous ID SR-15033
Radar None
Original Reporter @mattyoung
Type Bug
Environment Xcode Version 13.0 beta 4 (13A5201i)
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug | |Assignee | @hborla | |Priority | Medium | md5: ec63ff96d1fef552ade6af181f1d18f8

Issue Description:

Create an Xcode SwiftUI iOS app project, create a new SwiftUI view file HierarchyList.swift, paste the following and then preview to see Abort: trap 6 or run to see many more errors in Xcode

// https://gist.github.com/zntfdr/9d94cac84235f04f021cb6c7e8c1a1c5

// Original article here: https://www.fivestars.blog/code/swiftui-hierarchy-list.html

import SwiftUI

struct FileItem: Identifiable {
    var name: String
    var children: [FileItem]?

    let id = UUID()

    static let spmData: [FileItem] = [
        FileItem(name: ".gitignore"),
        FileItem(name: "Package.swift"),
        FileItem(name: "README.md"),
        FileItem(name: "Sources", children: [
            FileItem(name: "fivestars", children: [
                FileItem(name: "main.swift")
            ]),
        ]),
        FileItem(name: "Tests", children: [
            FileItem(name: "fivestarsTests", children: [
                FileItem(name: "fivestarsTests.swift"),
                FileItem(name: "XCTestManifests.swift"),
            ]),
            FileItem(name: "LinuxMain.swift")
        ])
    ]
}

// This takes a binding to array of recursive item struct
public struct HierarchyList<Data, RowContent>: View where Data: MutableCollection, Data: RandomAccessCollection, Data.Index: Hashable, Data.Element: Identifiable, RowContent: View {
    private let recursiveView: RecursiveView<Data, RowContent>

    public init(@Binding data: Data, children: KeyPath<Binding<Data.Element>, Binding<Data?>>, rowContent: @escaping (Binding<Data.Element>) -> RowContent) {
        recursiveView = RecursiveView(data: $data, children: children, rowContent: rowContent)
    }

    public var body: some View {
        List {
            recursiveView
        }
    }
}

// !!!
// this version uses ForEach w/ SE-0293 @Binding closure parameter.
// this version is preferred but doesn't compile as of Xcode Version 13.0 beta 4 (13A5201i)
//private struct RecursiveView__<Data, RowContent>: View where Data: MutableCollection, Data: RandomAccessCollection, Data.Index: Hashable, Data.Element: Identifiable, RowContent: View {
//    @Binding var data: Data
//    let children: KeyPath<Binding<Data.Element>, Binding<Data?>>
//    let rowContent: (Binding<Data.Element>) -> RowContent
//
//    var body: some View {
//        // https://forums.swift.org/t/xcode-abort-trap-6-and-non-sensical-compile-error-maybe-related-to-se-0293/50907/2
//        // Use SE-0293 @Binding property wrapper closure parameter:
//        ForEach($data) { $item in       // !!! compile error: Cannot convert value of type 'Binding<Data>.Element' (aka 'Binding<Data.Element>') to expected argument type 'Binding<Data.Element>'
//            if let unwrappedBindingToChildren = Binding($item[keyPath: children]) {
//                DisclosureGroup(content: {
//                    RecursiveView(data: unwrappedBindingToChildren, children: children, rowContent: rowContent)
//                }, label: {
//                    rowContent($item)
//                })
//            } else {
//                rowContent($item)
//            }
//        }
//    }
//}

// the above do not compile, so use ForEach(.indices, ...) instead
private struct RecursiveView<Data, RowContent>: View where Data: MutableCollection, Data: RandomAccessCollection, Data.Index: Hashable, Data.Element: Identifiable, RowContent: View {
    @Binding var data: Data
    let children: KeyPath<Binding<Data.Element>, Binding<Data?>>
    let rowContent: (Binding<Data.Element>) -> RowContent

    var body: some View {
        ForEach(data.indices, id: \.self) { index in
            if let bindingToChildren = Binding($data[index][keyPath: children]) {
                DisclosureGroup(content: {
                    RecursiveView(data: bindingToChildren, children: children, rowContent: rowContent)
                }, label: {
                    rowContent($data[index])
                })
            } else {
                rowContent($data[index])
            }
        }
    }
}

struct HierarchyListDemo: View {
    @State private var data = FileItem.spmData

    var body: some View {
        // rowContent receive a binding to data item, enabling mutate on the item
        HierarchyList($data: $data, children: \.children, rowContent: { $item in TextField("Edit", text: $item.name) })
    }
}

struct HierarchyList_Previews: PreviewProvider {
    static var previews: some View {
        HierarchyListDemo()
    }
}

See https://forums.swift.org/t/xcode-abort-trap-6-and-non-sensical-compile-error-maybe-related-to-se-0293/50907

hborla commented 2 years ago

Reduced:

struct Data {}

@propertyWrapper
struct Wrapper<T> {
  var wrappedValue: T

  init(wrappedValue: T) { self.wrappedValue = wrappedValue }

  var projectedValue: Self { self }

  init(projectedValue: Self) { self = projectedValue }
}

struct S {
  init(@Wrapper data: Data) {
    _ = data
  }
}

The issue here is the wrapped parameter in the initializer; the workaround is to use the wrapper/projection type directly, or use a local property wrapper instead.

hborla commented 2 years ago

This will be fixed by https://github.com/apple/swift/pull/38854