-
Notifications
You must be signed in to change notification settings - Fork 543
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
Conversation
src/param_env.md
Outdated
@@ -0,0 +1,17 @@ | |||
# Parameter Environment | |||
|
|||
When working with associated constants or generic types it is often relevant to |
There was a problem hiding this comment.
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)`. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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).
There was a problem hiding this 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) |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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` |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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 Frame
s within one evaluation - I don't believe nested Frame
s 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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
It originated as an acronym though. I mean, that must have been how it was invented. Sure, the fact it sounds like a human name helps, but it’s worth mentioning where the acronym came from, even we use the ordinary name in all other places.
…Sent from my iPhone
On 6 Feb 2018, at 23:56, Scott Olson ***@***.***> wrote:
@solson commented on this pull request.
In src/glossary.md:
> @@ -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))
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.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
That has a completely different context. No likelihood of being confused...
… On 7 Feb 2018, at 01:33, eternaleye ***@***.***> wrote:
@eternaleye commented on this pull request.
In src/glossary.md <#45 (comment)>:
> @@ -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))
There's also that MIRI collides with another thing entirely <https://intelligence.org/>
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#45 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAEF3IOsIS6ME7jr0PPMK46zjRuWMqqwks5tSP1ggaJpZM4R7fcr>.
|
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this 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 |
There was a problem hiding this comment.
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; | ||
} | ||
} | ||
``` |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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>()]`. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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. :-)
There was a problem hiding this comment.
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.
Okay. A find & replace is in order then!
…Sent from my iPhone
On 9 Feb 2018, at 08:12, Oliver Schneider ***@***.***> wrote:
@oli-obk commented on this pull request.
In src/miri.md:
> +`Value::ByVal(PrimVal::Undef)`. When the initialization of `_1` is invoked, the
+value of the `FOO` constant is required, and triggers another call to
+`tcx.const_eval`, which will not be shown here. If the evaluation of FOO is
+successful, 42 will be subtracted by its value `4096` and the result stored in
+`_1` as `Value::ByValPair(PrimVal::Bytes(4054), PrimVal::Bytes(0))`. The first
+part of the pair is the computed value, the second part is a bool that's true if
+an overflow happened.
+
+The next statement asserts that said boolean is `0`. In case the assertion
+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
+location directly.
+
+After the evaluation is done, the virtual memory allocation is interned into the
yea... except in this case it's not strongly tied to a query
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
replacing "interned" with "memoizated"? If we do that, we should probably be changing it in the source, too. There's a function called |
No, replacing |
Changed all the links to actual hyperlinks and tested them. |
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? |
Travis failures:
|
Rebased and fixed links |
Ping @nikomatsakis did you want to rereview (as per your content above)? |
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... |
fixes #30
cc @eddyb