rustc_hir_typeck/
expectation.rs

1use rustc_middle::ty::{self, Ty};
2use rustc_span::Span;
3
4use super::Expectation::*;
5use super::FnCtxt;
6
7/// When type-checking an expression, we propagate downward
8/// whatever type hint we are able in the form of an `Expectation`.
9#[derive(Copy, Clone, Debug)]
10pub(crate) enum Expectation<'tcx> {
11    /// We know nothing about what type this expression should have.
12    NoExpectation,
13
14    /// This expression should have the type given (or some subtype).
15    ExpectHasType(Ty<'tcx>),
16
17    /// This expression will be cast to the `Ty`.
18    ExpectCastableToType(Ty<'tcx>),
19
20    /// This rvalue expression will be wrapped in `&` or `Box` and coerced
21    /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
22    ExpectRvalueLikeUnsized(Ty<'tcx>),
23}
24
25impl<'a, 'tcx> Expectation<'tcx> {
26    // Disregard "castable to" expectations because they
27    // can lead us astray. Consider for example `if cond
28    // {22} else {c} as u8` -- if we propagate the
29    // "castable to u8" constraint to 22, it will pick the
30    // type 22u8, which is overly constrained (c might not
31    // be a u8). In effect, the problem is that the
32    // "castable to" expectation is not the tightest thing
33    // we can say, so we want to drop it in this case.
34    // The tightest thing we can say is "must unify with
35    // else branch". Note that in the case of a "has type"
36    // constraint, this limitation does not hold.
37
38    // If the expected type is just a type variable, then don't use
39    // an expected type. Otherwise, we might write parts of the type
40    // when checking the 'then' block which are incompatible with the
41    // 'else' branch.
42    pub(super) fn try_structurally_resolve_and_adjust_for_branches(
43        &self,
44        fcx: &FnCtxt<'a, 'tcx>,
45        span: Span,
46    ) -> Expectation<'tcx> {
47        match *self {
48            ExpectHasType(ety) => {
49                let ety = fcx.try_structurally_resolve_type(span, ety);
50                if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation }
51            }
52            ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety),
53            _ => NoExpectation,
54        }
55    }
56
57    /// Provides an expectation for an rvalue expression given an *optional*
58    /// hint, which is not required for type safety (the resulting type might
59    /// be checked higher up, as is the case with `&expr` and `box expr`), but
60    /// is useful in determining the concrete type.
61    ///
62    /// The primary use case is where the expected type is a wide pointer,
63    /// like `&[isize]`. For example, consider the following statement:
64    ///
65    ///    let x: &[isize] = &[1, 2, 3];
66    ///
67    /// In this case, the expected type for the `&[1, 2, 3]` expression is
68    /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
69    /// expectation `ExpectHasType([isize])`, that would be too strong --
70    /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
71    /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
72    /// to the type `&[isize]`. Therefore, we propagate this more limited hint,
73    /// which still is useful, because it informs integer literals and the like.
74    /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
75    /// for examples of where this comes up,.
76    pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
77        // FIXME: This is not right, even in the old solver...
78        match fcx.tcx.struct_tail_raw(ty, |ty| ty, || {}).kind() {
79            ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
80            _ => ExpectHasType(ty),
81        }
82    }
83
84    /// Resolves `expected` by a single level if it is a variable. If
85    /// there is no expected type or resolution is not possible (e.g.,
86    /// no constraints yet present), just returns `self`.
87    fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
88        match self {
89            NoExpectation => NoExpectation,
90            ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(t)),
91            ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(t)),
92            ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t)),
93        }
94    }
95
96    pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
97        match self.resolve(fcx) {
98            NoExpectation => None,
99            ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty),
100        }
101    }
102
103    /// It sometimes happens that we want to turn an expectation into
104    /// a **hard constraint** (i.e., something that must be satisfied
105    /// for the program to type-check). `only_has_type` will return
106    /// such a constraint, if it exists.
107    pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
108        match self {
109            ExpectHasType(ty) => Some(fcx.resolve_vars_if_possible(ty)),
110            NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
111        }
112    }
113
114    /// Like `only_has_type`, but instead of returning `None` if no
115    /// hard constraint exists, creates a fresh type variable.
116    pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
117        self.only_has_type(fcx).unwrap_or_else(|| fcx.next_ty_var(span))
118    }
119}