Skip to content

Instantly share code, notes, and snippets.

@nikomatsakis
Created October 6, 2017 19:33
Show Gist options
  • Save nikomatsakis/e69252a2b57e6d97d044c2f254c177f1 to your computer and use it in GitHub Desktop.
Save nikomatsakis/e69252a2b57e6d97d044c2f254c177f1 to your computer and use it in GitHub Desktop.
/// This "type relation" matches up the expected types that we get
/// from a function signature with the types that the user supplied.
/// Our goal is to re-use the **expected signature** ESig en masse,
/// with the caveat that if there are hitherto unresolved variables
/// appearing in ESig, that we may take values for those variables
/// from the **supplied signature** SSig.
///
/// The expectation matcher is always applied to types, E and S,
/// where E is an input/output type from the expected signature,
/// and S is the corresponding type that the user supplied. Note that
/// the user may not provide types for all inputs/outputs -- e.g.,
/// in a call like this:
///
/// foo(|x, y: u32| -> i32 { .. })
///
/// the user supplied values for `y` and the return type, but not
/// `x`. In that case, if ESig is `(T1, T2) -> T3`, the
/// `ExpectationMatcher` will be invoked on the pairs (T2, u32) and
/// (T3, i32) (note that T1 does not participate).
///
/// For each pair (E0, S0) of types, we will relate the types
/// structurally, producing a new type R that will be used for the
/// type in the final signature. One subtle point: both types may
/// contain late-bound regions with depth 1: these late-bound regions
/// will be bound by the closure signature.
///
/// If all the type pairs in the signatures are relatable, we are
/// attempting to ensure two key invariants regarding the results:
///
/// - First, each type R only contains **bound regions that
/// originated from the corresponding expected type E**.
/// - That is, if the user wrote `|x: &str, y|`, the resulting bound
/// region name in `x` (`Anon(0)`) would not be combined with some
/// bound region name that appears in the expected type for `y`. The bound region
/// names from the expected/supplied types effectively inherit distinct
/// namespaces, and should never be mixed.
/// - Second, any bound region appears that appears in some expected type E,
/// also appears in the corresponding spot in the resulting type R.
/// - In particular, imagine that we have an expected signature
/// `(&str) -> &str`, but the user wrote `|x: &'a str| ...`. In
/// the supplied type, the region `'a` is free.
/// - We could choose to use the final signature `(&'a str) ->
/// &str` without violating the first invariant, since we did
/// "intermix" any bound region names from the supplied types
/// with those of the expected type.
/// - However, if we did so, we have lost the constraint expressed
/// in the expected signature, that the return type must relate
/// to the input. In fact, this resulting signature is
/// inconsistent, since a bound region cannot only appear in the
/// input (this was #38714).
///
/// To produce the type R, we relate the types recursively, which
/// requires that the types generally match. If that is not true, an
/// error results, which will result in the expected signature being
/// ignored. The places where the types do not have to match, and the
/// resulting type in those scenarios, are as follows:
///
/// - If the expected type E is an (unbound) inference variable,
/// and S is not an inference variable, then the resulting type will be S,
/// so long as S does not contain anonymous regions.
/// - If the supplied type S is an unbound inference variable (i.e.,
/// the user wrote `_`), we will use E.
/// - When we encounter types with internal binders, e.g. `fn(&str)`
/// or `dyn Fn(&str)`, then we want to take the supplied type
/// (justification below). To preserve the invariants listed above,
/// we will error if either of the following is true (but otherwise
/// use the supplied type):
/// - the supplied type includes any regions bound at the closure depth
/// - in this case, using the supplied type would inject names from the supplied
/// side into the closure binder
/// - the expected type includes any regions bound at the closure depth
/// - in this case, using the supplied type would **remove** names from the closure
/// binder that the expected type included
///
/// Regions are allowed to differ as follows:
///
/// - If S supplies a late-bound region bound at the closure depth (1):
/// - If that region is anonymous, we use the region from E (which may also be free).
/// - Otherwise, we yield an error. Note that this case is not possible with Rust's
/// present syntax, it would require something like `foo(for<'a> |x: &'a u32| ...)`.
/// - If both E and S supply a free region, we use the region from S (which,
/// due to the limits of Rust's syntax -- must incidentally be a named region).
/// - If E supplies a bound region but S does not, we error out.
///
/// # Why we use the supplied type with bound regions
///
/// When we encounter types with internal binders, such as `fn(&u8)`,
/// we always take the **supplied type** as it is. So e.g. imagine that
/// we had an expected signature like:
///
/// for<'a> (&'a u8, &'b u32, fn(&'a u8)) -> ()
///
/// and the user supplied the types:
///
/// |x: &u8, y: &u32, z: fn(&u64)|
///
/// In the first two cases, we would be happy to use the expected
/// type, which means that `x` would wind up with a region bound in
/// the closure signature (`'a`), but `y` would wind up with a free
/// region (`'b`). However, for the variable `z`, we would yield an
/// error. (Due to the rule forbidding the expected type to reference
/// regions bound in the closure signature; `'a` in this case.)
///
/// This error ultimately stems from our desire to not "adjust" the
/// binding level for regions that appear bound within the user's
/// types themselves. You can see from `x` and `y` that we *are*
/// willing to "adjust" the binding level for regions that appear free
/// within the signature. You might wonder at the discrepancy.
///
/// The reasoning is that, from the point of view of the **closure
/// body**, the regions in `x` and `y` appear "free" either way. This
/// is not the case in the type of `z`. In other words, in the closure
/// body, the regions appearing in the types of `x` and `y` are both
/// free.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment