-
-
Save nikomatsakis/e69252a2b57e6d97d044c2f254c177f1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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