Const block expressions

Hey folks,

I was just reading the docs about the new const blocks. They say:

A const block is a variant of a block expression whose body evaluates at compile-time instead of at runtime.

(...)

If the const block expression is not executed at runtime, it may or may not be evaluated (...)

reference: https://doc.rust-lang.org/nightly/reference/expressions/block-expr.html#const-blocks

Isn't this contradicting? :thinking:

1 Like

What would be contradicting? It just says that if a const is not actually used at runtime, then the compiler may chose not to materialize it at all. That's normal and it's how all other consts work, too.

And a whole lot of other things work in the same way in any compiled, optimized language. Dead code elimination is a basic optimization that almost all modern compilers perform.

4 Likes

Ok, thanks! :slight_smile: My reading of the first sentence was that it is absolute and therefore const blocks are always evaluated at compile time. So I was confused why it later said that it depends more or less on the case.

1 Like

The background premise of the original quoted text, I think, is that for every expression, there is some relationship between “when this expression is evaluated” and “when its sub-expressions are evaluated”.

  • For a expression of most kinds, the relationship is containment: some or all its sub-expressions are evaluated while it is evaluated.
  • A closure expression is an exception: its body is evaluated after it is evaluated (because you must evaluate the closure expression to get a function that can be called).
  • A const block expression is an exception in the opposite direction: its body is evaluated before it is evaluated.

None of this, by itself, says anything about what happens to the body if the expression is not evaluated. There's another very general question lurking here: do expressions have any effects (such as, in particular, failing the compilation) if they are not executed? That's a tough one to pick a single answer to, because sometimes we want the compiler to save us some time by saying “this code is guaranteed to fail at run time”, but sometimes we know that failing code won't be reached at run time.

There is an absolute principle to be found here, but it's one with the quantifiers moved around a bit, and which is perhaps easier to state in the negative:

If a const block body is evaluated, then it is evaluated at compile time.

In other words: const block bodies are never evaluated at run time.

3 Likes

Also note that there's no substantial difference (at runtime) between evaluating a const expression and not evaluating it. Since a const expression by definition can't do anything that would affect the runtime behavior of the program, whether it's "actually evaluated" is pretty much a subjective and philosophical question, depending on your definition of "actually evaluated", rather than a practical or useful distinction.

(The only observable effect I can imagine is failing or passing compilation based on whether a panicking const expression happens to be hit by the compiler. But if you are relying on the compiler optimizing out some otherwise wrong expression for compiling your program, then you may very well be in for a numbery of nasty surprises anyway.)

2 Likes

whether it's "actually evaluated" is pretty much a subjective and philosophical question, depending on your definition of "actually evaluated", rather than a practical or useful distinction.

No, whether it is evaluated is actually very important, because const blocks can panic, and panics are not just about unforeseeable failures. One of the things that held up stabilization of const blocks was the question under what conditions such errors are guaranteed to be reported. For example, consider the following program:

fn only_works_on_even_numbers<const N: usize>(input: [u16; N]) {
    let something_precomputed = const {
        assert!(N % 2 == 0);
        
        N / 2 // imagine this is more complex
    };
    
    // ...
}

fn main() {
    only_works_on_even_numbers([0; 13]);
}

This program will, currently, fail cargo build (which evaluates constants) and pass cargo check (which does not). The language and compiler teams consider this unfortunate but acceptable; they'd rather that it failed consistently, to avoid surprising users with programs that look OK under check but are not, but there currently isn't a way to do that efficiently.

5 Likes

I already addressed that point. If you are relying on the compiler accidentally not hitting your incorrect const code for avoiding a compilation error, then you are doing it horribly wrong.

Ah, I see, you mean whether the const block expression is evaluated, not whether its body is evaluated. Sorry for the confusion — I was trying to be precise and then didn't notice what you said. My bad.

1 Like

Yes, and also your point stands wrt. the body, of course!

No, it uses the verb "executed", and it’s not super obvious what the difference between "executed" and "evaluated" is, especially given that there’s no actual execution going on as the expression is just a value at runtime.

Also, to be precise, (the value of) a const expression does not actually have to be used at runtime; the expression can be evaluated for its compile-time side effects. Case in point:

const { assert!(FOO == 42) }

must be evaluated even though its "value" is not used and indeed doesn’t exist at runtime.

2 Likes

Hmm.. are the rules by which I can find out if some const stuff that is not used at runtime get actually evaluated at compile time somewhere documented?

... or is the general opinion about it just don't rely on this behaviour as it is more or less not standardized?

Based on the wording of the reference, I presume that the intent is essentially the same as with runtime expressions: if the expression's position in the source code is provably unreachable (ie. dead code), or if the expression has provably no observable effect, the compiler doesn't have to evaluate it. But of course the compiler cannot always prove that something is unreachable, so a const expression may sometimes be evaluated even if it's not reachable.

1 Like