Существует ли * какая-либо * ситуация, при которой «for _ in [1,2,3]» вообще не будет l oop? - PullRequest
2 голосов
/ 09 мая 2020

Я писал код и сделал ошибку, которая упрощается до:

func f() -> Int {
    for _ in [1,2,3] {
        return 1
    }
}

И компилятор показывает мне ошибку, говоря, что f не хватает возврата, что заставило меня осознать свою ошибку. Я забыл поместить оператор if вокруг return!

Но потом я понял, что компилятор на самом деле лжет! Функция всегда возвращает значение. Или будет? Есть ли ситуация, при которой for l oop не будет l oop?

Я спрашиваю об этом, потому что другие тавтологические конструкции компилируются нормально:

if 1 < 2 {
    return 1
}

while true {
    return 1
}

И Я также понимаю, что компилятор не может оценивать каждое выражение во время компиляции, чтобы увидеть, являются ли они тавтологиями. Я знаю, что доступ к свойствам и вызовы методов обычно не оцениваются во время компиляции, поэтому не ожидается, что он будет компилироваться:

if "".isEmpty {
    return 1
}

Но обычно литералы в порядке, верно? В конце концов, компилятор должен вычислить литерал [1,2,3], чтобы преобразовать его в машинный код, который говорит: «Создайте массив с 1, 2, 3».

Так почему же он недостаточно умен, чтобы вычислить для l oop? Будет ли for l oop работать в редких случаях?

Ответы [ 2 ]

2 голосов
/ 09 мая 2020

В то время как для человека тривиально увидеть, что l oop всегда будет повторяться три раза, поскольку литерал списка является константой с тремя элементами, это нетривиальная вещь для компилятора на уровне семанти c анализа.

Во время анализа семанти c компилятор оценит «обобщенный c литерал списка» ([1,2,3]) и определит, что это выражение типа Array<Int>. Но теперь, конечно, теряется информация о том, что это константа или что этот массив содержит три элемента.

Анализ Семанти c обычно выполняется с использованием того же (или очень похожего) типа системы, который использует программист. Поскольку от присоединения количества элементов в массиве к типу (количество элементов обычно не известно во время компиляции) было бы мало пользы по сравнению с затратами, обычно это не делается. С другой стороны, сворачивание констант (if 1 < 2 {) проще реализовать и происходит чаще.

На более низком уровне компилятор, скорее всего, развернет этот l oop и будет использовать постоянные значения, это происходит намного позже - в Swift после генерации представления промежуточного языка Swift - и, вероятно, только во время генерации кода - после того, как SIL был отправлен в LLVM IR и когда была запущена оптимизация LLVM.

1 голос
/ 09 мая 2020

for-in не знает, все ли, что он получит от итератора, будет nil. Вы получите одно и то же сообщение об ошибке, Missing return in a function expected to return 'Int', независимо от последовательности.

extension Bool: Sequence, IteratorProtocol {
  public func next() -> Void? { () }
}
for _ in true {  
...