I don't know why the compiler thinks "<" can throw

I’m using Xcode 9.3 (9E145) on macOS High Sierra 10.13.4.

Trying out some sorting routines in a linked-list protocol:

/// Transfers out a suffix such that the remaining elements are strictly stored according to the given predicate.
@discardableResult
public mutating func removeAfterMissort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Self? {
    var lastIncluded = startIndex
    for i in indices.dropFirst() {
        if try !areInIncreasingOrder(self[lastIncluded], self[i]) {
            return removeLast(after: lastIncluded)
        }
        lastIncluded = i
    }
    return nil
}

And I can specialize for Comparable:

/// Transfers the elements after where the collection first stops increasing to a new instance.
@discardableResult
public mutating func removeNonincreasingSuffix() -> Self? {
    return removeAfterMissort(by: <)
}

But it doesn’t work silently for another method:

/// Creates an instance by merging the elements of two other instances, with a non-decreasing prefix.
public init(stealFromAndMergeWithNondecreasingPrefix first: inout Self, and second: inout Self) {
   self.init(stealFromAndMerge: &first, and: &second, admittingFirstOverSecondBy: <)
}

This gives an error that I need to handle throwing. I got around it by adding “try” to the statement and marking the method with “throws,” but I thought that “<” is always non-throwing, and such any method channelling it through rethrows is also non-throwing.

I don’t think my primary method throws anywhere else besides the comparison:

/// Creates an instance by merging the elements of two other instances, with admission order controlled by a closure.
public init(stealFromAndMerge first: inout Self, and second: inout Self, admittingFirstOverSecondBy body: (Element, Element) throws -> Bool) rethrows {
    // Determine the order of insertion.
    var firstIndex = first.startIndex, secondIndex = second.startIndex
    var chooseFirst = [Bool]()
    chooseFirst.reserveCapacity(first.underestimatedCount + second.underestimatedCount)
    while firstIndex != first.endIndex, secondIndex != second.endIndex {
        let goWithFirst = try body(first[firstIndex], second[secondIndex])
        chooseFirst.append(goWithFirst)
        if goWithFirst {
            first.formIndex(after: &firstIndex)
        } else {
            second.formIndex(after: &secondIndex)
        }
    }

    // Do the transfers.
    self.init()
    chooseFirst.forEach {
        var endSelf = endIndex >** endIndex
        var otherRange: LeftOpenRange<Index>
        if $0 {
            otherRange = first.beforeStartIndex >** first.startIndex
            trade(&endSelf, with: &first, along: &otherRange)
        } else {
            otherRange = second.beforeStartIndex >** second.startIndex
            trade(&endSelf, with: &second, along: &otherRange)
        }
    }
    append(stealingFrom: &first)
    append(stealingFrom: &second)
}

Is there a compiler problem? Or is there a throwing spot I’m missing?

I got the code up on a Gist. Note that none of it is tested. Especially the protocol, since I haven’t made a corresponding type.

Hi,

I think this is probably a compiler bug. I reproduced it with a smaller example:

struct S {
 	init(_: () throws -> Void) rethrows {}

    init() {
       	// error: call can throw, but it is not marked with 'try'
    	self.init({ print("testing") })
    }
}

let s = S({ print("testing") }) 	// this is fine

As you can see, the regular initializer call works. It is only a problem when you use self.init(). Also, it has probably nothing to do with the < operator.

5 Likes

Mind creating a new issue at bugs.swift.org ?

2 Likes

Ok, I created a bug report here: https://bugs.swift.org/browse/SR-7392

4 Likes