@@ -6,14 +6,17 @@ use std::ptr;
66
77use rustc_hir:: def:: { DefKind , Res } ;
88use rustc_hir:: { Expr , ExprKind , ImplItem , ImplItemKind , Item , ItemKind , Node , TraitItem , TraitItemKind , UnOp } ;
9+ use rustc_infer:: traits:: specialization_graph;
910use rustc_lint:: { LateContext , LateLintPass , Lint } ;
1011use rustc_middle:: ty:: adjustment:: Adjust ;
11- use rustc_middle:: ty:: { Ty , TypeFlags } ;
12+ use rustc_middle:: ty:: fold:: TypeFoldable as _;
13+ use rustc_middle:: ty:: { AssocKind , Ty , TypeFlags } ;
1214use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1315use rustc_span:: { InnerSpan , Span , DUMMY_SP } ;
1416use rustc_typeck:: hir_ty_to_ty;
1517
16- use crate :: utils:: { in_constant, is_copy, qpath_res, span_lint_and_then} ;
18+ use crate :: utils:: { in_constant, qpath_res, span_lint_and_then} ;
19+ use if_chain:: if_chain;
1720
1821declare_clippy_lint ! {
1922 /// **What it does:** Checks for declaration of `const` items which is interior
@@ -83,11 +86,10 @@ declare_clippy_lint! {
8386 "referencing `const` with interior mutability"
8487}
8588
86- #[ allow( dead_code) ]
8789#[ derive( Copy , Clone ) ]
8890enum Source {
8991 Item { item : Span } ,
90- Assoc { item : Span , ty : Span } ,
92+ Assoc { item : Span } ,
9193 Expr { expr : Span } ,
9294}
9395
@@ -110,10 +112,15 @@ impl Source {
110112}
111113
112114fn verify_ty_bound < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , source : Source ) {
113- if ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) || is_copy ( cx, ty) {
114- // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
115- // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
116- // as well.
115+ // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
116+ // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
117+ // 'unfrozen'. However, this code causes a false negative in which
118+ // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
119+ // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
120+ // since it works when a pointer indirection involves (`Cell<*const T>`).
121+ // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
122+ // but I'm not sure whether it's a decent way, if possible.
123+ if cx. tcx . layout_of ( cx. param_env . and ( ty) ) . is_err ( ) || ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) {
117124 return ;
118125 }
119126
@@ -127,11 +134,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
127134 let const_kw_span = span. from_inner ( InnerSpan :: new ( 0 , 5 ) ) ;
128135 diag. span_label ( const_kw_span, "make this a static item (maybe with lazy_static)" ) ;
129136 } ,
130- Source :: Assoc { ty : ty_span, .. } => {
131- if ty. flags ( ) . intersects ( TypeFlags :: HAS_FREE_LOCAL_NAMES ) {
132- diag. span_label ( ty_span, & format ! ( "consider requiring `{}` to be `Copy`" , ty) ) ;
133- }
134- } ,
137+ Source :: Assoc { .. } => ( ) ,
135138 Source :: Expr { .. } => {
136139 diag. help ( "assign this const to a local or static variable, and use the variable here" ) ;
137140 } ,
@@ -152,32 +155,58 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
152155 fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , trait_item : & ' tcx TraitItem < ' _ > ) {
153156 if let TraitItemKind :: Const ( hir_ty, ..) = & trait_item. kind {
154157 let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
155- verify_ty_bound (
156- cx,
157- ty,
158- Source :: Assoc {
159- ty : hir_ty. span ,
160- item : trait_item. span ,
161- } ,
162- ) ;
158+ // Normalize assoc types because ones originated from generic params
159+ // bounded other traits could have their bound.
160+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
161+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : trait_item. span } ) ;
163162 }
164163 }
165164
166165 fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx ImplItem < ' _ > ) {
167166 if let ImplItemKind :: Const ( hir_ty, ..) = & impl_item. kind {
168167 let item_hir_id = cx. tcx . hir ( ) . get_parent_node ( impl_item. hir_id ) ;
169168 let item = cx. tcx . hir ( ) . expect_item ( item_hir_id) ;
170- // Ensure the impl is an inherent impl.
171- if let ItemKind :: Impl { of_trait : None , .. } = item. kind {
172- let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
173- verify_ty_bound (
174- cx,
175- ty,
176- Source :: Assoc {
177- ty : hir_ty. span ,
178- item : impl_item. span ,
179- } ,
180- ) ;
169+
170+ match & item. kind {
171+ ItemKind :: Impl {
172+ of_trait : Some ( of_trait_ref) ,
173+ ..
174+ } => {
175+ if_chain ! {
176+ // Lint a trait impl item only when the definition is a generic type,
177+ // assuming a assoc const is not meant to be a interior mutable type.
178+ if let Some ( of_trait_def_id) = of_trait_ref. trait_def_id( ) ;
179+ if let Some ( of_assoc_item) = specialization_graph:: Node :: Trait ( of_trait_def_id)
180+ . item( cx. tcx, impl_item. ident, AssocKind :: Const , of_trait_def_id) ;
181+ if cx. tcx
182+ // Normalize assoc types because ones originated from generic params
183+ // bounded other traits could have their bound at the trait defs;
184+ // and, in that case, the definition is *not* generic.
185+ . normalize_erasing_regions(
186+ cx. tcx. param_env( of_trait_def_id) ,
187+ cx. tcx. type_of( of_assoc_item. def_id) ,
188+ )
189+ . has_type_flags( TypeFlags :: HAS_PROJECTION | TypeFlags :: HAS_TY_PARAM ) ;
190+ then {
191+ let ty = hir_ty_to_ty( cx. tcx, hir_ty) ;
192+ let normalized = cx. tcx. normalize_erasing_regions( cx. param_env, ty) ;
193+ verify_ty_bound(
194+ cx,
195+ normalized,
196+ Source :: Assoc {
197+ item: impl_item. span,
198+ } ,
199+ ) ;
200+ }
201+ }
202+ } ,
203+ ItemKind :: Impl { of_trait : None , .. } => {
204+ let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
205+ // Normalize assoc types originated from generic params.
206+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
207+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : impl_item. span } ) ;
208+ } ,
209+ _ => ( ) ,
181210 }
182211 }
183212 }
0 commit comments