]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/check.rs
Rollup merge of #104797 - weihanglo:stream-write-dwp, r=jackh726
[rust.git] / compiler / rustc_hir_typeck / src / check.rs
1 use crate::coercion::CoerceMany;
2 use crate::gather_locals::GatherLocalsVisitor;
3 use crate::{FnCtxt, Inherited};
4 use crate::{GeneratorTypes, UnsafetyState};
5 use rustc_hir as hir;
6 use rustc_hir::def::DefKind;
7 use rustc_hir::intravisit::Visitor;
8 use rustc_hir::lang_items::LangItem;
9 use rustc_hir_analysis::check::fn_maybe_err;
10 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
11 use rustc_infer::infer::RegionVariableOrigin;
12 use rustc_middle::ty::{self, Ty, TyCtxt};
13 use rustc_span::def_id::LocalDefId;
14 use rustc_trait_selection::traits;
15 use std::cell::RefCell;
16
17 /// Helper used for fns and closures. Does the grungy work of checking a function
18 /// body and returns the function context used for that purpose, since in the case of a fn item
19 /// there is still a bit more to do.
20 ///
21 /// * ...
22 /// * inherited: other fields inherited from the enclosing fn (if any)
23 #[instrument(skip(inherited, body), level = "debug")]
24 pub(super) fn check_fn<'a, 'tcx>(
25     inherited: &'a Inherited<'tcx>,
26     param_env: ty::ParamEnv<'tcx>,
27     fn_sig: ty::FnSig<'tcx>,
28     decl: &'tcx hir::FnDecl<'tcx>,
29     fn_def_id: LocalDefId,
30     body: &'tcx hir::Body<'tcx>,
31     can_be_generator: Option<hir::Movability>,
32 ) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
33     let fn_id = inherited.tcx.hir().local_def_id_to_hir_id(fn_def_id);
34
35     // Create the function context. This is either derived from scratch or,
36     // in the case of closures, based on the outer context.
37     let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
38     fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id));
39
40     let tcx = fcx.tcx;
41     let hir = tcx.hir();
42
43     let declared_ret_ty = fn_sig.output();
44
45     let ret_ty =
46         fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(
47             declared_ret_ty,
48             body.value.hir_id,
49             decl.output.span(),
50             param_env,
51         ));
52
53     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
54
55     let span = body.value.span;
56
57     fn_maybe_err(tcx, span, fn_sig.abi);
58
59     if let Some(kind) = body.generator_kind && can_be_generator.is_some() {
60         let yield_ty = if kind == hir::GeneratorKind::Gen {
61             let yield_ty = fcx
62                 .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
63             fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
64             yield_ty
65         } else {
66             tcx.mk_unit()
67         };
68
69         // Resume type defaults to `()` if the generator has no argument.
70         let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());
71
72         fcx.resume_yield_tys = Some((resume_ty, yield_ty));
73     }
74
75     GatherLocalsVisitor::new(&fcx).visit_body(body);
76
77     // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
78     // (as it's created inside the body itself, not passed in from outside).
79     let maybe_va_list = if fn_sig.c_variadic {
80         let span = body.params.last().unwrap().span;
81         let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
82         let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
83
84         Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()]))
85     } else {
86         None
87     };
88
89     // Add formal parameters.
90     let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
91     let inputs_fn = fn_sig.inputs().iter().copied();
92     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
93         // Check the pattern.
94         let ty_span = try { inputs_hir?.get(idx)?.span };
95         fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
96
97         // Check that argument is Sized.
98         // The check for a non-trivial pattern is a hack to avoid duplicate warnings
99         // for simple cases like `fn foo(x: Trait)`,
100         // where we would error once on the parameter as a whole, and once on the binding `x`.
101         if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params {
102             fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
103         }
104
105         fcx.write_ty(param.hir_id, param_ty);
106     }
107
108     inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
109
110     if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() {
111         // FIXME: We need to verify that the return type is `Sized` after the return expression has
112         // been evaluated so that we have types available for all the nodes being returned, but that
113         // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this
114         // causes unsized errors caused by the `declared_ret_ty` to point at the return expression,
115         // while keeping the current ordering we will ignore the tail expression's type because we
116         // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr`
117         // because we will trigger "unreachable expression" lints unconditionally.
118         // Because of all of this, we perform a crude check to know whether the simplest `!Sized`
119         // case that a newcomer might make, returning a bare trait, and in that case we populate
120         // the tail expression's type so that the suggestion will be correct, but ignore all other
121         // possible cases.
122         fcx.check_expr(&body.value);
123         fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
124     } else {
125         fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
126         fcx.check_return_expr(&body.value, false);
127     }
128
129     // We insert the deferred_generator_interiors entry after visiting the body.
130     // This ensures that all nested generators appear before the entry of this generator.
131     // resolve_generator_interiors relies on this property.
132     let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
133         let interior = fcx
134             .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
135         fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
136
137         let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
138         Some(GeneratorTypes {
139             resume_ty,
140             yield_ty,
141             interior,
142             movability: can_be_generator.unwrap(),
143         })
144     } else {
145         None
146     };
147
148     // Finalize the return check by taking the LUB of the return types
149     // we saw and assigning it to the expected return type. This isn't
150     // really expected to fail, since the coercions would have failed
151     // earlier when trying to find a LUB.
152     let coercion = fcx.ret_coercion.take().unwrap().into_inner();
153     let mut actual_return_ty = coercion.complete(&fcx);
154     debug!("actual_return_ty = {:?}", actual_return_ty);
155     if let ty::Dynamic(..) = declared_ret_ty.kind() {
156         // We have special-cased the case where the function is declared
157         // `-> dyn Foo` and we don't actually relate it to the
158         // `fcx.ret_coercion`, so just substitute a type variable.
159         actual_return_ty =
160             fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span });
161         debug!("actual_return_ty replaced with {:?}", actual_return_ty);
162     }
163
164     // HACK(oli-obk, compiler-errors): We should be comparing this against
165     // `declared_ret_ty`, but then anything uninferred would be inferred to
166     // the opaque type itself. That again would cause writeback to assume
167     // we have a recursive call site and do the sadly stabilized fallback to `()`.
168     fcx.demand_suptype(span, ret_ty, actual_return_ty);
169
170     // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
171     if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
172         && panic_impl_did == hir.local_def_id(fn_id).to_def_id()
173     {
174         check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
175     }
176
177     (fcx, gen_ty)
178 }
179
180 fn check_panic_info_fn(
181     tcx: TyCtxt<'_>,
182     fn_id: LocalDefId,
183     fn_sig: ty::FnSig<'_>,
184     decl: &hir::FnDecl<'_>,
185     declared_ret_ty: Ty<'_>,
186 ) {
187     let Some(panic_info_did) = tcx.lang_items().panic_info() else {
188         tcx.sess.err("language item required, but not found: `panic_info`");
189         return;
190     };
191
192     if *declared_ret_ty.kind() != ty::Never {
193         tcx.sess.span_err(decl.output.span(), "return type should be `!`");
194     }
195
196     let inputs = fn_sig.inputs();
197     if inputs.len() != 1 {
198         tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument");
199         return;
200     }
201
202     let arg_is_panic_info = match *inputs[0].kind() {
203         ty::Ref(region, ty, mutbl) => match *ty.kind() {
204             ty::Adt(ref adt, _) => {
205                 adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static()
206             }
207             _ => false,
208         },
209         _ => false,
210     };
211
212     if !arg_is_panic_info {
213         tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
214     }
215
216     let DefKind::Fn = tcx.def_kind(fn_id) else {
217         let span = tcx.def_span(fn_id);
218         tcx.sess.span_err(span, "should be a function");
219         return;
220     };
221
222     let generic_counts = tcx.generics_of(fn_id).own_counts();
223     if generic_counts.types != 0 {
224         let span = tcx.def_span(fn_id);
225         tcx.sess.span_err(span, "should have no type parameters");
226     }
227     if generic_counts.consts != 0 {
228         let span = tcx.def_span(fn_id);
229         tcx.sess.span_err(span, "should have no const parameters");
230     }
231 }