88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- use middle:: subst:: { SelfSpace } ;
11+ use middle:: subst:: { SelfSpace , FnSpace } ;
1212use middle:: traits;
1313use middle:: traits:: { SelectionError , OutputTypeParameterMismatch , Overflow , Unimplemented } ;
1414use middle:: traits:: { Obligation , obligation_for_builtin_bound} ;
@@ -21,8 +21,7 @@ use middle::typeck::infer;
2121use std:: rc:: Rc ;
2222use syntax:: ast;
2323use syntax:: codemap:: Span ;
24- use util:: ppaux:: UserString ;
25- use util:: ppaux:: Repr ;
24+ use util:: ppaux:: { UserString , Repr , ty_to_string} ;
2625
2726pub fn check_object_cast ( fcx : & FnCtxt ,
2827 cast_expr : & ast:: Expr ,
@@ -46,6 +45,7 @@ pub fn check_object_cast(fcx: &FnCtxt,
4645
4746 // Ensure that if ~T is cast to ~Trait, then T : Trait
4847 push_cast_obligation ( fcx, cast_expr, object_trait, referent_ty) ;
48+ check_object_safety ( fcx. tcx ( ) , object_trait, source_expr. span ) ;
4949 }
5050
5151 ( & ty:: ty_rptr( referent_region, ty:: mt { ty : referent_ty,
@@ -68,6 +68,8 @@ pub fn check_object_cast(fcx: &FnCtxt,
6868 infer:: RelateObjectBound ( source_expr. span ) ,
6969 target_region,
7070 referent_region) ;
71+
72+ check_object_safety ( fcx. tcx ( ) , object_trait, source_expr. span ) ;
7173 }
7274 }
7375
@@ -128,6 +130,103 @@ pub fn check_object_cast(fcx: &FnCtxt,
128130 }
129131}
130132
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+
131230pub fn register_object_cast_obligations ( fcx : & FnCtxt ,
132231 span : Span ,
133232 object_trait : & ty:: TyTrait ,
0 commit comments