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

AST 로워링

AST 로워링 단계는 AST를 HIR로 변환합니다. 즉, 타입 분석이나 이와 유사한 구문 독립적 분석과 관련이 없는 많은 구조가 제거됩니다. 이러한 구조의 예는 다음과 같으며, 이에 국한되지 않습니다.

  • 괄호
    • 대체 없이 제거되며, 트리 구조가 순서를 명시적으로 나타냅니다.
  • for 루프
    • match + loop + match로 변환됩니다.
  • 전칭 impl Trait
    • 제네릭 인자로 변환됩니다(단, 사용자가 직접 작성하지 않았음을 알 수 있도록 몇 가지 플래그가 붙습니다).
  • 존재 impl Trait
    • 가상의 existential type 선언으로 변환됩니다.

AST 로워링의 구현은 rustc_ast_lowering 크레이트에 있습니다. 진입점은 lower_to_hir이며, 이 함수는 TyCtxt에서 확장 이후의 AST와 리졸버 데이터를 가져와 크레이트 전체에 대한 hir::Crate를 빌드합니다.

로워링은 HIR 소유자를 중심으로 구성됩니다. lower_to_hir가 먼저 크레이트를 인덱싱한 다음, ItemLowerer::lower_node가 각 크레이트, 항목, 연관 항목, 외부 항목을 로워링합니다.

로워링 로직의 대부분은 LoweringContext에 있습니다. 구현은 rustc_ast_lowering 크레이트 안의 item.rs, expr.rs, pat.rs, path.rs 등 여러 파일로 나뉘어 있지만, 모두 동일한 LoweringContext 상태와 ID 로워링 메커니즘을 공유합니다.

각 소유자는 자신만의 with_hir_id_owner 스코프 안에서 로워링됩니다. 이 때문에 아래의 HirId 불변 조건이 중요합니다. lower_node_id는 AST NodeId를 현재 소유자에 매핑하고, next_id는 디슈가링 과정에서 도입되는 HIR 전용 노드를 새로 만듭니다.

로워링은 compiler/rustc_passes/src/hir_id_validator.rs의 정합성 검사에 걸리지 않도록 몇 가지 불변 조건을 지켜야 합니다.

  1. HirId를 생성했다면 반드시 사용해야 합니다. 따라서 lower_node_id를 사용했다면, 그 결과로 나온 NodeId나 HirId를 반드시 사용해야 합니다(HIR에 있는 모든 NodeId는 대응하는 HirId가 존재하는지 검사되므로 둘 중 어느 것을 사용해도 됩니다).
  2. HirId의 로워링은 그것을 소유하는 아이템의 스코프 안에서 이루어져야 합니다. 즉, 현재 로워링 중인 아이템이 아닌 다른 아이템의 일부를 생성하는 경우에는 with_hir_id_owner를 사용해야 합니다. 예를 들어 존재 impl Trait를 로워링할 때 이런 상황이 발생합니다.
  3. HIR 구조에 들어갈 NodeId는 그 HirId가 사용되지 않더라도 반드시 로워링해야 합니다. let _ = self.lower_node_id(node_id);를 호출하는 것은 전적으로 올바른 방법입니다.
  4. AST에 존재하지 않던 새 노드를 생성하는 경우에는 그 노드들을 위한 새 ID를 반드시 생성해야 합니다. 이는 next_id 메서드를 호출하여 수행하며, 이 메서드는 새 NodeId를 생성하는 동시에 자동으로 로워링까지 해 주므로 HirId도 함께 얻을 수 있습니다.

새 DefId를 생성하는 경우, 각 DefId에는 대응하는 NodeId가 있어야 하므로 로워링 중에 새 NodeId를 생성하지 않아도 되도록 이러한 NodeId를 AST에 추가해 두는 것이 좋습니다. 이렇게 하면 어떤 대상의 NodeId를 통해 그 DefId를 찾을 수 있는 방법이 생긴다는 이점이 있습니다. 로워링에서 이 DefId가 여러 곳에 필요한 경우, 그 모든 곳에서 새 NodeId를 생성할 수는 없습니다. 그렇게 하면 새로운 DefId도 함께 생기기 때문입니다. AST에서 가져온 NodeId를 사용하면 이 문제가 발생하지 않습니다.

또한 NodeId가 있으면 로워링이 그때그때 DefId를 생성하는 대신 DefCollector가 DefId를 생성할 수 있습니다. DefId 생성을 한곳에 모으면 리팩터링하기도 쉽고 코드를 이해하기도 쉬워집니다.