11
11
use dep_graph:: DepNode ;
12
12
use hir:: def:: Def ;
13
13
use 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 } ;
19
18
20
19
use syntax:: abi:: Abi :: RustIntrinsic ;
21
20
use syntax:: ast;
@@ -24,219 +23,148 @@ use hir::intravisit::{self, Visitor, FnKind};
24
23
use hir;
25
24
26
25
pub 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
32
28
} ;
33
29
tcx. visit_all_items_in_krate ( DepNode :: IntrinsicCheck , & mut visitor) ;
34
30
}
35
31
36
- struct IntrinsicCheckingVisitor < ' a , ' tcx : ' a > {
37
- tcx : & ' a TyCtxt < ' tcx > ,
32
+ struct ItemVisitor < ' a , ' tcx : ' a > {
33
+ tcx : & ' a TyCtxt < ' tcx >
34
+ }
38
35
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
+ }
44
48
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 >
51
51
}
52
52
53
- impl < ' a , ' tcx > IntrinsicCheckingVisitor < ' a , ' tcx > {
53
+ impl < ' a , ' tcx > ExprVisitor < ' a , ' tcx > {
54
54
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 {
56
56
ty:: TyFnDef ( _, _, ref bfty) => bfty. abi == RustIntrinsic ,
57
57
_ => return false
58
58
} ;
59
- intrinsic && self . tcx . item_name ( def_id) . as_str ( ) == "transmute"
59
+ intrinsic && self . infcx . tcx . item_name ( def_id) . as_str ( ) == "transmute"
60
60
}
61
61
62
62
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 ) ;
65
65
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 ;
72
70
}
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
- }
91
71
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
+ }
152
85
}
153
86
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 ( )
192
104
}
105
+ } ;
193
106
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
+ }
200
114
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
+ }
205
125
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) ;
210
131
}
211
132
}
212
133
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
+ }
216
140
}
217
- }
218
141
219
- impl < ' a , ' tcx , ' v > Visitor < ' v > for IntrinsicCheckingVisitor < ' a , ' tcx > {
220
142
fn visit_fn ( & mut self , fk : FnKind < ' v > , fd : & ' v hir:: FnDecl ,
221
143
b : & ' v hir:: Block , s : Span , id : ast:: NodeId ) {
222
144
match fk {
223
145
FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => {
224
146
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) ;
228
154
}
229
155
FnKind :: Closure ( ..) => {
230
- intravisit :: walk_fn ( self , fk , fd , b , s ) ;
156
+ span_bug ! ( s , "intrinsicck: closure outside of function" )
231
157
}
232
158
}
233
159
}
160
+ }
234
161
162
+ impl < ' a , ' tcx , ' v > Visitor < ' v > for ExprVisitor < ' a , ' tcx > {
235
163
fn visit_expr ( & mut self , expr : & hir:: Expr ) {
236
164
if let hir:: ExprPath ( ..) = expr. node {
237
- match self . tcx . resolve_expr ( expr) {
165
+ match self . infcx . tcx . resolve_expr ( expr) {
238
166
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 ) ;
240
168
match typ. sty {
241
169
ty:: TyFnDef ( _, _, ref bare_fn_ty) if bare_fn_ty. abi == RustIntrinsic => {
242
170
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> {
256
184
intravisit:: walk_expr ( self , expr) ;
257
185
}
258
186
}
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