曖昧/非曖昧な型と const
AST/HIR における型と const 引数は、曖昧 (ambig) または非曖昧 (unambig) の 2 種類の位置に置かれる可能性があります。曖昧な位置とは、型または const のどちらとして解析しても妥当な位置であり、非曖昧な位置とは、片方の種類としてのみ解析することが妥当な位置です。
#![allow(unused)]
fn main() {
fn func<T, const N: usize>(arg: T) {
// ^ 非曖昧な型位置
let a: _ = arg;
// ^ 非曖昧な型位置
func::<T, N>(arg);
// ^ ^
// ^^^^ 曖昧な位置
let _: [u8; 10];
// ^^ ^^ 非曖昧な const 位置
// ^^ 非曖昧な型位置
}
}
曖昧な位置にあるほとんどの型/const は、解析中に型または const のどちらかとして曖昧性を解消できます。これに対する唯一の例外は、パスと推論されたジェネリック引数です。
パス
#![allow(unused)]
fn main() {
struct Foo<const N: usize>;
fn foo<const N: usize>(_: Foo<N>) {}
}
解析時には、中括弧で囲まれていないすべてのジェネリック引数を 型 として解析します(つまり、それらは [ast::GenericArg::Ty] になります)。上記の例では、これは Foo へのジェネリック引数を、[ast::Ty::Path(N)] をラップする ast::GenericArg::Ty として解析することを意味します。
その後、名前解決中に:
- ジェネリック引数位置で、ジェネリック引数を持たない単一セグメントのパスに遭遇した場合、まず型名前空間で解決を試み、それが失敗した場合に値名前空間での解決を試みます。
- それ以外の種類のパスはすべて、型名前空間でのみ解決を試みます。
これが実装されている場所については、[LateResolutionVisitor::visit_generic_arg] を参照してください。
最後に AST lowering 中に、型引数を lower しようとするとき、まずそれが Ty::Path であるかどうか、そしてそれが値名前空間内の何かに解決されたかどうかを確認します。そうであれば、anon const を作成し、型引数ではなく const 引数へ lower します。
これが実装されている場所については、[LoweringContext::lower_generic_arg] を参照してください。
パスの曖昧性は HIR には伝播されないことに注意してください。HIR ty lowering 中に Ty または Const のどちらかに変換される hir::GenericArg::Path は存在しません(ただし、そのようなことを行うことは可能です)。
推論された引数 (_)
#![allow(unused)]
fn main() {
struct Foo<const N: usize>;
fn foo() {
let _unused: Foo<_>;
}
}
lowering 後も曖昧なまま残る唯一のジェネリック引数は、パスセグメント内の推論されたジェネリック引数 (_) です。上記の例では、解析時点では Foo への _ 引数が、推論された型引数なのか、推論された const 引数なのかは明確ではありません。
曖昧な AST 位置では、推論された引数は [ast::Ty::Infer] をラップする [ast::GenericArg::Ty] として解析されます。その後、AST lowering 中に ast::GenericArg::Ty を lower するとき、それが推論された型であるかどうかを確認し、そうであれば [hir::GenericArg::Infer] へ lower します。
非曖昧な AST 位置では、推論された引数は ast::Ty::Infer または [ast::AnonConst] のどちらかとして解析されます。AnonConst のケースはかなり奇妙です。実際にはこれを HIR 内の anon const へ lower するわけではありませんが、「anon const」の「本体」を表すために [ast::ExprKind::Underscore] を使用しています。
AST に ast::GenericArg::Infer を持たせるようにリファクタリングし、この AnonConst の多義的な意味と、曖昧な位置における ast::Ty::Infer の再利用をなくせるかどうか検討する価値があるかもしれません。
非曖昧な AST 位置では、AST lowering 中に、推論された引数を、それが型位置か const 位置かに応じて、それぞれ [hir::TyKind::Infer][ty_infer] または [hir::ConstArgKind::Infer][const_infer] へ lower します。
曖昧な AST 位置では、AST lowering 中に、推論された引数を [hir::GenericArg::Infer][generic_arg_infer] へ lower します。これが実装されている場所については、[LoweringContext::lower_generic_arg] を参照してください。
この単純な実装では、HIR の構造を見ると、HIR 内で推論された型/const が見つかる可能性があると思われる場所が最大 5 つ存在することになります:
- 非曖昧な型位置における
TyKind::Infer - 非曖昧な const 引数位置における
ConstArgKind::Infer - 曖昧な位置における [
GenericArg::Type(TyKind::Infer)][generic_arg_ty] - 曖昧な位置における [
GenericArg::Const(ConstArgKind::Infer)][generic_arg_const] - 曖昧な位置における
GenericArg::Infer
場所 3 と 4 は、実際に遭遇することは決してないことに注意してください。これは、ジェネリック引数位置では常に GenericArg::Infer へ lower するためです。
これにはいくつかの失敗モードがあります:
GenericArg::Inferをチェックするもののhir::TyKind/ConstArgKind::Inferのチェックを忘れる visitor を書いてしまい、偶然にも曖昧な位置にある infer だけを処理してしまう可能性があります。TyKind/ConstArgKind::InferをチェックするもののGenericArg::Inferのチェックを忘れる visitor を書いてしまい、偶然にも非曖昧な位置にある infer だけを処理してしまう可能性があります。GenericArg::Type/Const(TyKind/ConstArgKind::Infer)とGenericArg::Inferをチェックする visitor を書いてしまい、曖昧な位置では推論された型/const をGenericArg::Type/Constとして表現することは決してないと気づかない可能性があります。ConstArgKind::InferではなくTyKind::Inferだけ をチェックする visitor を書いてしまい、推論された const 引数も存在することを忘れる可能性があります(またはその逆)。
推論された型/const に関心があるときに HIR visitor を書く際のエラーを減らすため、比較的複雑なシステムを用意しています:
-
型または const が非曖昧な位置にある場合と曖昧な位置にある場合とで、コンパイラ内に異なる型、
Ty<AmbigArg>とTy<()>があります。[AmbigArg][ambig_arg] は非居住型であり、曖昧な位置にいる場合にTyKindとConstArgKindのInferバリアントを選択的に「無効化」するために使用します。 -
HIR visitor の [
visit_ty][visit_ty] メソッドと [visit_const_arg][visit_const_arg] メソッドは、型/const の曖昧な位置バージョンのみを受け取ります。非曖昧な型/const は、visit 処理中に暗黙的に曖昧な型/const へ変換され、Inferバリアントは専用の [visit_infer][visit_infer] メソッドによって処理されます。
これには多くの利点があります:
GenericArg::Type/Constが推論された型/const 引数を表現できないことが明確ですvisit_tyとvisit_const_argの実装者が推論された型/const に遭遇することは決してないため、一見正しく動作するがエッジケースを誤って処理する visitor を書くことは不可能になりますvisit_inferメソッドは HIR 内の推論された型/const の すべて のケースを処理するため、visitor は推論された型/const を専用の 1 か所で簡単に処理でき、ケースを忘れることがありません [ty_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.TyKind.html#variant.Infer [const_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ConstArgKind.html#variant.Infer [generic_arg_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Type [generic_arg_const]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Const [generic_arg_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer [ambig_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.AmbigArg.html [visit_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_ty [visit_const_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_const_arg [visit_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_infer [LateResolutionVisitor::visit_generic_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.LateResolutionVisitor.html#method.visit_generic_arg [LoweringContext::lower_generic_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/struct.LoweringContext.html#method.lower_generic_arg [ast::GenericArg::Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.GenericArg.html#variant.Type [ast::Ty::Infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Infer [ast::AnonConst]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.AnonConst.html [hir::GenericArg::Infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer [ast::ExprKind::Underscore]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.ExprKind.html#variant.Underscore [ast::Ty::Path(N)]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Path