]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/coherence/builtin.rs
Auto merge of #41433 - estebank:constructor, r=michaelwoerister
[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::lang_items::UnsizeTraitLangItem;
16
17 use rustc::traits::{self, ObligationCause, Reveal};
18 use rustc::ty::{self, Ty, TyCtxt};
19 use rustc::ty::ParameterEnvironment;
20 use rustc::ty::TypeFoldable;
21 use rustc::ty::adjustment::CoerceUnsizedInfo;
22 use rustc::ty::subst::Subst;
23 use rustc::ty::util::CopyImplementationError;
24 use rustc::infer;
25
26 use rustc::hir::def_id::DefId;
27 use rustc::hir::map as hir_map;
28 use rustc::hir::{self, ItemImpl};
29
30 pub fn check_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def_id: DefId) {
31     Checker { tcx, trait_def_id }
32         .check(tcx.lang_items.drop_trait(), visit_implementation_of_drop)
33         .check(tcx.lang_items.copy_trait(), visit_implementation_of_copy)
34         .check(tcx.lang_items.coerce_unsized_trait(),
35                visit_implementation_of_coerce_unsized);
36 }
37
38 struct Checker<'a, 'tcx: 'a> {
39     tcx: TyCtxt<'a, 'tcx, 'tcx>,
40     trait_def_id: DefId
41 }
42
43 impl<'a, 'tcx> Checker<'a, 'tcx> {
44     fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
45         where F: FnMut(TyCtxt<'a, 'tcx, 'tcx>, DefId, DefId)
46     {
47         if Some(self.trait_def_id) == trait_def_id {
48             for &impl_id in self.tcx.hir.trait_impls(self.trait_def_id) {
49                 let impl_def_id = self.tcx.hir.local_def_id(impl_id);
50                 f(self.tcx, self.trait_def_id, impl_def_id);
51             }
52         }
53         self
54     }
55 }
56
57 fn visit_implementation_of_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
58                                           _drop_did: DefId,
59                                           impl_did: DefId) {
60     match tcx.type_of(impl_did).sty {
61         ty::TyAdt(..) => {}
62         _ => {
63             // Destructors only work on nominal types.
64             if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_did) {
65                 match tcx.hir.find(impl_node_id) {
66                     Some(hir_map::NodeItem(item)) => {
67                         let span = match item.node {
68                             ItemImpl(.., ref ty, _) => ty.span,
69                             _ => item.span,
70                         };
71                         struct_span_err!(tcx.sess,
72                                          span,
73                                          E0120,
74                                          "the Drop trait may only be implemented on \
75                                          structures")
76                             .span_label(span, &format!("implementing Drop requires a struct"))
77                             .emit();
78                     }
79                     _ => {
80                         bug!("didn't find impl in ast map");
81                     }
82                 }
83             } else {
84                 bug!("found external impl of Drop trait on \
85                       something other than a struct");
86             }
87         }
88     }
89 }
90
91 fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
92                                           _copy_did: DefId,
93                                           impl_did: DefId) {
94     debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
95
96     let impl_node_id = if let Some(n) = tcx.hir.as_local_node_id(impl_did) {
97         n
98     } else {
99         debug!("visit_implementation_of_copy(): impl not in this \
100                 crate");
101         return;
102     };
103
104     let self_type = tcx.type_of(impl_did);
105     debug!("visit_implementation_of_copy: self_type={:?} (bound)",
106            self_type);
107
108     let span = tcx.hir.span(impl_node_id);
109     let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
110     let self_type = self_type.subst(tcx, &param_env.free_substs);
111     assert!(!self_type.has_escaping_regions());
112
113     debug!("visit_implementation_of_copy: self_type={:?} (free)",
114            self_type);
115
116     match param_env.can_type_implement_copy(tcx, self_type, span) {
117         Ok(()) => {}
118         Err(CopyImplementationError::InfrigingField(field)) => {
119             let item = tcx.hir.expect_item(impl_node_id);
120             let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
121                 tr.path.span
122             } else {
123                 span
124             };
125
126             struct_span_err!(tcx.sess,
127                              span,
128                              E0204,
129                              "the trait `Copy` may not be implemented for this type")
130                 .span_label(
131                     tcx.def_span(field.did),
132                     &"this field does not implement `Copy`")
133                 .emit()
134         }
135         Err(CopyImplementationError::NotAnAdt) => {
136             let item = tcx.hir.expect_item(impl_node_id);
137             let span = if let ItemImpl(.., ref ty, _) = item.node {
138                 ty.span
139             } else {
140                 span
141             };
142
143             struct_span_err!(tcx.sess,
144                              span,
145                              E0206,
146                              "the trait `Copy` may not be implemented for this type")
147                 .span_label(span, &format!("type is not a structure or enumeration"))
148                 .emit();
149         }
150         Err(CopyImplementationError::HasDestructor) => {
151             struct_span_err!(tcx.sess,
152                              span,
153                              E0184,
154                              "the trait `Copy` may not be implemented for this type; the \
155                               type has a destructor")
156                 .span_label(span, &format!("Copy not allowed on types with destructors"))
157                 .emit();
158         }
159     }
160 }
161
162 fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
163                                                     _: DefId,
164                                                     impl_did: DefId) {
165     debug!("visit_implementation_of_coerce_unsized: impl_did={:?}",
166            impl_did);
167
168     // Just compute this for the side-effects, in particular reporting
169     // errors; other parts of the code may demand it for the info of
170     // course.
171     if impl_did.is_local() {
172         let span = tcx.def_span(impl_did);
173         tcx.at(span).coerce_unsized_info(impl_did);
174     }
175 }
176
177 pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
178                                      impl_did: DefId)
179                                      -> CoerceUnsizedInfo {
180     debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
181     let coerce_unsized_trait = tcx.lang_items.coerce_unsized_trait().unwrap();
182
183     let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
184         Ok(id) => id,
185         Err(err) => {
186             tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
187         }
188     };
189
190     // this provider should only get invoked for local def-ids
191     let impl_node_id = tcx.hir.as_local_node_id(impl_did).unwrap_or_else(|| {
192         bug!("coerce_unsized_info: invoked for non-local def-id {:?}", impl_did)
193     });
194
195     let source = tcx.type_of(impl_did);
196     let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
197     assert_eq!(trait_ref.def_id, coerce_unsized_trait);
198     let target = trait_ref.substs.type_at(1);
199     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)",
200            source,
201            target);
202
203     let span = tcx.hir.span(impl_node_id);
204     let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
205     let source = source.subst(tcx, &param_env.free_substs);
206     let target = target.subst(tcx, &param_env.free_substs);
207     assert!(!source.has_escaping_regions());
208
209     let err_info = CoerceUnsizedInfo { custom_kind: None };
210
211     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
212            source,
213            target);
214
215     tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
216         let cause = ObligationCause::misc(span, impl_node_id);
217         let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
218                            mt_b: ty::TypeAndMut<'tcx>,
219                            mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
220             if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
221                 infcx.report_mismatched_types(&cause,
222                                              mk_ptr(mt_b.ty),
223                                              target,
224                                              ty::error::TypeError::Mutability)
225                     .emit();
226             }
227             (mt_a.ty, mt_b.ty, unsize_trait, None)
228         };
229         let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
230             (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
231                 infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
232                 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
233             }
234
235             (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
236             (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
237                 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
238             }
239
240             (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() &&
241                                                                           def_b.is_struct() => {
242                 if def_a != def_b {
243                     let source_path = tcx.item_path_str(def_a.did);
244                     let target_path = tcx.item_path_str(def_b.did);
245                     span_err!(tcx.sess,
246                               span,
247                               E0377,
248                               "the trait `CoerceUnsized` may only be implemented \
249                                for a coercion between structures with the same \
250                                definition; expected {}, found {}",
251                               source_path,
252                               target_path);
253                     return err_info;
254                 }
255
256                 let fields = &def_a.struct_variant().fields;
257                 let diff_fields = fields.iter()
258                     .enumerate()
259                     .filter_map(|(i, f)| {
260                         let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
261
262                         if tcx.type_of(f.did).is_phantom_data() {
263                             // Ignore PhantomData fields
264                             return None;
265                         }
266
267                         // Ignore fields that aren't significantly changed
268                         if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
269                             if ok.obligations.is_empty() {
270                                 return None;
271                             }
272                         }
273
274                         // Collect up all fields that were significantly changed
275                         // i.e. those that contain T in coerce_unsized T -> U
276                         Some((i, a, b))
277                     })
278                     .collect::<Vec<_>>();
279
280                 if diff_fields.is_empty() {
281                     span_err!(tcx.sess,
282                               span,
283                               E0374,
284                               "the trait `CoerceUnsized` may only be implemented \
285                                for a coercion between structures with one field \
286                                being coerced, none found");
287                     return err_info;
288                 } else if diff_fields.len() > 1 {
289                     let item = tcx.hir.expect_item(impl_node_id);
290                     let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
291                         t.path.span
292                     } else {
293                         tcx.hir.span(impl_node_id)
294                     };
295
296                     let mut err = struct_span_err!(tcx.sess,
297                                                    span,
298                                                    E0375,
299                                                    "implementing the trait \
300                                                     `CoerceUnsized` requires multiple \
301                                                     coercions");
302                     err.note("`CoerceUnsized` may only be implemented for \
303                               a coercion between structures with one field being coerced");
304                     err.note(&format!("currently, {} fields need coercions: {}",
305                                       diff_fields.len(),
306                                       diff_fields.iter()
307                                           .map(|&(i, a, b)| {
308                                               format!("{} ({} to {})", fields[i].name, a, b)
309                                           })
310                                           .collect::<Vec<_>>()
311                                           .join(", ")));
312                     err.span_label(span, &format!("requires multiple coercions"));
313                     err.emit();
314                     return err_info;
315                 }
316
317                 let (i, a, b) = diff_fields[0];
318                 let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
319                 (a, b, coerce_unsized_trait, Some(kind))
320             }
321
322             _ => {
323                 span_err!(tcx.sess,
324                           span,
325                           E0376,
326                           "the trait `CoerceUnsized` may only be implemented \
327                            for a coercion between structures");
328                 return err_info;
329             }
330         };
331
332         let mut fulfill_cx = traits::FulfillmentContext::new();
333
334         // Register an obligation for `A: Trait<B>`.
335         let cause = traits::ObligationCause::misc(span, impl_node_id);
336         let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
337         fulfill_cx.register_predicate_obligation(&infcx, predicate);
338
339         // Check that all transitive obligations are satisfied.
340         if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
341             infcx.report_fulfillment_errors(&errors);
342         }
343
344         // Finally, resolve all regions.
345         let mut free_regions = FreeRegionMap::new();
346         free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
347             .caller_bounds);
348         infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
349
350         CoerceUnsizedInfo {
351             custom_kind: kind
352         }
353     })
354 }