1 use crate::{FnCtxt, LocalTy};
3 use rustc_hir::intravisit::{self, Visitor};
4 use rustc_hir::PatKind;
5 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
6 use rustc_middle::ty::Ty;
7 use rustc_middle::ty::UserType;
9 use rustc_trait_selection::traits;
11 /// A declaration is an abstraction of [hir::Local] and [hir::Let].
13 /// It must have a hir_id, as this is how we connect gather_locals to the check functions.
14 pub(super) struct Declaration<'a> {
15 pub hir_id: hir::HirId,
16 pub pat: &'a hir::Pat<'a>,
17 pub ty: Option<&'a hir::Ty<'a>>,
19 pub init: Option<&'a hir::Expr<'a>>,
20 pub els: Option<&'a hir::Block<'a>>,
23 impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
24 fn from(local: &'a hir::Local<'a>) -> Self {
25 let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local;
26 Declaration { hir_id, pat, ty, span, init, els }
30 impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
31 fn from(let_expr: &'a hir::Let<'a>) -> Self {
32 let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
33 Declaration { hir_id, pat, ty, span, init: Some(init), els: None }
37 pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
38 fcx: &'a FnCtxt<'a, 'tcx>,
39 // parameters are special cases of patterns, but we want to handle them as
40 // *distinct* cases. so track when we are hitting a pattern *within* an fn
42 outermost_fn_param_pat: Option<Span>,
45 impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
46 pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
47 Self { fcx, outermost_fn_param_pat: None }
50 fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
53 // Infer the variable's type.
54 let var_ty = self.fcx.next_ty_var(TypeVariableOrigin {
55 kind: TypeVariableOriginKind::TypeInference,
61 .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
65 // Take type that the user specified.
66 self.fcx.locals.borrow_mut().insert(nid, typ);
72 /// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have
73 /// a type annotation, then the LocalTy stored will be the resolved type. This may be found
74 /// again during type checking by querying [FnCtxt::local_ty] for the same hir_id.
75 fn declare(&mut self, decl: Declaration<'tcx>) {
76 let local_ty = match decl.ty {
78 let o_ty = self.fcx.to_ty(&ty);
80 let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty));
81 debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty);
85 .user_provided_types_mut()
86 .insert(ty.hir_id, c_ty);
88 Some(LocalTy { decl_ty: o_ty, revealed_ty: o_ty })
92 self.assign(decl.span, decl.hir_id, local_ty);
95 "local variable {:?} is assigned type {}",
97 self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
102 impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
103 // Add explicitly-declared locals.
104 fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
105 self.declare(local.into());
106 intravisit::walk_local(self, local)
109 fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) {
110 self.declare(let_expr.into());
111 intravisit::walk_let_expr(self, let_expr);
114 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
115 let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
116 intravisit::walk_param(self, param);
117 self.outermost_fn_param_pat = old_outermost_fn_param_pat;
120 // Add pattern bindings.
121 fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
122 if let PatKind::Binding(_, _, ident, _) = p.kind {
123 let var_ty = self.assign(p.span, p.hir_id, None);
125 if let Some(ty_span) = self.outermost_fn_param_pat {
126 if !self.fcx.tcx.features().unsized_fn_params {
127 self.fcx.require_type_is_sized(
130 traits::SizedArgumentType(Some(ty_span)),
134 if !self.fcx.tcx.features().unsized_locals {
135 self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id));
140 "pattern binding {} is assigned to {} with type {:?}",
142 self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
146 let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
147 intravisit::walk_pat(self, p);
148 self.outermost_fn_param_pat = old_outermost_fn_param_pat;
151 // Don't descend into the bodies of nested closures.
154 _: intravisit::FnKind<'tcx>,
155 _: &'tcx hir::FnDecl<'tcx>,