@@ -7,11 +7,10 @@ use rustc_hir as hir;
77use rustc_index:: Idx ;
88use rustc_middle:: bug;
99use 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 ;
1112use tracing:: trace;
1213
13- use super :: FnCtxt ;
14-
1514/// If the type is `Option<T>`, it will return `T`, otherwise
1615/// the type itself. Works on most `Option`-like types.
1716fn 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> {
3938 ty
4039}
4140
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" )
5551 } 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)
6156 }
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" ) ;
7157 }
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+ )
7589 }
90+ } ;
7691
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) ;
8195
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+ }
87100
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 ;
101111 }
102112
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 ;
155126 }
156127 }
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+ }
157154}
0 commit comments