@@ -6,14 +6,16 @@ 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:: { AssocKind , Ty } ;
1213use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1314use rustc_span:: { InnerSpan , Span , DUMMY_SP } ;
1415use rustc_typeck:: hir_ty_to_ty;
1516
16- use crate :: utils:: { in_constant, is_copy, qpath_res, span_lint_and_then} ;
17+ use crate :: utils:: { in_constant, qpath_res, span_lint_and_then} ;
18+ use if_chain:: if_chain;
1719
1820declare_clippy_lint ! {
1921 /// **What it does:** Checks for declaration of `const` items which is interior
@@ -83,11 +85,10 @@ declare_clippy_lint! {
8385 "referencing `const` with interior mutability"
8486}
8587
86- #[ allow( dead_code) ]
8788#[ derive( Copy , Clone ) ]
8889enum Source {
8990 Item { item : Span } ,
90- Assoc { item : Span , ty : Span } ,
91+ Assoc { item : Span } ,
9192 Expr { expr : Span } ,
9293}
9394
@@ -110,10 +111,15 @@ impl Source {
110111}
111112
112113fn 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.
114+ // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
115+ // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
116+ // 'unfrozen'. However, this code causes a false negative in which
117+ // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
118+ // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
119+ // since it works when a pointer indirection involves (`Cell<*const T>`).
120+ // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
121+ // but I'm not sure whether it's a decent way, if possible.
122+ if cx. tcx . layout_of ( cx. param_env . and ( ty) ) . is_err ( ) || ty. is_freeze ( cx. tcx . at ( DUMMY_SP ) , cx. param_env ) {
117123 return ;
118124 }
119125
@@ -127,11 +133,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
127133 let const_kw_span = span. from_inner ( InnerSpan :: new ( 0 , 5 ) ) ;
128134 diag. span_label ( const_kw_span, "make this a static item (maybe with lazy_static)" ) ;
129135 } ,
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- } ,
136+ Source :: Assoc { .. } => ( ) ,
135137 Source :: Expr { .. } => {
136138 diag. help ( "assign this const to a local or static variable, and use the variable here" ) ;
137139 } ,
@@ -152,32 +154,61 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
152154 fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , trait_item : & ' tcx TraitItem < ' _ > ) {
153155 if let TraitItemKind :: Const ( hir_ty, ..) = & trait_item. kind {
154156 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- ) ;
157+ // Normalize assoc types because ones originated from generic params
158+ // bounded other traits could have their bound.
159+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
160+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : trait_item. span } ) ;
163161 }
164162 }
165163
166164 fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx ImplItem < ' _ > ) {
167165 if let ImplItemKind :: Const ( hir_ty, ..) = & impl_item. kind {
168166 let item_hir_id = cx. tcx . hir ( ) . get_parent_node ( impl_item. hir_id ) ;
169167 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- ) ;
168+
169+ match & item. kind {
170+ ItemKind :: Impl {
171+ of_trait : Some ( of_trait_ref) ,
172+ ..
173+ } => {
174+ if_chain ! {
175+ // Lint a trait impl item only when the definition is a generic type,
176+ // assuming a assoc const is not meant to be a interior mutable type.
177+ if let Some ( of_trait_def_id) = of_trait_ref. trait_def_id( ) ;
178+ if let Some ( of_assoc_item) = specialization_graph:: Node :: Trait ( of_trait_def_id)
179+ . item( cx. tcx, impl_item. ident, AssocKind :: Const , of_trait_def_id) ;
180+ if cx
181+ . tcx
182+ . layout_of( cx. tcx. param_env( of_trait_def_id) . and(
183+ // Normalize assoc types because ones originated from generic params
184+ // bounded other traits could have their bound at the trait defs;
185+ // and, in that case, the definition is *not* generic.
186+ cx. tcx. normalize_erasing_regions(
187+ cx. tcx. param_env( of_trait_def_id) ,
188+ cx. tcx. type_of( of_assoc_item. def_id) ,
189+ ) ,
190+ ) )
191+ . is_err( ) ;
192+ then {
193+ let ty = hir_ty_to_ty( cx. tcx, hir_ty) ;
194+ let normalized = cx. tcx. normalize_erasing_regions( cx. param_env, ty) ;
195+ verify_ty_bound(
196+ cx,
197+ normalized,
198+ Source :: Assoc {
199+ item: impl_item. span,
200+ } ,
201+ ) ;
202+ }
203+ }
204+ } ,
205+ ItemKind :: Impl { of_trait : None , .. } => {
206+ let ty = hir_ty_to_ty ( cx. tcx , hir_ty) ;
207+ // Normalize assoc types originated from generic params.
208+ let normalized = cx. tcx . normalize_erasing_regions ( cx. param_env , ty) ;
209+ verify_ty_bound ( cx, normalized, Source :: Assoc { item : impl_item. span } ) ;
210+ } ,
211+ _ => ( ) ,
181212 }
182213 }
183214 }
0 commit comments