10
10
11
11
use metadata:: csearch;
12
12
use middle:: def:: DefFn ;
13
- use middle:: subst:: Subst ;
13
+ use middle:: subst:: { Subst , Substs , EnumeratedItems } ;
14
14
use middle:: ty:: { TransmuteRestriction , ctxt, ty_bare_fn} ;
15
15
use middle:: ty:: { mod, Ty } ;
16
+ use util:: ppaux:: Repr ;
16
17
17
18
use syntax:: abi:: RustIntrinsic ;
18
19
use syntax:: ast:: DefId ;
@@ -23,52 +24,31 @@ use syntax::parse::token;
23
24
use syntax:: visit:: Visitor ;
24
25
use syntax:: visit;
25
26
26
- fn type_size_is_affected_by_type_parameters < ' tcx > ( tcx : & ty:: ctxt < ' tcx > , typ : Ty < ' tcx > )
27
- -> bool {
28
- let mut result = false ;
29
- ty:: maybe_walk_ty ( typ, |typ| {
30
- match typ. sty {
31
- ty:: ty_uniq( _) | ty:: ty_ptr( _) | ty:: ty_rptr( ..) |
32
- ty:: ty_bare_fn( ..) | ty:: ty_closure( ..) => {
33
- false
34
- }
35
- ty:: ty_param( _) => {
36
- result = true ;
37
- // No need to continue; we now know the result.
38
- false
39
- }
40
- ty:: ty_enum( did, substs) => {
41
- for enum_variant in ( * ty:: enum_variants ( tcx, did) ) . iter ( ) {
42
- for argument_type in enum_variant. args . iter ( ) {
43
- let argument_type = argument_type. subst ( tcx, substs) ;
44
- result = result ||
45
- type_size_is_affected_by_type_parameters (
46
- tcx,
47
- argument_type) ;
48
- }
49
- }
50
-
51
- // Don't traverse substitutions.
52
- false
53
- }
54
- ty:: ty_struct( did, substs) => {
55
- for field in ty:: struct_fields ( tcx, did, substs) . iter ( ) {
56
- result = result ||
57
- type_size_is_affected_by_type_parameters ( tcx,
58
- field. mt . ty ) ;
59
- }
60
-
61
- // Don't traverse substitutions.
62
- false
63
- }
64
- _ => true ,
65
- }
66
- } ) ;
67
- result
27
+ pub fn check_crate ( tcx : & ctxt ) {
28
+ let mut visitor = IntrinsicCheckingVisitor {
29
+ tcx : tcx,
30
+ param_envs : Vec :: new ( ) ,
31
+ dummy_sized_ty : ty:: mk_int ( ) ,
32
+ dummy_unsized_ty : ty:: mk_vec ( tcx, ty:: mk_int ( ) , None ) ,
33
+ } ;
34
+ visit:: walk_crate ( & mut visitor, tcx. map . krate ( ) ) ;
68
35
}
69
36
70
37
struct IntrinsicCheckingVisitor < ' a , ' tcx : ' a > {
71
38
tcx : & ' a ctxt < ' tcx > ,
39
+
40
+ // As we traverse the AST, we keep a stack of the parameter
41
+ // environments for each function we encounter. When we find a
42
+ // call to `transmute`, we can check it in the context of the top
43
+ // of the stack (which ought not to be empty).
44
+ param_envs : Vec < ty:: ParameterEnvironment < ' tcx > > ,
45
+
46
+ // Dummy sized/unsized types that use to substitute for type
47
+ // parameters in order to estimate how big a type will be for any
48
+ // possible instantiation of the type parameters in scope. See
49
+ // `check_transmute` for more details.
50
+ dummy_sized_ty : Ty < ' tcx > ,
51
+ dummy_unsized_ty : Ty < ' tcx > ,
72
52
}
73
53
74
54
impl < ' a , ' tcx > IntrinsicCheckingVisitor < ' a , ' tcx > {
@@ -97,26 +77,175 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
97
77
}
98
78
99
79
fn check_transmute ( & self , span : Span , from : Ty < ' tcx > , to : Ty < ' tcx > , id : ast:: NodeId ) {
100
- if type_size_is_affected_by_type_parameters ( self . tcx , from) {
80
+ // Find the parameter environment for the most recent function that
81
+ // we entered.
82
+
83
+ let param_env = match self . param_envs . last ( ) {
84
+ Some ( p) => p,
85
+ None => {
86
+ self . tcx . sess . span_bug (
87
+ span,
88
+ "transmute encountered outside of any fn" ) ;
89
+ }
90
+ } ;
91
+
92
+ // Simple case: no type parameters involved.
93
+ if
94
+ !ty:: type_has_params ( from) && !ty:: type_has_self ( from) &&
95
+ !ty:: type_has_params ( to) && !ty:: type_has_self ( to)
96
+ {
97
+ let restriction = TransmuteRestriction {
98
+ span : span,
99
+ original_from : from,
100
+ original_to : to,
101
+ substituted_from : from,
102
+ substituted_to : to,
103
+ id : id,
104
+ } ;
105
+ self . push_transmute_restriction ( restriction) ;
106
+ return ;
107
+ }
108
+
109
+ // The rules around type parameters are a bit subtle. We are
110
+ // checking these rules before monomorphization, so there may
111
+ // be unsubstituted type parameters present in the
112
+ // types. Obviously we cannot create LLVM types for those.
113
+ // However, if a type parameter appears only indirectly (i.e.,
114
+ // through a pointer), it does not necessarily affect the
115
+ // size, so that should be allowed. The only catch is that we
116
+ // DO want to be careful around unsized type parameters, since
117
+ // fat pointers have a different size than a thin pointer, and
118
+ // hence `&T` and `&U` have different sizes if `T : Sized` but
119
+ // `U : Sized` does not hold.
120
+ //
121
+ // However, it's not as simple as checking whether `T :
122
+ // Sized`, because even if `T : Sized` does not hold, that
123
+ // just means that `T` *may* not be sized. After all, even a
124
+ // type parameter `Sized? T` could be bound to a sized
125
+ // type. (Issue #20116)
126
+ //
127
+ // To handle this, we first check for "interior" type
128
+ // parameters, which are always illegal. If there are none of
129
+ // those, then we know that the only way that all type
130
+ // parameters `T` are referenced indirectly, e.g. via a
131
+ // pointer type like `&T`. In that case, we only care whether
132
+ // `T` is sized or not, because that influences whether `&T`
133
+ // is a thin or fat pointer.
134
+ //
135
+ // One could imagine establishing a sophisticated constraint
136
+ // system to ensure that the transmute is legal, but instead
137
+ // we do something brutally dumb. We just substitute dummy
138
+ // sized or unsized types for every type parameter in scope,
139
+ // exhaustively checking all possible combinations. Here are some examples:
140
+ //
141
+ // ```
142
+ // fn foo<T,U>() {
143
+ // // T=int, U=int
144
+ // }
145
+ //
146
+ // fn bar<Sized? T,U>() {
147
+ // // T=int, U=int
148
+ // // T=[int], U=int
149
+ // }
150
+ //
151
+ // fn baz<Sized? T, Sized?U>() {
152
+ // // T=int, U=int
153
+ // // T=[int], U=int
154
+ // // T=int, U=[int]
155
+ // // T=[int], U=[int]
156
+ // }
157
+ // ```
158
+ //
159
+ // In all cases, we keep the original unsubstituted types
160
+ // around for error reporting.
161
+
162
+ let from_tc = ty:: type_contents ( self . tcx , from) ;
163
+ let to_tc = ty:: type_contents ( self . tcx , to) ;
164
+ if from_tc. interior_param ( ) || to_tc. interior_param ( ) {
101
165
span_err ! ( self . tcx. sess, span, E0139 ,
102
- "cannot transmute from a type that contains type parameters" ) ;
166
+ "cannot transmute to or from a type that contains \
167
+ type parameters in its interior") ;
168
+ return ;
103
169
}
104
- if type_size_is_affected_by_type_parameters ( self . tcx , to) {
105
- span_err ! ( self . tcx. sess, span, E0140 ,
106
- "cannot transmute to a type that contains type parameters" ) ;
170
+
171
+ let mut substs = param_env. free_substs . clone ( ) ;
172
+ self . with_each_combination (
173
+ param_env,
174
+ param_env. free_substs . types . iter_enumerated ( ) ,
175
+ & mut substs,
176
+ & mut |substs| {
177
+ let restriction = TransmuteRestriction {
178
+ span : span,
179
+ original_from : from,
180
+ original_to : to,
181
+ substituted_from : from. subst ( self . tcx , substs) ,
182
+ substituted_to : to. subst ( self . tcx , substs) ,
183
+ id : id,
184
+ } ;
185
+ self . push_transmute_restriction ( restriction) ;
186
+ } ) ;
187
+ }
188
+
189
+ fn with_each_combination ( & self ,
190
+ param_env : & ty:: ParameterEnvironment < ' tcx > ,
191
+ mut types_in_scope : EnumeratedItems < Ty < ' tcx > > ,
192
+ substs : & mut Substs < ' tcx > ,
193
+ callback : & mut FnMut ( & Substs < ' tcx > ) )
194
+ {
195
+ // This parameter invokes `callback` many times with different
196
+ // substitutions that replace all the parameters in scope with
197
+ // either `int` or `[int]`, depending on whether the type
198
+ // parameter is known to be sized. See big comment above for
199
+ // an explanation of why this is a reasonable thing to do.
200
+
201
+ match types_in_scope. next ( ) {
202
+ None => {
203
+ debug ! ( "with_each_combination(substs={})" ,
204
+ substs. repr( self . tcx) ) ;
205
+
206
+ callback. call_mut ( ( substs, ) ) ;
207
+ }
208
+
209
+ Some ( ( space, index, & param_ty) ) => {
210
+ debug ! ( "with_each_combination: space={}, index={}, param_ty={}" ,
211
+ space, index, param_ty. repr( self . tcx) ) ;
212
+
213
+ if !ty:: type_is_sized ( self . tcx , param_ty, param_env) {
214
+ debug ! ( "with_each_combination: param_ty is not known to be sized" ) ;
215
+
216
+ substs. types . get_mut_slice ( space) [ index] = self . dummy_unsized_ty ;
217
+ self . with_each_combination ( param_env, types_in_scope. clone ( ) , substs, callback) ;
218
+ }
219
+
220
+ substs. types . get_mut_slice ( space) [ index] = self . dummy_sized_ty ;
221
+ self . with_each_combination ( param_env, types_in_scope, substs, callback) ;
222
+ }
107
223
}
224
+ }
108
225
109
- let restriction = TransmuteRestriction {
110
- span : span,
111
- from : from,
112
- to : to,
113
- id : id,
114
- } ;
226
+ fn push_transmute_restriction ( & self , restriction : TransmuteRestriction < ' tcx > ) {
227
+ debug ! ( "Pushing transmute restriction: {}" , restriction. repr( self . tcx) ) ;
115
228
self . tcx . transmute_restrictions . borrow_mut ( ) . push ( restriction) ;
116
229
}
117
230
}
118
231
119
232
impl < ' a , ' tcx , ' v > Visitor < ' v > for IntrinsicCheckingVisitor < ' a , ' tcx > {
233
+ fn visit_fn ( & mut self , fk : visit:: FnKind < ' v > , fd : & ' v ast:: FnDecl ,
234
+ b : & ' v ast:: Block , s : Span , id : ast:: NodeId ) {
235
+ match fk {
236
+ visit:: FkItemFn ( ..) | visit:: FkMethod ( ..) => {
237
+ let param_env = ty:: ParameterEnvironment :: for_item ( self . tcx , id) ;
238
+ self . param_envs . push ( param_env) ;
239
+ visit:: walk_fn ( self , fk, fd, b, s) ;
240
+ self . param_envs . pop ( ) ;
241
+ }
242
+ visit:: FkFnBlock ( ..) => {
243
+ visit:: walk_fn ( self , fk, fd, b, s) ;
244
+ }
245
+ }
246
+
247
+ }
248
+
120
249
fn visit_expr ( & mut self , expr : & ast:: Expr ) {
121
250
if let ast:: ExprPath ( ..) = expr. node {
122
251
match ty:: resolve_expr ( self . tcx , expr) {
@@ -144,7 +273,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
144
273
}
145
274
}
146
275
147
- pub fn check_crate ( tcx : & ctxt ) {
148
- visit:: walk_crate ( & mut IntrinsicCheckingVisitor { tcx : tcx } ,
149
- tcx. map . krate ( ) ) ;
276
+ impl < ' tcx > Repr < ' tcx > for TransmuteRestriction < ' tcx > {
277
+ fn repr ( & self , tcx : & ty:: ctxt < ' tcx > ) -> String {
278
+ format ! ( "TransmuteRestriction(id={}, original=({},{}), substituted=({},{}))" ,
279
+ self . id,
280
+ self . original_from. repr( tcx) ,
281
+ self . original_to. repr( tcx) ,
282
+ self . substituted_from. repr( tcx) ,
283
+ self . substituted_to. repr( tcx) )
284
+ }
150
285
}
0 commit comments