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

rustc infinite loop: warning: Constant evaluating a complex constant, this might take some time #50637

Closed
vegard opened this issue May 10, 2018 · 5 comments
Labels
A-const-eval Area: Constant evaluation (MIR interpretation) A-diagnostics Area: Messages for errors, warnings, and lints P-medium Medium priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another.

Comments

@vegard
Copy link

vegard commented May 10, 2018

Not sure if this is really a bug, but I tried searching and couldn't find any hits on the warning message. Feel free to close the issue if this is intended/expected behaviour.

Input:

fn main() {
    [(); loop {}]
}

Output:

$ rustc -
error[E0019]: constant contains unimplemented expression type
 --> <anon>:2:10
  |
2 |     [(); loop {}]
  |          ^^^^^^^

warning: Constant evaluating a complex constant, this might take some time
 --> <anon>:2:10
  |
2 |     [(); loop {}]
  |          ^^^^^^^

At this point the rustc process hangs seemingly forever while consuming 100% CPU.

I'm on commit c166b03.

@scottmcm
Copy link
Member

This was decided in general over in rust-lang/rfcs#2344 (comment)

@oli-obk Would be be feasible to catch some simple cases of "this definitely won't finish"?

@porglezomp
Copy link
Contributor

Existing control-flow analysis gives a warning about unreachable code following a loop {}, I'm not sure if that same analysis is present in the constant evaluator but those cases seems like the "definitely won't finish" cases.

@oli-obk
Copy link
Contributor

oli-obk commented May 11, 2018

uuuuh... Reaching that warning is a bug in my book. The first error should have cause this expression from never being evaluated.

That said,

Would be be feasible to catch some simple cases of "this definitely won't finish"?

Oh yea, there's a super easy trick for doing that. The moment we emit the warning, we hash the state of the const evaluator and place it into a HashSet. Then, every (n?) step(s) from then on, we do the same. If we ever encounter the same hash again, we're 100% in an infinite loop, because const eval is deterministic and thus reaching the same state will mean we'll just reach it again soonish.

@oli-obk oli-obk added A-diagnostics Area: Messages for errors, warnings, and lints A-const-eval Area: Constant evaluation (MIR interpretation) labels May 11, 2018
@alexcrichton alexcrichton added the regression-from-stable-to-stable Performance or correctness regression from one stable version to another. label May 11, 2018
@nikomatsakis nikomatsakis added the P-medium Medium priority label May 17, 2018
@nikomatsakis
Copy link
Contributor

Definitely a bug. Not P-high, but we ought to fix. Marking as P-medium. My ideal scenario here is that @oli-obk writes up mentoring instructions and adds the appropriate labels, because this seems like a nice bug for someone looking to learn something about miri.

@oli-obk
Copy link
Contributor

oli-obk commented May 18, 2018

As a first step, we should implement #49980, which turns the unconditional warning into a lint.

Then, in order to actually detect true recursions and abort for them:

This will take riddiculous amounts of memory and computation time, but since we only start doing this once we hit a certain counter limit, this should be fine. If not, we can just increase the limit.

Once we have hit the limit we also need to set a flag so while we will now do the hashing at every step, the lint should only show up every 1M steps (or whatever the setting is).

ecstatic-morse added a commit to ecstatic-morse/rust that referenced this issue Jul 4, 2018
This test relies on the fact that restrictions on expressions in `const
fn` do not apply when computing array lengths. It is more difficult to
statically analyze than the simple `loop{}` mentioned in rust-lang#50637.

This test should be updated to ignore the warning after rust-lang#49980 is resolved.
bors added a commit that referenced this issue Jul 11, 2018
Infinite loop detection for const evaluation

Resolves #50637.

An `EvalContext` stores the transient state (stack, heap, etc.) of the MIRI virtual machine while it executing code. As long as MIRI only executes pure functions, we can detect if a program is in a state where it will never terminate by periodically taking a "snapshot" of this transient state and comparing it to previous ones. If any two states are exactly equal, the machine must be in an infinite loop.

Instead of fully cloning a snapshot every time the detector is run, we store a snapshot's hash. Only when a hash collision occurs do we fully clone the interpreter state. Future snapshots which cause a collision will be compared against this clone, causing the interpreter to abort if they are equal.

At the moment, snapshots are not taken until MIRI has progressed a certain amount. After this threshold, snapshots are taken every `DETECTOR_SNAPSHOT_PERIOD` steps. This means that an infinite loop with period `P` will be detected after a maximum of `2 * P * DETECTOR_SNAPSHOT_PERIOD` interpreter steps. The factor of 2 arises because we only clone a snapshot after it causes a hash collision.
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 (MIR interpretation) A-diagnostics Area: Messages for errors, warnings, and lints P-medium Medium priority regression-from-stable-to-stable Performance or correctness regression from one stable version to another.
Projects
None yet
Development

No branches or pull requests

6 participants