rust/src/librustc_trans/trans/doc.rs
Steve Klabnik cd5c8235c5 /*! -> //!
Sister pull request of https://github.com/rust-lang/rust/pull/19288, but
for the other style of block doc comment.
2014-11-26 16:50:14 -08:00

234 lines
12 KiB
XML

// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! # Documentation for the trans module
//!
//! This module contains high-level summaries of how the various modules
//! in trans work. It is a work in progress. For detailed comments,
//! naturally, you can refer to the individual modules themselves.
//!
//! ## The Expr module
//!
//! The expr module handles translation of expressions. The most general
//! translation routine is `trans()`, which will translate an expression
//! into a datum. `trans_into()` is also available, which will translate
//! an expression and write the result directly into memory, sometimes
//! avoiding the need for a temporary stack slot. Finally,
//! `trans_to_lvalue()` is available if you'd like to ensure that the
//! result has cleanup scheduled.
//!
//! Internally, each of these functions dispatches to various other
//! expression functions depending on the kind of expression. We divide
//! up expressions into:
//!
//! - **Datum expressions:** Those that most naturally yield values.
//! Examples would be `22`, `box x`, or `a + b` (when not overloaded).
//! - **DPS expressions:** Those that most naturally write into a location
//! in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`.
//! - **Statement expressions:** That that do not generate a meaningful
//! result. Examples would be `while { ... }` or `return 44`.
//!
//! ## The Datum module
//!
//! A `Datum` encapsulates the result of evaluating a Rust expression. It
//! contains a `ValueRef` indicating the result, a `Ty` describing
//! the Rust type, but also a *kind*. The kind indicates whether the datum
//! has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of
//! rvalues -- whether or not the value is "by ref" or "by value".
//!
//! The datum API is designed to try and help you avoid memory errors like
//! forgetting to arrange cleanup or duplicating a value. The type of the
//! datum incorporates the kind, and thus reflects whether it has cleanup
//! scheduled:
//!
//! - `Datum<Lvalue>` -- by ref, cleanup scheduled
//! - `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled
//! - `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>`
//!
//! Rvalue and expr datums are noncopyable, and most of the methods on
//! datums consume the datum itself (with some notable exceptions). This
//! reflects the fact that datums may represent affine values which ought
//! to be consumed exactly once, and if you were to try to (for example)
//! store an affine value multiple times, you would be duplicating it,
//! which would certainly be a bug.
//!
//! Some of the datum methods, however, are designed to work only on
//! copyable values such as ints or pointers. Those methods may borrow the
//! datum (`&self`) rather than consume it, but they always include
//! assertions on the type of the value represented to check that this
//! makes sense. An example is `shallow_copy()`, which duplicates
//! a datum value.
//!
//! Translating an expression always yields a `Datum<Expr>` result, but
//! the methods `to_[lr]value_datum()` can be used to coerce a
//! `Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as
//! needed. Coercing to an lvalue is fairly common, and generally occurs
//! whenever it is necessary to inspect a value and pull out its
//! subcomponents (for example, a match, or indexing expression). Coercing
//! to an rvalue is more unusual; it occurs when moving values from place
//! to place, such as in an assignment expression or parameter passing.
//!
//! ### Lvalues in detail
//!
//! An lvalue datum is one for which cleanup has been scheduled. Lvalue
//! datums are always located in memory, and thus the `ValueRef` for an
//! LLVM value is always a pointer to the actual Rust value. This means
//! that if the Datum has a Rust type of `int`, then the LLVM type of the
//! `ValueRef` will be `int*` (pointer to int).
//!
//! Because lvalues already have cleanups scheduled, the memory must be
//! zeroed to prevent the cleanup from taking place (presuming that the
//! Rust type needs drop in the first place, otherwise it doesn't
//! matter). The Datum code automatically performs this zeroing when the
//! value is stored to a new location, for example.
//!
//! Lvalues usually result from evaluating lvalue expressions. For
//! example, evaluating a local variable `x` yields an lvalue, as does a
//! reference to a field like `x.f` or an index `x[i]`.
//!
//! Lvalue datums can also arise by *converting* an rvalue into an lvalue.
//! This is done with the `to_lvalue_datum` method defined on
//! `Datum<Expr>`. Basically this method just schedules cleanup if the
//! datum is an rvalue, possibly storing the value into a stack slot first
//! if needed. Converting rvalues into lvalues occurs in constructs like
//! `&foo()` or `match foo() { ref x => ... }`, where the user is
//! implicitly requesting a temporary.
//!
//! Somewhat surprisingly, not all lvalue expressions yield lvalue datums
//! when trans'd. Ultimately the reason for this is to micro-optimize
//! the resulting LLVM. For example, consider the following code:
//!
//! fn foo() -> Box<int> { ... }
//! let x = *foo();
//!
//! The expression `*foo()` is an lvalue, but if you invoke `expr::trans`,
//! it will return an rvalue datum. See `deref_once` in expr.rs for
//! more details.
//!
//! ### Rvalues in detail
//!
//! Rvalues datums are values with no cleanup scheduled. One must be
//! careful with rvalue datums to ensure that cleanup is properly
//! arranged, usually by converting to an lvalue datum or by invoking the
//! `add_clean` method.
//!
//! ### Scratch datums
//!
//! Sometimes you need some temporary scratch space. The functions
//! `[lr]value_scratch_datum()` can be used to get temporary stack
//! space. As their name suggests, they yield lvalues and rvalues
//! respectively. That is, the slot from `lvalue_scratch_datum` will have
//! cleanup arranged, and the slot from `rvalue_scratch_datum` does not.
//!
//! ## The Cleanup module
//!
//! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow. The basic
//! idea is that the function context maintains a stack of cleanup scopes
//! that are pushed/popped as we traverse the AST tree. There is typically
//! at least one cleanup scope per AST node; some AST nodes may introduce
//! additional temporary scopes.
//!
//! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is popped, we will also generate the code for
//! each of its cleanups at that time. This corresponds to a normal exit
//! from a block (for example, an expression completing evaluation
//! successfully without panic). However, it is also possible to pop a
//! block *without* executing its cleanups; this is typically used to
//! guard intermediate values that must be cleaned up on panic, but not
//! if everything goes right. See the section on custom scopes below for
//! more details.
//!
//! Cleanup scopes come in three kinds:
//! - **AST scopes:** each AST node in a function body has a corresponding
//! AST scope. We push the AST scope when we start generate code for an AST
//! node and pop it once the AST node has been fully generated.
//! - **Loop scopes:** loops have an additional cleanup scope. Cleanups are
//! never scheduled into loop scopes; instead, they are used to record the
//! basic blocks that we should branch to when a `continue` or `break` statement
//! is encountered.
//! - **Custom scopes:** custom scopes are typically used to ensure cleanup
//! of intermediate values.
//!
//! ### When to schedule cleanup
//!
//! Although the cleanup system is intended to *feel* fairly declarative,
//! it's still important to time calls to `schedule_clean()` correctly.
//! Basically, you should not schedule cleanup for memory until it has
//! been initialized, because if an unwind should occur before the memory
//! is fully initialized, then the cleanup will run and try to free or
//! drop uninitialized memory. If the initialization itself produces
//! byproducts that need to be freed, then you should use temporary custom
//! scopes to ensure that those byproducts will get freed on unwind. For
//! example, an expression like `box foo()` will first allocate a box in the
//! heap and then call `foo()` -- if `foo()` should panic, this box needs
//! to be *shallowly* freed.
//!
//! ### Long-distance jumps
//!
//! In addition to popping a scope, which corresponds to normal control
//! flow exiting the scope, we may also *jump out* of a scope into some
//! earlier scope on the stack. This can occur in response to a `return`,
//! `break`, or `continue` statement, but also in response to panic. In
//! any of these cases, we will generate a series of cleanup blocks for
//! each of the scopes that is exited. So, if the stack contains scopes A
//! ... Z, and we break out of a loop whose corresponding cleanup scope is
//! X, we would generate cleanup blocks for the cleanups in X, Y, and Z.
//! After cleanup is done we would branch to the exit point for scope X.
//! But if panic should occur, we would generate cleanups for all the
//! scopes from A to Z and then resume the unwind process afterwards.
//!
//! To avoid generating tons of code, we cache the cleanup blocks that we
//! create for breaks, returns, unwinds, and other jumps. Whenever a new
//! cleanup is scheduled, though, we must clear these cached blocks. A
//! possible improvement would be to keep the cached blocks but simply
//! generate a new block which performs the additional cleanup and then
//! branches to the existing cached blocks.
//!
//! ### AST and loop cleanup scopes
//!
//! AST cleanup scopes are pushed when we begin and end processing an AST
//! node. They are used to house cleanups related to rvalue temporary that
//! get referenced (e.g., due to an expression like `&Foo()`). Whenever an
//! AST scope is popped, we always trans all the cleanups, adding the cleanup
//! code after the postdominator of the AST node.
//!
//! AST nodes that represent breakable loops also push a loop scope; the
//! loop scope never has any actual cleanups, it's just used to point to
//! the basic blocks where control should flow after a "continue" or
//! "break" statement. Popping a loop scope never generates code.
//!
//! ### Custom cleanup scopes
//!
//! Custom cleanup scopes are used for a variety of purposes. The most
//! common though is to handle temporary byproducts, where cleanup only
//! needs to occur on panic. The general strategy is to push a custom
//! cleanup scope, schedule *shallow* cleanups into the custom scope, and
//! then pop the custom scope (without transing the cleanups) when
//! execution succeeds normally. This way the cleanups are only trans'd on
//! unwind, and only up until the point where execution succeeded, at
//! which time the complete value should be stored in an lvalue or some
//! other place where normal cleanup applies.
//!
//! To spell it out, here is an example. Imagine an expression `box expr`.
//! We would basically:
//!
//! 1. Push a custom cleanup scope C.
//! 2. Allocate the box.
//! 3. Schedule a shallow free in the scope C.
//! 4. Trans `expr` into the box.
//! 5. Pop the scope C.
//! 6. Return the box as an rvalue.
//!
//! This way, if a panic occurs while transing `expr`, the custom
//! cleanup scope C is pushed and hence the box will be freed. The trans
//! code for `expr` itself is responsible for freeing any other byproducts
//! that may be in play.