]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/expectation.rs
Rollup merge of #92695 - Swatinem:cover-nested, r=wesleywiser
[rust.git] / compiler / rustc_typeck / src / check / expectation.rs
1 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2 use rustc_middle::ty::{self, Ty};
3 use rustc_span::DUMMY_SP;
4 use rustc_span::{self, Span};
5
6 use super::Expectation::*;
7 use super::FnCtxt;
8
9 /// When type-checking an expression, we propagate downward
10 /// whatever type hint we are able in the form of an `Expectation`.
11 #[derive(Copy, Clone, Debug)]
12 pub enum Expectation<'tcx> {
13     /// We know nothing about what type this expression should have.
14     NoExpectation,
15
16     /// This expression should have the type given (or some subtype).
17     ExpectHasType(Ty<'tcx>),
18
19     /// This expression will be cast to the `Ty`.
20     ExpectCastableToType(Ty<'tcx>),
21
22     /// This rvalue expression will be wrapped in `&` or `Box` and coerced
23     /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
24     ExpectRvalueLikeUnsized(Ty<'tcx>),
25
26     IsLast(Span),
27 }
28
29 impl<'a, 'tcx> Expectation<'tcx> {
30     // Disregard "castable to" expectations because they
31     // can lead us astray. Consider for example `if cond
32     // {22} else {c} as u8` -- if we propagate the
33     // "castable to u8" constraint to 22, it will pick the
34     // type 22u8, which is overly constrained (c might not
35     // be a u8). In effect, the problem is that the
36     // "castable to" expectation is not the tightest thing
37     // we can say, so we want to drop it in this case.
38     // The tightest thing we can say is "must unify with
39     // else branch". Note that in the case of a "has type"
40     // constraint, this limitation does not hold.
41
42     // If the expected type is just a type variable, then don't use
43     // an expected type. Otherwise, we might write parts of the type
44     // when checking the 'then' block which are incompatible with the
45     // 'else' branch.
46     pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
47         match self.strip_opaque(fcx) {
48             ExpectHasType(ety) => {
49                 let ety = fcx.shallow_resolve(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 fat 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         match fcx.tcx.struct_tail_without_normalization(ty).kind() {
78             ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
79             _ => ExpectHasType(ty),
80         }
81     }
82
83     // Resolves `expected` by a single level if it is a variable. If
84     // there is no expected type or resolution is not possible (e.g.,
85     // no constraints yet present), just returns `self`.
86     fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
87         match self {
88             NoExpectation => NoExpectation,
89             ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(t)),
90             ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(t)),
91             ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t)),
92             IsLast(sp) => IsLast(sp),
93         }
94     }
95
96     pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
97         match self.resolve(fcx) {
98             NoExpectation | IsLast(_) => 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.strip_opaque(fcx) {
109             ExpectHasType(ty) => Some(ty),
110             NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) | IsLast(_) => {
111                 None
112             }
113         }
114     }
115
116     /// We must not treat opaque types as expected types in their defining scope, as that
117     /// will break `fn foo() -> impl Trait { if cond { a } else { b } }` if `a` and `b` are
118     /// only "equal" if they coerce to a common target, like two different function items
119     /// coercing to a function pointer if they have the same signature.
120     fn strip_opaque(self, fcx: &FnCtxt<'a, 'tcx>) -> Self {
121         match self {
122             ExpectHasType(ty) => {
123                 let ty = fcx.resolve_vars_if_possible(ty);
124                 match *ty.kind() {
125                     ty::Opaque(def_id, _)
126                         if fcx.infcx.opaque_type_origin(def_id, DUMMY_SP).is_some() =>
127                     {
128                         NoExpectation
129                     }
130                     _ => self,
131                 }
132             }
133             _ => self,
134         }
135     }
136
137     /// Like `only_has_type`, but instead of returning `None` if no
138     /// hard constraint exists, creates a fresh type variable.
139     pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
140         self.only_has_type(fcx).unwrap_or_else(|| {
141             fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span })
142         })
143     }
144 }