]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/check.rs
Rollup merge of #107770 - notriddle:notriddle/br2nl, r=GuillaumeGomez
[rust.git] / compiler / rustc_hir_typeck / src / check.rs
1 use crate::coercion::CoerceMany;
2 use crate::errors::{
3     LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy,
4 };
5 use crate::gather_locals::GatherLocalsVisitor;
6 use crate::FnCtxt;
7 use crate::GeneratorTypes;
8 use rustc_hir as hir;
9 use rustc_hir::def::DefKind;
10 use rustc_hir::intravisit::Visitor;
11 use rustc_hir::lang_items::LangItem;
12 use rustc_hir_analysis::check::fn_maybe_err;
13 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
14 use rustc_infer::infer::RegionVariableOrigin;
15 use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
16 use rustc_span::def_id::LocalDefId;
17 use rustc_target::spec::abi::Abi;
18 use rustc_trait_selection::traits;
19 use std::cell::RefCell;
20
21 /// Helper used for fns and closures. Does the grungy work of checking a function
22 /// body and returns the function context used for that purpose, since in the case of a fn item
23 /// there is still a bit more to do.
24 ///
25 /// * ...
26 /// * inherited: other fields inherited from the enclosing fn (if any)
27 #[instrument(skip(fcx, body), level = "debug")]
28 pub(super) fn check_fn<'a, 'tcx>(
29     fcx: &mut FnCtxt<'a, 'tcx>,
30     fn_sig: ty::FnSig<'tcx>,
31     decl: &'tcx hir::FnDecl<'tcx>,
32     fn_def_id: LocalDefId,
33     body: &'tcx hir::Body<'tcx>,
34     can_be_generator: Option<hir::Movability>,
35 ) -> Option<GeneratorTypes<'tcx>> {
36     let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
37
38     let tcx = fcx.tcx;
39     let hir = tcx.hir();
40
41     let declared_ret_ty = fn_sig.output();
42
43     let ret_ty =
44         fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(
45             declared_ret_ty,
46             fn_def_id,
47             decl.output.span(),
48             fcx.param_env,
49         ));
50
51     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
52
53     let span = body.value.span;
54
55     fn_maybe_err(tcx, span, fn_sig.abi);
56
57     if let Some(kind) = body.generator_kind && can_be_generator.is_some() {
58         let yield_ty = if kind == hir::GeneratorKind::Gen {
59             let yield_ty = fcx
60                 .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
61             fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
62             yield_ty
63         } else {
64             tcx.mk_unit()
65         };
66
67         // Resume type defaults to `()` if the generator has no argument.
68         let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());
69
70         fcx.resume_yield_tys = Some((resume_ty, yield_ty));
71     }
72
73     GatherLocalsVisitor::new(&fcx).visit_body(body);
74
75     // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
76     // (as it's created inside the body itself, not passed in from outside).
77     let maybe_va_list = if fn_sig.c_variadic {
78         let span = body.params.last().unwrap().span;
79         let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span));
80         let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span));
81
82         Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()]))
83     } else {
84         None
85     };
86
87     // Add formal parameters.
88     let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs);
89     let inputs_fn = fn_sig.inputs().iter().copied();
90     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
91         // Check the pattern.
92         let ty_span = try { inputs_hir?.get(idx)?.span };
93         fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
94
95         // Check that argument is Sized.
96         // The check for a non-trivial pattern is a hack to avoid duplicate warnings
97         // for simple cases like `fn foo(x: Trait)`,
98         // where we would error once on the parameter as a whole, and once on the binding `x`.
99         if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params {
100             fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
101         }
102
103         fcx.write_ty(param.hir_id, param_ty);
104     }
105
106     fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
107
108     if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() {
109         // FIXME: We need to verify that the return type is `Sized` after the return expression has
110         // been evaluated so that we have types available for all the nodes being returned, but that
111         // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this
112         // causes unsized errors caused by the `declared_ret_ty` to point at the return expression,
113         // while keeping the current ordering we will ignore the tail expression's type because we
114         // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr`
115         // because we will trigger "unreachable expression" lints unconditionally.
116         // Because of all of this, we perform a crude check to know whether the simplest `!Sized`
117         // case that a newcomer might make, returning a bare trait, and in that case we populate
118         // the tail expression's type so that the suggestion will be correct, but ignore all other
119         // possible cases.
120         fcx.check_expr(&body.value);
121         fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
122     } else {
123         fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
124         fcx.check_return_expr(&body.value, false);
125     }
126
127     // We insert the deferred_generator_interiors entry after visiting the body.
128     // This ensures that all nested generators appear before the entry of this generator.
129     // resolve_generator_interiors relies on this property.
130     let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
131         let interior = fcx
132             .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
133         fcx.deferred_generator_interiors.borrow_mut().push((
134             fn_def_id,
135             body.id(),
136             interior,
137             gen_kind,
138         ));
139
140         let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
141         Some(GeneratorTypes {
142             resume_ty,
143             yield_ty,
144             interior,
145             movability: can_be_generator.unwrap(),
146         })
147     } else {
148         None
149     };
150
151     // Finalize the return check by taking the LUB of the return types
152     // we saw and assigning it to the expected return type. This isn't
153     // really expected to fail, since the coercions would have failed
154     // earlier when trying to find a LUB.
155     let coercion = fcx.ret_coercion.take().unwrap().into_inner();
156     let mut actual_return_ty = coercion.complete(&fcx);
157     debug!("actual_return_ty = {:?}", actual_return_ty);
158     if let ty::Dynamic(..) = declared_ret_ty.kind() {
159         // We have special-cased the case where the function is declared
160         // `-> dyn Foo` and we don't actually relate it to the
161         // `fcx.ret_coercion`, so just substitute a type variable.
162         actual_return_ty =
163             fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span });
164         debug!("actual_return_ty replaced with {:?}", actual_return_ty);
165     }
166
167     // HACK(oli-obk, compiler-errors): We should be comparing this against
168     // `declared_ret_ty`, but then anything uninferred would be inferred to
169     // the opaque type itself. That again would cause writeback to assume
170     // we have a recursive call site and do the sadly stabilized fallback to `()`.
171     fcx.demand_suptype(span, ret_ty, actual_return_ty);
172
173     // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
174     if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
175         && panic_impl_did == fn_def_id.to_def_id()
176     {
177         check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
178     }
179
180     if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == fn_def_id.to_def_id() {
181         check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
182     }
183
184     gen_ty
185 }
186
187 fn check_panic_info_fn(
188     tcx: TyCtxt<'_>,
189     fn_id: LocalDefId,
190     fn_sig: ty::FnSig<'_>,
191     decl: &hir::FnDecl<'_>,
192     declared_ret_ty: Ty<'_>,
193 ) {
194     let Some(panic_info_did) = tcx.lang_items().panic_info() else {
195         tcx.sess.err("language item required, but not found: `panic_info`");
196         return;
197     };
198
199     if *declared_ret_ty.kind() != ty::Never {
200         tcx.sess.span_err(decl.output.span(), "return type should be `!`");
201     }
202
203     let inputs = fn_sig.inputs();
204     if inputs.len() != 1 {
205         tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument");
206         return;
207     }
208
209     let arg_is_panic_info = match *inputs[0].kind() {
210         ty::Ref(region, ty, mutbl) => match *ty.kind() {
211             ty::Adt(ref adt, _) => {
212                 adt.did() == panic_info_did && mutbl.is_not() && !region.is_static()
213             }
214             _ => false,
215         },
216         _ => false,
217     };
218
219     if !arg_is_panic_info {
220         tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`");
221     }
222
223     let DefKind::Fn = tcx.def_kind(fn_id) else {
224         let span = tcx.def_span(fn_id);
225         tcx.sess.span_err(span, "should be a function");
226         return;
227     };
228
229     let generic_counts = tcx.generics_of(fn_id).own_counts();
230     if generic_counts.types != 0 {
231         let span = tcx.def_span(fn_id);
232         tcx.sess.span_err(span, "should have no type parameters");
233     }
234     if generic_counts.consts != 0 {
235         let span = tcx.def_span(fn_id);
236         tcx.sess.span_err(span, "should have no const parameters");
237     }
238 }
239
240 fn check_lang_start_fn<'tcx>(
241     tcx: TyCtxt<'tcx>,
242     fn_sig: ty::FnSig<'tcx>,
243     decl: &'tcx hir::FnDecl<'tcx>,
244     def_id: LocalDefId,
245 ) {
246     let inputs = fn_sig.inputs();
247
248     let arg_count = inputs.len();
249     if arg_count != 4 {
250         tcx.sess.emit_err(LangStartIncorrectNumberArgs {
251             params_span: tcx.def_span(def_id),
252             found_param_count: arg_count,
253         });
254     }
255
256     // only check args if they should exist by checking the count
257     // note: this does not handle args being shifted or their order swapped very nicely
258     // but it's a lang item, users shouldn't frequently encounter this
259
260     // first arg is `main: fn() -> T`
261     if let Some(&main_arg) = inputs.get(0) {
262         // make a Ty for the generic on the fn for diagnostics
263         // FIXME: make the lang item generic checks check for the right generic *kind*
264         // for example `start`'s generic should be a type parameter
265         let generics = tcx.generics_of(def_id);
266         let fn_generic = generics.param_at(0, tcx);
267         let generic_tykind =
268             ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name });
269         let generic_ty = tcx.mk_ty(generic_tykind);
270         let expected_fn_sig =
271             tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
272         let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
273
274         // we emit the same error to suggest changing the arg no matter what's wrong with the arg
275         let emit_main_fn_arg_err = || {
276             tcx.sess.emit_err(LangStartIncorrectParam {
277                 param_span: decl.inputs[0].span,
278                 param_num: 1,
279                 expected_ty: expected_ty,
280                 found_ty: main_arg,
281             });
282         };
283
284         if let ty::FnPtr(main_fn_sig) = main_arg.kind() {
285             let main_fn_inputs = main_fn_sig.inputs();
286             if main_fn_inputs.iter().count() != 0 {
287                 emit_main_fn_arg_err();
288             }
289
290             let output = main_fn_sig.output();
291             output.map_bound(|ret_ty| {
292                 // if the output ty is a generic, it's probably the right one
293                 if !matches!(ret_ty.kind(), ty::Param(_)) {
294                     emit_main_fn_arg_err();
295                 }
296             });
297         } else {
298             emit_main_fn_arg_err();
299         }
300     }
301
302     // second arg is isize
303     if let Some(&argc_arg) = inputs.get(1) {
304         if argc_arg != tcx.types.isize {
305             tcx.sess.emit_err(LangStartIncorrectParam {
306                 param_span: decl.inputs[1].span,
307                 param_num: 2,
308                 expected_ty: tcx.types.isize,
309                 found_ty: argc_arg,
310             });
311         }
312     }
313
314     // third arg is `*const *const u8`
315     if let Some(&argv_arg) = inputs.get(2) {
316         let mut argv_is_okay = false;
317         if let ty::RawPtr(outer_ptr) = argv_arg.kind() {
318             if outer_ptr.mutbl.is_not() {
319                 if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() {
320                     if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 {
321                         argv_is_okay = true;
322                     }
323                 }
324             }
325         }
326
327         if !argv_is_okay {
328             let inner_ptr_ty =
329                 tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
330             let expected_ty =
331                 tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
332             tcx.sess.emit_err(LangStartIncorrectParam {
333                 param_span: decl.inputs[2].span,
334                 param_num: 3,
335                 expected_ty,
336                 found_ty: argv_arg,
337             });
338         }
339     }
340
341     // fourth arg is `sigpipe: u8`
342     if let Some(&sigpipe_arg) = inputs.get(3) {
343         if sigpipe_arg != tcx.types.u8 {
344             tcx.sess.emit_err(LangStartIncorrectParam {
345                 param_span: decl.inputs[3].span,
346                 param_num: 4,
347                 expected_ty: tcx.types.u8,
348                 found_ty: sigpipe_arg,
349             });
350         }
351     }
352
353     // output type is isize
354     if fn_sig.output() != tcx.types.isize {
355         tcx.sess.emit_err(LangStartIncorrectRetTy {
356             ret_span: decl.output.span(),
357             expected_ty: tcx.types.isize,
358             found_ty: fn_sig.output(),
359         });
360     }
361 }