Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Async クロージャ/“コルーチン・クロージャ”

この機能の一般的な動機を理解するには、RFC 3668 を読んでください。これは非常に技術的で、やや「縦割り」の章です。理想的には、これを分割して関連するすべての章に散りばめるべきですが、async クロージャを全体的に理解する目的のために、ここではすべてを 1 つの章にまとめています。

コルーチン・クロージャ – 技術的な詳細解説

コルーチン・クロージャは async クロージャの一般化であり、コルーチンを返すクロージャ式のための特別な構文です。特に、そのコルーチンはクロージャの upvar からキャプチャすることが許可されています。

現時点で使用可能な唯一のコルーチン・クロージャの種類は async クロージャであり、async クロージャのサポートがこの PR の範囲です。最終的には gen || {} などをサポートする可能性があり、この文書で説明する問題や興味深い点の大半は、一般にすべてのコルーチン・クロージャに当てはまります。

コードがある程度汎用的であることの結果として、この文書ではそれらを「async クロージャ」と呼んだり「コルーチン・クロージャ」と呼んだりする場合があります。async クロージャによって返される future は、一般に「コルーチン」または「子コルーチン」と呼ばれます。

HIR

async クロージャ(および将来的には gen などの他のコルーチン種別)は、HIR では hir::Closure として表現されます。 hir::Closure の closure-kind は ClosureKind::CoroutineClosure(_)1 で、これは async ブロックをラップします。この async ブロックも HIR では hir::Closure として表現されます。 async ブロックの closure-kind は ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))2 です。

async fn と同様に、async クロージャの本体を lowering する際には、クロージャのすべての引数を無条件に本体へ move し、それらがキャプチャされるようにする必要があります。これは lower_coroutine_body_with_moved_arguments3 によって処理されます。この関数に関する唯一の注目すべき癖は、最終的に生成される async ブロックが CaptureBy::ByRef4 というキャプチャ種別になることです。後でクロージャの引数はすべて by-value でキャプチャされるように強制します5 が、async ブロック全体async move であるかのように振る舞ってほしくはありません。そうすると async クロージャの自己借用の目的が損なわれてしまうためです。

rustc_middle::ty 表現

実装をできるだけ将来互換に保つ目的で(つまり gen || {}async gen || {} に対応できるように)、このセクションの大部分では async クロージャを「コルーチン・クロージャ」と呼びます。

この PR が導入する主なものは、CoroutineClosure6 という新しい TyKind と、typeck および borrowck の他の関連する enum(UpvarArgs, DefiningTy, AggregateKind)上の対応するバリアントです。

既存の TyKind::Closure を一般化するのではなく、新しい TyKind を導入するのは、型の表現に大きな違いがあるためです。CoroutineClosure の主な違いは、まずコルーチン・クロージャのジェネリクスの「アンパック済み」表現である CoroutineClosureArgsParts を調べることで確認できます。

クロージャとの類似点

クロージャと同様に、parent_argsclosure_kind_tytupled_upvars_ty があります。これらはクロージャにおける対応物と同じものを表します。すなわち、クロージャが定義されている本体から継承されるジェネリクス、クロージャの最大の「呼び出し能力」(つまり、FnOnce のように呼び出すために消費されなければならないのか、それとも by-ref で呼び出せるのか)、そしてクロージャ自身のキャプチャされた upvar です。

シグネチャ

従来のクロージャには、クロージャのシグネチャを表現するために使用する fn_sig_as_fn_ptr_ty があります。これに対して、コルーチン・クロージャのシグネチャは、やや「展開された」形で格納します。これは、コルーチン・クロージャには、それをどの AsyncFn* トレイトで呼び出すかに応じて2 つのシグネチャがあるためです(以下のセクションを参照)。

概念的には、コルーチン・クロージャは、by-ref で呼び出されるか by-move で呼び出されるかに応じて、複数の異なるシグネチャ型を含むものと考えることができます。

これら両方のシグネチャを簡単に再作成するために、signature_parts_ty はこのコルーチン・クロージャによって返されるコルーチンの関連するすべての部分を格納します。この signature parts 型は、一般に fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty) という形になります。ここで resume_tyreturn_tyyield_ty は、コルーチン・クロージャによって返されるコルーチンのそれぞれの型です7

コンパイラは主に CoroutineClosureSignature8 を扱います。これは、上記の fn() ptr 型から関連する型を抽出して作成されるもので、コルーチン・クロージャが最終的に返すコルーチンを構築するために使用できるメソッドを公開しています。

Coroutine 戻り値型を構築するために持ち運ぶ必要があるデータ

シグネチャに格納されたデータに加えて、返す TyKind::Coroutine を構築するには、コルーチンの「witness」も格納する必要があります。

では、返される Coroutine の upvar はどうなるのでしょうか。AsyncFnOnce(つまり call-by-move)の場合、これは単純に、そのコルーチンが返す upvar と同じです。しかし AsyncFnMut/AsyncFn の場合、コルーチン・クロージャから返されるコルーチンは、与えられた「環境」ライフタイム9 を持つコルーチン・クロージャからデータを借用します。これは、AsyncFnMut/AsyncFn 呼び出しシグネチャ上の &self ライフタイム10 と、ByRef の GAT ライフタイム11 に対応します。

実際にコルーチンの戻り値型を取得する

コルーチン・クロージャが返す Coroutine を最も簡単に構築するには、CoroutineClosureSignature 上の to_coroutine_given_kind_and_upvars12 ヘルパーを使用できます。これは CoroutineClosureArgs から取得できます。

その関数への引数の大半は、CoroutineArgs から取り出せるコンポーネントになります。ただし、goal_kind: ClosureKind は例外で、これは渡された ClosureKind に基づいて返すコルーチンの種類を制御します。つまり、ClosureKind::Fn | ClosureKind::FnMut の場合は by-ref コルーチンを準備し、ClosureKind::FnOnce の場合は by-move コルーチンを準備します。

トレイト階層

に実装される、Fn* トレイトの並行した階層を導入します。導入の動機はブログ記事 Async Closures で説明されています。

現在 stable なすべての callable 型(つまり、クロージャ、関数アイテム、関数ポインタ、dyn Fn* トレイトオブジェクト)は、何らかの出力型 Fut について Fn*() -> Fut を実装し、かつ FutFuture<Output = T> を実装する場合、自動的に AsyncFn*() -> T を実装します13

async クロージャは、その本体が許す範囲で AsyncFn* を実装します。つまり、upvar を互換性のある方法で使用する場合です(すなわち、upvar を消費または変更する場合、それが AsyncFn および AsyncFnMut を実装するかどうかに影響する可能性があります…)。

Lending

将来的には、AsyncFn* をより一般的な LendingFn* トレイト群の上に移す可能性があります。しかし、現在のコンパイラで LendingFn を人間工学的に使用する能力を制限している、いくつかの具体的な技術的実装上の詳細があります。これらは次に関係しています。

  • クロージャシグネチャ推論。
  • higher-ranked trait bounds に関する制限。
  • エラーメッセージの不備。

これらの制限に加えて、基盤となるトレイトは async クロージャや async Fn トレイト境界のユーザー体験に影響すべきではないという事実から、今のところ AsyncFn* に行き着きます。最終的にこれらのより一般的なトレイトへ移行できるようにするため、正確な AsyncFn* トレイト定義(関連型を含む)は実装詳細として残されています。

async クロージャはいつ通常の Fn* トレイトを実装するのか?

上では「通常の」callable 型が AsyncFn* を実装できることに触れましたが、その逆の問いとして「async クロージャも Fn* を実装できるのか?」があります。短い答えは「それが妥当なとき」、つまり AsyncFn/AsyncFnMut から返されるはずだったコルーチンが、親の coroutine-closure から「貸し出されている」upvar を実際には持っていない場合です。

詳しい答えについては、下の「follow-up: when do…」セクションを参照してください。完全な答えでは、かなり興味深く、またできれば包括的であることを意図したヒューリスティックについて説明しています。これは、ほとんどの async クロージャが「そのまま動く」ことを保証するために使われています。

2 つの本体の話…

async クロージャが AsyncFn/AsyncFnMut で呼び出されると、クロージャから借用するコルーチンを返します。しかし、AsyncFnOnce 経由で呼び出される場合は、そのクロージャを消費するため、すでに drop されたデータから借用するコルーチンを返すことはできません。

この制限を回避するため、by-ref で呼び出せる coroutine-closure に対して AsyncFnOnce::call_once を呼び出すための、別個の by-move MIR 本体を合成します。

この本体は、coroutine-closure を呼び出して返される「通常の」コルーチンと同一に動作します。ただし、親の coroutine-closure から子コルーチンへキャプチャを move しなければならないため、異なる upvar の集合を持つ点が異なります。

by-move 本体の合成

coroutine-closure から返されるコルーチンの by-move 本体にアクセスしたい場合、coroutine_by_move_body_def_id14 クエリを通じて行えます。

このクエリは、コルーチンの MIR 本体をコピーし、本体の意味論を維持するために追加の deref とフィールド projection15 を挿入することで、新しい MIR 本体を合成します。

新しい def id を合成しているため、このクエリは MIR 本体に関連する他の多数のクエリへ feed する責任もあります。このクエリは、コルーチンの built mir 上で動作するため、mir_promoted クエリ中に ensure() されます16

クロージャシグネチャ推論

async クロージャのクロージャシグネチャ推論アルゴリズムは、「従来の」クロージャの推論アルゴリズムよりも少し複雑です。クロージャと同様に、(渡された期待型に対して)関連する可能性があるすべての clause を反復処理します17

シグネチャを抽出するため、次の 2 つの状況を考慮します。

  • AsyncFnOnce::Output を伴う projection predicate。これは、クロージャの入力と出力型を抽出するために使用します。これは F: AsyncFn*() -> T 境界があった状況に対応します18
  • FnOnce::Output を伴う projection predicate。これは、入力を抽出するために使用します。出力については、関連する Future::Output projection predicate を探すことによって、出力の推定も試みます。これは F: Fn*() -> T, T: Future<Output = U> 境界があった状況に対応します。19
    • Future 境界がない場合は、出力に新しい推論変数を単に使用します。これは、Option::map のようなコンビネータ関数に async クロージャを渡せるケースに対応します。20

後者のケースをサポートしているのは、first-class な AsyncFn* トレイトが利用可能になる前に設計された API を呼び出している場合でも、ユーザーが async || {} 構文を簡単にそのまま差し込めるようにするためです。

kind が推論される前にクロージャを呼び出す

coroutine-closure の「kind」(つまり最大の呼び出しモード: AsyncFnOnce/AsyncFnMut/AsyncFn)の計算は、typeck の最後まで defer します21。しかし、typeck の終わりより前にその coroutine-closure を呼び出せるようにしたいため、それより前に coroutine-closure の戻り値型を決める必要があります。

具体的には、返されるコルーチンの def-id は変わりませんが、upvar22(親コルーチンクロージャから借用されるかムーブされるもの)と coroutine-kind23 は呼び出しモードに依存します。

AsyncFnKindHelper トレイトを導入します。これにより、「このコルーチンクロージャはこの呼び出しモードをサポートするか」24という問いはトレイトゴールを介して遅延でき、「この呼び出しモードにおけるタプル化された upvar は何か」25という問いは関連型を介して遅延できます。この関連型は、upvar 解析中に計算された upvar または “by ref” upvar のいずれかに、コルーチンクロージャの入力型を追加することで計算できます。

なるほど、ではなぜそうするのか?

これは少し回りくどく複雑に見えますし、実際その通りだと認めます。しかし、「何もしない」代替案について考えてみましょう。代わりに、すべての AsyncFn* ゴールを upvar 解析まで曖昧なものとして扱うこともできます。その時点になれば、返すコルーチンの upvar に何を入れるべきかを正確に把握できます。しかし、これは実際にはプログラム内の推論にとって非常に有害です。というのも、次のようなプログラムが妥当でなくなるからです。

let c = async || -> String { .. };
let s = c().await;
// ^^^ `<{c} as AsyncFn>::call()` をコルーチンへ射影できない場合、`.await` の内部にある `IntoFuture::into_future` 呼び出しが停止し、`s` の型は推論変数として制約されないままになります。
s.as_bytes();
// ^^^ つまり、コルーチンクロージャの await された戻り値に対して、どんなメソッドも呼び出せなくなるということです。まったく何も!

そこで代わりに、このエイリアス(この場合は射影: AsyncFnKindHelper::Upvars<'env, ...>)を使って、タプル化された upvar の計算を遅延させ、その場に置ける何かを用意します。一方で、TyKind::Coroutine(これは固定的な型です)を返すことは引き続き可能であり、必要な組み込みトレイト(今回の場合は Future)を正常に確認できます。なぜなら、Future の実装は upvar にまったく依存しないからです。

Upvar 解析

概して、コルーチンクロージャとその子コルーチンに対する upvar 解析は、通常の upvar 解析と同じように進みます。ただし、async クロージャの特殊な性質を考慮するために、いくつか興味深い点があります。

すべての入力をキャプチャさせる

async fn と同様に、すべての入力引数はキャプチャされます。これらの入力はすべて明示的に move でキャプチャするよう強制します26。これにより、async クロージャが返す future コルーチンが、その入力が本体で使用されるかどうかに依存しないようにします。そうでなければ、興味深い semver 上の危険性をもたらすことになります。

by-ref キャプチャの計算

AsyncFn/AsyncFnMut をサポートするコルーチンクロージャについては、コルーチンクロージャのキャプチャとその子コルーチンのキャプチャの関係も計算する必要があります。具体的には、コルーチンクロージャは upvar をそのキャプチャへ move する場合がありますが、コルーチンはその upvar を借用するだけの場合があります。

coroutine_captures_by_ref_ty” は、子コルーチンのすべてのキャプチャを見て、それらを親コルーチンクロージャの対応するキャプチャと比較することで計算します27。この coroutine_captures_by_ref_ty は最終的に for<'env> fn() -> captures... 型として表現され、追加の binder ライフタイムは AsyncFn::async_call または AsyncFnMut::async_call_mut を呼び出す際の “&self” ライフタイムを表します。実際にメソッドを呼び出すときに、後でその binder をインスタンス化します。

親コルーチンクロージャからのすべての by-ref キャプチャが “lending” 借用になるわけではない点に注意してください。詳細については、下の 補足: async クロージャはいつ通常の Fn* トレイトを実装するのか? セクションを参照してください。これは、コルーチンクロージャが Fn* 系のトレイトを実装できるかどうかに密接に影響します。

by-move 本体 + FnOnce の特異性

クロージャの upvar 解析が、コルーチンクロージャの子コルーチンに対して過度に緩い upvar を推論してしまい、結果として borrow-checker エラーにつながる状況がいくつかあります。これは例で示すのが最も分かりやすいです。たとえば、次の場合を考えます。

#![allow(unused)]
fn main() {
fn force_fnonce<T: async FnOnce()>(t: T) -> T { t }

let x = String::new();
let c = force_fnonce(async move || {
    println!("{x}");
});
}

x はコルーチンクロージャへムーブされますが、返されるコルーチンは &x を借用するだけになります。しかし、force_fnonce はコルーチンクロージャを AsyncFnOnce に強制し、これは lending ではありません。そのため、キャプチャを by-move で発生させるよう強制しなければなりません28

同様に、次の場合もあります。

#![allow(unused)]
fn main() {
let x = String::new();
let y = String::new();
let c = async move || {
    drop(y);
    println!("{x}");
};
}

x はコルーチンクロージャへムーブされますが、返されるコルーチンは &x を借用するだけになります。しかし、y もキャプチャして drop しているため、コルーチンクロージャは AsyncFnOnce であることを強制されます。この場合も、x のキャプチャを by-move で発生させるよう強制しなければなりません。特にこの状況を判定するには、前の例とは異なり coroutine-kind の closure-kind がまだ制約されていないため、コルーチンクロージャの本体を解析して、すべての upvar がどのように使われているかを確認し、それらが “consuming” な方法で使われているか、つまり FnOnce を強制するような使われ方をしているかを判定する必要があります29

補足: async クロージャはいつ通常の Fn* トレイトを実装するのか?

まず第一に、すべての async クロージャは FnOnce を実装します。なぜなら、常に少なくとも 1 回は呼び出せるからです。

Fn/FnMut については、詳細な答えは関連する問いに答えることを含みます。つまり、そのコルーチンクロージャは lending か、という問いです。もし lending であれば、非 lending な Fn/FnMut トレイトを実装できません。

コルーチンクロージャがいつその upvar を lend しなければならないかの判定は、should_reborrow_from_env_of_parent_coroutine_closure ヘルパー関数30に実装されています。具体的には、これは 2 か所で発生する必要があります。

  1. 親クロージャが所有するデータを借用しているか?それが該当するかどうかは、親のキャプチャが by-move かどうかを確認することで判定できます。ただし、deref 射影を適用している場合は例外です。これは、by-move でキャプチャした参照を再借用していることを意味します。
#![allow(unused)]
fn main() {
let x = &1i32; // このライフタイムを `'1` と呼ぶことにします。
let c = async move || {
    println!("{:?}", *x);
    // 内側のコルーチンは参照で借用していますが、キャプチャしているのは `*x` だけであり、
    // `x` ではないため、内側のクロージャはそのデータを `'1` の間、再借用できます。
};
}
  1. コルーチンが親のキャプチャから可変に借用している場合、その可変借用は、親または元の upvar に対して持っている借用のいずれよりも長くは生存できません。したがって、子のキャプチャは常に親コルーチンクロージャの環境のライフタイムで借用する必要があります。
#![allow(unused)]
fn main() {
let mut x = 1i32;
let c = async || {
    x = 1;
    // 親は `x` を何らかの `&'1 mut i32` として借用します。
    // しかし、`c()` を呼び出すとき、`AsyncFnMut::async_call_mut` のシグネチャに合わせて
    // 暗黙に autoref します。そのライフタイムを `'call` と呼ぶことにします。
    // `&'call mut &'1 mut i32` を再借用できる最大範囲は `&'call mut i32` であるため、
    // 内側のコルーチンはコルーチンクロージャのライフタイムでキャプチャすべきです。
};
}

これらのいずれかのケースが該当する場合、借用は親コルーチンクロージャの環境のライフタイムでキャプチャすべきです。幸い、この関数が正しくなかったとしても、プログラムが unsound になることはありません。なぜなら、私たちは依然として borrowck を行い、この関数で行われた選択を検証するからです。唯一の副作用は、ユーザーが不要な borrowck エラーを受け取る可能性があることです。

インスタンス解決

コルーチンクロージャのクロージャ種別が FnOnce の場合、その AsyncFnOnce::call_once および FnOnce::call_once の実装はコルーチンクロージャの本体31に解決され、返されるコルーチンの Future::poll は子クロージャの本体に解決されます。

コルーチンクロージャのクロージャ種別が FnMut/Fn の場合、同じことが AsyncFn と、返されるコルーチンの対応する Future 実装にも当てはまります。31 ただし、AsyncFnOnce::call_once/FnOnce::call_once の実装32と、存在する場合は Fn::call/FnMut::call_mut インスタンス33を生成するために MIR shim を使用します。

これは ConstructCoroutineInClosureShim34 によって表現されます。これが Fn::call/FnMut::call_mut のインスタンスである場合、receiver_by_ref bool は true になります。35 これらすべてのインスタンスが返すコルーチンは、この時点までに合成されている by-move 本体に対応します。36

借用チェック

async クロージャの借用チェックはかなり単純であることがわかります。新しい DefiningTy::CoroutineClosure37 バリアントを追加し、コルーチンクロージャのシグネチャを生成する方法を borrowck に教えた後38、borrowck はまったく問題なく進行します。

注意すべき点の 1 つは、by-move コルーチンのために作成する合成本体については借用チェックしないということです。これは、構築方法により(また、それが由来する by-ref コルーチン本体の妥当性により)、妥当でなければならないためです。


  1. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_ast_lowering/src/expr.rs#L1147

  2. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_ast_lowering/src/expr.rs#L1117

  3. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_ast_lowering/src/item.rs#L1096-L1100

  4. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_ast_lowering/src/item.rs#L1276-L1279

  5. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/upvar.rs#L250-L256

  6. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_type_ir/src/ty_kind.rs#L163-L168

  7. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_type_ir/src/ty_kind/closure.rs#L221-L229

  8. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_type_ir/src/ty_kind/closure.rs#L362

  9. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_type_ir/src/ty_kind/closure.rs#L447-L455

  10. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/library/core/src/ops/async_function.rs#L36

  11. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/library/core/src/ops/async_function.rs#L30

  12. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_type_ir/src/ty_kind/closure.rs#L419

  13. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs#L404-L409

  14. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs#L1-L70

  15. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs#L131-L195

  16. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_mir_transform/src/lib.rs#L339-L342

  17. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/closure.rs#L345-L362

  18. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/closure.rs#L486-L487

  19. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/closure.rs#L517-L534

  20. https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/closure.rs#L575-L590

  21. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_hir_typeck/src/callee.rs#L169-L210 通常のクロージャでは、どの Fn* トレイトで呼び出すかによって戻り値の型が変わることはありません。一方、コルーチンクロージャでは、呼び出しに使われる AsyncFn* トレイトの種類に応じて、最終的に異なるコルーチン型を返すことになります。

  22. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_type_ir/src/ty_kind/closure.rs#L574-L576

  23. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_type_ir/src/ty_kind/closure.rs#L554-L563

  24. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/library/core/src/ops/async_function.rs#L135-L144

  25. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/library/core/src/ops/async_function.rs#L146-L154

  26. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_hir_typeck/src/upvar.rs#L250-L259

  27. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_hir_typeck/src/upvar.rs#L375-L471

  28. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_hir_typeck/src/upvar.rs#L211-L248

  29. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_hir_typeck/src/upvar.rs#L532-L539

  30. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_hir_typeck/src/upvar.rs#L1818-L1860

  31. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_ty_utils/src/instance.rs#L351 ↩2

  32. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_ty_utils/src/instance.rs#L341-L349

  33. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_ty_utils/src/instance.rs#L312-L326

  34. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_middle/src/ty/instance.rs#L129-L134

  35. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_middle/src/ty/instance.rs#L136-L141

  36. https://github.com/rust-lang/rust/blob/07cbbdd69363da97075650e9be24b78af0bcdd23/compiler/rustc_middle/src/ty/instance.rs#L841

  37. https://github.com/rust-lang/rust/blob/705cfe0e966399e061d64dd3661bfbc57553ed87/compiler/rustc_borrowck/src/universal_regions.rs#L110-L115

  38. https://github.com/rust-lang/rust/blob/7c7bb7dc017545db732f5cffec684bbaeae0a9a0/compiler/rustc_borrowck/src/universal_regions.rs#L743-L790