元ネタ
https://twitter.com/orga_chem/status/1026420884196605952
基本の形
func a(_ f: @escaping (Int) -> Void) {}
func b(_ f: (Int) -> Void) {
a(f)
}
aaa.swift:26:7: error: passing non-escaping parameter 'f' to function expecting an @escaping closure
正しく @escaping
についての問題であると表示している。
この出力が構築されるのは、 FailureDiagnosis::diagnoseContextualConversionError -> tryDiagnoseNonEscapingParameterToEscaping で diag::passing_noescape_to_escaping になるから。
今回の形だが問題が無い場合
func a() {
let f: ((Int) -> Void) -> Void
f = { (g: @escaping (Int) -> Void) -> Void in
}
}
error: cannot assign value of type '(@escaping (Int) -> Void) -> Void' to type '((Int) -> Void) -> Void'
一般の変換失敗エラーとして表示しているが、@escaping
を表示しているので、問題点はわかる。
基本の形で発動した、 FailureDiagnosis::diagnoseContextualConversionError -> tryDiagnoseNonEscapingParameterToEscaping にはヒットしない
// Try to simplify irrelevant details of function types. のフローに入るが、特にどのパターンにも当たらず、すり抜ける。 ここでチェックしているのは関数それ自体の属性であって、 関数が引数に受ける関数の属性(ここで問題になっているescaping)には関係ない。 よって、デフォルトの diag::cannot_convert_argument_value のまま進行する。
最終的に関数 DiagnosticEngine::formatDiagnosticText の中の formatDiagnosticArgument の中で type->getString() された結果が、文言として埋め込まれる。 それが "(@escaping (Int) -> Void) -> Void" になっている。
今回の問題が出る場合
func c(_ g: @escaping (Int) -> Void) {}
func a() {
let f: ((Int) -> Void) -> Void
f = { (g) -> Void in
c(g)
}
}
error: cannot assign value of type '((Int) -> Void) -> Void' to type '((Int) -> Void) -> Void'
このように、 g
の型を明記せずに、
推論によって構築させると、 escaping が表示されない。
コンパイラのフローは同じだが、 最終的に type->getString() が "((Int) -> Void) -> Void" を返している!
さかのぼって調べると、
FailureDiagnosis::diagnoseContextualConversionError において、
auto srcFT = exprType->getAs<FunctionType>();
srcFT->getParams()[0].isEscaping()
の時点で false になっている。
よって、Semaで推論してFunctionTypeを構築するときに、 ParamのParameterTypeFlagsを正しくコピーできていない気がする!