]>
Commit | Line | Data |
---|---|---|
2b03887a | 1 | use crate::coercion::CoerceMany; |
9c376795 FG |
2 | use crate::errors::{ |
3 | LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy, | |
4 | }; | |
2b03887a | 5 | use crate::gather_locals::GatherLocalsVisitor; |
487cf647 FG |
6 | use crate::FnCtxt; |
7 | use crate::GeneratorTypes; | |
2b03887a FG |
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; | |
2b03887a FG |
12 | use rustc_hir_analysis::check::fn_maybe_err; |
13 | use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; | |
14 | use rustc_infer::infer::RegionVariableOrigin; | |
9c376795 | 15 | use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; |
2b03887a | 16 | use rustc_span::def_id::LocalDefId; |
9c376795 | 17 | use rustc_target::spec::abi::Abi; |
2b03887a FG |
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) | |
487cf647 | 27 | #[instrument(skip(fcx, body), level = "debug")] |
2b03887a | 28 | pub(super) fn check_fn<'a, 'tcx>( |
487cf647 | 29 | fcx: &mut FnCtxt<'a, 'tcx>, |
2b03887a FG |
30 | fn_sig: ty::FnSig<'tcx>, |
31 | decl: &'tcx hir::FnDecl<'tcx>, | |
487cf647 | 32 | fn_def_id: LocalDefId, |
2b03887a FG |
33 | body: &'tcx hir::Body<'tcx>, |
34 | can_be_generator: Option<hir::Movability>, | |
487cf647 FG |
35 | ) -> Option<GeneratorTypes<'tcx>> { |
36 | let fn_id = fcx.tcx.hir().local_def_id_to_hir_id(fn_def_id); | |
2b03887a FG |
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 | body.value.hir_id, | |
47 | decl.output.span(), | |
487cf647 | 48 | fcx.param_env, |
2b03887a | 49 | )); |
2b03887a FG |
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 | ||
487cf647 FG |
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 | |
2b03887a | 63 | } else { |
487cf647 FG |
64 | tcx.mk_unit() |
65 | }; | |
2b03887a FG |
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(¶m.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 | ||
487cf647 | 106 | fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); |
2b03887a | 107 | |
487cf647 | 108 | if let ty::Dynamic(_, _, ty::Dyn) = declared_ret_ty.kind() { |
2b03887a FG |
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 | } | |
2b03887a FG |
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((body.id(), interior, gen_kind)); | |
134 | ||
135 | let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); | |
136 | Some(GeneratorTypes { | |
137 | resume_ty, | |
138 | yield_ty, | |
139 | interior, | |
140 | movability: can_be_generator.unwrap(), | |
141 | }) | |
142 | } else { | |
143 | None | |
144 | }; | |
145 | ||
146 | // Finalize the return check by taking the LUB of the return types | |
147 | // we saw and assigning it to the expected return type. This isn't | |
148 | // really expected to fail, since the coercions would have failed | |
149 | // earlier when trying to find a LUB. | |
150 | let coercion = fcx.ret_coercion.take().unwrap().into_inner(); | |
151 | let mut actual_return_ty = coercion.complete(&fcx); | |
152 | debug!("actual_return_ty = {:?}", actual_return_ty); | |
153 | if let ty::Dynamic(..) = declared_ret_ty.kind() { | |
154 | // We have special-cased the case where the function is declared | |
155 | // `-> dyn Foo` and we don't actually relate it to the | |
156 | // `fcx.ret_coercion`, so just substitute a type variable. | |
157 | actual_return_ty = | |
158 | fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); | |
159 | debug!("actual_return_ty replaced with {:?}", actual_return_ty); | |
160 | } | |
161 | ||
162 | // HACK(oli-obk, compiler-errors): We should be comparing this against | |
163 | // `declared_ret_ty`, but then anything uninferred would be inferred to | |
164 | // the opaque type itself. That again would cause writeback to assume | |
165 | // we have a recursive call site and do the sadly stabilized fallback to `()`. | |
166 | fcx.demand_suptype(span, ret_ty, actual_return_ty); | |
167 | ||
168 | // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` | |
169 | if let Some(panic_impl_did) = tcx.lang_items().panic_impl() | |
170 | && panic_impl_did == hir.local_def_id(fn_id).to_def_id() | |
171 | { | |
172 | check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); | |
173 | } | |
174 | ||
9c376795 FG |
175 | if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() { |
176 | check_lang_start_fn(tcx, fn_sig, decl, fn_def_id); | |
177 | } | |
178 | ||
487cf647 | 179 | gen_ty |
2b03887a FG |
180 | } |
181 | ||
182 | fn check_panic_info_fn( | |
183 | tcx: TyCtxt<'_>, | |
184 | fn_id: LocalDefId, | |
185 | fn_sig: ty::FnSig<'_>, | |
186 | decl: &hir::FnDecl<'_>, | |
187 | declared_ret_ty: Ty<'_>, | |
188 | ) { | |
189 | let Some(panic_info_did) = tcx.lang_items().panic_info() else { | |
190 | tcx.sess.err("language item required, but not found: `panic_info`"); | |
191 | return; | |
192 | }; | |
193 | ||
194 | if *declared_ret_ty.kind() != ty::Never { | |
195 | tcx.sess.span_err(decl.output.span(), "return type should be `!`"); | |
196 | } | |
197 | ||
198 | let inputs = fn_sig.inputs(); | |
199 | if inputs.len() != 1 { | |
200 | tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); | |
201 | return; | |
202 | } | |
203 | ||
204 | let arg_is_panic_info = match *inputs[0].kind() { | |
205 | ty::Ref(region, ty, mutbl) => match *ty.kind() { | |
206 | ty::Adt(ref adt, _) => { | |
487cf647 | 207 | adt.did() == panic_info_did && mutbl.is_not() && !region.is_static() |
2b03887a FG |
208 | } |
209 | _ => false, | |
210 | }, | |
211 | _ => false, | |
212 | }; | |
213 | ||
214 | if !arg_is_panic_info { | |
215 | tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); | |
216 | } | |
217 | ||
218 | let DefKind::Fn = tcx.def_kind(fn_id) else { | |
219 | let span = tcx.def_span(fn_id); | |
220 | tcx.sess.span_err(span, "should be a function"); | |
221 | return; | |
222 | }; | |
223 | ||
224 | let generic_counts = tcx.generics_of(fn_id).own_counts(); | |
225 | if generic_counts.types != 0 { | |
226 | let span = tcx.def_span(fn_id); | |
227 | tcx.sess.span_err(span, "should have no type parameters"); | |
228 | } | |
229 | if generic_counts.consts != 0 { | |
230 | let span = tcx.def_span(fn_id); | |
231 | tcx.sess.span_err(span, "should have no const parameters"); | |
232 | } | |
233 | } | |
9c376795 FG |
234 | |
235 | fn check_lang_start_fn<'tcx>( | |
236 | tcx: TyCtxt<'tcx>, | |
237 | fn_sig: ty::FnSig<'tcx>, | |
238 | decl: &'tcx hir::FnDecl<'tcx>, | |
239 | def_id: LocalDefId, | |
240 | ) { | |
241 | let inputs = fn_sig.inputs(); | |
242 | ||
243 | let arg_count = inputs.len(); | |
244 | if arg_count != 4 { | |
245 | tcx.sess.emit_err(LangStartIncorrectNumberArgs { | |
246 | params_span: tcx.def_span(def_id), | |
247 | found_param_count: arg_count, | |
248 | }); | |
249 | } | |
250 | ||
251 | // only check args if they should exist by checking the count | |
252 | // note: this does not handle args being shifted or their order swapped very nicely | |
253 | // but it's a lang item, users shouldn't frequently encounter this | |
254 | ||
255 | // first arg is `main: fn() -> T` | |
256 | if let Some(&main_arg) = inputs.get(0) { | |
257 | // make a Ty for the generic on the fn for diagnostics | |
258 | // FIXME: make the lang item generic checks check for the right generic *kind* | |
259 | // for example `start`'s generic should be a type parameter | |
260 | let generics = tcx.generics_of(def_id); | |
261 | let fn_generic = generics.param_at(0, tcx); | |
262 | let generic_tykind = | |
263 | ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name }); | |
264 | let generic_ty = tcx.mk_ty(generic_tykind); | |
265 | let expected_fn_sig = | |
266 | tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust); | |
267 | let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig)); | |
268 | ||
269 | // we emit the same error to suggest changing the arg no matter what's wrong with the arg | |
270 | let emit_main_fn_arg_err = || { | |
271 | tcx.sess.emit_err(LangStartIncorrectParam { | |
272 | param_span: decl.inputs[0].span, | |
273 | param_num: 1, | |
274 | expected_ty: expected_ty, | |
275 | found_ty: main_arg, | |
276 | }); | |
277 | }; | |
278 | ||
279 | if let ty::FnPtr(main_fn_sig) = main_arg.kind() { | |
280 | let main_fn_inputs = main_fn_sig.inputs(); | |
281 | if main_fn_inputs.iter().count() != 0 { | |
282 | emit_main_fn_arg_err(); | |
283 | } | |
284 | ||
285 | let output = main_fn_sig.output(); | |
286 | output.map_bound(|ret_ty| { | |
287 | // if the output ty is a generic, it's probably the right one | |
288 | if !matches!(ret_ty.kind(), ty::Param(_)) { | |
289 | emit_main_fn_arg_err(); | |
290 | } | |
291 | }); | |
292 | } else { | |
293 | emit_main_fn_arg_err(); | |
294 | } | |
295 | } | |
296 | ||
297 | // second arg is isize | |
298 | if let Some(&argc_arg) = inputs.get(1) { | |
299 | if argc_arg != tcx.types.isize { | |
300 | tcx.sess.emit_err(LangStartIncorrectParam { | |
301 | param_span: decl.inputs[1].span, | |
302 | param_num: 2, | |
303 | expected_ty: tcx.types.isize, | |
304 | found_ty: argc_arg, | |
305 | }); | |
306 | } | |
307 | } | |
308 | ||
309 | // third arg is `*const *const u8` | |
310 | if let Some(&argv_arg) = inputs.get(2) { | |
311 | let mut argv_is_okay = false; | |
312 | if let ty::RawPtr(outer_ptr) = argv_arg.kind() { | |
313 | if outer_ptr.mutbl.is_not() { | |
314 | if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() { | |
315 | if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 { | |
316 | argv_is_okay = true; | |
317 | } | |
318 | } | |
319 | } | |
320 | } | |
321 | ||
322 | if !argv_is_okay { | |
323 | let inner_ptr_ty = | |
324 | tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 }); | |
325 | let expected_ty = | |
326 | tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty }); | |
327 | tcx.sess.emit_err(LangStartIncorrectParam { | |
328 | param_span: decl.inputs[2].span, | |
329 | param_num: 3, | |
330 | expected_ty, | |
331 | found_ty: argv_arg, | |
332 | }); | |
333 | } | |
334 | } | |
335 | ||
336 | // fourth arg is `sigpipe: u8` | |
337 | if let Some(&sigpipe_arg) = inputs.get(3) { | |
338 | if sigpipe_arg != tcx.types.u8 { | |
339 | tcx.sess.emit_err(LangStartIncorrectParam { | |
340 | param_span: decl.inputs[3].span, | |
341 | param_num: 4, | |
342 | expected_ty: tcx.types.u8, | |
343 | found_ty: sigpipe_arg, | |
344 | }); | |
345 | } | |
346 | } | |
347 | ||
348 | // output type is isize | |
349 | if fn_sig.output() != tcx.types.isize { | |
350 | tcx.sess.emit_err(LangStartIncorrectRetTy { | |
351 | ret_span: decl.output.span(), | |
352 | expected_ty: tcx.types.isize, | |
353 | found_ty: fn_sig.output(), | |
354 | }); | |
355 | } | |
356 | } |