@@ -2,29 +2,219 @@ use std::borrow::Cow;
2
2
use std:: iter;
3
3
4
4
use 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
+ } ;
6
9
use 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
+ } ;
8
13
use rustc_infer:: infer:: { self , InferCtxt , TyCtxtInferExt } ;
9
14
use rustc_infer:: traits:: { ObligationCause , ObligationCauseCode } ;
10
15
use rustc_middle:: ty;
11
16
use rustc_middle:: ty:: TyCtxt ;
12
- use rustc_middle:: ty:: error:: { ExpectedFound , TypeError , TypeError } ;
17
+ use rustc_middle:: ty:: error:: { ExpectedFound , ExpectedFound , TypeError , TypeError , TypeError } ;
13
18
use rustc_span:: { ErrorGuaranteed , Ident , Span } ;
14
19
use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
15
20
use rustc_trait_selection:: regions:: InferCtxtRegionExt ;
16
21
use rustc_trait_selection:: traits:: ObligationCtxt ;
17
22
use rustc_type_ir:: TypingMode ;
18
23
use tracing:: { debug, instrument} ;
19
24
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
+
20
206
// checks whether the signature of some `external_impl`, matches
21
207
// the signature of `declaration`, which it is supposed to be compatible
22
208
// with in order to implement the item.
23
209
pub ( crate ) fn compare_eii_function_types < ' tcx > (
24
210
tcx : TyCtxt < ' tcx > ,
25
211
external_impl : LocalDefId ,
26
212
declaration : DefId ,
213
+ eii_name : Symbol ,
214
+ eii_attr_span : Span ,
27
215
) -> Result < ( ) , ErrorGuaranteed > {
216
+ check_is_structurally_compatible ( tcx, external_impl, declaration, eii_name, eii_attr_span) ?;
217
+
28
218
let external_impl_span = tcx. def_span ( external_impl) ;
29
219
let cause = ObligationCause :: new (
30
220
external_impl_span,
@@ -52,6 +242,12 @@ pub(crate) fn compare_eii_function_types<'tcx>(
52
242
// type.
53
243
54
244
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
+
55
251
let external_impl_sig = infcx. instantiate_binder_with_fresh_vars (
56
252
external_impl_span,
57
253
infer:: HigherRankedType ,
@@ -60,16 +256,9 @@ pub(crate) fn compare_eii_function_types<'tcx>(
60
256
infcx. fresh_args_for_item ( external_impl_span, external_impl. to_def_id ( ) ) ,
61
257
) ,
62
258
) ;
63
-
64
- let norm_cause = ObligationCause :: misc ( external_impl_span, external_impl) ;
65
259
let external_impl_sig = ocx. normalize ( & norm_cause, param_env, external_impl_sig) ;
66
260
debug ! ( ?external_impl_sig) ;
67
261
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
-
73
262
// FIXME: We'd want to keep more accurate spans than "the method signature" when
74
263
// processing the comparison between the trait and impl fn, but we sadly lose them
75
264
// 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>(
201
390
} ;
202
391
203
392
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) {
206
394
sig. decl . inputs . iter ( ) . map ( |t| t. span ) . chain ( iter:: once ( sig. decl . output . span ( ) ) )
207
395
} else {
208
396
panic ! ( "expected {def_id:?} to be a foreign function" ) ;
@@ -218,3 +406,8 @@ fn extract_spans_for_error_reporting<'tcx>(
218
406
_ => ( cause. span , tcx. hir ( ) . span_if_local ( declaration) , external_impl_name) ,
219
407
}
220
408
}
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