@@ -56,10 +56,10 @@ use crate::lints::{
5656 BuiltinIncompleteFeaturesHelp , BuiltinInternalFeatures , BuiltinKeywordIdents ,
5757 BuiltinMissingCopyImpl , BuiltinMissingDebugImpl , BuiltinMissingDoc , BuiltinMutablesTransmutes ,
5858 BuiltinNoMangleGeneric , BuiltinNonShorthandFieldPatterns , BuiltinSpecialModuleNameUsed ,
59- BuiltinTrivialBounds , BuiltinTypeAliasBounds , BuiltinUngatedAsyncFnTrackCaller ,
60- BuiltinUnpermittedTypeInit , BuiltinUnpermittedTypeInitSub , BuiltinUnreachablePub ,
61- BuiltinUnsafe , BuiltinUnstableFeatures , BuiltinUnusedDocComment , BuiltinUnusedDocCommentSub ,
62- BuiltinWhileTrue , InvalidAsmLabel ,
59+ BuiltinTransmuteInteriorMutability , BuiltinTrivialBounds , BuiltinTypeAliasBounds ,
60+ BuiltinUngatedAsyncFnTrackCaller , BuiltinUnpermittedTypeInit , BuiltinUnpermittedTypeInitSub ,
61+ BuiltinUnreachablePub , BuiltinUnsafe , BuiltinUnstableFeatures , BuiltinUnusedDocComment ,
62+ BuiltinUnusedDocCommentSub , BuiltinWhileTrue , InvalidAsmLabel ,
6363} ;
6464use crate :: nonstandard_style:: { MethodLateContext , method_context} ;
6565use crate :: {
@@ -1103,41 +1103,68 @@ declare_lint! {
11031103 "transmuting &T to &mut T is undefined behavior, even if the reference is unused"
11041104}
11051105
1106+ declare_lint ! {
1107+ /// The `transmute_to_interior_mutability` lint catches transmuting from `&T` or `*const T` to another type
1108+ /// with interior mutability because changing its value is [undefined behavior].
1109+ ///
1110+ /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1111+ ///
1112+ /// ### Example
1113+ ///
1114+ /// ```rust,compile_fail
1115+ /// unsafe {
1116+ /// let x = std::mem::transmute::<&i32, UnsafeCell<i32>>(&5);
1117+ /// let y = std::mem::transmute::<&i32, AtomicI32>(&5);
1118+ /// }
1119+ /// ```
1120+ ///
1121+ /// {{produces}}
1122+ ///
1123+ /// ### Explanation
1124+ ///
1125+ /// It's probably a mistake to transmute a type without interior mutability to a type with it.
1126+ TRANSMUTE_TO_INTERIOR_MUTABILITY ,
1127+ Warn ,
1128+ "transmuting from a type without interior mutability to a type with interior mutability is undefined behaviour if you modify the content"
1129+ }
1130+
1131+ declare_lint_pass ! ( TransmuteToInteriorMutability => [ TRANSMUTE_TO_INTERIOR_MUTABILITY ] ) ;
11061132declare_lint_pass ! ( MutableTransmutes => [ MUTABLE_TRANSMUTES ] ) ;
11071133
11081134impl < ' tcx > LateLintPass < ' tcx > for MutableTransmutes {
11091135 fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) {
1110- if let Some ( ( & ty :: Ref ( _ , _ , from_mutbl ) , & ty :: Ref ( _ , _ , to_mutbl ) ) ) =
1111- get_transmute_from_to ( cx , expr ) . map ( | ( ty1 , ty2 ) | ( ty1 . kind ( ) , ty2 . kind ( ) ) )
1112- {
1113- if from_mutbl < to_mutbl {
1114- cx. emit_span_lint ( MUTABLE_TRANSMUTES , expr . span , BuiltinMutablesTransmutes ) ;
1115- }
1136+ // Is our expr mem::transmute?
1137+ let hir :: ExprKind :: Path ( ref qpath ) = expr . kind else { return } ;
1138+ let def : Res = cx . qpath_res ( qpath , expr . hir_id ) ;
1139+ let Res :: Def ( DefKind :: Fn , def_id ) = def else { return } ;
1140+ if ! cx. tcx . is_intrinsic ( def_id , sym :: transmute ) {
1141+ return ;
11161142 }
1143+ let sig = cx. typeck_results ( ) . node_type ( expr. hir_id ) . fn_sig ( cx. tcx ) ;
1144+ let input = sig. inputs ( ) . skip_binder ( ) [ 0 ] ;
1145+ let output = sig. output ( ) . skip_binder ( ) ;
11171146
1118- fn get_transmute_from_to < ' tcx > (
1119- cx : & LateContext < ' tcx > ,
1120- expr : & hir:: Expr < ' _ > ,
1121- ) -> Option < ( Ty < ' tcx > , Ty < ' tcx > ) > {
1122- let def = if let hir:: ExprKind :: Path ( ref qpath) = expr. kind {
1123- cx. qpath_res ( qpath, expr. hir_id )
1124- } else {
1125- return None ;
1126- } ;
1127- if let Res :: Def ( DefKind :: Fn , did) = def {
1128- if !def_id_is_transmute ( cx, did) {
1129- return None ;
1147+ // For both checks we only care if the input is an immutable ref
1148+ let & ty:: Ref ( _, input_deref, Mutability :: Not ) = input. kind ( ) else { return } ;
1149+ match output. kind ( ) {
1150+ // If the output is a mutable reference that's bad
1151+ & ty:: Ref ( _, _, Mutability :: Mut ) => {
1152+ cx. emit_span_lint ( MUTABLE_TRANSMUTES , expr. span , BuiltinMutablesTransmutes ) ;
1153+ }
1154+ & ty:: Ref ( _, output_deref, Mutability :: Not ) => {
1155+ // If the input type doesn't have interior mutability but the output type does:
1156+ if input_deref. is_freeze ( cx. tcx , cx. typing_env ( ) )
1157+ && !output_deref. is_freeze ( cx. tcx , cx. typing_env ( ) )
1158+ {
1159+ cx. emit_span_lint (
1160+ TRANSMUTE_TO_INTERIOR_MUTABILITY ,
1161+ expr. span ,
1162+ BuiltinTransmuteInteriorMutability { output_ty : output_deref } ,
1163+ ) ;
11301164 }
1131- let sig = cx. typeck_results ( ) . node_type ( expr. hir_id ) . fn_sig ( cx. tcx ) ;
1132- let from = sig. inputs ( ) . skip_binder ( ) [ 0 ] ;
1133- let to = sig. output ( ) . skip_binder ( ) ;
1134- return Some ( ( from, to) ) ;
11351165 }
1136- None
1137- }
1138-
1139- fn def_id_is_transmute ( cx : & LateContext < ' _ > , def_id : DefId ) -> bool {
1140- cx. tcx . is_intrinsic ( def_id, sym:: transmute)
1166+ // The user is transmuting a reference into something else, warn...?
1167+ _ => ( ) ,
11411168 }
11421169 }
11431170}
@@ -1596,6 +1623,7 @@ declare_lint_pass!(
15961623 NO_MANGLE_CONST_ITEMS ,
15971624 NO_MANGLE_GENERIC_ITEMS ,
15981625 MUTABLE_TRANSMUTES ,
1626+ TRANSMUTE_TO_INTERIOR_MUTABILITY ,
15991627 UNSTABLE_FEATURES ,
16001628 UNREACHABLE_PUB ,
16011629 TYPE_ALIAS_BOUNDS ,
0 commit comments