Module rustc_mir_build::build::expr

source ·
Expand description

Builds MIR from expressions. As a caller into this module, you have many options, but the first thing you have to decide is whether you are evaluating this expression for its value, its location, or as a constant.

Typically, you want the value: e.g., if you are doing expr_a + expr_b, you want the values of those expressions. In that case, you want one of the following functions. Note that if the expr has a type that is not Copy, then using any of these functions will “move” the value out of its current home (if any).

  • expr_into_dest – writes the value into a specific location, which should be uninitialized
  • as_operand – evaluates the value and yields an Operand, suitable for use as an argument to an Rvalue
  • as_temp – evaluates into a temporary; this is similar to as_operand except it always returns a fresh place, even for constants
  • as_rvalue – yields an Rvalue, suitable for use in an assignment; as of this writing, never needed outside of the expr module itself

Sometimes though want the expression’s location. An example would be during a match statement, or the operand of the & operator. In that case, you want as_place. This will create a temporary if necessary.

Finally, if it’s a constant you seek, then call as_constant. This creates a Constant<H>, but naturally it can only be used on constant expressions and hence is needed only in very limited contexts.

§Implementation notes

For any given kind of expression, there is generally one way that can be lowered most naturally. This is specified by the Category::of function in the category module. For example, a struct expression (or other expression that creates a new value) is typically easiest to write in terms of as_rvalue or into, whereas a reference to a field is easiest to write in terms of as_place. (The exception to this is scope and paren expressions, which have no category.)

Therefore, the various functions above make use of one another in a descending fashion. For any given expression, you should pick the most suitable spot to implement it, and then just let the other fns cycle around. The handoff works like this:

  • into(place) -> fallback is to create an rvalue with as_rvalue and assign it to place
  • as_rvalue -> fallback is to create an Operand with as_operand and use Rvalue::use
  • as_operand -> either invokes as_constant or as_temp
  • as_constant -> (no fallback)
  • as_temp -> creates a temporary and either calls as_place or into
  • as_place -> for rvalues, falls back to as_temp and returns that

As you can see, there is a cycle where into can (in theory) fallback to as_temp which can fallback to into. So if one of the ExprKind variants is not, in fact, implemented in the category where it is supposed to be, there will be a problem.

Of those fallbacks, the most interesting one is into, because it discriminates based on the category of the expression. This is basically the point where the “by value” operations are bridged over to the “by reference” mode (as_place).