Skip to content

const if to supress instantiation/codegen of unreachable branches #3582

@the8472

Description

@the8472

Currently there's a suboptimal interaction between const asserts and const generics.

A more general function that is valid for all N cannot call a function that asserts at compile time that N fulfills some conditions even if the call is on a branch that's dead for the invalid N.

I.e. this does not compile:

#![feature(inline_const)]

// method in std
fn method_with_precondition<const N: usize>() {
    const { assert!(N > 0) };
}

// user method, no way to opt out of asserts for N = 0
fn generic_caller_method<const N: usize>() -> fn() {
    if N > 0 {
        panic_on_zero::<N>
    } else {
        || {} // fallback
    }
}

fn main() {
    let _fun = foo::<0>();
}
error[E0080]: evaluation of `panic_on_zero::<0>::{constant#0}` failed
 --> src/main.rs:5:13
  |
5 |     const { assert!(N > 0) };
  |             ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: N > 0', src/main.rs:5:13
  |
  = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)

This blocks / requires unpleasant choices in several T-libs features that want to check for N != 0 or similar constraints:

If const-eval in the dead branches is expensive it might also help compile perf.

There are efforts (rust-lang/rust#99682) to move monomorphization-time errors to check time and also apply those checks to dead code (rust-lang/rust#112879), which will make it even more difficult to discharge compile-time obligations imposed by callees.

Currently the language does not seem to have any way to select different code paths in generic contexts without also instantiating those paths. Even generic_const_exprs does not offer this unless it gets extended rust-lang/project-const-generics#26

Therefore it would be useful if we could write the case above as

fn foo<const N: usize>() -> fn() {
    const if N > 0 { 
        panic_on_zero::<N>
    } else {
        || {} // fallback
    }
}

so that panic_on_zero::<0> will never be instantiated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions