Skip to content

Add some documentation for const eval and related topics #45

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

Merged
merged 1 commit into from
Feb 23, 2018
Merged

Conversation

oli-obk
Copy link
Contributor

@oli-obk oli-obk commented Feb 6, 2018

fixes #30

cc @eddyb

src/param_env.md Outdated
@@ -0,0 +1,17 @@
# Parameter Environment

When working with associated constants or generic types it is often relevant to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be more general, maybe "generic and/or associated items (types, constants, functions/methods)"?

src/param_env.md Outdated
things.

Another great thing about `ParamEnv` is that you can use it to eliminate the
generic parameters from a `Ty` by calling `param_env.and(ty)`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That eliminates irrelevant bounds from the param_env based on ty (or anything else with types in it, like Substs or Instance), without changing the ty. cc @nikomatsakis

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this phrasing was confusing to me. Probably it makes sense to explain ParamEnvAnv a bit more -- basically, saying that often we need to pair up the environment with the thing we are operating on. When doing that, we use param_env.and(foo). If, however, foo doesn't refer to generic parameters, we'll swap in an empty environment since the where clauses shouldn't matter (I'm not sure if this behavior is worth mentioning though; it's kind of a detail and anyhow I expect it to change at some point).

Copy link
Member

@mark-i-m mark-i-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oli-obk Thanks for the PR! This is a great addition to the book. I left a bunch of comments below. The content is great, but a little hard on the uninitiated (like me) :)

Let me know what you think!

@@ -19,5 +19,6 @@
- [MIR construction](./mir-construction.md)
- [MIR borrowck](./mir-borrowck.md)
- [MIR optimizations](./mir-optimizations.md)
- [Constant evaluation](./const-eval.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add ./miri and ./param_env to this file?

src/glossary.md Outdated
@@ -17,6 +17,7 @@ ICE | internal compiler error. When the compiler crashes.
ICH | incremental compilation hash. ICHs are used as fingerprints for things such as HIR and crate metadata, to check if changes have been made. This is useful in incremental compilation to see if part of a crate has changed and should be recompiled.
infcx | the inference context (see `librustc/infer`)
MIR | the Mid-level IR that is created after type-checking for use by borrowck and trans ([see more](./mir.html))
miri | an interpreter for MIR used for constant evaluation ([see more](./miri.html))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add that "MIRI" is "MIR Interpreter"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, nit: could you capitalize "MIRI"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've never capitalized it so far, just like rustc is never capitalized, but maybe we should?
@nikomatsakis @solson @oli-obk Opinions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've always used "miri" and "Miri", never "MIRI", though I guess that's an arbitrary aesthetic choice.

It particularly makes sense to go with lowercase miri when you're talking about a binary (like rustc).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It originated as an acronym though, so probably makes sense to capitalise it, even if we write it like an ordinary word in informal contexts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doubles as an acronym but was also named because it coincides with a human name. Plus writing it in all caps would be inconsistent with all existing documentation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also that MIRI collides with another thing entirely

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer "miri" to MIRI. I'm hard put to justify this, but I guess that to me it doesn't feel like an "acronym", more like a "thing".

src/miri.md Outdated
Miri (**MIR** **I**nterpreter) is a virtual machine for executing MIR without
compiling to machine code. It is usually invoked via `tcx.const_eval`.

Its core datastructures can be found in `src/librustc/mir/interpret`. This is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this a live link to the source tree on github? That way, we can check if links break.

src/miri.md Outdated
## Miri interpretation

Although the main entry point to constant evaluation is the `tcx.const_eval`
query, there are additional functions in `src/librustc_mir/interpret/const_eval`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this a live link to the source tree on github? That way, we can check if links break.

src/miri.md Outdated
variables at the time of writing this guide.

A stack frame is defined by the `Frame` type in
`src/librustc_mir/interpret/eval_context.rs` and contains all the local
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this a live link to the source tree on github? That way, we can check if links break.

@@ -0,0 +1,47 @@
# Miri

Miri (**MIR** **I**nterpreter) is a virtual machine for executing MIR without
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to define what the model of the virtual machine is. For example, is it a stack-machine? Or does it have a flat byte-addressable memory? This would provide helpful context when discussing allocations and virtual memory below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those two are not exclusive, as many stack machines are also RAM machines (and if you're a RAM machine, adding a stack is easy).

However, miri isn't either of those, as each virtual allocation forms its own "address space", and the stack frames have fixed shapes (determined by the MIR of the fn body/const initializer being evaluated), instead of having more general data stack manipulation primitives.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the stack is implemented more as a linked list of Frame in some sense (where each Frame is created based on what you are miri-ing)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's a stack of Frames within one evaluation - I don't believe nested Frames are ever created except for const fn calls - if you need the value of a different const, it would be evaluated separately, with an entirely different stack.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it :)

It would be good to add this info to the chapter.

src/miri.md Outdated
never have to access an `Allocation` directly except for translating it to the
compilation target (atm just LLVM).

### Details
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove this heading.

src/miri.md Outdated
anything that can't be representad as a `u64`) or `to_raw_bits` which results
in an `Option<u128>` yielding the `ByVal` if possible.

## Miri allocations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't 100% clear to me what role allocations play. IIUC, an allocation is just a virtual memory location that gets modified by step, right?

@@ -0,0 +1,47 @@
# Miri
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start!

One thing I would find helpful is an example. Something like,

const MY_CONST: usize = 1 << 12;
let x = [0; MY_CONST]

would, I think, be sufficient. Maybe a walk through of evaluating the MIR to get the length of x would be good?

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: type Foo = [u8; MY_CONST]; may add less confusion, as nesting const inside a fn body can be misleading (even if it has no semantic effects).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heck yes to an example! =) Maybe use a const fn to explain?

@@ -0,0 +1,32 @@
# Constant Evaluation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to mention where in the compiler "pipeline" const eval happens. IIUC, it is after MIR is constructed and borrowck?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the "pipeline" model is no longer entirely accurate. We care more about dependencies - and yes, miri requires MIR to be constructed to run on, but e.g. the type-checking, MIR construction and MIR evaluation of a constant might happen before any of those ever happen on a different constant/function/etc.

We have to to do all that to understand even the type [T; 8] - type-checking and working MIR is therefore reentrant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the "pipeline" model is no longer entirely accurate.

Yep, that's why I put it in "quotes" 😛 What I was getting at more was what dependencies const eval has, as you noted.

@mark-i-m mark-i-m mentioned this pull request Feb 6, 2018
@alexreg
Copy link
Contributor

alexreg commented Feb 7, 2018 via email

@alexreg
Copy link
Contributor

alexreg commented Feb 7, 2018 via email

src/param_env.md Outdated
# Parameter Environment

When working with associated constants or generic types it is often relevant to
have more information about the `Self` or generic parameters. Trait bounds and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the easiest way to explain the parameter environment is that it represents the set of where clauses in scope. I would also always try to tie to an example:

fn foo<T: Copy>(t: T) {
}

when you request tcx.param_env(def_id_for_foo) you get back [T: Copy], basically.

Although you can obtain a valid `ParamEnv` for any item via
`tcx.param_env(def_id)`, this `ParamEnv` can be too generic for your use case.
Using the `ParamEnv` from the surrounding context can allow you to evaluate more
things.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this sentence. It seems like you should use the param-env that is correct, and no other. =) Maybe the question is more about determining what is correct? Can you give a specific example of what you had in mind here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The place I ran into issues with this was when evaluating promoted constants. If I ran tcx.param_env(promoted_const_def_id) and then evaluated the constant, I got type resolution problems left and right. If I used tcx.param_env(function_def_id) during the evaluation of the promoted, everything was good.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

promoted_const_def_id doesn't exist though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... maybe it was associated constants and not promoteds.

I'm definitely using the param_env of the surrounding function when evaluating constants, not just when calling Instance::resolve.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule is that you use the ParamEnv for the body you're monomorphizing. So a function referring to a constant or anything else, will use whatever is in the MIR, substituted with the Substs and the ParamEnv (if needed) for the function.

That is, if you got the MIR for a def_id, typesystem things you get from that MIR are valid wrt tcx.param_env(def_id).subst(tcx, substs), after applying .subst(tcx, substs).

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great @oli-obk -- I'll check back in when more of @mark-i-m's comments are addressed, since I think they said most of what I would say.

Copy link
Member

@mark-i-m mark-i-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! Thanks :)

I left a few minor nits. Also, this chapter borders a bit on too much detail, but I think it is ok.

Besides the nits, LGTM 👍

src/miri.md Outdated
create items that use the type (locals, constants, function arguments, ...).

To obtain the (in this case empty) parameter environment, one can call
`let param_env = tcx.param_env(def_id);`. The `GlobalId` needed is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the def_id is that of FOO not Foo, right?

return;
}
}
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... is the exact MIR likely to change over time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already simplified, and unlikely to change much or in relevant ways.

src/miri.md Outdated
```

Before the evaluation, a virtual memory location is created for storing the
evaluation result, in this case a `vec![u8; size_of::<usize>()]`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is a bit ambiguous. Does "in this case a vec..." refer to the result or the virtual memory location?

src/miri.md Outdated
fails, its error message is used for reporting a compile-time error.

Since it does not fail, `Value::ByVal(PrimVal::Bytes(4054))` is stored in the
virtual memory was allocated before th evaluation. `_0` always refers to that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/th /the /

src/miri.md Outdated
virtual memory was allocated before th evaluation. `_0` always refers to that
location directly.

After the evaluation is done, the virtual memory allocation is interned into the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "interned" mean? Is that basically the memoization of a query?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea... except in this case it's not strongly tied to a query

src/miri.md Outdated
## Datastructures

Miri's core datastructures can be found in
https://github.com/rust-lang/rust/blob/master/src/librustc/mir/interpret . This
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel very strongly, but I would prefer something of the format [librustc/mir/interpret](https://github.com/rust-lang/rust/blob/master/src/librustc/mir/interpret)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mark-i-m I think the convention in the rest of the guide is just to do e.g. src/librustc/mir/interpret (with the src/ prefix, note), and not link... I don't mind this way, but just thinking we should be consistent. :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/librustc/mir/interpret (with the src/ prefix, note), and not link..

Generally, we have been trying to make links. That way, travis can warn us if they break.

@alexreg
Copy link
Contributor

alexreg commented Feb 9, 2018 via email

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 9, 2018

replacing "interned" with "memoizated"? If we do that, we should probably be changing it in the source, too. There's a function called intern and various fields having interned in their name.

@alexreg
Copy link
Contributor

alexreg commented Feb 9, 2018

replacing "interned" with "memoizated"? If we do that, we should probably be changing it in the source, too. There's a function called intern and various fields having interned in their name.

No, replacing src/foo.rs with [`src/foo.rs`](https://...).

@eddyb
Copy link
Member

eddyb commented Feb 9, 2018

@oli-obk @alexreg Note that we have both memoization (of functions) and interning (of data).
We shouldn't really mix the two IMO.

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 9, 2018

Changed all the links to actual hyperlinks and tested them.

@mark-i-m
Copy link
Member

Ooops. Sorry, I didn't realize the ball was in my court 😛

@nikomatsakis Did you also want to re-review this PR, or should I merge (#45 (review))?

@oli-obk It looks like a rebase is needed?

@nikomatsakis nikomatsakis mentioned this pull request Feb 12, 2018
20 tasks
@mark-i-m mark-i-m added the S-waiting-on-author Status: this PR is waiting for additional action by the OP label Feb 15, 2018
@mark-i-m
Copy link
Member

Travis failures:

trait-resolution.md#436: Found "./param_env.md", did you mean "./param_env.html"?
const-eval.md#24: Found "./param_env.md", did you mean "./param_env.html"?
const-eval.md#33: Found "./miri.md", did you mean "./miri.html"?

@oli-obk
Copy link
Contributor Author

oli-obk commented Feb 20, 2018

Rebased and fixed links

@mark-i-m
Copy link
Member

Ping @nikomatsakis did you want to rereview (as per your content above)?

@mark-i-m mark-i-m added S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content and removed S-waiting-on-author Status: this PR is waiting for additional action by the OP labels Feb 23, 2018
@mark-i-m
Copy link
Member

I think Niko is busy. Let's go ahead and merge this because I really want this chapter in the book. We can iterate on it later...

@mark-i-m mark-i-m merged commit 6e1eccd into master Feb 23, 2018
@mark-i-m mark-i-m deleted the miri branch February 28, 2018 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: this PR is waiting for a reviewer to verify its content
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Chapter on MIRI?
7 participants