]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/vtable.rs
Rollup merge of #21964 - semarie:openbsd-env, r=alexcrichton
[rust.git] / src / librustc_typeck / check / vtable.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use check::{FnCtxt, structurally_resolved_type};
12 use middle::traits::{self, ObjectSafetyViolation, MethodViolationCode};
13 use middle::traits::{Obligation, ObligationCause};
14 use middle::traits::report_fulfillment_errors;
15 use middle::ty::{self, Ty, AsPredicate};
16 use middle::infer;
17 use syntax::ast;
18 use syntax::codemap::Span;
19 use util::nodemap::FnvHashSet;
20 use util::ppaux::{Repr, UserString};
21
22 pub fn check_object_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
23                                    cast_expr: &ast::Expr,
24                                    source_expr: &ast::Expr,
25                                    target_object_ty: Ty<'tcx>)
26 {
27     debug!("check_object_cast(cast_expr={}, target_object_ty={})",
28            cast_expr.repr(fcx.tcx()),
29            target_object_ty.repr(fcx.tcx()));
30
31     // Look up vtables for the type we're casting to,
32     // passing in the source and target type.  The source
33     // must be a pointer type suitable to the object sigil,
34     // e.g.: `&x as &Trait` or `box x as Box<Trait>`
35     let source_ty = fcx.expr_ty(source_expr);
36     let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty);
37     debug!("source_ty={}", source_ty.repr(fcx.tcx()));
38     match (&source_ty.sty, &target_object_ty.sty) {
39         (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => {
40             let object_trait = object_trait(&object_trait_ty);
41
42             // Ensure that if ~T is cast to ~Trait, then T : Trait
43             push_cast_obligation(fcx, cast_expr, object_trait, referent_ty);
44             check_object_safety(fcx.tcx(), object_trait, source_expr.span);
45         }
46
47         (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty,
48                                                 mutbl: referent_mutbl }),
49          &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty,
50                                               mutbl: target_mutbl })) =>
51         {
52             let object_trait = object_trait(&object_trait_ty);
53             if !mutability_allowed(referent_mutbl, target_mutbl) {
54                 span_err!(fcx.tcx().sess, source_expr.span, E0188,
55                                         "types differ in mutability");
56             } else {
57                 // Ensure that if &'a T is cast to &'b Trait, then T : Trait
58                 push_cast_obligation(fcx, cast_expr,
59                                      object_trait,
60                                      referent_ty);
61
62                 // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a
63                 infer::mk_subr(fcx.infcx(),
64                                infer::RelateObjectBound(source_expr.span),
65                                *target_region,
66                                *referent_region);
67
68                 check_object_safety(fcx.tcx(), object_trait, source_expr.span);
69             }
70         }
71
72         (_, &ty::ty_uniq(..)) => {
73             span_err!(fcx.ccx.tcx.sess, source_expr.span, E0189,
74                 "can only cast a boxed pointer \
75                          to a boxed object, not a {}",
76                       ty::ty_sort_string(fcx.tcx(), source_ty));
77         }
78
79         (_, &ty::ty_rptr(..)) => {
80             span_err!(fcx.ccx.tcx.sess, source_expr.span, E0190,
81                 "can only cast a &-pointer \
82                          to an &-object, not a {}",
83                         ty::ty_sort_string(fcx.tcx(), source_ty));
84         }
85
86         _ => {
87             fcx.tcx().sess.span_bug(
88                 source_expr.span,
89                 "expected object type");
90         }
91     }
92
93     fn object_trait<'a, 'tcx>(t: &'a Ty<'tcx>) -> &'a ty::TyTrait<'tcx> {
94         match t.sty {
95             ty::ty_trait(ref ty_trait) => &**ty_trait,
96             _ => panic!("expected ty_trait")
97         }
98     }
99
100     fn mutability_allowed(a_mutbl: ast::Mutability,
101                           b_mutbl: ast::Mutability)
102                           -> bool {
103         a_mutbl == b_mutbl ||
104             (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable)
105     }
106
107     fn push_cast_obligation<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
108                                       cast_expr: &ast::Expr,
109                                       object_trait: &ty::TyTrait<'tcx>,
110                                       referent_ty: Ty<'tcx>) {
111         let object_trait_ref =
112             register_object_cast_obligations(fcx,
113                                              cast_expr.span,
114                                              object_trait,
115                                              referent_ty);
116
117         // Finally record the object_trait_ref for use during trans
118         // (it would prob be better not to do this, but it's just kind
119         // of a pain to have to reconstruct it).
120         fcx.write_object_cast(cast_expr.id, object_trait_ref);
121     }
122 }
123
124 // Check that a trait is 'object-safe'. This should be checked whenever a trait object
125 // is created (by casting or coercion, etc.). A trait is object-safe if all its
126 // methods are object-safe. A trait method is object-safe if it does not take
127 // self by value, has no type parameters and does not use the `Self` type, except
128 // in self position.
129 pub fn check_object_safety<'tcx>(tcx: &ty::ctxt<'tcx>,
130                                  object_trait: &ty::TyTrait<'tcx>,
131                                  span: Span)
132 {
133     let object_trait_ref =
134         object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
135
136     if traits::is_object_safe(tcx, object_trait_ref.clone()) {
137         return;
138     }
139
140     span_err!(tcx.sess, span, E0038,
141               "cannot convert to a trait object because trait `{}` is not object-safe",
142               ty::item_path_str(tcx, object_trait_ref.def_id()));
143
144     let violations = traits::object_safety_violations(tcx, object_trait_ref.clone());
145     for violation in violations {
146         match violation {
147             ObjectSafetyViolation::SizedSelf => {
148                 tcx.sess.span_note(
149                     span,
150                     "the trait cannot require that `Self : Sized`");
151             }
152
153             ObjectSafetyViolation::Method(method, MethodViolationCode::ByValueSelf) => {
154                 tcx.sess.span_note(
155                     span,
156                     &format!("method `{}` has a receiver type of `Self`, \
157                               which cannot be used with a trait object",
158                              method.name.user_string(tcx)));
159             }
160
161             ObjectSafetyViolation::Method(method, MethodViolationCode::StaticMethod) => {
162                 tcx.sess.span_note(
163                     span,
164                     &format!("method `{}` has no receiver",
165                              method.name.user_string(tcx)));
166             }
167
168             ObjectSafetyViolation::Method(method, MethodViolationCode::ReferencesSelf) => {
169                 tcx.sess.span_note(
170                     span,
171                     &format!("method `{}` references the `Self` type \
172                               in its arguments or return type",
173                              method.name.user_string(tcx)));
174             }
175
176             ObjectSafetyViolation::Method(method, MethodViolationCode::Generic) => {
177                 tcx.sess.span_note(
178                     span,
179                     &format!("method `{}` has generic type parameters",
180                              method.name.user_string(tcx)));
181             }
182         }
183     }
184 }
185
186 pub fn register_object_cast_obligations<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
187                                                   span: Span,
188                                                   object_trait: &ty::TyTrait<'tcx>,
189                                                   referent_ty: Ty<'tcx>)
190                                                   -> ty::PolyTraitRef<'tcx>
191 {
192     // We can only make objects from sized types.
193     fcx.register_builtin_bound(
194         referent_ty,
195         ty::BoundSized,
196         traits::ObligationCause::new(span, fcx.body_id, traits::ObjectSized));
197
198     // This is just for better error reporting. Kinda goofy. The object type stuff
199     // needs some refactoring so there is a more convenient type to pass around.
200     let object_trait_ty =
201         ty::mk_trait(fcx.tcx(),
202                      object_trait.principal.clone(),
203                      object_trait.bounds.clone());
204
205     debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}",
206            referent_ty.repr(fcx.tcx()),
207            object_trait_ty.repr(fcx.tcx()));
208
209     let cause = ObligationCause::new(span,
210                                      fcx.body_id,
211                                      traits::ObjectCastObligation(object_trait_ty));
212
213     // Create the obligation for casting from T to Trait.
214     let object_trait_ref =
215         object_trait.principal_trait_ref_with_self_ty(fcx.tcx(), referent_ty);
216     let object_obligation =
217         Obligation::new(cause.clone(), object_trait_ref.as_predicate());
218     fcx.register_predicate(object_obligation);
219
220     // Create additional obligations for all the various builtin
221     // bounds attached to the object cast. (In other words, if the
222     // object type is Foo+Send, this would create an obligation
223     // for the Send check.)
224     for builtin_bound in &object_trait.bounds.builtin_bounds {
225         fcx.register_builtin_bound(
226             referent_ty,
227             builtin_bound,
228             cause.clone());
229     }
230
231     // Create obligations for the projection predicates.
232     let projection_bounds =
233         object_trait.projection_bounds_with_self_ty(fcx.tcx(), referent_ty);
234     for projection_bound in &projection_bounds {
235         let projection_obligation =
236             Obligation::new(cause.clone(), projection_bound.as_predicate());
237         fcx.register_predicate(projection_obligation);
238     }
239
240     // Finally, check that there IS a projection predicate for every associated type.
241     check_object_type_binds_all_associated_types(fcx.tcx(),
242                                                  span,
243                                                  object_trait);
244
245     object_trait_ref
246 }
247
248 fn check_object_type_binds_all_associated_types<'tcx>(tcx: &ty::ctxt<'tcx>,
249                                                       span: Span,
250                                                       object_trait: &ty::TyTrait<'tcx>)
251 {
252     let object_trait_ref =
253         object_trait.principal_trait_ref_with_self_ty(tcx, tcx.types.err);
254
255     let mut associated_types: FnvHashSet<(ast::DefId, ast::Name)> =
256         traits::supertraits(tcx, object_trait_ref.clone())
257         .flat_map(|tr| {
258             let trait_def = ty::lookup_trait_def(tcx, tr.def_id());
259             trait_def.associated_type_names
260                 .clone()
261                 .into_iter()
262                 .map(move |associated_type_name| (tr.def_id(), associated_type_name))
263         })
264         .collect();
265
266     for projection_bound in &object_trait.bounds.projection_bounds {
267         let pair = (projection_bound.0.projection_ty.trait_ref.def_id,
268                     projection_bound.0.projection_ty.item_name);
269         associated_types.remove(&pair);
270     }
271
272     for (trait_def_id, name) in associated_types {
273         span_err!(tcx.sess, span, E0191,
274             "the value of the associated type `{}` (from the trait `{}`) must be specified",
275                     name.user_string(tcx),
276                     ty::item_path_str(tcx, trait_def_id));
277     }
278 }
279
280 pub fn select_all_fcx_obligations_and_apply_defaults(fcx: &FnCtxt) {
281     debug!("select_all_fcx_obligations_and_apply_defaults");
282
283     select_fcx_obligations_where_possible(fcx);
284     fcx.default_type_parameters();
285     select_fcx_obligations_where_possible(fcx);
286 }
287
288 pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) {
289     debug!("select_all_fcx_obligations_or_error");
290
291     // upvar inference should have ensured that all deferrred call
292     // resolutions are handled by now.
293     assert!(fcx.inh.deferred_call_resolutions.borrow().is_empty());
294
295     select_all_fcx_obligations_and_apply_defaults(fcx);
296     let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut();
297     let r = fulfillment_cx.select_all_or_error(fcx.infcx(), fcx);
298     match r {
299         Ok(()) => { }
300         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
301     }
302 }
303
304 /// Select as many obligations as we can at present.
305 pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt)
306 {
307     match
308         fcx.inh.fulfillment_cx
309         .borrow_mut()
310         .select_where_possible(fcx.infcx(), fcx)
311     {
312         Ok(()) => { }
313         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
314     }
315 }
316
317 /// Try to select any fcx obligation that we haven't tried yet, in an effort to improve inference.
318 /// You could just call `select_fcx_obligations_where_possible` except that it leads to repeated
319 /// work.
320 pub fn select_new_fcx_obligations(fcx: &FnCtxt) {
321     match
322         fcx.inh.fulfillment_cx
323         .borrow_mut()
324         .select_new_obligations(fcx.infcx(), fcx)
325     {
326         Ok(()) => { }
327         Err(errors) => { report_fulfillment_errors(fcx.infcx(), &errors); }
328     }
329 }
330