@@ -2,29 +2,219 @@ use std::borrow::Cow;
22use std:: iter;
33
44use rustc_data_structures:: fx:: FxIndexSet ;
5- use rustc_errors:: { Applicability , E0053 , E0053 , struct_span_code_err, struct_span_code_err} ;
5+ use rustc_errors:: {
6+ Applicability , Applicability , E0050 , E0053 , E0053 , E0053 , struct_span_code_err,
7+ struct_span_code_err, struct_span_code_err,
8+ } ;
69use rustc_hir:: def_id:: { DefId , LocalDefId } ;
7- use rustc_hir:: { self as hir, self as hir, HirId , HirId , ItemKind , ItemKind } ;
10+ use rustc_hir:: {
11+ self as hir, self as hir, self as hir, FnSig , HirId , HirId , HirId , ItemKind , ItemKind , ItemKind ,
12+ } ;
813use rustc_infer:: infer:: { self , InferCtxt , TyCtxtInferExt } ;
914use rustc_infer:: traits:: { ObligationCause , ObligationCauseCode } ;
1015use rustc_middle:: ty;
1116use rustc_middle:: ty:: TyCtxt ;
12- use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeError } ;
17+ use rustc_middle:: ty:: error:: { ExpectedFound , ExpectedFound , TypeError , TypeError , TypeError } ;
1318use rustc_span:: { ErrorGuaranteed , Ident , Span } ;
1419use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
1520use rustc_trait_selection:: regions:: InferCtxtRegionExt ;
1621use rustc_trait_selection:: traits:: ObligationCtxt ;
1722use rustc_type_ir:: TypingMode ;
1823use tracing:: { debug, instrument} ;
1924
25+ use super :: potentially_plural_count;
26+ use crate :: errors:: LifetimesOrBoundsMismatchOnEII ;
27+
28+ /// Checks a bunch of different properties of the impl/trait methods for
29+ /// compatibility, such as asyncness, number of argument, self receiver kind,
30+ /// and number of early- and late-bound generics.
31+ fn check_is_structurally_compatible < ' tcx > (
32+ tcx : TyCtxt < ' tcx > ,
33+ external_impl : LocalDefId ,
34+ declaration : DefId ,
35+ eii_name : Symbol ,
36+ eii_attr_span : Span ,
37+ ) -> Result < ( ) , ErrorGuaranteed > {
38+ // FIXME(jdonszelmann): check no generics
39+ compare_number_of_method_arguments ( tcx, external_impl, declaration, eii_name, eii_attr_span) ?;
40+ check_region_bounds_on_impl_item ( tcx, external_impl, declaration, eii_attr_span) ?;
41+ Ok ( ( ) )
42+ }
43+
44+ fn check_region_bounds_on_impl_item < ' tcx > (
45+ tcx : TyCtxt < ' tcx > ,
46+ external_impl : LocalDefId ,
47+ declaration : DefId ,
48+ eii_attr_span : Span ,
49+ ) -> Result < ( ) , ErrorGuaranteed > {
50+ let external_impl_generics = tcx. generics_of ( external_impl. to_def_id ( ) ) ;
51+ let external_impl_params = external_impl_generics. own_counts ( ) . lifetimes ;
52+
53+ let declaration_generics = tcx. generics_of ( declaration) ;
54+ let declaration_params = declaration_generics. own_counts ( ) . lifetimes ;
55+
56+ debug ! ( ?declaration_generics, ?external_impl_generics) ;
57+
58+ // Must have same number of early-bound lifetime parameters.
59+ // Unfortunately, if the user screws up the bounds, then this
60+ // will change classification between early and late. E.g.,
61+ // if in trait we have `<'a,'b:'a>`, and in impl we just have
62+ // `<'a,'b>`, then we have 2 early-bound lifetime parameters
63+ // in trait but 0 in the impl. But if we report "expected 2
64+ // but found 0" it's confusing, because it looks like there
65+ // are zero. Since I don't quite know how to phrase things at
66+ // the moment, give a kind of vague error message.
67+ if declaration_params != external_impl_params {
68+ let span = tcx
69+ . hir_get_generics ( external_impl)
70+ . expect ( "expected impl item to have generics or else we can't compare them" )
71+ . span ;
72+
73+ let mut generics_span = None ;
74+ let mut bounds_span = vec ! [ ] ;
75+ let mut where_span = None ;
76+ if let Some ( declaration_node) = tcx. hir_get_if_local ( declaration)
77+ && let Some ( declaration_generics) = declaration_node. generics ( )
78+ {
79+ generics_span = Some ( declaration_generics. span ) ;
80+ // FIXME: we could potentially look at the impl's bounds to not point at bounds that
81+ // *are* present in the impl.
82+ for p in declaration_generics. predicates {
83+ if let hir:: WherePredicateKind :: BoundPredicate ( pred) = p. kind {
84+ for b in pred. bounds {
85+ if let hir:: GenericBound :: Outlives ( lt) = b {
86+ bounds_span. push ( lt. ident . span ) ;
87+ }
88+ }
89+ }
90+ }
91+ if let Some ( declaration_node) = tcx. hir_get_if_local ( declaration)
92+ && let Some ( declaration_generics) = declaration_node. generics ( )
93+ {
94+ let mut impl_bounds = 0 ;
95+ for p in declaration_generics. predicates {
96+ if let hir:: WherePredicateKind :: BoundPredicate ( pred) = p. kind {
97+ for b in pred. bounds {
98+ if let hir:: GenericBound :: Outlives ( _) = b {
99+ impl_bounds += 1 ;
100+ }
101+ }
102+ }
103+ }
104+ if impl_bounds == bounds_span. len ( ) {
105+ bounds_span = vec ! [ ] ;
106+ } else if declaration_generics. has_where_clause_predicates {
107+ where_span = Some ( declaration_generics. where_clause_span ) ;
108+ }
109+ }
110+ }
111+ let mut diag = tcx. dcx ( ) . create_err ( LifetimesOrBoundsMismatchOnEII {
112+ span,
113+ ident : tcx. item_name ( external_impl. to_def_id ( ) ) ,
114+ generics_span,
115+ bounds_span,
116+ where_span,
117+ } ) ;
118+
119+ diag. span_label ( eii_attr_span, format ! ( "required because of this attribute" ) ) ;
120+ return Err ( diag. emit ( ) ) ;
121+ }
122+
123+ Ok ( ( ) )
124+ }
125+
126+ fn compare_number_of_method_arguments < ' tcx > (
127+ tcx : TyCtxt < ' tcx > ,
128+ external_impl : LocalDefId ,
129+ declaration : DefId ,
130+ eii_name : Symbol ,
131+ eii_attr_span : Span ,
132+ ) -> Result < ( ) , ErrorGuaranteed > {
133+ let external_impl_fty = tcx. fn_sig ( external_impl) ;
134+ let declaration_fty = tcx. fn_sig ( declaration) ;
135+ let declaration_number_args = declaration_fty. skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) ;
136+ let external_impl_number_args = external_impl_fty. skip_binder ( ) . inputs ( ) . skip_binder ( ) . len ( ) ;
137+ let external_impl_name = tcx. item_name ( external_impl. to_def_id ( ) ) ;
138+
139+ if declaration_number_args != external_impl_number_args {
140+ let declaration_span = declaration
141+ . as_local ( )
142+ . and_then ( |def_id| {
143+ let declaration_sig = get_declaration_sig ( tcx, def_id) . expect ( "foreign item sig" ) ;
144+ let pos = declaration_number_args. saturating_sub ( 1 ) ;
145+ declaration_sig. decl . inputs . get ( pos) . map ( |arg| {
146+ if pos == 0 {
147+ arg. span
148+ } else {
149+ arg. span . with_lo ( declaration_sig. decl . inputs [ 0 ] . span . lo ( ) )
150+ }
151+ } )
152+ } )
153+ . or_else ( || tcx. hir ( ) . span_if_local ( declaration) ) ;
154+
155+ let ( external_impl_sig, _, _) = & tcx. hir ( ) . expect_item ( external_impl) . expect_fn ( ) ;
156+ let pos = external_impl_number_args. saturating_sub ( 1 ) ;
157+ let impl_span = external_impl_sig
158+ . decl
159+ . inputs
160+ . get ( pos)
161+ . map ( |arg| {
162+ if pos == 0 {
163+ arg. span
164+ } else {
165+ arg. span . with_lo ( external_impl_sig. decl . inputs [ 0 ] . span . lo ( ) )
166+ }
167+ } )
168+ . unwrap_or_else ( || tcx. def_span ( external_impl) ) ;
169+
170+ let mut err = struct_span_code_err ! (
171+ tcx. dcx( ) ,
172+ impl_span,
173+ E0050 , // FIXME(jdonszelmann): new error code
174+ "`{external_impl_name}` has {} but #[{eii_name}] requires it to have {}" ,
175+ potentially_plural_count( external_impl_number_args, "parameter" ) ,
176+ declaration_number_args
177+ ) ;
178+
179+ if let Some ( declaration_span) = declaration_span {
180+ err. span_label (
181+ declaration_span,
182+ format ! (
183+ "requires {}" ,
184+ potentially_plural_count( declaration_number_args, "parameter" )
185+ ) ,
186+ ) ;
187+ }
188+
189+ err. span_label (
190+ impl_span,
191+ format ! (
192+ "expected {}, found {}" ,
193+ potentially_plural_count( declaration_number_args, "parameter" ) ,
194+ external_impl_number_args
195+ ) ,
196+ ) ;
197+
198+ err. span_label ( eii_attr_span, format ! ( "required because of this attribute" ) ) ;
199+
200+ return Err ( err. emit ( ) ) ;
201+ }
202+
203+ Ok ( ( ) )
204+ }
205+
20206// checks whether the signature of some `external_impl`, matches
21207// the signature of `declaration`, which it is supposed to be compatible
22208// with in order to implement the item.
23209pub ( crate ) fn compare_eii_function_types < ' tcx > (
24210 tcx : TyCtxt < ' tcx > ,
25211 external_impl : LocalDefId ,
26212 declaration : DefId ,
213+ eii_name : Symbol ,
214+ eii_attr_span : Span ,
27215) -> Result < ( ) , ErrorGuaranteed > {
216+ check_is_structurally_compatible ( tcx, external_impl, declaration, eii_name, eii_attr_span) ?;
217+
28218 let external_impl_span = tcx. def_span ( external_impl) ;
29219 let cause = ObligationCause :: new (
30220 external_impl_span,
@@ -52,6 +242,12 @@ pub(crate) fn compare_eii_function_types<'tcx>(
52242 // type.
53243
54244 let wf_tys = FxIndexSet :: default ( ) ;
245+ let norm_cause = ObligationCause :: misc ( external_impl_span, external_impl) ;
246+
247+ let declaration_sig = tcx. fn_sig ( declaration) . instantiate_identity ( ) ;
248+ let declaration_sig = infcx. enter_forall_and_leak_universe ( declaration_sig) ;
249+ let declaration_sig = ocx. normalize ( & norm_cause, param_env, declaration_sig) ;
250+
55251 let external_impl_sig = infcx. instantiate_binder_with_fresh_vars (
56252 external_impl_span,
57253 infer:: HigherRankedType ,
@@ -60,16 +256,9 @@ pub(crate) fn compare_eii_function_types<'tcx>(
60256 infcx. fresh_args_for_item ( external_impl_span, external_impl. to_def_id ( ) ) ,
61257 ) ,
62258 ) ;
63-
64- let norm_cause = ObligationCause :: misc ( external_impl_span, external_impl) ;
65259 let external_impl_sig = ocx. normalize ( & norm_cause, param_env, external_impl_sig) ;
66260 debug ! ( ?external_impl_sig) ;
67261
68- let declaration_sig = tcx. fn_sig ( declaration) . instantiate_identity ( ) ;
69- let declaration_sig =
70- tcx. liberate_late_bound_regions ( external_impl. to_def_id ( ) , declaration_sig) ;
71- let declaration_sig = ocx. normalize ( & norm_cause, param_env, declaration_sig) ;
72-
73262 // FIXME: We'd want to keep more accurate spans than "the method signature" when
74263 // processing the comparison between the trait and impl fn, but we sadly lose them
75264 // and point at the whole signature when a trait bound or specific input or output
@@ -201,8 +390,7 @@ fn extract_spans_for_error_reporting<'tcx>(
201390 } ;
202391
203392 let declaration_args = declaration. as_local ( ) . map ( |def_id| {
204- let hir_id: HirId = tcx. local_def_id_to_hir_id ( def_id) ;
205- if let Some ( sig) = tcx. hir_fn_sig_by_hir_id ( hir_id) {
393+ if let Some ( sig) = get_declaration_sig ( tcx, def_id) {
206394 sig. decl . inputs . iter ( ) . map ( |t| t. span ) . chain ( iter:: once ( sig. decl . output . span ( ) ) )
207395 } else {
208396 panic ! ( "expected {def_id:?} to be a foreign function" ) ;
@@ -218,3 +406,8 @@ fn extract_spans_for_error_reporting<'tcx>(
218406 _ => ( cause. span , tcx. hir ( ) . span_if_local ( declaration) , external_impl_name) ,
219407 }
220408}
409+
410+ fn get_declaration_sig < ' tcx > ( tcx : TyCtxt < ' tcx > , def_id : LocalDefId ) -> Option < & ' tcx FnSig < ' tcx > > {
411+ let hir_id: HirId = tcx. local_def_id_to_hir_id ( def_id) ;
412+ tcx. hir_fn_sig_by_hir_id ( hir_id)
413+ }
0 commit comments