rustc_hir_analysis/check/
compare_eii.rs1use std::borrow::Cow;
7use std::iter;
8
9use rustc_data_structures::fx::FxIndexSet;
10use rustc_errors::{Applicability, E0806, struct_span_code_err};
11use rustc_hir::def_id::{DefId, LocalDefId};
12use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
13use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
14use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
15use rustc_middle::ty::error::{ExpectedFound, TypeError};
16use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
17use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
18use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
19use rustc_trait_selection::regions::InferCtxtRegionExt;
20use rustc_trait_selection::traits::{self, ObligationCtxt};
21use tracing::{debug, instrument};
22
23use super::potentially_plural_count;
24use crate::check::compare_impl_item::{
25 CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions,
26};
27use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii};
28
29pub(crate) fn compare_eii_function_types<'tcx>(
33 tcx: TyCtxt<'tcx>,
34 external_impl: LocalDefId,
35 declaration: DefId,
36 eii_name: Symbol,
37 eii_attr_span: Span,
38) -> Result<(), ErrorGuaranteed> {
39 check_is_structurally_compatible(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
40
41 let external_impl_span = tcx.def_span(external_impl);
42 let cause = ObligationCause::new(
43 external_impl_span,
44 external_impl,
45 ObligationCauseCode::CompareEii { external_impl, declaration },
46 );
47
48 let param_env = tcx.param_env(declaration);
50
51 let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis());
52 let ocx = ObligationCtxt::new_with_diagnostics(infcx);
53
54 let mut wf_tys = FxIndexSet::default();
63 let norm_cause = ObligationCause::misc(external_impl_span, external_impl);
64
65 let declaration_sig = tcx.fn_sig(declaration).instantiate_identity();
66 let declaration_sig = tcx.liberate_late_bound_regions(external_impl.into(), declaration_sig);
67 debug!(?declaration_sig);
68
69 let unnormalized_external_impl_sig = infcx.instantiate_binder_with_fresh_vars(
70 external_impl_span,
71 infer::BoundRegionConversionTime::HigherRankedType,
72 tcx.fn_sig(external_impl).instantiate(
73 tcx,
74 infcx.fresh_args_for_item(external_impl_span, external_impl.to_def_id()),
75 ),
76 );
77 let external_impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_external_impl_sig);
78 debug!(?external_impl_sig);
79
80 wf_tys.extend(declaration_sig.inputs_and_output.iter());
84 let declaration_sig = ocx.normalize(&norm_cause, param_env, declaration_sig);
85 wf_tys.extend(external_impl_sig.inputs_and_output.iter());
88
89 let result = ocx.sup(&cause, param_env, declaration_sig, external_impl_sig);
97
98 if let Err(terr) = result {
99 debug!(?external_impl_sig, ?declaration_sig, ?terr, "sub_types failed");
100
101 let emitted = report_eii_mismatch(
102 infcx,
103 cause,
104 param_env,
105 terr,
106 (declaration, declaration_sig),
107 (external_impl, external_impl_sig),
108 eii_attr_span,
109 eii_name,
110 );
111 return Err(emitted);
112 }
113
114 if !(declaration_sig, external_impl_sig).references_error() {
115 for ty in unnormalized_external_impl_sig.inputs_and_output {
116 ocx.register_obligation(traits::Obligation::new(
117 infcx.tcx,
118 cause.clone(),
119 param_env,
120 ty::ClauseKind::WellFormed(ty.into()),
121 ));
122 }
123 }
124
125 let errors = ocx.evaluate_obligations_error_on_ambiguity();
128 if !errors.is_empty() {
129 let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
130 return Err(reported);
131 }
132
133 let errors = infcx.resolve_regions(external_impl, param_env, wf_tys);
136 if !errors.is_empty() {
137 return Err(infcx
138 .tainted_by_errors()
139 .unwrap_or_else(|| infcx.err_ctxt().report_region_errors(external_impl, &errors)));
140 }
141
142 Ok(())
143}
144
145fn check_is_structurally_compatible<'tcx>(
151 tcx: TyCtxt<'tcx>,
152 external_impl: LocalDefId,
153 declaration: DefId,
154 eii_name: Symbol,
155 eii_attr_span: Span,
156) -> Result<(), ErrorGuaranteed> {
157 check_no_generics(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
158 check_number_of_arguments(tcx, external_impl, declaration, eii_name, eii_attr_span)?;
159 check_early_region_bounds(tcx, external_impl, declaration, eii_attr_span)?;
160 Ok(())
161}
162
163fn check_no_generics<'tcx>(
165 tcx: TyCtxt<'tcx>,
166 external_impl: LocalDefId,
167 _declaration: DefId,
168 eii_name: Symbol,
169 eii_attr_span: Span,
170) -> Result<(), ErrorGuaranteed> {
171 let generics = tcx.generics_of(external_impl);
172 if generics.own_requires_monomorphization() {
173 tcx.dcx().emit_err(EiiWithGenerics {
174 span: tcx.def_span(external_impl),
175 attr: eii_attr_span,
176 eii_name,
177 });
178 }
179
180 Ok(())
181}
182
183fn check_early_region_bounds<'tcx>(
184 tcx: TyCtxt<'tcx>,
185 external_impl: LocalDefId,
186 declaration: DefId,
187 eii_attr_span: Span,
188) -> Result<(), ErrorGuaranteed> {
189 let external_impl_generics = tcx.generics_of(external_impl.to_def_id());
190 let external_impl_params = external_impl_generics.own_counts().lifetimes;
191
192 let declaration_generics = tcx.generics_of(declaration);
193 let declaration_params = declaration_generics.own_counts().lifetimes;
194
195 let Err(CheckNumberOfEarlyBoundRegionsError { span, generics_span, bounds_span, where_span }) =
196 check_number_of_early_bound_regions(
197 tcx,
198 external_impl,
199 declaration,
200 external_impl_generics,
201 external_impl_params,
202 declaration_generics,
203 declaration_params,
204 )
205 else {
206 return Ok(());
207 };
208
209 let mut diag = tcx.dcx().create_err(LifetimesOrBoundsMismatchOnEii {
210 span,
211 ident: tcx.item_name(external_impl.to_def_id()),
212 generics_span,
213 bounds_span,
214 where_span,
215 });
216
217 diag.span_label(eii_attr_span, format!("required because of this attribute"));
218 return Err(diag.emit());
219}
220
221fn check_number_of_arguments<'tcx>(
222 tcx: TyCtxt<'tcx>,
223 external_impl: LocalDefId,
224 declaration: DefId,
225 eii_name: Symbol,
226 eii_attr_span: Span,
227) -> Result<(), ErrorGuaranteed> {
228 let external_impl_fty = tcx.fn_sig(external_impl);
229 let declaration_fty = tcx.fn_sig(declaration);
230 let declaration_number_args = declaration_fty.skip_binder().inputs().skip_binder().len();
231 let external_impl_number_args = external_impl_fty.skip_binder().inputs().skip_binder().len();
232
233 if declaration_number_args == external_impl_number_args {
235 Ok(())
236 } else {
237 Err(report_number_of_arguments_mismatch(
238 tcx,
239 external_impl,
240 declaration,
241 eii_name,
242 eii_attr_span,
243 declaration_number_args,
244 external_impl_number_args,
245 ))
246 }
247}
248
249fn report_number_of_arguments_mismatch<'tcx>(
250 tcx: TyCtxt<'tcx>,
251 external_impl: LocalDefId,
252 declaration: DefId,
253 eii_name: Symbol,
254 eii_attr_span: Span,
255 declaration_number_args: usize,
256 external_impl_number_args: usize,
257) -> ErrorGuaranteed {
258 let external_impl_name = tcx.item_name(external_impl.to_def_id());
259
260 let declaration_span = declaration
261 .as_local()
262 .and_then(|def_id| {
263 let declaration_sig = get_declaration_sig(tcx, def_id).expect("foreign item sig");
264 let pos = declaration_number_args.saturating_sub(1);
265 declaration_sig.decl.inputs.get(pos).map(|arg| {
266 if pos == 0 {
267 arg.span
268 } else {
269 arg.span.with_lo(declaration_sig.decl.inputs[0].span.lo())
270 }
271 })
272 })
273 .or_else(|| tcx.hir_span_if_local(declaration))
274 .unwrap_or_else(|| tcx.def_span(declaration));
275
276 let (_, external_impl_sig, _, _) = &tcx.hir_expect_item(external_impl).expect_fn();
277 let pos = external_impl_number_args.saturating_sub(1);
278 let impl_span = external_impl_sig
279 .decl
280 .inputs
281 .get(pos)
282 .map(|arg| {
283 if pos == 0 {
284 arg.span
285 } else {
286 arg.span.with_lo(external_impl_sig.decl.inputs[0].span.lo())
287 }
288 })
289 .unwrap_or_else(|| tcx.def_span(external_impl));
290
291 let mut err = struct_span_code_err!(
292 tcx.dcx(),
293 impl_span,
294 E0806,
295 "`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}",
296 potentially_plural_count(external_impl_number_args, "parameter"),
297 declaration_number_args
298 );
299
300 err.span_label(
301 declaration_span,
302 format!("requires {}", potentially_plural_count(declaration_number_args, "parameter")),
303 );
304
305 err.span_label(
306 impl_span,
307 format!(
308 "expected {}, found {}",
309 potentially_plural_count(declaration_number_args, "parameter"),
310 external_impl_number_args
311 ),
312 );
313
314 err.span_label(eii_attr_span, format!("required because of this attribute"));
315
316 err.emit()
317}
318
319fn report_eii_mismatch<'tcx>(
320 infcx: &InferCtxt<'tcx>,
321 mut cause: ObligationCause<'tcx>,
322 param_env: ty::ParamEnv<'tcx>,
323 terr: TypeError<'tcx>,
324 (declaration_did, declaration_sig): (DefId, ty::FnSig<'tcx>),
325 (external_impl_did, external_impl_sig): (LocalDefId, ty::FnSig<'tcx>),
326 eii_attr_span: Span,
327 eii_name: Symbol,
328) -> ErrorGuaranteed {
329 let tcx = infcx.tcx;
330 let (impl_err_span, trait_err_span, external_impl_name) =
331 extract_spans_for_error_reporting(infcx, terr, &cause, declaration_did, external_impl_did);
332
333 let mut diag = struct_span_code_err!(
334 tcx.dcx(),
335 impl_err_span,
336 E0806,
337 "function `{}` has a type that is incompatible with the declaration of `#[{eii_name}]`",
338 external_impl_name
339 );
340
341 diag.span_note(eii_attr_span, "expected this because of this attribute");
342
343 match &terr {
344 TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
345 if declaration_sig.inputs().len() == *i {
346 if let ItemKind::Fn { sig, .. } = &tcx.hir_expect_item(external_impl_did).kind
349 && !sig.header.asyncness.is_async()
350 {
351 let msg = "change the output type to match the declaration";
352 let ap = Applicability::MachineApplicable;
353 match sig.decl.output {
354 hir::FnRetTy::DefaultReturn(sp) => {
355 let sugg = format!(" -> {}", declaration_sig.output());
356 diag.span_suggestion_verbose(sp, msg, sugg, ap);
357 }
358 hir::FnRetTy::Return(hir_ty) => {
359 let sugg = declaration_sig.output();
360 diag.span_suggestion_verbose(hir_ty.span, msg, sugg, ap);
361 }
362 };
363 };
364 } else if let Some(trait_ty) = declaration_sig.inputs().get(*i) {
365 diag.span_suggestion_verbose(
366 impl_err_span,
367 "change the parameter type to match the declaration",
368 trait_ty,
369 Applicability::MachineApplicable,
370 );
371 }
372 }
373 _ => {}
374 }
375
376 cause.span = impl_err_span;
377 infcx.err_ctxt().note_type_err(
378 &mut diag,
379 &cause,
380 trait_err_span.map(|sp| (sp, Cow::from("type in declaration"), false)),
381 Some(param_env.and(infer::ValuePairs::PolySigs(ExpectedFound {
382 expected: ty::Binder::dummy(declaration_sig),
383 found: ty::Binder::dummy(external_impl_sig),
384 }))),
385 terr,
386 false,
387 None,
388 );
389
390 diag.emit()
391}
392
393#[instrument(level = "debug", skip(infcx))]
394fn extract_spans_for_error_reporting<'tcx>(
395 infcx: &infer::InferCtxt<'tcx>,
396 terr: TypeError<'_>,
397 cause: &ObligationCause<'tcx>,
398 declaration: DefId,
399 external_impl: LocalDefId,
400) -> (Span, Option<Span>, Ident) {
401 let tcx = infcx.tcx;
402 let (mut external_impl_args, external_impl_name) = {
403 let item = tcx.hir_expect_item(external_impl);
404 let (ident, sig, _, _) = item.expect_fn();
405 (sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())), ident)
406 };
407
408 let declaration_args = declaration.as_local().map(|def_id| {
409 if let Some(sig) = get_declaration_sig(tcx, def_id) {
410 sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
411 } else {
412 panic!("expected {def_id:?} to be a foreign function");
413 }
414 });
415
416 match terr {
417 TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => (
418 external_impl_args.nth(i).unwrap(),
419 declaration_args.and_then(|mut args| args.nth(i)),
420 external_impl_name,
421 ),
422 _ => (
423 cause.span,
424 tcx.hir_span_if_local(declaration).or_else(|| Some(tcx.def_span(declaration))),
425 external_impl_name,
426 ),
427 }
428}
429
430fn get_declaration_sig<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option<&'tcx FnSig<'tcx>> {
431 let hir_id: HirId = tcx.local_def_id_to_hir_id(def_id);
432 tcx.hir_fn_sig_by_hir_id(hir_id)
433}