1111use dep_graph:: DepNode ;
1212use hir:: def:: Def ;
1313use hir:: def_id:: DefId ;
14- use ty:: subst:: { Subst , Substs , EnumeratedItems } ;
15- use ty:: { TransmuteRestriction , TyCtxt } ;
16- use ty:: { self , Ty , TypeFoldable } ;
17-
18- use std:: fmt;
14+ use infer:: { InferCtxt , new_infer_ctxt} ;
15+ use traits:: ProjectionMode ;
16+ use ty:: { self , Ty , TyCtxt } ;
17+ use ty:: layout:: { LayoutError , Pointer , SizeSkeleton } ;
1918
2019use syntax:: abi:: Abi :: RustIntrinsic ;
2120use syntax:: ast;
@@ -24,219 +23,148 @@ use hir::intravisit::{self, Visitor, FnKind};
2423use hir;
2524
2625pub fn check_crate ( tcx : & TyCtxt ) {
27- let mut visitor = IntrinsicCheckingVisitor {
28- tcx : tcx,
29- param_envs : Vec :: new ( ) ,
30- dummy_sized_ty : tcx. types . isize ,
31- dummy_unsized_ty : tcx. mk_slice ( tcx. types . isize ) ,
26+ let mut visitor = ItemVisitor {
27+ tcx : tcx
3228 } ;
3329 tcx. visit_all_items_in_krate ( DepNode :: IntrinsicCheck , & mut visitor) ;
3430}
3531
36- struct IntrinsicCheckingVisitor < ' a , ' tcx : ' a > {
37- tcx : & ' a TyCtxt < ' tcx > ,
32+ struct ItemVisitor < ' a , ' tcx : ' a > {
33+ tcx : & ' a TyCtxt < ' tcx >
34+ }
3835
39- // As we traverse the AST, we keep a stack of the parameter
40- // environments for each function we encounter. When we find a
41- // call to `transmute`, we can check it in the context of the top
42- // of the stack (which ought not to be empty).
43- param_envs : Vec < ty:: ParameterEnvironment < ' a , ' tcx > > ,
36+ impl < ' a , ' tcx > ItemVisitor < ' a , ' tcx > {
37+ fn visit_const ( & mut self , item_id : ast:: NodeId , expr : & hir:: Expr ) {
38+ let param_env = ty:: ParameterEnvironment :: for_item ( self . tcx , item_id) ;
39+ let infcx = new_infer_ctxt ( self . tcx , & self . tcx . tables ,
40+ Some ( param_env) ,
41+ ProjectionMode :: Any ) ;
42+ let mut visitor = ExprVisitor {
43+ infcx : & infcx
44+ } ;
45+ visitor. visit_expr ( expr) ;
46+ }
47+ }
4448
45- // Dummy sized/unsized types that use to substitute for type
46- // parameters in order to estimate how big a type will be for any
47- // possible instantiation of the type parameters in scope. See
48- // `check_transmute` for more details.
49- dummy_sized_ty : Ty < ' tcx > ,
50- dummy_unsized_ty : Ty < ' tcx > ,
49+ struct ExprVisitor < ' a , ' tcx : ' a > {
50+ infcx : & ' a InferCtxt < ' a , ' tcx >
5151}
5252
53- impl < ' a , ' tcx > IntrinsicCheckingVisitor < ' a , ' tcx > {
53+ impl < ' a , ' tcx > ExprVisitor < ' a , ' tcx > {
5454 fn def_id_is_transmute ( & self , def_id : DefId ) -> bool {
55- let intrinsic = match self . tcx . lookup_item_type ( def_id) . ty . sty {
55+ let intrinsic = match self . infcx . tcx . lookup_item_type ( def_id) . ty . sty {
5656 ty:: TyFnDef ( _, _, ref bfty) => bfty. abi == RustIntrinsic ,
5757 _ => return false
5858 } ;
59- intrinsic && self . tcx . item_name ( def_id) . as_str ( ) == "transmute"
59+ intrinsic && self . infcx . tcx . item_name ( def_id) . as_str ( ) == "transmute"
6060 }
6161
6262 fn check_transmute ( & self , span : Span , from : Ty < ' tcx > , to : Ty < ' tcx > , id : ast:: NodeId ) {
63- // Find the parameter environment for the most recent function that
64- // we entered.
63+ let sk_from = SizeSkeleton :: compute ( from , self . infcx ) ;
64+ let sk_to = SizeSkeleton :: compute ( to , self . infcx ) ;
6565
66- let param_env = match self . param_envs . last ( ) {
67- Some ( p) => p,
68- None => {
69- span_bug ! (
70- span,
71- "transmute encountered outside of any fn" ) ;
66+ // Check for same size using the skeletons.
67+ if let ( Ok ( sk_from) , Ok ( sk_to) ) = ( sk_from, sk_to) {
68+ if sk_from. same_size ( sk_to) {
69+ return ;
7270 }
73- } ;
74-
75- // Simple case: no type parameters involved.
76- if
77- !from. has_param_types ( ) && !from. has_self_ty ( ) &&
78- !to. has_param_types ( ) && !to. has_self_ty ( )
79- {
80- let restriction = TransmuteRestriction {
81- span : span,
82- original_from : from,
83- original_to : to,
84- substituted_from : from,
85- substituted_to : to,
86- id : id,
87- } ;
88- self . push_transmute_restriction ( restriction) ;
89- return ;
90- }
9171
92- // The rules around type parameters are a bit subtle. We are
93- // checking these rules before monomorphization, so there may
94- // be unsubstituted type parameters present in the
95- // types. Obviously we cannot create LLVM types for those.
96- // However, if a type parameter appears only indirectly (i.e.,
97- // through a pointer), it does not necessarily affect the
98- // size, so that should be allowed. The only catch is that we
99- // DO want to be careful around unsized type parameters, since
100- // fat pointers have a different size than a thin pointer, and
101- // hence `&T` and `&U` have different sizes if `T : Sized` but
102- // `U : Sized` does not hold.
103- //
104- // However, it's not as simple as checking whether `T :
105- // Sized`, because even if `T : Sized` does not hold, that
106- // just means that `T` *may* not be sized. After all, even a
107- // type parameter `T: ?Sized` could be bound to a sized
108- // type. (Issue #20116)
109- //
110- // To handle this, we first check for "interior" type
111- // parameters, which are always illegal. If there are none of
112- // those, then we know that the only way that all type
113- // parameters `T` are referenced indirectly, e.g. via a
114- // pointer type like `&T`. In that case, we only care whether
115- // `T` is sized or not, because that influences whether `&T`
116- // is a thin or fat pointer.
117- //
118- // One could imagine establishing a sophisticated constraint
119- // system to ensure that the transmute is legal, but instead
120- // we do something brutally dumb. We just substitute dummy
121- // sized or unsized types for every type parameter in scope,
122- // exhaustively checking all possible combinations. Here are some examples:
123- //
124- // ```
125- // fn foo<T, U>() {
126- // // T=int, U=int
127- // }
128- //
129- // fn bar<T: ?Sized, U>() {
130- // // T=int, U=int
131- // // T=[int], U=int
132- // }
133- //
134- // fn baz<T: ?Sized, U: ?Sized>() {
135- // // T=int, U=int
136- // // T=[int], U=int
137- // // T=int, U=[int]
138- // // T=[int], U=[int]
139- // }
140- // ```
141- //
142- // In all cases, we keep the original unsubstituted types
143- // around for error reporting.
144-
145- let from_tc = from. type_contents ( self . tcx ) ;
146- let to_tc = to. type_contents ( self . tcx ) ;
147- if from_tc. interior_param ( ) || to_tc. interior_param ( ) {
148- span_err ! ( self . tcx. sess, span, E0139 ,
149- "cannot transmute to or from a type that contains \
150- unsubstituted type parameters") ;
151- return ;
72+ match ( & from. sty , sk_to) {
73+ ( & ty:: TyFnDef ( ..) , SizeSkeleton :: Known ( size_to) )
74+ if size_to == Pointer . size ( & self . infcx . tcx . data_layout ) => {
75+ // FIXME #19925 Remove this warning after a release cycle.
76+ let msg = format ! ( "`{}` is now zero-sized and has to be cast \
77+ to a pointer before transmuting to `{}`",
78+ from, to) ;
79+ self . infcx . tcx . sess . add_lint (
80+ :: lint:: builtin:: TRANSMUTE_FROM_FN_ITEM_TYPES , id, span, msg) ;
81+ return ;
82+ }
83+ _ => { }
84+ }
15285 }
15386
154- let mut substs = param_env. free_substs . clone ( ) ;
155- self . with_each_combination (
156- span,
157- param_env,
158- param_env. free_substs . types . iter_enumerated ( ) ,
159- & mut substs,
160- & mut |substs| {
161- let restriction = TransmuteRestriction {
162- span : span,
163- original_from : from,
164- original_to : to,
165- substituted_from : from. subst ( self . tcx , substs) ,
166- substituted_to : to. subst ( self . tcx , substs) ,
167- id : id,
168- } ;
169- self . push_transmute_restriction ( restriction) ;
170- } ) ;
171- }
172-
173- fn with_each_combination ( & self ,
174- span : Span ,
175- param_env : & ty:: ParameterEnvironment < ' a , ' tcx > ,
176- mut types_in_scope : EnumeratedItems < Ty < ' tcx > > ,
177- substs : & mut Substs < ' tcx > ,
178- callback : & mut FnMut ( & Substs < ' tcx > ) )
179- {
180- // This parameter invokes `callback` many times with different
181- // substitutions that replace all the parameters in scope with
182- // either `int` or `[int]`, depending on whether the type
183- // parameter is known to be sized. See big comment above for
184- // an explanation of why this is a reasonable thing to do.
185-
186- match types_in_scope. next ( ) {
187- None => {
188- debug ! ( "with_each_combination(substs={:?})" ,
189- substs) ;
190-
191- callback ( substs) ;
87+ // Try to display a sensible error with as much information as possible.
88+ let skeleton_string = |ty : Ty < ' tcx > , sk| {
89+ match sk {
90+ Ok ( SizeSkeleton :: Known ( size) ) => {
91+ format ! ( "{} bits" , size. bits( ) )
92+ }
93+ Ok ( SizeSkeleton :: Pointer { tail, .. } ) => {
94+ format ! ( "pointer to {}" , tail)
95+ }
96+ Err ( LayoutError :: Unknown ( bad) ) => {
97+ if bad == ty {
98+ format ! ( "size can vary" )
99+ } else {
100+ format ! ( "size can vary because of {}" , bad)
101+ }
102+ }
103+ Err ( err) => err. to_string ( )
192104 }
105+ } ;
193106
194- Some ( ( space, index, & param_ty) ) => {
195- debug ! ( "with_each_combination: space={:?}, index={}, param_ty={:?}" ,
196- space, index, param_ty) ;
197-
198- if !param_ty. is_sized ( param_env, span) {
199- debug ! ( "with_each_combination: param_ty is not known to be sized" ) ;
107+ span_err ! ( self . infcx. tcx. sess, span, E0512 ,
108+ "transmute called with differently sized types: \
109+ {} ({}) to {} ({})",
110+ from, skeleton_string( from, sk_from) ,
111+ to, skeleton_string( to, sk_to) ) ;
112+ }
113+ }
200114
201- substs. types . get_mut_slice ( space) [ index] = self . dummy_unsized_ty ;
202- self . with_each_combination ( span, param_env, types_in_scope. clone ( ) ,
203- substs, callback) ;
204- }
115+ impl < ' a , ' tcx , ' v > Visitor < ' v > for ItemVisitor < ' a , ' tcx > {
116+ // const, static and N in [T; N].
117+ fn visit_expr ( & mut self , expr : & hir:: Expr ) {
118+ let infcx = new_infer_ctxt ( self . tcx , & self . tcx . tables ,
119+ None , ProjectionMode :: Any ) ;
120+ let mut visitor = ExprVisitor {
121+ infcx : & infcx
122+ } ;
123+ visitor. visit_expr ( expr) ;
124+ }
205125
206- substs. types . get_mut_slice ( space) [ index] = self . dummy_sized_ty ;
207- self . with_each_combination ( span, param_env, types_in_scope,
208- substs, callback) ;
209- }
126+ fn visit_trait_item ( & mut self , item : & hir:: TraitItem ) {
127+ if let hir:: ConstTraitItem ( _, Some ( ref expr) ) = item. node {
128+ self . visit_const ( item. id , expr) ;
129+ } else {
130+ intravisit:: walk_trait_item ( self , item) ;
210131 }
211132 }
212133
213- fn push_transmute_restriction ( & self , restriction : TransmuteRestriction < ' tcx > ) {
214- debug ! ( "Pushing transmute restriction: {:?}" , restriction) ;
215- self . tcx . transmute_restrictions . borrow_mut ( ) . push ( restriction) ;
134+ fn visit_impl_item ( & mut self , item : & hir:: ImplItem ) {
135+ if let hir:: ImplItemKind :: Const ( _, ref expr) = item. node {
136+ self . visit_const ( item. id , expr) ;
137+ } else {
138+ intravisit:: walk_impl_item ( self , item) ;
139+ }
216140 }
217- }
218141
219- impl < ' a , ' tcx , ' v > Visitor < ' v > for IntrinsicCheckingVisitor < ' a , ' tcx > {
220142 fn visit_fn ( & mut self , fk : FnKind < ' v > , fd : & ' v hir:: FnDecl ,
221143 b : & ' v hir:: Block , s : Span , id : ast:: NodeId ) {
222144 match fk {
223145 FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => {
224146 let param_env = ty:: ParameterEnvironment :: for_item ( self . tcx , id) ;
225- self . param_envs . push ( param_env) ;
226- intravisit:: walk_fn ( self , fk, fd, b, s) ;
227- self . param_envs . pop ( ) ;
147+ let infcx = new_infer_ctxt ( self . tcx , & self . tcx . tables ,
148+ Some ( param_env) ,
149+ ProjectionMode :: Any ) ;
150+ let mut visitor = ExprVisitor {
151+ infcx : & infcx
152+ } ;
153+ visitor. visit_fn ( fk, fd, b, s, id) ;
228154 }
229155 FnKind :: Closure ( ..) => {
230- intravisit :: walk_fn ( self , fk , fd , b , s ) ;
156+ span_bug ! ( s , "intrinsicck: closure outside of function" )
231157 }
232158 }
233159 }
160+ }
234161
162+ impl < ' a , ' tcx , ' v > Visitor < ' v > for ExprVisitor < ' a , ' tcx > {
235163 fn visit_expr ( & mut self , expr : & hir:: Expr ) {
236164 if let hir:: ExprPath ( ..) = expr. node {
237- match self . tcx . resolve_expr ( expr) {
165+ match self . infcx . tcx . resolve_expr ( expr) {
238166 Def :: Fn ( did) if self . def_id_is_transmute ( did) => {
239- let typ = self . tcx . node_id_to_type ( expr. id ) ;
167+ let typ = self . infcx . tcx . node_id_to_type ( expr. id ) ;
240168 match typ. sty {
241169 ty:: TyFnDef ( _, _, ref bare_fn_ty) if bare_fn_ty. abi == RustIntrinsic => {
242170 if let ty:: FnConverging ( to) = bare_fn_ty. sig . 0 . output {
@@ -256,14 +184,3 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
256184 intravisit:: walk_expr ( self , expr) ;
257185 }
258186}
259-
260- impl < ' tcx > fmt:: Debug for TransmuteRestriction < ' tcx > {
261- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
262- write ! ( f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))" ,
263- self . id,
264- self . original_from,
265- self . original_to,
266- self . substituted_from,
267- self . substituted_to)
268- }
269- }
0 commit comments