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 } ;
29use rustc_span:: def_id:: { DefId , LOCAL_CRATE } ;
10+ use rustc_span:: Span ;
311use 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+ } ;
416
517use crate :: lints:: { NonLocalDefinitionsCargoUpdateNote , NonLocalDefinitionsDiag } ;
618use crate :: { LateContext , LateLintPass , LintContext } ;
@@ -35,7 +47,7 @@ declare_lint! {
3547 /// All nested bodies (functions, enum discriminant, array length, consts) (expect for
3648 /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked.
3749 pub NON_LOCAL_DEFINITIONS ,
38- Allow ,
50+ Warn ,
3951 "checks for non-local definitions" ,
4052 report_in_external_macro
4153}
@@ -66,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
6678 return ;
6779 }
6880
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) ;
7084 let parent_def_kind = cx. tcx . def_kind ( parent) ;
7185 let parent_opt_item_name = cx. tcx . opt_item_name ( parent) ;
7286
@@ -155,9 +169,54 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
155169 . map ( |of_trait| path_has_local_parent ( of_trait. path , cx, parent, parent_parent) )
156170 . unwrap_or ( false ) ;
157171
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) {
161220 let const_anon = if self . body_depth == 1
162221 && parent_def_kind == DefKind :: Const
163222 && parent_opt_item_name != Some ( kw:: Underscore )
@@ -207,6 +266,47 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
207266 }
208267}
209268
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+
210310/// Given a path and a parent impl def id, this checks if the if parent resolution
211311/// def id correspond to the def id of the parent impl definition.
212312///
@@ -216,16 +316,29 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
216316/// std::convert::PartialEq<Foo<Bar>>
217317/// ^^^^^^^^^^^^^^^^^^^^^^^
218318/// ```
319+ #[ inline]
219320fn path_has_local_parent (
220321 path : & Path < ' _ > ,
221322 cx : & LateContext < ' _ > ,
222323 impl_parent : DefId ,
223324 impl_parent_parent : Option < DefId > ,
224325) -> 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+ }
231344}
0 commit comments