1use rustc_hir as hir;
2use rustc_hir::intravisit::{self, Visitor};
3use rustc_hir::{HirId, PatKind};
4use rustc_infer::traits::ObligationCauseCode;
5use rustc_middle::ty::{self, Ty};
6use rustc_span::Span;
7use rustc_span::def_id::LocalDefId;
8use tracing::debug;
9
10use crate::FnCtxt;
11
12#[derive(Debug, Copy, Clone)]
16pub(super) enum DeclOrigin<'a> {
17 LetExpr,
19 LocalDecl { els: Option<&'a hir::Block<'a>> },
21}
22
23impl<'a> DeclOrigin<'a> {
24 pub(super) fn try_get_else(&self) -> Option<&'a hir::Block<'a>> {
25 match self {
26 Self::LocalDecl { els } => *els,
27 Self::LetExpr => None,
28 }
29 }
30}
31
32pub(super) struct Declaration<'a> {
36 pub hir_id: HirId,
37 pub pat: &'a hir::Pat<'a>,
38 pub ty: Option<&'a hir::Ty<'a>>,
39 pub span: Span,
40 pub init: Option<&'a hir::Expr<'a>>,
41 pub origin: DeclOrigin<'a>,
42}
43
44impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> {
45 fn from(local: &'a hir::LetStmt<'a>) -> Self {
46 let hir::LetStmt { hir_id, super_: _, pat, ty, span, init, els, source: _ } = *local;
47 Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
48 }
49}
50
51impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> {
52 fn from((let_expr, hir_id): (&'a hir::LetExpr<'a>, HirId)) -> Self {
53 let hir::LetExpr { pat, ty, span, init, recovered: _ } = *let_expr;
54 Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr }
55 }
56}
57
58pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
67 fcx: &'a FnCtxt<'a, 'tcx>,
68 outermost_fn_param_pat: Option<(Span, HirId)>,
72}
73
74impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
78 pub(crate) fn gather_from_local(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::LetStmt<'tcx>) {
79 let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
80 visitor.declare(local.into());
81 visitor.visit_pat(local.pat);
82 }
83
84 pub(crate) fn gather_from_let_expr(
85 fcx: &'a FnCtxt<'a, 'tcx>,
86 let_expr: &'tcx hir::LetExpr<'tcx>,
87 expr_hir_id: hir::HirId,
88 ) {
89 let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
90 visitor.declare((let_expr, expr_hir_id).into());
91 visitor.visit_pat(let_expr.pat);
92 }
93
94 pub(crate) fn gather_from_param(fcx: &'a FnCtxt<'a, 'tcx>, param: &'tcx hir::Param<'tcx>) {
95 let mut visitor = GatherLocalsVisitor {
96 fcx,
97 outermost_fn_param_pat: Some((param.ty_span, param.hir_id)),
98 };
99 visitor.visit_pat(param.pat);
100 }
101
102 pub(crate) fn gather_from_arm(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::Arm<'tcx>) {
103 let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
104 visitor.visit_pat(local.pat);
105 }
106
107 fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
108 if let Some(&local) = self.fcx.locals.borrow_mut().get(&nid) {
114 self.fcx.dcx().span_delayed_bug(span, "evaluated expression more than once");
115 return local;
116 }
117
118 match ty_opt {
119 None => {
120 let var_ty = self.fcx.next_ty_var(span);
122 self.fcx.locals.borrow_mut().insert(nid, var_ty);
123 var_ty
124 }
125 Some(typ) => {
126 self.fcx.locals.borrow_mut().insert(nid, typ);
128 typ
129 }
130 }
131 }
132
133 fn declare(&mut self, decl: Declaration<'tcx>) {
137 let local_ty = match decl.ty {
138 Some(ref ty) => {
139 let o_ty = self.fcx.lower_ty(ty);
140
141 let c_ty = self.fcx.infcx.canonicalize_user_type_annotation(
142 ty::UserType::new_with_bounds(
143 ty::UserTypeKind::Ty(o_ty.raw),
144 self.fcx.collect_impl_trait_clauses_from_hir_ty(ty),
145 ),
146 );
147 debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
148 self.fcx
149 .typeck_results
150 .borrow_mut()
151 .user_provided_types_mut()
152 .insert(ty.hir_id, c_ty);
153
154 Some(o_ty.normalized)
155 }
156 None => None,
157 };
158 self.assign(decl.span, decl.hir_id, local_ty);
159
160 debug!(
161 "local variable {:?} is assigned type {}",
162 decl.pat,
163 self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&decl.hir_id).unwrap())
164 );
165 }
166}
167
168impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
169 fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) {
171 self.declare(local.into());
172 intravisit::walk_local(self, local)
173 }
174
175 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
176 if let hir::ExprKind::Let(let_expr) = expr.kind {
177 self.declare((let_expr, expr.hir_id).into());
178 }
179 intravisit::walk_expr(self, expr)
180 }
181
182 fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
184 if let PatKind::Binding(_, _, ident, _) = p.kind {
185 let var_ty = self.assign(p.span, p.hir_id, None);
186
187 if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
188 if !self.fcx.tcx.features().unsized_fn_params() {
189 self.fcx.require_type_is_sized(
190 var_ty,
191 ty_span,
192 ObligationCauseCode::SizedArgumentType(
195 if ty_span == ident.span
196 && self.fcx.tcx.is_closure_like(self.fcx.body_id.into())
197 {
198 None
199 } else {
200 Some(hir_id)
201 },
202 ),
203 );
204 }
205 } else {
206 self.fcx.require_type_is_sized(
207 var_ty,
208 p.span,
209 ObligationCauseCode::VariableType(p.hir_id),
210 );
211 }
212
213 debug!(
214 "pattern binding {} is assigned to {} with type {:?}",
215 ident,
216 self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&p.hir_id).unwrap()),
217 var_ty
218 );
219 }
220 let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
221 if let PatKind::Guard(subpat, _) = p.kind {
222 self.visit_pat(subpat);
224 } else {
225 intravisit::walk_pat(self, p);
226 }
227 self.outermost_fn_param_pat = old_outermost_fn_param_pat;
228 }
229
230 fn visit_fn(
232 &mut self,
233 _: intravisit::FnKind<'tcx>,
234 _: &'tcx hir::FnDecl<'tcx>,
235 _: hir::BodyId,
236 _: Span,
237 _: LocalDefId,
238 ) {
239 }
240}