]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/coherence/builtin.rs
Auto merge of #42023 - nikomatsakis:issue-36799-ostn15_phf, r=arielb1
[rust.git] / src / librustc_typeck / coherence / builtin.rs
1 // Copyright 2016 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 //! Check properties that are required by built-in traits and set
12 //! up data structures required by type-checking/translation.
13
14 use rustc::middle::free_region::FreeRegionMap;
15 use rustc::middle::region::RegionMaps;
16 use rustc::middle::lang_items::UnsizeTraitLangItem;
17
18 use rustc::traits::{self, ObligationCause, Reveal};
19 use rustc::ty::{self, Ty, TyCtxt};
20 use rustc::ty::TypeFoldable;
21 use rustc::ty::adjustment::CoerceUnsizedInfo;
22 use rustc::ty::util::CopyImplementationError;
23 use rustc::infer;
24
25 use rustc::hir::def_id::DefId;
26 use rustc::hir::map as hir_map;
27 use rustc::hir::{self, ItemImpl};
28
29 pub fn check_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def_id: DefId) {
30     Checker { tcx, trait_def_id }
31         .check(tcx.lang_items.drop_trait(), visit_implementation_of_drop)
32         .check(tcx.lang_items.copy_trait(), visit_implementation_of_copy)
33         .check(tcx.lang_items.coerce_unsized_trait(),
34                visit_implementation_of_coerce_unsized);
35 }
36
37 struct Checker<'a, 'tcx: 'a> {
38     tcx: TyCtxt<'a, 'tcx, 'tcx>,
39     trait_def_id: DefId
40 }
41
42 impl<'a, 'tcx> Checker<'a, 'tcx> {
43     fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
44         where F: FnMut(TyCtxt<'a, 'tcx, 'tcx>, DefId, DefId)
45     {
46         if Some(self.trait_def_id) == trait_def_id {
47             for &impl_id in self.tcx.hir.trait_impls(self.trait_def_id) {
48                 let impl_def_id = self.tcx.hir.local_def_id(impl_id);
49                 f(self.tcx, self.trait_def_id, impl_def_id);
50             }
51         }
52         self
53     }
54 }
55
56 fn visit_implementation_of_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
57                                           _drop_did: DefId,
58                                           impl_did: DefId) {
59     match tcx.type_of(impl_did).sty {
60         ty::TyAdt(..) => {}
61         _ => {
62             // Destructors only work on nominal types.
63             if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_did) {
64                 match tcx.hir.find(impl_node_id) {
65                     Some(hir_map::NodeItem(item)) => {
66                         let span = match item.node {
67                             ItemImpl(.., ref ty, _) => ty.span,
68                             _ => item.span,
69                         };
70                         struct_span_err!(tcx.sess,
71                                          span,
72                                          E0120,
73                                          "the Drop trait may only be implemented on \
74                                          structures")
75                             .span_label(span, "implementing Drop requires a struct")
76                             .emit();
77                     }
78                     _ => {
79                         bug!("didn't find impl in ast map");
80                     }
81                 }
82             } else {
83                 bug!("found external impl of Drop trait on \
84                       something other than a struct");
85             }
86         }
87     }
88 }
89
90 fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
91                                           _copy_did: DefId,
92                                           impl_did: DefId) {
93     debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
94
95     let impl_node_id = if let Some(n) = tcx.hir.as_local_node_id(impl_did) {
96         n
97     } else {
98         debug!("visit_implementation_of_copy(): impl not in this \
99                 crate");
100         return;
101     };
102
103     let self_type = tcx.type_of(impl_did);
104     debug!("visit_implementation_of_copy: self_type={:?} (bound)",
105            self_type);
106
107     let span = tcx.hir.span(impl_node_id);
108     let param_env = tcx.param_env(impl_did);
109     assert!(!self_type.has_escaping_regions());
110
111     debug!("visit_implementation_of_copy: self_type={:?} (free)",
112            self_type);
113
114     match param_env.can_type_implement_copy(tcx, self_type, span) {
115         Ok(()) => {}
116         Err(CopyImplementationError::InfrigingField(field)) => {
117             let item = tcx.hir.expect_item(impl_node_id);
118             let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
119                 tr.path.span
120             } else {
121                 span
122             };
123
124             struct_span_err!(tcx.sess,
125                              span,
126                              E0204,
127                              "the trait `Copy` may not be implemented for this type")
128                 .span_label(
129                     tcx.def_span(field.did),
130                     "this field does not implement `Copy`")
131                 .emit()
132         }
133         Err(CopyImplementationError::NotAnAdt) => {
134             let item = tcx.hir.expect_item(impl_node_id);
135             let span = if let ItemImpl(.., ref ty, _) = item.node {
136                 ty.span
137             } else {
138                 span
139             };
140
141             struct_span_err!(tcx.sess,
142                              span,
143                              E0206,
144                              "the trait `Copy` may not be implemented for this type")
145                 .span_label(span, "type is not a structure or enumeration")
146                 .emit();
147         }
148         Err(CopyImplementationError::HasDestructor) => {
149             struct_span_err!(tcx.sess,
150                              span,
151                              E0184,
152                              "the trait `Copy` may not be implemented for this type; the \
153                               type has a destructor")
154                 .span_label(span, "Copy not allowed on types with destructors")
155                 .emit();
156         }
157     }
158 }
159
160 fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
161                                                     _: DefId,
162                                                     impl_did: DefId) {
163     debug!("visit_implementation_of_coerce_unsized: impl_did={:?}",
164            impl_did);
165
166     // Just compute this for the side-effects, in particular reporting
167     // errors; other parts of the code may demand it for the info of
168     // course.
169     if impl_did.is_local() {
170         let span = tcx.def_span(impl_did);
171         tcx.at(span).coerce_unsized_info(impl_did);
172     }
173 }
174
175 pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
176                                      impl_did: DefId)
177                                      -> CoerceUnsizedInfo {
178     debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
179     let coerce_unsized_trait = tcx.lang_items.coerce_unsized_trait().unwrap();
180
181     let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
182         Ok(id) => id,
183         Err(err) => {
184             tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
185         }
186     };
187
188     // this provider should only get invoked for local def-ids
189     let impl_node_id = tcx.hir.as_local_node_id(impl_did).unwrap_or_else(|| {
190         bug!("coerce_unsized_info: invoked for non-local def-id {:?}", impl_did)
191     });
192
193     let source = tcx.type_of(impl_did);
194     let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
195     assert_eq!(trait_ref.def_id, coerce_unsized_trait);
196     let target = trait_ref.substs.type_at(1);
197     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)",
198            source,
199            target);
200
201     let span = tcx.hir.span(impl_node_id);
202     let param_env = tcx.param_env(impl_did);
203     assert!(!source.has_escaping_regions());
204
205     let err_info = CoerceUnsizedInfo { custom_kind: None };
206
207     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
208            source,
209            target);
210
211     tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
212         let cause = ObligationCause::misc(span, impl_node_id);
213         let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
214                            mt_b: ty::TypeAndMut<'tcx>,
215                            mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
216             if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
217                 infcx.report_mismatched_types(&cause,
218                                              mk_ptr(mt_b.ty),
219                                              target,
220                                              ty::error::TypeError::Mutability)
221                     .emit();
222             }
223             (mt_a.ty, mt_b.ty, unsize_trait, None)
224         };
225         let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
226             (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
227                 infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
228                 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
229             }
230
231             (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
232             (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
233                 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
234             }
235
236             (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() &&
237                                                                           def_b.is_struct() => {
238                 if def_a != def_b {
239                     let source_path = tcx.item_path_str(def_a.did);
240                     let target_path = tcx.item_path_str(def_b.did);
241                     span_err!(tcx.sess,
242                               span,
243                               E0377,
244                               "the trait `CoerceUnsized` may only be implemented \
245                                for a coercion between structures with the same \
246                                definition; expected {}, found {}",
247                               source_path,
248                               target_path);
249                     return err_info;
250                 }
251
252                 // Here we are considering a case of converting
253                 // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
254                 // which acts like a pointer to `U`, but carries along some extra data of type `T`:
255                 //
256                 //     struct Foo<T, U> {
257                 //         extra: T,
258                 //         ptr: *mut U,
259                 //     }
260                 //
261                 // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
262                 // to `Foo<T, [i32]>`. That impl would look like:
263                 //
264                 //   impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
265                 //
266                 // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
267                 // when this coercion occurs, we would be changing the
268                 // field `ptr` from a thin pointer of type `*mut [i32;
269                 // 3]` to a fat pointer of type `*mut [i32]` (with
270                 // extra data `3`).  **The purpose of this check is to
271                 // make sure that we know how to do this conversion.**
272                 //
273                 // To check if this impl is legal, we would walk down
274                 // the fields of `Foo` and consider their types with
275                 // both substitutes. We are looking to find that
276                 // exactly one (non-phantom) field has changed its
277                 // type, which we will expect to be the pointer that
278                 // is becoming fat (we could probably generalize this
279                 // to mutiple thin pointers of the same type becoming
280                 // fat, but we don't). In this case:
281                 //
282                 // - `extra` has type `T` before and type `T` after
283                 // - `ptr` has type `*mut U` before and type `*mut V` after
284                 //
285                 // Since just one field changed, we would then check
286                 // that `*mut U: CoerceUnsized<*mut V>` is implemented
287                 // (in other words, that we know how to do this
288                 // conversion). This will work out because `U:
289                 // Unsize<V>`, and we have a builtin rule that `*mut
290                 // U` can be coerced to `*mut V` if `U: Unsize<V>`.
291                 let fields = &def_a.struct_variant().fields;
292                 let diff_fields = fields.iter()
293                     .enumerate()
294                     .filter_map(|(i, f)| {
295                         let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
296
297                         if tcx.type_of(f.did).is_phantom_data() {
298                             // Ignore PhantomData fields
299                             return None;
300                         }
301
302                         // Ignore fields that aren't changed; it may
303                         // be that we could get away with subtyping or
304                         // something more accepting, but we use
305                         // equality because we want to be able to
306                         // perform this check without computing
307                         // variance where possible. (This is because
308                         // we may have to evaluate constraint
309                         // expressions in the course of execution.)
310                         // See e.g. #41936.
311                         if let Ok(ok) = infcx.eq_types(false, &cause, b, a) {
312                             if ok.obligations.is_empty() {
313                                 return None;
314                             }
315                         }
316
317                         // Collect up all fields that were significantly changed
318                         // i.e. those that contain T in coerce_unsized T -> U
319                         Some((i, a, b))
320                     })
321                     .collect::<Vec<_>>();
322
323                 if diff_fields.is_empty() {
324                     span_err!(tcx.sess,
325                               span,
326                               E0374,
327                               "the trait `CoerceUnsized` may only be implemented \
328                                for a coercion between structures with one field \
329                                being coerced, none found");
330                     return err_info;
331                 } else if diff_fields.len() > 1 {
332                     let item = tcx.hir.expect_item(impl_node_id);
333                     let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
334                         t.path.span
335                     } else {
336                         tcx.hir.span(impl_node_id)
337                     };
338
339                     let mut err = struct_span_err!(tcx.sess,
340                                                    span,
341                                                    E0375,
342                                                    "implementing the trait \
343                                                     `CoerceUnsized` requires multiple \
344                                                     coercions");
345                     err.note("`CoerceUnsized` may only be implemented for \
346                               a coercion between structures with one field being coerced");
347                     err.note(&format!("currently, {} fields need coercions: {}",
348                                       diff_fields.len(),
349                                       diff_fields.iter()
350                                           .map(|&(i, a, b)| {
351                                               format!("{} ({} to {})", fields[i].name, a, b)
352                                           })
353                                           .collect::<Vec<_>>()
354                                           .join(", ")));
355                     err.span_label(span, "requires multiple coercions");
356                     err.emit();
357                     return err_info;
358                 }
359
360                 let (i, a, b) = diff_fields[0];
361                 let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
362                 (a, b, coerce_unsized_trait, Some(kind))
363             }
364
365             _ => {
366                 span_err!(tcx.sess,
367                           span,
368                           E0376,
369                           "the trait `CoerceUnsized` may only be implemented \
370                            for a coercion between structures");
371                 return err_info;
372             }
373         };
374
375         let mut fulfill_cx = traits::FulfillmentContext::new();
376
377         // Register an obligation for `A: Trait<B>`.
378         let cause = traits::ObligationCause::misc(span, impl_node_id);
379         let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
380         fulfill_cx.register_predicate_obligation(&infcx, predicate);
381
382         // Check that all transitive obligations are satisfied.
383         if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
384             infcx.report_fulfillment_errors(&errors);
385         }
386
387         // Finally, resolve all regions.
388         let region_maps = RegionMaps::new();
389         let mut free_regions = FreeRegionMap::new();
390         free_regions.relate_free_regions_from_predicates(&infcx.param_env
391             .caller_bounds);
392         infcx.resolve_regions_and_report_errors(impl_did, &region_maps, &free_regions);
393
394         CoerceUnsizedInfo {
395             custom_kind: kind
396         }
397     })
398 }