1+ //! This module is very similar to `compare_impl_item`.
2+ //! Most logic is taken from there,
3+ //! since in a very similar way we're comparing some declaration of a signature to an implementation.
4+ //! The major difference is that we don't bother with self types, since for EIIs we're comparing freestanding item.
5+
16use std:: borrow:: Cow ;
27use std:: iter;
38
@@ -8,17 +13,20 @@ use rustc_hir::{self as hir, FnSig, HirId, ItemKind};
813use rustc_infer:: infer:: { self , InferCtxt , TyCtxtInferExt } ;
914use rustc_infer:: traits:: { ObligationCause , ObligationCauseCode } ;
1015use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
11- use rustc_middle:: ty:: { self , TyCtxt , TypingMode } ;
16+ use rustc_middle:: ty:: { self , TyCtxt , TypeVisitableExt , TypingMode } ;
1217use rustc_span:: { ErrorGuaranteed , Ident , Span , Symbol } ;
1318use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
1419use rustc_trait_selection:: regions:: InferCtxtRegionExt ;
15- use rustc_trait_selection:: traits:: ObligationCtxt ;
20+ use rustc_trait_selection:: traits:: { self , ObligationCtxt } ;
1621use tracing:: { debug, instrument} ;
1722
1823use super :: potentially_plural_count;
24+ use crate :: check:: compare_impl_item:: {
25+ CheckNumberOfEarlyBoundRegionsError , check_number_of_early_bound_regions,
26+ } ;
1927use crate :: errors:: { EiiWithGenerics , LifetimesOrBoundsMismatchOnEii } ;
2028
21- /// checks whether the signature of some `external_impl`, matches
29+ /// Checks whether the signature of some `external_impl`, matches
2230/// the signature of `declaration`, which it is supposed to be compatible
2331/// with in order to implement the item.
2432pub ( crate ) fn compare_eii_function_types < ' tcx > (
@@ -43,33 +51,43 @@ pub(crate) fn compare_eii_function_types<'tcx>(
4351 let infcx = & tcx. infer_ctxt ( ) . build ( TypingMode :: non_body_analysis ( ) ) ;
4452 let ocx = ObligationCtxt :: new_with_diagnostics ( infcx) ;
4553
46- // We now need to check that the signature of the impl method is
47- // compatible with that of the trait method . We do this by
54+ // We now need to check that the signature of the implementation is
55+ // compatible with that of the declaration . We do this by
4856 // checking that `impl_fty <: trait_fty`.
4957 //
50- // FIXME: We manually instantiate the trait method here as we need
58+ // FIXME: We manually instantiate the declaration here as we need
5159 // to manually compute its implied bounds. Otherwise this could just
5260 // be ocx.sub(impl_sig, trait_sig).
5361
54- let wf_tys = FxIndexSet :: default ( ) ;
62+ let mut wf_tys = FxIndexSet :: default ( ) ;
5563 let norm_cause = ObligationCause :: misc ( external_impl_span, external_impl) ;
5664
5765 let declaration_sig = tcx. fn_sig ( declaration) . instantiate_identity ( ) ;
58- let declaration_sig = infcx . enter_forall_and_leak_universe ( declaration_sig) ;
59- let declaration_sig = ocx . normalize ( & norm_cause , param_env , declaration_sig) ;
66+ let declaration_sig = tcx . liberate_late_bound_regions ( external_impl . into ( ) , declaration_sig) ;
67+ debug ! ( ? declaration_sig) ;
6068
61- let external_impl_sig = infcx. instantiate_binder_with_fresh_vars (
69+ let unnormalized_external_impl_sig = infcx. instantiate_binder_with_fresh_vars (
6270 external_impl_span,
6371 infer:: BoundRegionConversionTime :: HigherRankedType ,
6472 tcx. fn_sig ( external_impl) . instantiate (
6573 tcx,
6674 infcx. fresh_args_for_item ( external_impl_span, external_impl. to_def_id ( ) ) ,
6775 ) ,
6876 ) ;
69- let external_impl_sig = ocx. normalize ( & norm_cause, param_env, external_impl_sig ) ;
77+ let external_impl_sig = ocx. normalize ( & norm_cause, param_env, unnormalized_external_impl_sig ) ;
7078 debug ! ( ?external_impl_sig) ;
7179
72- // FIXME: We'd want to keep more accurate spans than "the method signature" when
80+ // Next, add all inputs and output as well-formed tys. Importantly,
81+ // we have to do this before normalization, since the normalized ty may
82+ // not contain the input parameters. See issue #87748.
83+ wf_tys. extend ( declaration_sig. inputs_and_output . iter ( ) ) ;
84+ let declaration_sig = ocx. normalize ( & norm_cause, param_env, declaration_sig) ;
85+ // We also have to add the normalized declaration
86+ // as we don't normalize during implied bounds computation.
87+ wf_tys. extend ( external_impl_sig. inputs_and_output . iter ( ) ) ;
88+
89+ // FIXME: Copied over from compare impl items, same issue:
90+ // We'd want to keep more accurate spans than "the method signature" when
7391 // processing the comparison between the trait and impl fn, but we sadly lose them
7492 // and point at the whole signature when a trait bound or specific input or output
7593 // type would be more appropriate. In other places we have a `Vec<Span>`
@@ -93,6 +111,17 @@ pub(crate) fn compare_eii_function_types<'tcx>(
93111 return Err ( emitted) ;
94112 }
95113
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+
96125 // Check that all obligations are satisfied by the implementation's
97126 // version.
98127 let errors = ocx. evaluate_obligations_error_on_ambiguity ( ) ;
@@ -116,6 +145,8 @@ pub(crate) fn compare_eii_function_types<'tcx>(
116145/// Checks a bunch of different properties of the impl/trait methods for
117146/// compatibility, such as asyncness, number of argument, self receiver kind,
118147/// and number of early- and late-bound generics.
148+ ///
149+ /// Corresponds to `check_method_is_structurally_compatible` for impl method compatibility checks.
119150fn check_is_structurally_compatible < ' tcx > (
120151 tcx : TyCtxt < ' tcx > ,
121152 external_impl : LocalDefId ,
@@ -124,11 +155,12 @@ fn check_is_structurally_compatible<'tcx>(
124155 eii_attr_span : Span ,
125156) -> Result < ( ) , ErrorGuaranteed > {
126157 check_no_generics ( tcx, external_impl, declaration, eii_name, eii_attr_span) ?;
127- compare_number_of_method_arguments ( tcx, external_impl, declaration, eii_name, eii_attr_span) ?;
128- check_region_bounds_on_impl_item ( tcx, external_impl, declaration, 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) ?;
129160 Ok ( ( ) )
130161}
131162
163+ /// externally implementable items can't have generics
132164fn check_no_generics < ' tcx > (
133165 tcx : TyCtxt < ' tcx > ,
134166 external_impl : LocalDefId ,
@@ -148,7 +180,45 @@ fn check_no_generics<'tcx>(
148180 Ok ( ( ) )
149181}
150182
151- fn compare_number_of_method_arguments < ' tcx > (
183+ fn 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+
221+ fn check_number_of_arguments < ' tcx > (
152222 tcx : TyCtxt < ' tcx > ,
153223 external_impl : LocalDefId ,
154224 declaration : DefId ,
@@ -159,152 +229,91 @@ fn compare_number_of_method_arguments<'tcx>(
159229 let declaration_fty = tcx. fn_sig ( declaration) ;
160230 let declaration_number_args = declaration_fty. skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) ;
161231 let external_impl_number_args = external_impl_fty. skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) ;
162- let external_impl_name = tcx. item_name ( external_impl. to_def_id ( ) ) ;
163-
164- if declaration_number_args != external_impl_number_args {
165- let declaration_span = declaration
166- . as_local ( )
167- . and_then ( |def_id| {
168- let declaration_sig = get_declaration_sig ( tcx, def_id) . expect ( "foreign item sig" ) ;
169- let pos = declaration_number_args. saturating_sub ( 1 ) ;
170- declaration_sig. decl . inputs . get ( pos) . map ( |arg| {
171- if pos == 0 {
172- arg. span
173- } else {
174- arg. span . with_lo ( declaration_sig. decl . inputs [ 0 ] . span . lo ( ) )
175- }
176- } )
177- } )
178- . or_else ( || tcx. hir_span_if_local ( declaration) )
179- . unwrap_or_else ( || tcx. def_span ( declaration) ) ;
180-
181- let ( _, external_impl_sig, _, _) = & tcx. hir_expect_item ( external_impl) . expect_fn ( ) ;
182- let pos = external_impl_number_args. saturating_sub ( 1 ) ;
183- let impl_span = external_impl_sig
184- . decl
185- . inputs
186- . get ( pos)
187- . map ( |arg| {
188- if pos == 0 {
189- arg. span
190- } else {
191- arg. span . with_lo ( external_impl_sig. decl . inputs [ 0 ] . span . lo ( ) )
192- }
193- } )
194- . unwrap_or_else ( || tcx. def_span ( external_impl) ) ;
195-
196- let mut err = struct_span_code_err ! (
197- tcx. dcx( ) ,
198- impl_span,
199- E0805 ,
200- "`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}" ,
201- potentially_plural_count( external_impl_number_args, "parameter" ) ,
202- declaration_number_args
203- ) ;
204-
205- // if let Some(declaration_span) = declaration_span {
206- err. span_label (
207- declaration_span,
208- format ! ( "requires {}" , potentially_plural_count( declaration_number_args, "parameter" ) ) ,
209- ) ;
210- // }
211-
212- err. span_label (
213- impl_span,
214- format ! (
215- "expected {}, found {}" ,
216- potentially_plural_count( declaration_number_args, "parameter" ) ,
217- external_impl_number_args
218- ) ,
219- ) ;
220232
221- err. span_label ( eii_attr_span, format ! ( "required because of this attribute" ) ) ;
222-
223- return Err ( err. emit ( ) ) ;
233+ // if the number of args are equal, we're trivially done
234+ 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+ ) )
224246 }
225-
226- Ok ( ( ) )
227247}
228248
229- fn check_region_bounds_on_impl_item < ' tcx > (
249+ fn report_number_of_arguments_mismatch < ' tcx > (
230250 tcx : TyCtxt < ' tcx > ,
231251 external_impl : LocalDefId ,
232252 declaration : DefId ,
253+ eii_name : Symbol ,
233254 eii_attr_span : Span ,
234- ) -> Result < ( ) , ErrorGuaranteed > {
235- let external_impl_generics = tcx. generics_of ( external_impl. to_def_id ( ) ) ;
236- let external_impl_params = external_impl_generics. own_counts ( ) . lifetimes ;
237-
238- let declaration_generics = tcx. generics_of ( declaration) ;
239- let declaration_params = declaration_generics. own_counts ( ) . lifetimes ;
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 ( ) ) ;
240259
241- debug ! ( ?declaration_generics, ?external_impl_generics) ;
242-
243- // Must have same number of early-bound lifetime parameters.
244- // Unfortunately, if the user screws up the bounds, then this
245- // will change classification between early and late. E.g.,
246- // if in trait we have `<'a,'b:'a>`, and in impl we just have
247- // `<'a,'b>`, then we have 2 early-bound lifetime parameters
248- // in trait but 0 in the impl. But if we report "expected 2
249- // but found 0" it's confusing, because it looks like there
250- // are zero. Since I don't quite know how to phrase things at
251- // the moment, give a kind of vague error message.
252- if declaration_params != external_impl_params {
253- let span = tcx
254- . hir_get_generics ( external_impl)
255- . expect ( "expected impl item to have generics or else we can't compare them" )
256- . span ;
257-
258- let mut generics_span = None ;
259- let mut bounds_span = vec ! [ ] ;
260- let mut where_span = None ;
261-
262- if let Some ( declaration_node) = tcx. hir_get_if_local ( declaration)
263- && let Some ( declaration_generics) = declaration_node. generics ( )
264- {
265- generics_span = Some ( declaration_generics. span ) ;
266- // FIXME: we could potentially look at the impl's bounds to not point at bounds that
267- // *are* present in the impl.
268- for p in declaration_generics. predicates {
269- if let hir:: WherePredicateKind :: BoundPredicate ( pred) = p. kind {
270- for b in pred. bounds {
271- if let hir:: GenericBound :: Outlives ( lt) = b {
272- bounds_span. push ( lt. ident . span ) ;
273- }
274- }
275- }
276- }
277- if let Some ( implementation_generics) = tcx. hir_get_generics ( external_impl) {
278- let mut impl_bounds = 0 ;
279- for p in implementation_generics. predicates {
280- if let hir:: WherePredicateKind :: BoundPredicate ( pred) = p. kind {
281- for b in pred. bounds {
282- if let hir:: GenericBound :: Outlives ( _) = b {
283- impl_bounds += 1 ;
284- }
285- }
286- }
287- }
288- if impl_bounds == bounds_span. len ( ) {
289- bounds_span = vec ! [ ] ;
290- } else if implementation_generics. has_where_clause_predicates {
291- where_span = Some ( implementation_generics. where_clause_span ) ;
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 ( ) )
292270 }
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 ( ) )
293287 }
294- }
295- let mut diag = tcx. dcx ( ) . create_err ( LifetimesOrBoundsMismatchOnEii {
296- span,
297- ident : tcx. item_name ( external_impl. to_def_id ( ) ) ,
298- generics_span,
299- bounds_span,
300- where_span,
301- } ) ;
288+ } )
289+ . unwrap_or_else ( || tcx. def_span ( external_impl) ) ;
302290
303- diag. span_label ( eii_attr_span, format ! ( "required because of this attribute" ) ) ;
304- return Err ( diag. emit ( ) ) ;
305- }
291+ let mut err = struct_span_code_err ! (
292+ tcx. dcx( ) ,
293+ impl_span,
294+ E0805 ,
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+ ) ;
306299
307- Ok ( ( ) )
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 ( )
308317}
309318
310319fn report_eii_mismatch < ' tcx > (
0 commit comments