Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lazy const evaluation incompatible with const fn #29928

Closed
oli-obk opened this issue Nov 19, 2015 · 2 comments
Closed

lazy const evaluation incompatible with const fn #29928

oli-obk opened this issue Nov 19, 2015 · 2 comments
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...)

Comments

@oli-obk
Copy link
Contributor

oli-obk commented Nov 19, 2015

The following code fails to compile

#![feature(const_fn)]
const fn f(x: usize) -> A {
    A { field: x }
}
struct A {
    field: usize,
}
fn main() {
    let _ = [0; f(5).field];
}

The problem is that the A { field: x } expression is lazily evaluated. So we evaluate it when we access field in f(5).field. But now we already lost the function argument x since we are no longer evaluating the const fn. When we try to access x in the const evaluator it bails out with non-const expr. The easy fix would be to evaluate everything eagerly (as it is done in trans/consts). But then we get breaking changes left and right.

Example: the following is legal (see #28189):

struct S<T>(T) where [T; (||{}, 1).1]: Copy;                                    
fn main() {}

But we don't support closures. In fact, you can write arbitrary expression that typecheck and they will compile:

unsafe fn bla() -> i32 { 5 }
struct S<T>(T) where [T; (unsafe { bla() }, 1).1]: Copy;                                    
fn main() {}

So we can't make the const evaluator eager without introducing breaking changes.

  1. As a remedy we can make it eager, but report errors lazily: const_eval evaluates everything eagerly, but stores a Result<ConstVal, ConstEvalErr> instead of a ConstVal wherever we currently allow lazy evaluation.
  2. As an alternative someone pushes the eager const evaluator I implemented through crater and we go with the breaking change (yay?).
  3. Another alternative is to make const fn lazy, too. So we just pass in the expression that generated the function argument, instead of evaluating the function argument. Then we do the same for structs and tuples, so instead of just lazily storing the expression that generates them, we also store the expressions of their fields.
    • imo this is not a nice solution, and probably the most complex implementation-wise
@Mark-Simulacrum
Copy link
Member

This seems fixed, though I'm uncertain. Please reopen if I misjudged the issue and provide an example we should next gate closing this issue upon, if possible. Closing at least for now though.

Compiles successfully:

#![feature(const_fn)]
const fn f(x: usize) -> A {
    A { field: x }
}
struct A {
    field: usize,
}
fn main() {
    let _ = [0; f(5).field];
}

Does not compile:

struct S<T>(T) where [T; (||{}, 1).1]: Copy;                                    
fn main() {}
unsafe fn bla() -> i32 { 5 }
struct S<T>(T) where [T; (unsafe { bla() }, 1).1]: Copy;                                    
fn main() {}

@oli-obk
Copy link
Contributor Author

oli-obk commented May 2, 2017

Was fixed in #38813

@RalfJung RalfJung added the A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) label Dec 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants