8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use middle:: subst:: { SelfSpace } ;
11
+ use middle:: subst:: { SelfSpace , FnSpace } ;
12
12
use middle:: traits;
13
13
use middle:: traits:: { SelectionError , OutputTypeParameterMismatch , Overflow , Unimplemented } ;
14
14
use middle:: traits:: { Obligation , obligation_for_builtin_bound} ;
@@ -21,8 +21,7 @@ use middle::typeck::infer;
21
21
use std:: rc:: Rc ;
22
22
use syntax:: ast;
23
23
use syntax:: codemap:: Span ;
24
- use util:: ppaux:: UserString ;
25
- use util:: ppaux:: Repr ;
24
+ use util:: ppaux:: { UserString , Repr , ty_to_string} ;
26
25
27
26
pub fn check_object_cast ( fcx : & FnCtxt ,
28
27
cast_expr : & ast:: Expr ,
@@ -46,6 +45,7 @@ pub fn check_object_cast(fcx: &FnCtxt,
46
45
47
46
// Ensure that if ~T is cast to ~Trait, then T : Trait
48
47
push_cast_obligation ( fcx, cast_expr, object_trait, referent_ty) ;
48
+ check_object_safety ( fcx. tcx ( ) , object_trait, source_expr. span ) ;
49
49
}
50
50
51
51
( & ty:: ty_rptr( referent_region, ty:: mt { ty : referent_ty,
@@ -68,6 +68,8 @@ pub fn check_object_cast(fcx: &FnCtxt,
68
68
infer:: RelateObjectBound ( source_expr. span ) ,
69
69
target_region,
70
70
referent_region) ;
71
+
72
+ check_object_safety ( fcx. tcx ( ) , object_trait, source_expr. span ) ;
71
73
}
72
74
}
73
75
@@ -128,6 +130,103 @@ pub fn check_object_cast(fcx: &FnCtxt,
128
130
}
129
131
}
130
132
133
+ // Check that a trait is 'object-safe'. This should be checked whenever a trait object
134
+ // is created (by casting or coercion, etc.). A trait is object-safe if all its
135
+ // methods are object-safe. A trait method is object-safe if it does not take
136
+ // self by value, has no type parameters and does not use the `Self` type, except
137
+ // in self position.
138
+ pub fn check_object_safety ( tcx : & ty:: ctxt , object_trait : & ty:: TyTrait , span : Span ) {
139
+ // Skip the fn_once lang item trait since only the compiler should call
140
+ // `call_once` which is the method which takes self by value. What could go
141
+ // wrong?
142
+ match tcx. lang_items . fn_once_trait ( ) {
143
+ Some ( def_id) if def_id == object_trait. def_id => return ,
144
+ _ => { }
145
+ }
146
+
147
+ let trait_items = ty:: trait_items ( tcx, object_trait. def_id ) ;
148
+
149
+ let mut errors = Vec :: new ( ) ;
150
+ for item in trait_items. iter ( ) {
151
+ match * item {
152
+ ty:: MethodTraitItem ( ref m) => {
153
+ errors. push ( check_object_safety_of_method ( tcx, & * * m) )
154
+ }
155
+ ty:: TypeTraitItem ( _) => { }
156
+ }
157
+ }
158
+
159
+ let mut errors = errors. iter ( ) . flat_map ( |x| x. iter ( ) ) . peekable ( ) ;
160
+ if errors. peek ( ) . is_some ( ) {
161
+ let trait_name = ty:: item_path_str ( tcx, object_trait. def_id ) ;
162
+ span_err ! ( tcx. sess, span, E0038 ,
163
+ "cannot convert to a trait object because trait `{}` is not object-safe" ,
164
+ trait_name) ;
165
+
166
+ for msg in errors {
167
+ tcx. sess . note ( msg. as_slice ( ) ) ;
168
+ }
169
+ }
170
+
171
+ // Returns a vec of error messages. If hte vec is empty - no errors!
172
+ fn check_object_safety_of_method ( tcx : & ty:: ctxt , method : & ty:: Method ) -> Vec < String > {
173
+ /*!
174
+ * There are some limitations to calling functions through an
175
+ * object, because (a) the self type is not known
176
+ * (that's the whole point of a trait instance, after all, to
177
+ * obscure the self type) and (b) the call must go through a
178
+ * vtable and hence cannot be monomorphized.
179
+ */
180
+ let mut msgs = Vec :: new ( ) ;
181
+
182
+ let method_name = method. name . repr ( tcx) ;
183
+
184
+ match method. explicit_self {
185
+ ty:: ByValueExplicitSelfCategory => { // reason (a) above
186
+ msgs. push ( format ! ( "cannot call a method (`{}`) with a by-value \
187
+ receiver through a trait object", method_name) )
188
+ }
189
+
190
+ ty:: StaticExplicitSelfCategory |
191
+ ty:: ByReferenceExplicitSelfCategory ( ..) |
192
+ ty:: ByBoxExplicitSelfCategory => { }
193
+ }
194
+
195
+ // reason (a) above
196
+ let check_for_self_ty = |ty| {
197
+ if ty:: type_has_self ( ty) {
198
+ Some ( format ! (
199
+ "cannot call a method (`{}`) whose type contains \
200
+ a self-type (`{}`) through a trait object",
201
+ method_name, ty_to_string( tcx, ty) ) )
202
+ } else {
203
+ None
204
+ }
205
+ } ;
206
+ let ref sig = method. fty . sig ;
207
+ for & input_ty in sig. inputs [ 1 ..] . iter ( ) {
208
+ match check_for_self_ty ( input_ty) {
209
+ Some ( msg) => msgs. push ( msg) ,
210
+ _ => { }
211
+ }
212
+ }
213
+ if let ty:: FnConverging ( result_type) = sig. output {
214
+ match check_for_self_ty ( result_type) {
215
+ Some ( msg) => msgs. push ( msg) ,
216
+ _ => { }
217
+ }
218
+ }
219
+
220
+ if method. generics . has_type_params ( FnSpace ) {
221
+ // reason (b) above
222
+ msgs. push ( format ! ( "cannot call a generic method (`{}`) through a trait object" ,
223
+ method_name) ) ;
224
+ }
225
+
226
+ msgs
227
+ }
228
+ }
229
+
131
230
pub fn register_object_cast_obligations ( fcx : & FnCtxt ,
132
231
span : Span ,
133
232
object_trait : & ty:: TyTrait ,
0 commit comments