이펙트, const 트레이트, const 조건 검사
HostEffect 술어
HostEffectPredicate는 [const] Tr 또는 const Tr 바운드에서 비롯되는 술어의 일종입니다. 이 술어는 트레이트 참조와 constness를 가지며, constness는 바운드에 따라 Maybe 또는 Const가 될 수 있습니다. [const] Tr, 더 정확히 말하면 Maybe 바운드는 어떤 컨텍스트에 있느냐에 따라 다르게 적용되므로 일반 바운드와는 다르게 동작합니다. T: Tr와 같은 함수의 일반 트레이트 바운드는 함수가 호출될 때 증명되고 함수 내부에서는 가정되도록 clauses_of 쿼리 안에 수집되는 반면, T: [const] Tr와 같은 바운드는 일반 트레이트 바운드처럼 동작하여 clauses_of의 결과에 T: Tr를 추가할 뿐만 아니라 const_conditions 쿼리에도 HostEffectPredicate를 추가합니다.
반면 T: const Tr 바운드는 컨텍스트에 따라 의미가 바뀌지 않으므로, HostEffect(T: Tr, const)가 const_conditions가 아닌 clauses_of에 추가됩니다.
const_conditions 쿼리
clauses_of는 아이템을 사용하기 위해 증명해야 하는 절(clause)의 집합을 나타냅니다. 예를 들어, 아래 예제에서 foo를 사용하려면 다음과 같습니다:
#![allow(unused)]
fn main() {
fn foo<T>() where T: Default {}
}
T가 Default를 구현한다는 것을 증명할 수 있어야 합니다. 이와 비슷하게, const_conditions는 const 컨텍스트에서 아이템을 사용하기 위해 증명해야 하는 술어의 집합을 나타냅니다. 위 예제를 const 트레이트 바운드를 사용하도록 수정하면 다음과 같습니다:
#![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 아이템 포함)에서 이루어지는 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>;
}
트레이트 impl이 적격하려면, impl의 환경으로부터 트레이트의 const_conditions를 증명할 수 있어야 합니다. 이는 wfcheck::check_impl에서 검사됩니다.
다음은 예제입니다:
#![allow(unused)]
fn main() {
const trait Bar {}
const trait Foo: [const] Bar {}
// `const_conditions` contains `HostEffect(Self: Bar, maybe)`
impl const Bar for () {}
impl const Foo for () {}
// ^ here we check `const_conditions` for the impl to be well-formed
}
트레이트 impl의 메서드는 자신이 구현하는 트레이트 메서드보다 더 엄격한 바운드를 가져서는 안 됩니다. 메서드들이 호환되는지 검사하기 위해 impl의 술어와 트레이트 메서드의 술어를 합친 혼합 환경을 구성하고, 그 환경에서 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>();
// ^ we can't prove `T: [const] PartialEq` given `T: [const] Clone` and
// `T: [const] Default`, therefore we know that the method on the impl
// is stricter than the method on the trait.
}
}
이러한 검사는 compare_method_clause_entailment에서 수행됩니다. 연관 타입에 대해 같은 검사를 수행하는 유사한 함수는 compare_type_clause_entailment입니다. 두 함수 모두 const 컨텍스트에서는 const_conditions를 고려해야 합니다.
MIR에서는 const 검사의 일부로, 호출되는 아이템의 const_conditions가 Checker::revalidate_conditional_constness에서 다시 검증됩니다.
연관 타입과 트레이트에 대한 explicit_implied_const_bounds
다음과 같은 연관 타입, 불투명 타입, 슈퍼트레이트에 대한 바운드는 다르게 표현됩니다:
#![allow(unused)]
fn main() {
trait Foo: [const] PartialEq {
type X: [const] PartialEq;
}
fn foo() -> impl [const] PartialEq {
// ^ unimplemented syntax
}
}
호출자에 대해 증명되어야 하고 정의 내부에서는 가정할 수 있는 const_conditions(예: 함수의 트레이트 바운드)와 달리, 이 바운드들은 정의 시점(impl에서, 또는 불투명 타입을 반환할 때)에 증명되어야 하지만 호출자 측에서는 가정할 수 있습니다. 이 바운드들에 대응하는 non-const 바운드는 explicit_item_bounds라고 부릅니다.
이 바운드들은 HIR 타입 검사에서는 compare_impl_item::check_type_bounds에서, 기존 솔버에서는 evaluate_host_effect_from_item_bounds에서, 새 솔버에서는 consider_additional_alias_assumptions에서 검사됩니다.
HostEffectPredicate 증명하기
HostEffectPredicate는 기존 솔버와 새 트레이트 솔버 모두에 구현되어 있습니다. 일반적으로 다음 조건 중 하나가 충족되면 HostEffect 술어를 증명할 수 있습니다.
- 호출자 바운드로부터 술어를 가정할 수 있는 경우,
- 해당 타입이 그 트레이트에 대한
constimpl을 가지고 있고, 그리고 그 impl의 const 조건이 성립하며, 그리고 트레이트의explicit_implied_const_bounds가 성립하는 경우, 또는 - 해당 타입이 const 컨텍스트에서 그 트레이트에 대한 내장 구현을 가지고 있는 경우입니다. 예를 들어, 함수 항목은 const 조건이 충족되면
Fn을 구현할 수 있으며, 타입이 컴파일 시점에 드롭될 수 있으면 const 컨텍스트에서Destruct가 구현됩니다.
const 트레이트에 대한 추가 내용
추후 보완될 예정입니다.
#[rustc_non_const_trait_method] 속성
이 속성은 내부(표준 라이브러리) 용도로만 사용하도록 만들어졌습니다. 트레이트 메서드에 이 속성을 적용하면, 컴파일러는 이 메서드의 기본 본문이 컴파일 시점에 실행될 수 있는지 검사하지 않습니다. 또한 트레이트 사용자는 const 컨텍스트에서 이 트레이트 메서드를 사용할 수 없습니다. 이 속성은 주로 Iterator와 같은 큰 트레이트를 const화할 때, 그 모든 메서드를 한꺼번에 const로 만들지 않아도 되도록 하는 데 사용됩니다.
트레이트를 const로 안정화할 때는 이 속성이 남아 있어서는 안 됩니다.