@@ -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,68 @@ 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
+
1131
+ declare_lint_pass ! ( TransmuteToInteriorMutability => [ TRANSMUTE_TO_INTERIOR_MUTABILITY ] ) ;
1106
1132
declare_lint_pass ! ( MutableTransmutes => [ MUTABLE_TRANSMUTES ] ) ;
1107
1133
1108
1134
impl < ' tcx > LateLintPass < ' tcx > for MutableTransmutes {
1109
1135
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 ;
1116
1142
}
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 ( ) ;
1117
1146
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
+ ) ;
1130
1164
}
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
1165
}
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
+ _ => ( ) ,
1141
1168
}
1142
1169
}
1143
1170
}
@@ -1596,6 +1623,7 @@ declare_lint_pass!(
1596
1623
NO_MANGLE_CONST_ITEMS ,
1597
1624
NO_MANGLE_GENERIC_ITEMS ,
1598
1625
MUTABLE_TRANSMUTES ,
1626
+ TRANSMUTE_TO_INTERIOR_MUTABILITY ,
1599
1627
UNSTABLE_FEATURES ,
1600
1628
UNREACHABLE_PUB ,
1601
1629
TYPE_ALIAS_BOUNDS ,
0 commit comments