エフェクト、const trait、および const 条件チェック
HostEffect 述語
HostEffectPredicate は、[const] Tr または const Tr 境界に由来する述語の一種です。
これは trait 参照と、境界に応じて Maybe または
Const になり得る constness を持ちます。
[const] Tr、より正確には Maybe 境界は、
それが置かれているコンテキストに応じて異なる適用のされ方をするため、通常の境界とは異なる
振る舞いをします。
T: Tr のような関数上の通常の trait 境界は、関数が呼び出されるときに証明され、
関数内で仮定されるものとして predicates_of クエリ内に収集されますが、
T: [const] Tr のような境界は通常の trait 境界として振る舞い、predicates_of の結果に
T: Tr を追加する一方で、const_conditions クエリにも HostEffectPredicate を追加します。
一方、T: const Tr 境界はコンテキストをまたいでも意味が変わらないため、
predicates_of には HostEffect(T: Tr, const) が追加され、
const_conditions には追加されません。
const_conditions クエリ
predicates_of は、ある item を使用するために証明する必要がある述語の集合を表します。
たとえば、以下の例で foo を使用するには次のようにします。
#![allow(unused)]
fn main() {
fn foo<T>() where T: Default {}
}
T が Default を実装していることを証明できなければなりません。
同様に、
const_conditions は、ある item を const コンテキスト内で 使用するために
証明する必要がある述語の集合を表します。上の例を const trait 境界を使うように調整すると、次のようになります。
#![allow(unused)]
fn main() {
const fn foo<T>() where T: [const] Default {}
}
この場合、foo は const_conditions クエリ内に HostEffect(T: Default, maybe) を持つことになり、
const コンテキストから foo を呼び出すためには、T が Default の const 実装を持つことを
証明しなければならないことを示します。
const_conditions の強制
const_conditions は現在、さまざまな場所でチェックされています。
const コンテキスト(const fn と const item を含む)からの HIR 内のすべての呼び出しでは、
呼び出している関数の const_conditions が成り立つことをチェックします。
これは FnCtxt::enforce_context_effects で行われます。
以下のコードはコンパイルされる必要があるため、
関数が参照されているだけで呼び出されていない場合はチェックしないことに注意してください。
#![allow(unused)]
fn main() {
const fn hi<T: [const] Default>() -> T {
T::default()
}
const X: fn() -> u32 = hi::<u32>;
}
trait impl が well-formed であるためには、impl の環境から trait の
const_conditions を証明できなければなりません。
これは wfcheck::check_impl でチェックされます。
例を示します。
#![allow(unused)]
fn main() {
const trait Bar {}
const trait Foo: [const] Bar {}
// `const_conditions` には `HostEffect(Self: Bar, maybe)` が含まれる
impl const Bar for () {}
impl const Foo for () {}
// ^ ここで impl が well-formed であるための `const_conditions` をチェックする
}
trait impl のメソッドは、それが実装している trait のメソッドよりも厳しい境界を持ってはなりません。
メソッドに互換性があることをチェックするために、
impl の述語に trait メソッドの述語を加えたハイブリッド環境が構築され、
impl メソッドの述語を証明しようとします。
const_conditions についても同じことを行います。
#![allow(unused)]
fn main() {
const trait Foo {
fn hi<T: [const] Default>();
}
impl<T: [const] Clone> Foo for Vec<T> {
fn hi<T: [const] PartialEq>();
// ^ `T: [const] Clone` と `T: [const] Default` が与えられても
// `T: [const] PartialEq` を証明できないため、impl 上のメソッドが
// trait 上のメソッドよりも厳しいことがわかる。
}
}
これらのチェックは compare_method_predicate_entailment で行われます。
関連型に対して同じチェックを行う類似の関数は
compare_type_predicate_entailment と呼ばれます。
これらはどちらも、const コンテキスト内では const_conditions を考慮する必要があります。
MIR では、const チェックの一部として、呼び出される item の const_conditions が
Checker::revalidate_conditional_constness で再度検証されます。
関連型と trait における explicit_implied_const_bounds
以下のような関連型、不透明型、および supertrait 上の境界は、 その境界が異なる形で表現されます。
#![allow(unused)]
fn main() {
trait Foo: [const] PartialEq {
type X: [const] PartialEq;
}
fn foo() -> impl [const] PartialEq {
// ^ 未実装の構文
}
}
const_conditions は呼び出し元に対して証明される必要があり、
定義内(たとえば関数上の trait
境界)では仮定できますが、これらの境界は定義時(impl 内、
または opaque を返すとき)に証明される必要があり、呼び出し元では仮定できます。
これらの境界の非 const 版は explicit_item_bounds と呼ばれます。
これらの境界は、HIR
typeck では compare_impl_item::check_type_bounds、古い solver では evaluate_host_effect_from_item_bounds、
新しい solver では consider_additional_alias_assumptions でチェックされます。
HostEffectPredicate の証明
HostEffectPredicate は [old solver] と [new trait solver] の両方で実装されています。
一般に、次のいずれかの条件を満たす場合に HostEffect 述語を証明できます。
* 述語は呼び出し元の境界から仮定できます。
* 型がそのトレイトに対する `const` `impl` を持ち、*かつ* その impl 上の const 条件が
成立し、*かつ* そのトレイトの `explicit_implied_const_bounds` が成立する場合。または
* 型が const コンテキストにおいてそのトレイトの組み込み実装を持つ場合。
たとえば、`Fn` は、その const 条件が満たされていれば関数アイテムによって実装されることがあり、
また `Destruct` は、その型をコンパイル時にドロップできる場合に const コンテキストで実装されます。
[old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html
[new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html
## const トレイトの詳細
後で拡充予定です。
### `#[rustc_non_const_trait_method]` 属性
これは内部(標準ライブラリ)での使用のみを意図しています。
この属性をトレイトメソッドに適用すると、コンパイラはこのメソッドのデフォルト本体が
コンパイル時に実行可能かどうかをチェックしません。
そのトレイトのユーザーも、このトレイトメソッドを const コンテキストで使用することは許可されません。
この属性は主に、
`Iterator` のような大規模なトレイトを、そのすべてのメソッドを同時に `const` にすることなく
const 化するために使用されます。
この属性は、トレイトを `const` として安定化する際には存在していてはなりません。