rustcompile-timeunreachable-code

Check for unreachable at compilation time in Rust


I have enum consisting of a few variants:

enum Foo {
  A,
  B,
  C,
}

and i have a function, that has loop with if and match inside, all branches of which is diverging:

fn my_function() -> i32 {
  // some vars
  for ... in ... { // also may be `while` or `loop`
    // some code
    if some_condition {
      let foo: Foo = ...;
      match foo {
        Foo::A => { return 42 }
        Foo::B => { continue }
        Foo::C => {
          // do something with vars
          continue
        }
      }
      // this place is currently unreachable
      // and must **always** be **totally** unreachable
      // because the code after `if` must never execute in this(`some_condition`) case
    }
    // some code
  }
}

But with some future changes i may add new variant(s) to enum, so i want to check at compilation time, that that line is unreachable. I feel like this should be possible, but i don't know how to do it.

"Prior art":

If only there existed something like const_unreachable! or soft_compile_error! which would fill this exact scenario of giving compile error if and only if there exists execution path that leads to it...

So what are possible solutions (including rust nightly and any features) to this problem?


Solution

  • On nightly, you can affect the result of your match to a variable with the "never" type:

    #![feature(never_type)]
    
    enum Foo {
      A,
      B,
    }
    
    fn main() {
        let x = Foo::A;
        let _: ! = match x {
            Foo::A => { println!("A"); return; }
            Foo::B => { println!("B"); /* Forgot `return` here */ }
        };
    }
    

    Playground

    Gives:

    error[E0308]: mismatched types
      --> src/main.rs:12:19
       |
    12 | Foo::B => { println!("B"); /* Forgot `return` here */ }
       |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `!`, found `()`
       |
       = note:   expected type `!`
               found unit type `()` 
    

    As pointed out by @rodrigo, until the never type gets stabilized the same can be achieved on stable Rust using std::convert::Infallible:

    enum Foo {
      A,
      B,
    }
    
    fn main() {
        let x = Foo::A;
        let _: std::convert::Infallible = match x {
            Foo::A => { println!("A"); return; }
            Foo::B => { println!("B"); /* Forgot `return` here */ }
        };
    }
    

    Playground