]> git.proxmox.com Git - rustc.git/blob - src/librustc_typeck/coherence/builtin.rs
New upstream version 1.17.0+dfsg1
[rustc.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::subst::Subst;
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.item_type(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, &format!("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.item_type(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 = ParameterEnvironment::for_item(tcx, impl_node_id);
109 let self_type = self_type.subst(tcx, &param_env.free_substs);
110 assert!(!self_type.has_escaping_regions());
111
112 debug!("visit_implementation_of_copy: self_type={:?} (free)",
113 self_type);
114
115 match param_env.can_type_implement_copy(tcx, self_type, span) {
116 Ok(()) => {}
117 Err(CopyImplementationError::InfrigingField(field)) => {
118 let item = tcx.hir.expect_item(impl_node_id);
119 let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
120 tr.path.span
121 } else {
122 span
123 };
124
125 struct_span_err!(tcx.sess,
126 span,
127 E0204,
128 "the trait `Copy` may not be implemented for this type")
129 .span_label(
130 tcx.def_span(field.did),
131 &"this field does not implement `Copy`")
132 .emit()
133 }
134 Err(CopyImplementationError::NotAnAdt) => {
135 let item = tcx.hir.expect_item(impl_node_id);
136 let span = if let ItemImpl(.., ref ty, _) = item.node {
137 ty.span
138 } else {
139 span
140 };
141
142 struct_span_err!(tcx.sess,
143 span,
144 E0206,
145 "the trait `Copy` may not be implemented for this type")
146 .span_label(span, &format!("type is not a structure or enumeration"))
147 .emit();
148 }
149 Err(CopyImplementationError::HasDestructor) => {
150 struct_span_err!(tcx.sess,
151 span,
152 E0184,
153 "the trait `Copy` may not be implemented for this type; the \
154 type has a destructor")
155 .span_label(span, &format!("Copy not allowed on types with destructors"))
156 .emit();
157 }
158 }
159 }
160
161 fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
162 coerce_unsized_trait: DefId,
163 impl_did: DefId) {
164 debug!("visit_implementation_of_coerce_unsized: impl_did={:?}",
165 impl_did);
166
167 let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
168 Ok(id) => id,
169 Err(err) => {
170 tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
171 }
172 };
173
174 let impl_node_id = if let Some(n) = tcx.hir.as_local_node_id(impl_did) {
175 n
176 } else {
177 debug!("visit_implementation_of_coerce_unsized(): impl not \
178 in this crate");
179 return;
180 };
181
182 let source = tcx.item_type(impl_did);
183 let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
184 let target = trait_ref.substs.type_at(1);
185 debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)",
186 source,
187 target);
188
189 let span = tcx.hir.span(impl_node_id);
190 let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
191 let source = source.subst(tcx, &param_env.free_substs);
192 let target = target.subst(tcx, &param_env.free_substs);
193 assert!(!source.has_escaping_regions());
194
195 debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
196 source,
197 target);
198
199 tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
200 let cause = ObligationCause::misc(span, impl_node_id);
201 let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
202 mt_b: ty::TypeAndMut<'tcx>,
203 mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
204 if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
205 infcx.report_mismatched_types(&cause,
206 mk_ptr(mt_b.ty),
207 target,
208 ty::error::TypeError::Mutability)
209 .emit();
210 }
211 (mt_a.ty, mt_b.ty, unsize_trait, None)
212 };
213 let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
214 (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
215 infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
216 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
217 }
218
219 (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
220 (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
221 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
222 }
223
224 (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() &&
225 def_b.is_struct() => {
226 if def_a != def_b {
227 let source_path = tcx.item_path_str(def_a.did);
228 let target_path = tcx.item_path_str(def_b.did);
229 span_err!(tcx.sess,
230 span,
231 E0377,
232 "the trait `CoerceUnsized` may only be implemented \
233 for a coercion between structures with the same \
234 definition; expected {}, found {}",
235 source_path,
236 target_path);
237 return;
238 }
239
240 let fields = &def_a.struct_variant().fields;
241 let diff_fields = fields.iter()
242 .enumerate()
243 .filter_map(|(i, f)| {
244 let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
245
246 if tcx.item_type(f.did).is_phantom_data() {
247 // Ignore PhantomData fields
248 return None;
249 }
250
251 // Ignore fields that aren't significantly changed
252 if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
253 if ok.obligations.is_empty() {
254 return None;
255 }
256 }
257
258 // Collect up all fields that were significantly changed
259 // i.e. those that contain T in coerce_unsized T -> U
260 Some((i, a, b))
261 })
262 .collect::<Vec<_>>();
263
264 if diff_fields.is_empty() {
265 span_err!(tcx.sess,
266 span,
267 E0374,
268 "the trait `CoerceUnsized` may only be implemented \
269 for a coercion between structures with one field \
270 being coerced, none found");
271 return;
272 } else if diff_fields.len() > 1 {
273 let item = tcx.hir.expect_item(impl_node_id);
274 let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
275 t.path.span
276 } else {
277 tcx.hir.span(impl_node_id)
278 };
279
280 let mut err = struct_span_err!(tcx.sess,
281 span,
282 E0375,
283 "implementing the trait \
284 `CoerceUnsized` requires multiple \
285 coercions");
286 err.note("`CoerceUnsized` may only be implemented for \
287 a coercion between structures with one field being coerced");
288 err.note(&format!("currently, {} fields need coercions: {}",
289 diff_fields.len(),
290 diff_fields.iter()
291 .map(|&(i, a, b)| {
292 format!("{} ({} to {})", fields[i].name, a, b)
293 })
294 .collect::<Vec<_>>()
295 .join(", ")));
296 err.span_label(span, &format!("requires multiple coercions"));
297 err.emit();
298 return;
299 }
300
301 let (i, a, b) = diff_fields[0];
302 let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
303 (a, b, coerce_unsized_trait, Some(kind))
304 }
305
306 _ => {
307 span_err!(tcx.sess,
308 span,
309 E0376,
310 "the trait `CoerceUnsized` may only be implemented \
311 for a coercion between structures");
312 return;
313 }
314 };
315
316 let mut fulfill_cx = traits::FulfillmentContext::new();
317
318 // Register an obligation for `A: Trait<B>`.
319 let cause = traits::ObligationCause::misc(span, impl_node_id);
320 let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
321 fulfill_cx.register_predicate_obligation(&infcx, predicate);
322
323 // Check that all transitive obligations are satisfied.
324 if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
325 infcx.report_fulfillment_errors(&errors);
326 }
327
328 // Finally, resolve all regions.
329 let mut free_regions = FreeRegionMap::new();
330 free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
331 .caller_bounds);
332 infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
333
334 if let Some(kind) = kind {
335 tcx.maps.custom_coerce_unsized_kind.borrow_mut().insert(impl_did, kind);
336 }
337 });
338 }