Сравнение с универсальным типом в xcode 10.2 - PullRequest
3 голосов
/ 03 апреля 2019

До обновления xcode 10.2 (которое включает поддержку swift 5) в следующем коде и "a", и "b" были бы "true", как и предполагалось. Теперь в xcode 10.2 с swift 4.2 в ранее созданном проекте «a» начал оценивать как «false».

Если я изменю проект на swift 5, использую более старую версию xcode или игровую площадку в xcode 10.2, «a» оценивается как «true», как и предполагалось.

Насколько я могу судить, и "a", и "b" никогда не должны быть разными, поскольку T и Any? - это одно и то же в этом случае.

Почему эта логика изменится и что я могу сделать, чтобы достичь того же результата?

class Class<T> {

    var val: String?

    func test(val: Any?) {
        let a = val is T
        let b = val is Any?
    }
}

let thing = Class<Any?>()
thing.test(val: nil)

1 Ответ

3 голосов
/ 03 апреля 2019

Извините, этот мой плохой. Подведем итог истории:

  • В Swift 4.1 при приведении из необязательного значения к универсальному заполнителю T компилятор предполагал, что T был необязательным типом, и поэтому перед выполнением приведения развернул необязательное значение. Это означает, что val is T в вашем примере будет оцениваться как false, так как оно будет оценено как val.map { $0 is T } ?? false (не фактическое преобразование AST).

  • В прошлом году я открыл запрос на извлечение ( # 13910 ), чтобы исправить это поведение, так что компилятор теперь более консервативен с развертыванием, позволяя val is T оценить до true.

  • Это исправление внесено в 4.2, однако из-за того, что -swift-version 5 не защищено, вызвало регрессию совместимости ( SR-8704 ).

  • Чтобы разрешить регрессию совместимости, для Swift 5 исправление было ограничено только режимом Swift 5 ( # 19217 ), чтобы сохранить исходное поведение в режимах совместимости Swift 4 и 4.2. , Я надеялся, что мы могли бы также включить это в 4.2.1 ( # 19562 ), чтобы минимизировать триггеры в поведении между 4.2 и 5.0, но, к сожалению, этого не произошло.

Короче говоря, в -swift-version 4.2 вы получаете оригинальное поведение при касте. Чтобы получить новое поведение, вы можете либо использовать -swift-version 5, либо стереть опцию источника из компилятора, например:

class Class<T> {

  var val: String?

  func test(val: Any?) {
    let a = (val as Any) is T
    let b = val is Any?
    print(a, b)
  }
}

или со специальной функцией:

func valueIs<T, U>(_ x: T, ofType _: U.Type) -> Bool {
  return x is U
}

class Class<T> {

  var val: String?

  func test(val: Any?) {
    let a = valueIs(val, ofType: T.self)
    let b = val is Any?
    print(a, b)
  }
}
...