1
- use rustc_hir:: { def:: DefKind , Body , Item , ItemKind , Node , Path , QPath , TyKind } ;
1
+ use rustc_hir:: { def:: DefKind , Body , Item , ItemKind , Node , TyKind } ;
2
+ use rustc_hir:: { Path , QPath } ;
3
+ use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
4
+ use rustc_infer:: infer:: InferCtxt ;
5
+ use rustc_infer:: traits:: { Obligation , ObligationCause } ;
6
+ use rustc_middle:: query:: Key ;
7
+ use rustc_middle:: ty:: TypeSuperFoldable ;
8
+ use rustc_middle:: ty:: { self , Binder , Ty , TyCtxt , TypeFoldable , TypeFolder } ;
2
9
use rustc_span:: def_id:: { DefId , LOCAL_CRATE } ;
10
+ use rustc_span:: Span ;
3
11
use rustc_span:: { sym, symbol:: kw, ExpnKind , MacroKind } ;
12
+ use rustc_trait_selection:: infer:: TyCtxtInferExt ;
13
+ use rustc_trait_selection:: traits:: error_reporting:: ambiguity:: {
14
+ compute_applicable_impls_for_diagnostics, Ambiguity ,
15
+ } ;
4
16
5
17
use crate :: lints:: { NonLocalDefinitionsCargoUpdateNote , NonLocalDefinitionsDiag } ;
6
18
use crate :: { LateContext , LateLintPass , LintContext } ;
@@ -35,7 +47,7 @@ declare_lint! {
35
47
/// All nested bodies (functions, enum discriminant, array length, consts) (expect for
36
48
/// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked.
37
49
pub NON_LOCAL_DEFINITIONS ,
38
- Allow ,
50
+ Warn ,
39
51
"checks for non-local definitions" ,
40
52
report_in_external_macro
41
53
}
@@ -66,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
66
78
return ;
67
79
}
68
80
69
- let parent = cx. tcx . parent ( item. owner_id . def_id . into ( ) ) ;
81
+ let def_id = item. owner_id . def_id . into ( ) ;
82
+
83
+ let parent = cx. tcx . parent ( def_id) ;
70
84
let parent_def_kind = cx. tcx . def_kind ( parent) ;
71
85
let parent_opt_item_name = cx. tcx . opt_item_name ( parent) ;
72
86
@@ -155,9 +169,54 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
155
169
. map ( |of_trait| path_has_local_parent ( of_trait. path , cx, parent, parent_parent) )
156
170
. unwrap_or ( false ) ;
157
171
158
- // If none of them have a local parent (LOGICAL NOR) this means that
159
- // this impl definition is a non-local definition and so we lint on it.
160
- if !( self_ty_has_local_parent || of_trait_has_local_parent) {
172
+ // Detecting if the impl definition is leaking outside of it's defining scope.
173
+ //
174
+ // Rule: for each impl, instantiate all local types with inference vars and
175
+ // then assemble candidates for that goal, if there are more than 1 (non-private
176
+ // impls), it does not leak.
177
+ //
178
+ // https://github.com/rust-lang/rust/issues/121621#issuecomment-1976826895
179
+ let impl_is_not_leaky = cx
180
+ . tcx
181
+ . impl_trait_ref ( def_id)
182
+ . map ( |binder| {
183
+ let infcx = cx. tcx . infer_ctxt ( ) . build ( ) ;
184
+ let trait_ref = binder
185
+ . instantiate ( cx. tcx , infcx. fresh_args_for_item ( item. span , def_id) ) ;
186
+
187
+ let trait_ref = trait_ref. fold_with ( & mut LocalTypeInferenceFolder {
188
+ infcx : & infcx,
189
+ to_ignore : 1 ,
190
+ impl_parent : parent,
191
+ impl_parent_parent : parent_parent,
192
+ span : item. span ,
193
+ } ) ;
194
+
195
+ let poly_trait_obligation = Obligation :: new (
196
+ cx. tcx ,
197
+ ObligationCause :: dummy ( ) ,
198
+ ty:: ParamEnv :: empty ( ) ,
199
+ Binder :: dummy ( trait_ref) ,
200
+ ) ;
201
+
202
+ let ambiguities = compute_applicable_impls_for_diagnostics (
203
+ & infcx,
204
+ & poly_trait_obligation,
205
+ ) ;
206
+
207
+ let mut it = ambiguities. iter ( ) . filter ( |ambi| match ambi {
208
+ Ambiguity :: DefId ( did) => {
209
+ !did_has_local_parent ( * did, cx. tcx , parent, parent_parent)
210
+ }
211
+ Ambiguity :: ParamEnv ( _) => false ,
212
+ } ) ;
213
+
214
+ let _ = it. next ( ) ;
215
+ it. next ( ) . is_some ( )
216
+ } )
217
+ . unwrap_or ( false ) ;
218
+
219
+ if !( self_ty_has_local_parent || of_trait_has_local_parent || impl_is_not_leaky) {
161
220
let const_anon = if self . body_depth == 1
162
221
&& parent_def_kind == DefKind :: Const
163
222
&& parent_opt_item_name != Some ( kw:: Underscore )
@@ -207,6 +266,47 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
207
266
}
208
267
}
209
268
269
+ /// Replace every local type by inference variable.
270
+ ///
271
+ /// ```text
272
+ /// <Global<Local> as std::cmp::PartialEq<Global<Local>>>
273
+ /// to
274
+ /// <Global<_> as std::cmp::PartialEq<Global<_>>>
275
+ /// ```
276
+ struct LocalTypeInferenceFolder < ' a , ' tcx > {
277
+ infcx : & ' a InferCtxt < ' tcx > ,
278
+ to_ignore : u32 ,
279
+ impl_parent : DefId ,
280
+ impl_parent_parent : Option < DefId > ,
281
+ span : Span ,
282
+ }
283
+
284
+ impl < ' a , ' tcx > TypeFolder < TyCtxt < ' tcx > > for LocalTypeInferenceFolder < ' a , ' tcx > {
285
+ fn interner ( & self ) -> TyCtxt < ' tcx > {
286
+ self . infcx . tcx
287
+ }
288
+
289
+ fn fold_ty ( & mut self , t : Ty < ' tcx > ) -> Ty < ' tcx > {
290
+ if let Some ( ty_did) = t. ty_def_id ( )
291
+ && did_has_local_parent (
292
+ ty_did,
293
+ self . infcx . tcx ,
294
+ self . impl_parent ,
295
+ self . impl_parent_parent ,
296
+ )
297
+ && self . to_ignore == 0
298
+ {
299
+ self . infcx . next_ty_var ( TypeVariableOrigin {
300
+ kind : TypeVariableOriginKind :: TypeInference ,
301
+ span : self . span ,
302
+ } )
303
+ } else {
304
+ self . to_ignore = self . to_ignore . saturating_sub ( 1 ) ;
305
+ t. super_fold_with ( self )
306
+ }
307
+ }
308
+ }
309
+
210
310
/// Given a path and a parent impl def id, this checks if the if parent resolution
211
311
/// def id correspond to the def id of the parent impl definition.
212
312
///
@@ -216,16 +316,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
216
316
/// std::convert::PartialEq<Foo<Bar>>
217
317
/// ^^^^^^^^^^^^^^^^^^^^^^^
218
318
/// ```
319
+ #[ inline]
219
320
fn path_has_local_parent (
220
321
path : & Path < ' _ > ,
221
322
cx : & LateContext < ' _ > ,
222
323
impl_parent : DefId ,
223
324
impl_parent_parent : Option < DefId > ,
224
325
) -> bool {
225
- path. res . opt_def_id ( ) . is_some_and ( |did| {
226
- did. is_local ( ) && {
227
- let res_parent = cx. tcx . parent ( did) ;
228
- res_parent == impl_parent || Some ( res_parent) == impl_parent_parent
229
- }
230
- } )
326
+ path. res
327
+ . opt_def_id ( )
328
+ . is_some_and ( |did| did_has_local_parent ( did, cx. tcx , impl_parent, impl_parent_parent) )
329
+ }
330
+
331
+ /// Given a def id and a parent impl def id, this checks if the parent
332
+ /// def id correspond to the def id of the parent impl definition.
333
+ #[ inline]
334
+ fn did_has_local_parent (
335
+ did : DefId ,
336
+ tcx : TyCtxt < ' _ > ,
337
+ impl_parent : DefId ,
338
+ impl_parent_parent : Option < DefId > ,
339
+ ) -> bool {
340
+ did. is_local ( ) && {
341
+ let res_parent = tcx. parent ( did) ;
342
+ res_parent == impl_parent || Some ( res_parent) == impl_parent_parent
343
+ }
231
344
}
0 commit comments