@@ -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- 
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 :: codegen ( 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