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