Skip to content

Evaluatable bounds and preventing monomorphization errors #41

Open
@lcnr

Description

@lcnr

What is this

This is a design document for const generics. Any discussions about its content should be on zulip. The conclusions of these discussions should then be edited back into this issue. Please do not post any comments directly in this issue.

Content

If we do not require a proof that generic constants evaluate successfully, we end up with errors during monomorphization. These are bad™ as these don't get detected by cargo check.

Evaluatable bounds vs conditions

There are generally two possible ways to add bounds which rely on CTFE. We can require that some expression evaluates to true(a condition). Alternatively we can require that some expression successfully evaluates (an evaluatable bound).

It is still unclear which kind of bound we should use and whether evaluatable bounds should be required. By requiring evaluatable bounds for all constants used in the type system, we remove a major future source of errors during monomorphization.

Comparing usability of evaluatable bounds and conditions

Depending on why a bound is needed, it can be clearer to express it as either an evaluatable bound or a condition.

If we require some generic parameter to be prime, is_true { is_prime(N) } is simpler than
evaluatable { assert!(is_prime(N)) }.

However, if we don't want some expression like N / 4 - 2 to panic, adding evaluatable { N / 4 - 2 } is easier to add and less prone to mistakes than adding is_true { N > 7 }. This does however mean that we now require users to understand the evaluatable bound. For users of a library conditions are probably always easier to understand than evaluatable bounds.

Conditions cannot prevent all monomorphization errors by themselves

It is not possible to rely on conditions to prevent errors during monomorphization by themselves, as it not possible for the compiler to prove that a condition completely covers the cases where some other constant panics. This is related to silent CTFE errors.

Having a lint which suggests adding conditions for each potentially panicking constant is also very difficult, as the compiler does not look into functions for this. We also have the issue that const evaluation is Turing-complete. That means that it is impossible for the compiler figure out which inputs may cause a panic.

Evaluatable bounds for constants used outside of the type system

We already allow the use of generic associated constants in expressions, which causes errors during monomorphization.

struct HasAssoc<const N: usize>;
impl<const N: usize> HasAssoc<N> {
    const MINUS_ONE: usize = N - 1;
}
fn minus_one<const N: usize>() -> usize {
    HasAssoc::<N>::MINUS_ONE
}

fn passes_cargo_check() {
    minus_one::<0>();
}

fn main() {
    // causes an error during monomorphization.
    passes_cargo_check();
}

It isn't trivially obvious whether there is a meaningful difference between constants used in the type system and constants only used in expressions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-eval-boundsConst evaluatability boundsC-design-docsCategory: This is part of our design documentationK-behaviorDocument Kind: regarding user visible behaviorP-necessaryPriority: will be needed at some pointS-active

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions