@@ -8,10 +8,9 @@ use rustc_index::Idx;
8
8
use rustc_middle:: bug;
9
9
use rustc_middle:: ty:: layout:: { LayoutError , SizeSkeleton } ;
10
10
use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeVisitableExt } ;
11
+ use rustc_span:: def_id:: LocalDefId ;
11
12
use tracing:: trace;
12
13
13
- use super :: FnCtxt ;
14
-
15
14
/// If the type is `Option<T>`, it will return `T`, otherwise
16
15
/// the type itself. Works on most `Option`-like types.
17
16
fn unpack_option_like < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
@@ -39,119 +38,115 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
39
38
ty
40
39
}
41
40
42
- impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
43
- /// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques,
44
- /// and we shouldn't need to check anything here if the typeck results are tainted.
45
- pub ( crate ) fn check_transmute ( & self , from : Ty < ' tcx > , to : Ty < ' tcx > , hir_id : HirId ) {
46
- let tcx = self . tcx ;
47
- let dl = & tcx. data_layout ;
48
- let span = tcx. hir_span ( hir_id) ;
49
- let normalize = |ty| {
50
- let ty = self . resolve_vars_if_possible ( ty) ;
51
- if let Ok ( ty) =
52
- self . tcx . try_normalize_erasing_regions ( self . typing_env ( self . param_env ) , ty)
53
- {
54
- ty
55
- } else {
56
- Ty :: new_error_with_message (
57
- tcx,
58
- span,
59
- "tried to normalize non-wf type in check_transmute" ,
60
- )
61
- }
62
- } ;
63
- let from = normalize ( from) ;
64
- let to = normalize ( to) ;
65
- trace ! ( ?from, ?to) ;
66
- if from. has_non_region_infer ( ) || to. has_non_region_infer ( ) {
67
- // Note: this path is currently not reached in any test, so any
68
- // example that triggers this would be worth minimizing and
69
- // converting into a test.
70
- self . dcx ( ) . span_bug ( span, "argument to transmute has inference variables" ) ;
71
- }
72
- // Transmutes that are only changing lifetimes are always ok.
73
- if from == to {
74
- return ;
41
+ fn check_transmute < ' tcx > (
42
+ tcx : TyCtxt < ' tcx > ,
43
+ typing_env : ty:: TypingEnv < ' tcx > ,
44
+ from : Ty < ' tcx > ,
45
+ to : Ty < ' tcx > ,
46
+ hir_id : HirId ,
47
+ ) {
48
+ let dl = & tcx. data_layout ;
49
+ let span = tcx. hir_span ( hir_id) ;
50
+ let normalize = |ty| {
51
+ if let Ok ( ty) = tcx. try_normalize_erasing_regions ( typing_env, ty) {
52
+ ty
53
+ } else {
54
+ Ty :: new_error_with_message (
55
+ tcx,
56
+ span,
57
+ format ! ( "tried to normalize non-wf type {ty:#?} in check_transmute" ) ,
58
+ )
75
59
}
60
+ } ;
61
+ let from = normalize ( from) ;
62
+ let to = normalize ( to) ;
63
+ trace ! ( ?from, ?to) ;
64
+ if from. has_non_region_infer ( ) || to. has_non_region_infer ( ) {
65
+ // Note: this path is currently not reached in any test, so any
66
+ // example that triggers this would be worth minimizing and
67
+ // converting into a test.
68
+ tcx. sess . dcx ( ) . span_bug ( span, "argument to transmute has inference variables" ) ;
69
+ }
70
+ // Transmutes that are only changing lifetimes are always ok.
71
+ if from == to {
72
+ return ;
73
+ }
76
74
77
- let skel = |ty| SizeSkeleton :: compute ( ty, tcx, self . typing_env ( self . param_env ) ) ;
78
- let sk_from = skel ( from) ;
79
- let sk_to = skel ( to) ;
80
- trace ! ( ?sk_from, ?sk_to) ;
75
+ let skel = |ty| SizeSkeleton :: compute ( ty, tcx, typing_env) ;
76
+ let sk_from = skel ( from) ;
77
+ let sk_to = skel ( to) ;
78
+ trace ! ( ?sk_from, ?sk_to) ;
81
79
82
- // Check for same size using the skeletons.
83
- if let ( Ok ( sk_from) , Ok ( sk_to) ) = ( sk_from, sk_to) {
84
- if sk_from. same_size ( sk_to) {
85
- return ;
86
- }
80
+ // Check for same size using the skeletons.
81
+ if let ( Ok ( sk_from) , Ok ( sk_to) ) = ( sk_from, sk_to) {
82
+ if sk_from. same_size ( sk_to) {
83
+ return ;
84
+ }
87
85
88
- // Special-case transmuting from `typeof(function)` and
89
- // `Option<typeof(function)>` to present a clearer error.
90
- let from = unpack_option_like ( tcx, from) ;
91
- if let ( & ty:: FnDef ( ..) , SizeSkeleton :: Known ( size_to, _) ) = ( from. kind ( ) , sk_to)
92
- && size_to == Pointer ( dl. instruction_address_space ) . size ( & tcx)
93
- {
94
- struct_span_code_err ! ( self . dcx( ) , span, E0591 , "can't transmute zero-sized type" )
95
- . with_note ( format ! ( "source type: {from}" ) )
96
- . with_note ( format ! ( "target type: {to}" ) )
97
- . with_help ( "cast with `as` to a pointer instead" )
98
- . emit ( ) ;
99
- return ;
100
- }
86
+ // Special-case transmuting from `typeof(function)` and
87
+ // `Option<typeof(function)>` to present a clearer error.
88
+ let from = unpack_option_like ( tcx, from) ;
89
+ if let ( & ty:: FnDef ( ..) , SizeSkeleton :: Known ( size_to, _) ) = ( from. kind ( ) , sk_to)
90
+ && size_to == Pointer ( dl. instruction_address_space ) . size ( & tcx)
91
+ {
92
+ struct_span_code_err ! ( tcx. sess. dcx( ) , span, E0591 , "can't transmute zero-sized type" )
93
+ . with_note ( format ! ( "source type: {from}" ) )
94
+ . with_note ( format ! ( "target type: {to}" ) )
95
+ . with_help ( "cast with `as` to a pointer instead" )
96
+ . emit ( ) ;
97
+ return ;
101
98
}
99
+ }
102
100
103
- // Try to display a sensible error with as much information as possible.
104
- let skeleton_string = |ty : Ty < ' tcx > , sk : Result < _ , & _ > | match sk {
105
- Ok ( SizeSkeleton :: Pointer { tail, .. } ) => format ! ( "pointer to `{tail}`" ) ,
106
- Ok ( SizeSkeleton :: Known ( size, _) ) => {
107
- if let Some ( v) = u128:: from ( size. bytes ( ) ) . checked_mul ( 8 ) {
108
- format ! ( "{v} bits" )
109
- } else {
110
- // `u128` should definitely be able to hold the size of different architectures
111
- // larger sizes should be reported as error `are too big for the target architecture`
112
- // otherwise we have a bug somewhere
113
- bug ! ( "{:?} overflow for u128" , size)
114
- }
115
- }
116
- Ok ( SizeSkeleton :: Generic ( size) ) => {
117
- if let Some ( size) =
118
- self . try_structurally_resolve_const ( span, size) . try_to_target_usize ( tcx)
119
- {
120
- format ! ( "{size} bytes" )
121
- } else {
122
- format ! ( "generic size {size}" )
123
- }
124
- }
125
- Err ( LayoutError :: TooGeneric ( bad) ) => {
126
- if * bad == ty {
127
- "this type does not have a fixed size" . to_owned ( )
128
- } else {
129
- format ! ( "size can vary because of {bad}" )
130
- }
101
+ // Try to display a sensible error with as much information as possible.
102
+ let skeleton_string = |ty : Ty < ' tcx > , sk : Result < _ , & _ > | match sk {
103
+ Ok ( SizeSkeleton :: Pointer { tail, .. } ) => format ! ( "pointer to `{tail}`" ) ,
104
+ Ok ( SizeSkeleton :: Known ( size, _) ) => {
105
+ if let Some ( v) = u128:: from ( size. bytes ( ) ) . checked_mul ( 8 ) {
106
+ format ! ( "{v} bits" )
107
+ } else {
108
+ // `u128` should definitely be able to hold the size of different architectures
109
+ // larger sizes should be reported as error `are too big for the target architecture`
110
+ // otherwise we have a bug somewhere
111
+ bug ! ( "{:?} overflow for u128" , size)
131
112
}
132
- Err ( err) => err. to_string ( ) ,
133
- } ;
134
-
135
- let mut err = struct_span_code_err ! (
136
- self . dcx( ) ,
137
- span,
138
- E0512 ,
139
- "cannot transmute between types of different sizes, \
140
- or dependently-sized types"
141
- ) ;
142
- if from == to {
143
- err. note ( format ! ( "`{from}` does not have a fixed size" ) ) ;
144
- err. emit ( ) ;
145
- } else {
146
- err. note ( format ! ( "source type: `{}` ({})" , from, skeleton_string( from, sk_from) ) )
147
- . note ( format ! ( "target type: `{}` ({})" , to, skeleton_string( to, sk_to) ) ) ;
148
- if let Err ( LayoutError :: ReferencesError ( _) ) = sk_from {
149
- err. delay_as_bug ( ) ;
150
- } else if let Err ( LayoutError :: ReferencesError ( _) ) = sk_to {
151
- err. delay_as_bug ( ) ;
113
+ }
114
+ Ok ( SizeSkeleton :: Generic ( size) ) => {
115
+ format ! ( "generic size {size}" )
116
+ }
117
+ Err ( LayoutError :: TooGeneric ( bad) ) => {
118
+ if * bad == ty {
119
+ "this type does not have a fixed size" . to_owned ( )
152
120
} else {
153
- err . emit ( ) ;
121
+ format ! ( "size can vary because of {bad}" )
154
122
}
155
123
}
124
+ Err ( err) => err. to_string ( ) ,
125
+ } ;
126
+
127
+ let mut err = struct_span_code_err ! (
128
+ tcx. sess. dcx( ) ,
129
+ span,
130
+ E0512 ,
131
+ "cannot transmute between types of different sizes, or dependently-sized types"
132
+ ) ;
133
+ if from == to {
134
+ err. note ( format ! ( "`{from}` does not have a fixed size" ) ) ;
135
+ err. emit ( ) ;
136
+ } else {
137
+ err. note ( format ! ( "source type: `{}` ({})" , from, skeleton_string( from, sk_from) ) ) ;
138
+ err. note ( format ! ( "target type: `{}` ({})" , to, skeleton_string( to, sk_to) ) ) ;
139
+ err. emit ( ) ;
140
+ }
141
+ }
142
+
143
+ pub ( crate ) fn check_transmutes ( tcx : TyCtxt < ' _ > , owner : LocalDefId ) {
144
+ assert ! ( !tcx. is_typeck_child( owner. to_def_id( ) ) ) ;
145
+ let typeck_results = tcx. typeck ( owner) ;
146
+ let None = typeck_results. tainted_by_errors else { return } ;
147
+
148
+ let typing_env = ty:: TypingEnv :: post_analysis ( tcx, owner) ;
149
+ for & ( from, to, hir_id) in & typeck_results. transmutes_to_check {
150
+ check_transmute ( tcx, typing_env, from, to, hir_id) ;
156
151
}
157
152
}
0 commit comments