Taking constant evaluation to the limit

oli-obk



Why const eval?

saving runtime resources

Why const eval?

saving runtime resources

reducing type system trickery

Why const eval?

saving runtime resources

reducing type system trickery

proving constraints at compile-time

Why const eval?

saving runtime resources

reducing type system trickery

proving constraints at compile-time

because we can

Grading constant evaluators

omnipotent

restricted omnipotent

inherently limited

Where are we right now?

very restricted omnipotent

Where do we want to be?

restricted omnipotent

Why restrict ourselves?

soundness

determinism

complexity of the const evaluator

What restrictions do we want?

mutation of global state

random number generator

nondeterminism

SIMD instructions

What unnecessary restrictions do we have?

Vec::new

Box::new, Vec::push

loop

if

calling trait methods

format!

unsafe

Ok, but I came to this talk for awesome stuff, so stop telling me what I can’t do

str::len, str::is_empty, str::as_bytes

panic!

let and let mut

int::overflowing_*, int::wrapping_*

ptr::offset_from


Accepted RFCs

loop

if

Open RFC

calling trait methods

format!

Vec::new

No RFC yet

heap allocations

unsafe

Details, details, details …

Heap allocations…

… have problems

Heap problems

const FOO: String = format!(...);
let x = FOO;
drop(x);

Heap problems

const BAD: String = format!(...);
const OK: &String = &format!(...);
const OK2: &str = &format!(...);
const OK3: MostNonHeapType = ...;

remind you of anything?

Send + Sync but for const heap

auto trait ConstSafe {}

auto trait ConstRefSafe {}

Example with String

unsafe impl ConstRefSafe for String {}

impl Send for &Sync

unsafe impl<'a, T: ConstRefSafe> ConstSafe for &'a T {}

Unsafe in constants

Introducing…

compile time undefined behavior

UB

const X: i32 = unsafe { *(42 as *const i32) };
const Y: bool = unsafe { transmute(3) };

Not UB

const X: *const i32 = &42 as *const i32;
const Y: i32 = unsafe { *X };

U B or not U B…

… that is the question

Much stricter rules at compile-time

const PINEAPPLE: usize = &42 as *const i32 as usize;
const PIZZA: usize = 99;
const LOLWAT: usize = PINEAPPLE / PIZZA;

Less stricter rules at compile-time

let x = [0; 8];
let y = &x as *const _ as *const u64;
let z = *x;

Summary

no theoretical limits

work is happening right now

many “features” need several base features

need more feature requests

Appendix

Don’t try this at home

-Zunleash-the-miri-inside-of-you

Const assertions on stable rust

[()][(!ok) as usize];

static_assertions crate wraps this for you

Fibonacci at compile-time

let _: [(); {
    let mut fib = 1;
    let mut prev = 1;
    let mut n = 0;
    while n != 10 {
        let swap = fib;
        fib += prev;
        prev = swap;
        n += 1;
    }
    fib
}] = [(); 42];

Spot the fib

error[E0019]: constant contains unimplemented expression type
  --> src/main.rs:6:9
   |
6  | /         while n != 10 {
7  | |             let swap = fib;
8  | |             fib += prev;
9  | |             prev = swap;
10 | |             n += 1;
11 | |         }
   | |_________^

Spot the fib

error[E0308]: mismatched types
  --> src/main.rs:13:10
   |
13 |     }] = [(); 42];
   |          ^^^^^^^^ expected an array with a fixed size of 144 elements, found one with 42 elements
   |
   = note: expected type `[(); 144]`
              found type `[(); 42]`