@@ -56,10 +56,10 @@ use crate::lints::{
56
56
BuiltinIncompleteFeaturesHelp , BuiltinInternalFeatures , BuiltinKeywordIdents ,
57
57
BuiltinMissingCopyImpl , BuiltinMissingDebugImpl , BuiltinMissingDoc , BuiltinMutablesTransmutes ,
58
58
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 ,
63
63
} ;
64
64
use crate :: nonstandard_style:: { MethodLateContext , method_context} ;
65
65
use crate :: {
@@ -1103,41 +1103,67 @@ declare_lint! {
1103
1103
"transmuting &T to &mut T is undefined behavior, even if the reference is unused"
1104
1104
}
1105
1105
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
+
1106
1131
declare_lint_pass ! ( MutableTransmutes => [ MUTABLE_TRANSMUTES ] ) ;
1107
1132
1108
1133
impl < ' tcx > LateLintPass < ' tcx > for MutableTransmutes {
1109
1134
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
- }
1135
+ // Is our expr mem::transmute?
1136
+ let hir :: ExprKind :: Path ( ref qpath ) = expr . kind else { return } ;
1137
+ let def : Res = cx . qpath_res ( qpath , expr . hir_id ) ;
1138
+ let Res :: Def ( DefKind :: Fn , def_id ) = def else { return } ;
1139
+ if ! cx. tcx . is_intrinsic ( def_id , sym :: transmute ) {
1140
+ return ;
1116
1141
}
1142
+ let sig = cx. typeck_results ( ) . node_type ( expr. hir_id ) . fn_sig ( cx. tcx ) ;
1143
+ let input = sig. inputs ( ) . skip_binder ( ) [ 0 ] ;
1144
+ let output = sig. output ( ) . skip_binder ( ) ;
1117
1145
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 ;
1146
+ // For both checks we only care if the input is an immutable ref
1147
+ let & ty:: Ref ( _, input_deref, Mutability :: Not ) = input. kind ( ) else { return } ;
1148
+ match output. kind ( ) {
1149
+ // If the output is a mutable reference that's bad
1150
+ & ty:: Ref ( _, _, Mutability :: Mut ) => {
1151
+ cx. emit_span_lint ( MUTABLE_TRANSMUTES , expr. span , BuiltinMutablesTransmutes ) ;
1152
+ }
1153
+ & ty:: Ref ( _, output_deref, Mutability :: Not ) => {
1154
+ // If the input type doesn't have interior mutability but the output type does:
1155
+ if input_deref. is_freeze ( cx. tcx , cx. typing_env ( ) )
1156
+ && !output_deref. is_freeze ( cx. tcx , cx. typing_env ( ) )
1157
+ {
1158
+ cx. emit_span_lint (
1159
+ TRANSMUTE_TO_INTERIOR_MUTABILITY ,
1160
+ expr. span ,
1161
+ BuiltinTransmuteInteriorMutability { output_ty : output_deref } ,
1162
+ ) ;
1130
1163
}
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) ) ;
1135
1164
}
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)
1165
+ // The user is transmuting a reference into something else, warn...?
1166
+ _ => ( ) ,
1141
1167
}
1142
1168
}
1143
1169
}
0 commit comments