Skip to content

Commit f896ddf

Browse files
Moved the Expectation enum to its own file
1 parent 428a8c6 commit f896ddf

File tree

2 files changed

+119
-111
lines changed

2 files changed

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

compiler/rustc_typeck/src/check/mod.rs

Lines changed: 2 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ mod compare_method;
7272
pub mod demand;
7373
mod diverges;
7474
pub mod dropck;
75+
mod expectation;
7576
mod expr;
7677
mod fn_ctxt;
7778
mod gather_locals;
@@ -88,6 +89,7 @@ mod wfcheck;
8889
pub mod writeback;
8990

9091
pub use diverges::Diverges;
92+
pub use expectation::Expectation;
9193
pub use fn_ctxt::FnCtxt;
9294
pub use inherited::{Inherited, InheritedBuilder};
9395

@@ -153,117 +155,6 @@ pub struct LocalTy<'tcx> {
153155
revealed_ty: Ty<'tcx>,
154156
}
155157

156-
/// When type-checking an expression, we propagate downward
157-
/// whatever type hint we are able in the form of an `Expectation`.
158-
#[derive(Copy, Clone, Debug)]
159-
pub enum Expectation<'tcx> {
160-
/// We know nothing about what type this expression should have.
161-
NoExpectation,
162-
163-
/// This expression should have the type given (or some subtype).
164-
ExpectHasType(Ty<'tcx>),
165-
166-
/// This expression will be cast to the `Ty`.
167-
ExpectCastableToType(Ty<'tcx>),
168-
169-
/// This rvalue expression will be wrapped in `&` or `Box` and coerced
170-
/// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
171-
ExpectRvalueLikeUnsized(Ty<'tcx>),
172-
}
173-
174-
impl<'a, 'tcx> Expectation<'tcx> {
175-
// Disregard "castable to" expectations because they
176-
// can lead us astray. Consider for example `if cond
177-
// {22} else {c} as u8` -- if we propagate the
178-
// "castable to u8" constraint to 22, it will pick the
179-
// type 22u8, which is overly constrained (c might not
180-
// be a u8). In effect, the problem is that the
181-
// "castable to" expectation is not the tightest thing
182-
// we can say, so we want to drop it in this case.
183-
// The tightest thing we can say is "must unify with
184-
// else branch". Note that in the case of a "has type"
185-
// constraint, this limitation does not hold.
186-
187-
// If the expected type is just a type variable, then don't use
188-
// an expected type. Otherwise, we might write parts of the type
189-
// when checking the 'then' block which are incompatible with the
190-
// 'else' branch.
191-
fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
192-
match *self {
193-
ExpectHasType(ety) => {
194-
let ety = fcx.shallow_resolve(ety);
195-
if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation }
196-
}
197-
ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety),
198-
_ => NoExpectation,
199-
}
200-
}
201-
202-
/// Provides an expectation for an rvalue expression given an *optional*
203-
/// hint, which is not required for type safety (the resulting type might
204-
/// be checked higher up, as is the case with `&expr` and `box expr`), but
205-
/// is useful in determining the concrete type.
206-
///
207-
/// The primary use case is where the expected type is a fat pointer,
208-
/// like `&[isize]`. For example, consider the following statement:
209-
///
210-
/// let x: &[isize] = &[1, 2, 3];
211-
///
212-
/// In this case, the expected type for the `&[1, 2, 3]` expression is
213-
/// `&[isize]`. If however we were to say that `[1, 2, 3]` has the
214-
/// expectation `ExpectHasType([isize])`, that would be too strong --
215-
/// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`.
216-
/// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
217-
/// to the type `&[isize]`. Therefore, we propagate this more limited hint,
218-
/// which still is useful, because it informs integer literals and the like.
219-
/// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
220-
/// for examples of where this comes up,.
221-
fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> {
222-
match fcx.tcx.struct_tail_without_normalization(ty).kind() {
223-
ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty),
224-
_ => ExpectHasType(ty),
225-
}
226-
}
227-
228-
// Resolves `expected` by a single level if it is a variable. If
229-
// there is no expected type or resolution is not possible (e.g.,
230-
// no constraints yet present), just returns `None`.
231-
fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
232-
match self {
233-
NoExpectation => NoExpectation,
234-
ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(&t)),
235-
ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(&t)),
236-
ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(&t)),
237-
}
238-
}
239-
240-
fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
241-
match self.resolve(fcx) {
242-
NoExpectation => None,
243-
ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty),
244-
}
245-
}
246-
247-
/// It sometimes happens that we want to turn an expectation into
248-
/// a **hard constraint** (i.e., something that must be satisfied
249-
/// for the program to type-check). `only_has_type` will return
250-
/// such a constraint, if it exists.
251-
fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> {
252-
match self.resolve(fcx) {
253-
ExpectHasType(ty) => Some(ty),
254-
NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None,
255-
}
256-
}
257-
258-
/// Like `only_has_type`, but instead of returning `None` if no
259-
/// hard constraint exists, creates a fresh type variable.
260-
fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> {
261-
self.only_has_type(fcx).unwrap_or_else(|| {
262-
fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span })
263-
})
264-
}
265-
}
266-
267158
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
268159
pub enum Needs {
269160
MutPlace,

0 commit comments

Comments
 (0)