首页 > 解决方案 > 是否存在“for _ in [1,2,3]”根本不会循环的*任何*情况?

问题描述

我正在编写一些代码并犯了一个错误,简化为:

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

编译器向我显示一个错误,说f缺少返回,这使我意识到我的错误。我忘了在return!

但后来我意识到编译器实际上在撒谎!该函数将始终返回一个值。还是会?有没有什么情况下for循环不会循环?

我问这个是因为其他重言式结构编译得很好:

if 1 < 2 {
    return 1
}

while true {
    return 1
}

而且我也明白编译器无法在编译时评估每个表达式以查看它们是否是重言式。我知道属性访问和方法调用通常不会在编译时进行评估,因此预计不会编译:

if "".isEmpty {
    return 1
}

但通常文字是可以的,对吧?毕竟,编译器必须评估文字[1,2,3]以将其翻译成机器代码,即“创建一个包含 1、2、3 的数组”。

那么为什么它不够聪明,无法弄清楚 for 循环呢?for 循环不会在某些罕见的情况下运行吗?

标签: swiftconstant-expression

解决方案


虽然对于人类来说,看到循环总是重复三次是微不足道的,因为列表文字是具有三个元素的常量,这对于在语义分析级别的编译器来说是一件不平凡的事情。

在语义分析期间,编译器将评估“通用列表文字”([1,2,3])并确定它是类型的表达式Array<Int>。但是现在,当然,这是一个常数或这个数组包含三个元素的信息丢失了。

语义分析通常使用程序员使用的相同(或非常相似)类型系统来执行。由于与成本相比,将数组中的元素数量附加到类型(元素数量通常在编译时未知)几乎没有好处,因此通常不会这样做。另一方面,常量折叠 ( if 1 < 2 {) 更容易实现并且更频繁地发生。

虽然在较低级别上,编译器可能会展开此循环并使用常量值,但这发生在很久以后——在 Swift 中,在生成 Swift 中间语言表示之后——并且可能仅在代码生成期间——在 SIL 已被发送到 LLVM IR 和运行 LLVM 优化时。


推荐阅读