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 클로저를 전체적으로 이해하기 위해 이 장 하나에 모두 모아 두었습니다.

코루틴-클로저 – 기술적 심층 분석

코루틴-클로저는 async 클로저를 일반화한 것으로, 코루틴을 반환하는 클로저 표현식을 위한 특수 구문입니다. 특히 이 코루틴은 클로저의 upvar로부터 캡처할 수 있습니다.

현재 사용할 수 있는 코루틴-클로저의 종류는 async 클로저뿐이며, async 클로저를 지원하는 것이 이 PR의 범위입니다. 향후에는 gen || {} 등도 지원할 수 있으며, 이 문서에서 설명하는 문제와 특이점 대부분은 코루틴-클로저 전반에 적용됩니다.

코드가 다소 일반적으로 작성되어 있기 때문에, 이 문서에서는 이를 “async 클로저“와 “코루틴-클로저“라는 이름으로 번갈아 부를 수 있습니다. async 클로저가 반환하는 future는 일반적으로 “코루틴” 또는 “자식 코루틴“이라고 부릅니다.

HIR

async 클로저(그리고 향후에는 gen과 같은 다른 코루틴 종류)는 HIR에서 hir::Closure로 표현됩니다. hir::Closure의 클로저 종류는 ClosureKind::CoroutineClosure(_)1이며, 이는 async 블록을 감쌉니다. 이 async 블록 역시 HIR에서 hir::Closure로 표현됩니다. async 블록의 클로저 종류는 ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))2입니다.

async fn과 마찬가지로, async 클로저의 본문을 로워링할 때는 클로저의 모든 인자가 캡처되도록 무조건 본문 안으로 이동시켜야 합니다. 이 작업은 lower_coroutine_body_with_moved_arguments3가 처리합니다. 이 함수에서 주목할 만한 유일한 특이점은 최종적으로 생성되는 async 블록의 캡처 종류가 CaptureBy::ByRef4라는 점입니다. 이후에 모든 클로저 인자가 값으로 캡처되도록 강제하지만5, async 블록 전체가 async move인 것처럼 동작하기를 원하지는 않습니다. 그렇게 되면 async 클로저의 자기 빌림이라는 목적이 무의미해지기 때문입니다.

rustc_middle::ty 표현

구현을 대체로 향후 호환 가능하게(즉, gen || {} 및 async gen || {}와 호환되도록) 유지하기 위해, 이 절의 대부분에서는 async 클로저를 “코루틴-클로저“라고 부릅니다.

이 PR이 도입하는 핵심은 CoroutineClosure6라는 새로운 TyKind와, typeck 및 borrowck의 다른 관련 열거형(UpvarArgs, DefiningTy, AggregateKind)에 추가된 대응 변형입니다.

타입 표현 방식에 큰 차이가 있기 때문에, 기존의 TyKind::Closure를 일반화하는 대신 새로운 TyKind를 도입합니다. CoroutineClosure의 주요 차이점은 먼저 코루틴-클로저의 제네릭을 “풀어 놓은” 표현인 CoroutineClosureArgsParts를 살펴보면 알 수 있습니다.

클로저와의 유사점

클로저와 마찬가지로 parent_args, closure_kind_ty, tupled_upvars_ty가 있습니다. 이들은 클로저에서 대응되는 요소와 같은 것을 나타냅니다. 즉, 클로저가 정의된 본문으로부터 상속받은 제네릭, 클로저의 최대 “호출 능력”(즉, FnOnce처럼 호출하려면 소비되어야 하는지, 아니면 참조로 호출할 수 있는지), 그리고 클로저 자체가 캡처한 upvar입니다.

시그니처

기존 클로저는 클로저의 시그니처를 표현하는 데 사용하는 fn_sig_as_fn_ptr_ty를 가지고 있습니다. 이와 달리, 코루틴 클로저의 시그니처는 다소 “분해된” 방식으로 저장합니다. 코루틴-클로저는 어떤 AsyncFn* 트레이트로 호출하느냐에 따라 두 가지 시그니처를 가지기 때문입니다(아래 절 참조).

개념적으로, 코루틴-클로저는 참조로 호출되는지 이동으로 호출되는지에 따라 여러 가지 다른 시그니처 타입을 포함하는 것으로 생각할 수 있습니다.

이 두 시그니처를 편리하게 재구성할 수 있도록, signature_parts_ty는 이 코루틴-클로저가 반환하는 코루틴의 관련 부분을 모두 저장합니다. 이 시그니처 부분 타입은 대체로 fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty) 형태를 가지며, 여기서 resume_ty, return_ty, yield_ty는 각각 코루틴-클로저가 반환하는 코루틴의 해당 타입입니다7.

컴파일러는 주로 CoroutineClosureSignature 타입8을 다룹니다. 이 타입은 위에서 설명한 fn() 포인터 타입에서 관련 타입을 추출하여 생성되며, 코루틴-클로저가 최종적으로 반환하는 코루틴을 구성하는 데 사용할 수 있는 메서드를 제공합니다.

Coroutine 반환 타입을 구성하기 위해 함께 전달해야 하는 데이터

반환할 TyKind::Coroutine을 구성하려면 시그니처에 저장된 데이터와 함께 코루틴의 “증인(witness)“도 저장해야 합니다.

그렇다면 반환되는 Coroutine의 upvar는 어떻습니까? AsyncFnOnce(즉, 이동에 의한 호출)의 경우, 이는 단순히 코루틴이 반환하는 것과 동일한 upvar입니다. 그러나 AsyncFnMut/AsyncFn의 경우, 코루틴-클로저가 반환하는 코루틴은 주어진 “환경” 라이프타임9으로 코루틴-클로저의 데이터를 빌림합니다. 이는 AsyncFnMut/AsyncFn 호출 시그니처의 &self 라이프타임10, 그리고 ByRef11의 GAT 라이프타임에 해당합니다.

코루틴 반환 타입을 실제로 얻기

코루틴-클로저가 반환하는 Coroutine을 가장 쉽게 구성하려면 CoroutineClosureSignature의 to_coroutine_given_kind_and_upvars12 헬퍼를 사용하면 됩니다. CoroutineClosureSignature는 CoroutineClosureArgs에서 얻을 수 있습니다.

이 함수의 인자 대부분은 CoroutineArgs에서 얻을 수 있는 구성 요소이지만, goal_kind: ClosureKind는 예외입니다. 이 인자는 전달된 ClosureKind에 따라 어떤 종류의 코루틴을 반환할지 제어합니다. 즉, ClosureKind::Fn | ClosureKind::FnMut이면 참조에 의한(by-ref) 코루틴을, ClosureKind::FnOnce이면 이동에 의한(by-move) 코루틴을 준비합니다.

트레이트 계층 구조

에 대해 구현되는 Fn* 트레이트와 병렬적인 계층 구조를 도입합니다. 이를 도입한 동기는 블로그 글 async 클로저에서 다루었습니다.

현재 안정화된 모든 호출 가능 타입(즉, 클로저, 함수 항목, 함수 포인터, dyn Fn* 트레이트 객체)은 어떤 출력 타입 Fut에 대해 Fn*() -> Fut을 구현하고 Fut이 Future<Output = T>을 구현하면 자동으로 AsyncFn*() -> T을 구현합니다13.

async 클로저는 본문이 허용하는 범위에서 AsyncFn*을 구현합니다. 즉, upvar를 호환되는 방식으로 사용하는지에 따라 달라집니다(즉, upvar를 소비하거나 변경하면 AsyncFn 및 AsyncFnMut을 구현하는지 여부에 영향을 줄 수 있습니다…).

대여(Lending)

향후 AsyncFn*을 보다 일반적인 LendingFn* 트레이트 집합으로 옮길 수도 있습니다. 그러나 현재 컴파일러에서 LendingFn을 편리하게 사용하는 데 제약이 되는 구체적인 기술적 구현 세부 사항이 몇 가지 있습니다. 이는 다음과 관련이 있습니다:

  • 클로저 시그니처 추론.
  • 고차 트레이트 바운드와 관련된 제한 사항입니다.
  • 오류 메시지의 단점.

이러한 제약에 더해, 기반 트레이트가 async 클로저와 async Fn 트레이트 바운드의 사용자 경험에 아무런 영향을 주지 않아야 한다는 점 때문에 현재로서는 AsyncFn*를 채택합니다. 궁극적으로 이러한 더 일반적인 트레이트로 옮겨 갈 수 있도록, AsyncFn* 트레이트의 정확한 정의(연관 타입 포함)는 구현 세부 사항으로 남겨 둡니다.

async 클로저는 언제 일반 Fn* 트레이트를 구현합니까?

앞에서 “일반” 호출 가능 타입이 AsyncFn*를 구현할 수 있다고 언급했지만, 반대로 “async 클로저도 Fn*를 구현할 수 있는가?“라는 질문도 있습니다. 짧게 답하면 “유효할 때“입니다. 즉, AsyncFn/AsyncFnMut에서 반환되었을 코루틴이 부모 코루틴-클로저로부터 “빌려 온” upvar를 실제로 하나도 갖지 않을 때입니다.

자세한 답은 아래의 “follow-up: when do…” 절을 참고하십시오. 전체 답에서는 대부분의 async 클로저가 “그냥 동작“하도록 보장하는 데 쓰이는, 꽤 흥미롭고 충분히 철저하기를 바라는 휴리스틱을 설명합니다.

두 본문 이야기…

async 클로저가 AsyncFn/AsyncFnMut로 호출되면, 클로저로부터 빌림하는 코루틴을 반환합니다. 그러나 AsyncFnOnce를 통해 호출되면 해당 클로저를 소비하므로, 이미 드롭된 데이터로부터 빌림하는 코루틴을 반환할 수 없습니다.

이 제약을 우회하기 위해, 참조로 호출할 수 있는 코루틴-클로저에 대해 AsyncFnOnce::call_once를 호출할 때 쓰는 별도의 by-move MIR 본문을 합성합니다.

이 본문은 코루틴-클로저를 호출하여 반환되는 “일반” 코루틴과 동일하게 동작하지만, 부모 코루틴-클로저의 캡처를 자식 코루틴으로 이동해야 하므로 upvar 집합이 다르다는 점만 다릅니다.

by-move 본문 합성하기

코루틴-클로저가 반환한 코루틴의 by-move 본문에 접근하려면 coroutine_by_move_body_def_id14 쿼리를 사용하면 됩니다.

이 쿼리는 코루틴의 MIR 본문을 복사한 뒤, 본문의 의미를 보존하기 위해 추가적인 역참조와 필드 프로젝션15을 삽입하여 새로운 MIR 본문을 합성합니다.

새로운 def id를 합성했으므로, 이 쿼리는 MIR 본문과 관련된 수많은 다른 쿼리에 값을 공급하는 역할도 맡습니다. 이 쿼리는 코루틴의 빌드된 MIR에 대해 동작하므로 mir_promoted 쿼리 중에 ensure()16됩니다.

클로저 시그니처 추론

async 클로저의 클로저 시그니처 추론 알고리즘은 “전통적인” 클로저의 추론 알고리즘보다 약간 더 복잡합니다. 클로저와 마찬가지로, (전달된 기대 타입에 대해) 관련이 있을 수 있는 모든 절을 순회합니다17.

시그니처를 추출하기 위해 두 가지 상황을 고려합니다.

  • AsyncFnOnce::Output에 대한 프로젝션 술어로, 이를 사용하여 클로저의 입력 타입과 출력 타입을 추출합니다. 이는 F: AsyncFn*() -> T 바운드18가 있었던 상황에 해당합니다.
  • FnOnce::Output에 대한 프로젝션 술어로, 이를 사용하여 입력을 추출합니다. 출력의 경우, 관련된 Future::Output 프로젝션 술어를 찾아 출력을 추론하는 것도 시도합니다. 이는 F: Fn*() -> T, T: Future<Output = U> 바운드가 있었던 상황에 해당합니다.19
    • Future 바운드가 없으면 출력에 대해 단순히 새로운 추론 변수를 사용합니다. 이는 Option::map과 같은 조합자 함수에 async 클로저를 전달할 수 있는 경우에 해당합니다.20

후자의 경우를 지원하는 이유는 단순히, 일급 AsyncFn* 트레이트가 도입되기 전에 설계된 API를 호출할 때에도 사용자가 async || {} 구문을 그대로 끼워 넣어 쓸 수 있도록 하기 위해서입니다.

클로저의 종류가 추론되기 전에 클로저 호출하기

코루틴-클로저의 “종류”(즉, 최대 호출 모드: AsyncFnOnce/AsyncFnMut/AsyncFn) 계산은 typeck가 끝날 때까지 미룹니다21. 그러나 typeck가 끝나기 전에 해당 코루틴-클로저를 호출할 수 있어야 하므로, 그 전에 코루틴-클로저의 반환 타입을 결정해야 합니다.

어떤 Fn* 트레이트로 호출하든 반환 타입이 바뀌지 않는 일반 클로저와 달리, 코루틴-클로저는 호출에 사용된 AsyncFn* 트레이트의 종류에 따라 실제로 서로 다른 코루틴 타입을 반환하게 됩니다.

구체적으로, 반환되는 코루틴의 def-id는 바뀌지 않지만, upvar22(부모 코루틴-클로저로부터 빌리거나 이동된 것)와 코루틴 종류23는 호출 모드에 따라 달라집니다.

AsyncFnKindHelper 트레이트를 도입하여, “이 코루틴-클로저가 이 호출 모드를 지원하는가”24라는 질문은 트레이트 목표를 통해, “이 호출 모드의 튜플화된 upvar는 무엇인가”25라는 질문은 연관 타입을 통해 미룰 수 있게 합니다. 이 연관 타입은 코루틴-클로저의 입력 타입을 upvar 분석 중에 계산된 upvar 또는 “참조” upvar에 덧붙여 계산할 수 있습니다.

그렇다면 왜 이렇게 할까요?

이 방식은 다소 우회적이고 복잡해 보이며, 실제로 그렇다는 점을 인정합니다. 하지만 “아무것도 하지 않는” 대안을 생각해 봅시다. 그 대신 upvar 분석까지 모든 AsyncFn* 목표를 모호한 것으로 표시해 둘 수도 있으며, 그 시점에는 반환할 코루틴의 upvar에 정확히 무엇을 넣어야 하는지 알게 됩니다. 그러나 이 방식은 실제로 프로그램의 추론에 매우 해로운데, 다음과 같은 프로그램이 유효하지 않게 되기 때문입니다:

let c = async || -> String { .. };
let s = c().await;
// ^^^ If we can't project `<{c} as AsyncFn>::call()` to a coroutine, then the `IntoFuture::into_future` call inside of the `.await` stalls, and the type of `s` is left unconstrained as an infer var.
s.as_bytes();
// ^^^ That means we can't call any methods on the awaited return of a coroutine-closure, like... at all!

그래서 그 대신 우리는 이 별칭(이 경우에는 프로젝션인 AsyncFnKindHelper::Upvars<'env, ...>)을 사용하여 튜플화된 upvar의 계산을 미루고 그 자리에 넣을 무언가를 마련합니다. 그러면서도 여전히 TyKind::Coroutine(경직된 타입)을 반환할 수 있으며, Future 구현은 upvar에 전혀 의존하지 않으므로 필요한 내장 트레이트(여기서는 Future)도 성공적으로 확인할 수 있습니다.

Upvar 분석

대체로 코루틴-클로저와 그 자식 코루틴에 대한 upvar 분석은 일반적인 upvar 분석과 같이 진행됩니다. 그러나 async 클로저의 특수한 성질을 처리하기 위해 일어나는 몇 가지 흥미로운 부분이 있습니다:

모든 입력을 강제로 캡처하기

async fn과 마찬가지로 모든 입력 인자는 캡처됩니다. async 클로저가 반환하는 future 코루틴이 입력이 본문에서 사용되는지 여부에 의존하지 않도록, 우리는 이러한 모든 입력이 이동으로 캡처되도록 명시적으로 강제합니다26. 그렇지 않으면 흥미로운 semver 위험이 생길 것입니다.

참조에 의한 캡처 계산하기

AsyncFn/AsyncFnMut을 지원하는 코루틴-클로저의 경우, 코루틴-클로저의 캡처와 그 자식 코루틴 사이의 관계도 계산해야 합니다. 구체적으로, 코루틴-클로저는 upvar를 자신의 캡처로 move할 수 있지만, 코루틴은 그 upvar를 빌림만 할 수 있습니다.

자식 코루틴의 모든 캡처를 살펴보고 이를 부모 코루틴-클로저의 대응하는 캡처와 비교하여 “coroutine_captures_by_ref_ty“를 계산합니다27. 이 coroutine_captures_by_ref_ty는 결국 for<'env> fn() -> captures... 타입으로 표현되며, 추가된 바인더 라이프타임은 AsyncFn::async_call 또는 AsyncFnMut::async_call_mut를 호출할 때의 “&self” 라이프타임을 나타냅니다. 이 바인더는 나중에 메서드를 실제로 호출할 때 인스턴스화합니다.

부모 코루틴-클로저에서 참조로 캡처한 것이 모두 “대여(lending)” 빌림이 되는 것은 아니라는 점에 유의하십시오. 이는 코루틴-클로저가 Fn* 계열 트레이트를 구현할 수 있는지 여부에 밀접하게 영향을 미치므로, 자세한 내용은 아래의 후속 논의: async 클로저는 언제 일반 Fn* 트레이트를 구현합니까? 절을 참조하십시오.

이동 방식 본문 + FnOnce의 특이점

클로저 upvar 분석이 코루틴-클로저의 자식 코루틴에 대해 지나치게 느슨한 upvar를 추론하여 결국 빌림 검사기 오류가 발생하는 상황이 몇 가지 있습니다. 이는 예제를 통해 설명하는 것이 가장 좋습니다. 예를 들어 다음과 같은 코드가 있다고 합시다.

#![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로 강제하므로, 캡처가 이동 방식으로 일어나도록 강제해야 합니다28.

마찬가지로 다음 코드를 봅시다.

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

x는 코루틴-클로저로 이동되지만, 반환되는 코루틴은 &x만 빌림합니다. 그러나 y도 캡처하여 드롭하므로, 코루틴-클로저는 AsyncFnOnce가 되도록 강제됩니다. 이 경우에도 x의 캡처가 이동 방식으로 일어나도록 강제해야 합니다. 특히 이 상황을 판별하려면, 앞의 예제와 달리 코루틴 종류의 클로저 종류가 아직 제약되지 않았으므로, 코루틴-클로저의 본문을 분석하여 모든 upvar가 어떻게 사용되는지 살펴보고 이들이 “소비하는” 방식, 즉 FnOnce로 강제하는 방식으로 사용되었는지 판단해야 합니다29.

후속 논의: async 클로저는 언제 일반 Fn* 트레이트를 구현합니까?

우선, 모든 async 클로저는 항상 최소 한 번은 호출될 수 있으므로 FnOnce를 구현합니다.

Fn/FnMut에 대한 자세한 답을 얻으려면 관련된 질문, 즉 코루틴-클로저가 대여를 하는지에 먼저 답해야 합니다. 만약 대여를 한다면, 대여하지 않는 Fn/FnMut 트레이트를 구현할 수 없기 때문입니다.

코루틴-클로저가 언제 자신의 upvar를 대여해야 하는지 판단하는 로직은 should_reborrow_from_env_of_parent_coroutine_closure 도우미 함수에 구현되어 있습니다30. 구체적으로, 이는 두 가지 경우에 필요합니다.

  1. 부모 클로저가 소유한 데이터를 빌리고 있습니까? 부모 캡처가 이동 방식인지 확인하여 이 경우에 해당하는지 판단할 수 있습니다. 단, 역참조 프로젝션을 적용하는 경우는 예외인데, 이는 이동 방식으로 캡처한 참조를 재빌림하고 있다는 뜻이기 때문입니다.
#![allow(unused)]
fn main() {
let x = &1i32; // Let's call this lifetime `'1`.
let c = async move || {
    println!("{:?}", *x);
    // Even though the inner coroutine borrows by ref, we're only capturing `*x`,
    // not `x`, so the inner closure is allowed to reborrow the data for `'1`.
};
}
  1. 코루틴이 부모 캡처로부터 가변으로 빌리고 있다면, 그 가변 빌림은 부모나 원래 upvar에 대해 가진 빌림 중 어느 것보다도 오래 살 수 없습니다. 따라서 자식 캡처는 항상 부모 코루틴-클로저 env의 라이프타임으로 빌림해야 합니다.
#![allow(unused)]
fn main() {
let mut x = 1i32;
let c = async || {
    x = 1;
    // The parent borrows `x` for some `&'1 mut i32`.
    // However, when we call `c()`, we implicitly autoref for the signature of
    // `AsyncFnMut::async_call_mut`. Let's call that lifetime `'call`. Since
    // the maximum that `&'call mut &'1 mut i32` can be reborrowed is `&'call mut i32`,
    // the inner coroutine should capture w/ the lifetime of the coroutine-closure.
};
}

이 두 경우 중 하나라도 해당하면, 부모 코루틴-클로저 env의 라이프타임으로 빌림을 캡처해야 합니다. 다행히 이 함수가 올바르지 않더라도 프로그램이 건전하지 않게 되지는 않습니다. 이 함수가 내린 선택은 여전히 borrowck로 검사하고 검증하기 때문입니다. 유일한 부작용은 사용자가 불필요한 borrowck 오류를 받을 수 있다는 점입니다.

인스턴스 해석

코루틴-클로저의 closure-kind가 FnOnce이면, 그 AsyncFnOnce::call_once 및 FnOnce::call_once 구현은 코루틴-클로저의 본문으로 해석되며31, 반환되는 코루틴의 Future::poll은 자식 클로저의 본문으로 해석됩니다.

코루틴-클로저의 closure-kind가 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는 아무 문제 없이 진행됩니다.

한 가지 주목할 점은 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 ↩

  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 ↩