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.
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.
11 //! Check properties that are required by built-in traits and set
12 //! up data structures required by type-checking/translation.
14 use rustc::middle::free_region::FreeRegionMap;
15 use rustc::middle::region::RegionMaps;
16 use rustc::middle::lang_items::UnsizeTraitLangItem;
18 use rustc::traits::{self, ObligationCause, Reveal};
19 use rustc::ty::{self, Ty, TyCtxt};
20 use rustc::ty::ParameterEnvironment;
21 use rustc::ty::TypeFoldable;
22 use rustc::ty::adjustment::CoerceUnsizedInfo;
23 use rustc::ty::subst::Subst;
24 use rustc::ty::util::CopyImplementationError;
27 use rustc::hir::def_id::DefId;
28 use rustc::hir::map as hir_map;
29 use rustc::hir::{self, ItemImpl};
31 pub fn check_trait<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_def_id: DefId) {
32 Checker { tcx, trait_def_id }
33 .check(tcx.lang_items.drop_trait(), visit_implementation_of_drop)
34 .check(tcx.lang_items.copy_trait(), visit_implementation_of_copy)
35 .check(tcx.lang_items.coerce_unsized_trait(),
36 visit_implementation_of_coerce_unsized);
39 struct Checker<'a, 'tcx: 'a> {
40 tcx: TyCtxt<'a, 'tcx, 'tcx>,
44 impl<'a, 'tcx> Checker<'a, 'tcx> {
45 fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
46 where F: FnMut(TyCtxt<'a, 'tcx, 'tcx>, DefId, DefId)
48 if Some(self.trait_def_id) == trait_def_id {
49 for &impl_id in self.tcx.hir.trait_impls(self.trait_def_id) {
50 let impl_def_id = self.tcx.hir.local_def_id(impl_id);
51 f(self.tcx, self.trait_def_id, impl_def_id);
58 fn visit_implementation_of_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
61 match tcx.type_of(impl_did).sty {
64 // Destructors only work on nominal types.
65 if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_did) {
66 match tcx.hir.find(impl_node_id) {
67 Some(hir_map::NodeItem(item)) => {
68 let span = match item.node {
69 ItemImpl(.., ref ty, _) => ty.span,
72 struct_span_err!(tcx.sess,
75 "the Drop trait may only be implemented on \
77 .span_label(span, "implementing Drop requires a struct")
81 bug!("didn't find impl in ast map");
85 bug!("found external impl of Drop trait on \
86 something other than a struct");
92 fn visit_implementation_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
95 debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
97 let impl_node_id = if let Some(n) = tcx.hir.as_local_node_id(impl_did) {
100 debug!("visit_implementation_of_copy(): impl not in this \
105 let self_type = tcx.type_of(impl_did);
106 debug!("visit_implementation_of_copy: self_type={:?} (bound)",
109 let span = tcx.hir.span(impl_node_id);
110 let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
111 let self_type = self_type.subst(tcx, ¶m_env.free_substs);
112 assert!(!self_type.has_escaping_regions());
114 debug!("visit_implementation_of_copy: self_type={:?} (free)",
117 match param_env.can_type_implement_copy(tcx, self_type, span) {
119 Err(CopyImplementationError::InfrigingField(field)) => {
120 let item = tcx.hir.expect_item(impl_node_id);
121 let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
127 struct_span_err!(tcx.sess,
130 "the trait `Copy` may not be implemented for this type")
132 tcx.def_span(field.did),
133 "this field does not implement `Copy`")
136 Err(CopyImplementationError::NotAnAdt) => {
137 let item = tcx.hir.expect_item(impl_node_id);
138 let span = if let ItemImpl(.., ref ty, _) = item.node {
144 struct_span_err!(tcx.sess,
147 "the trait `Copy` may not be implemented for this type")
148 .span_label(span, "type is not a structure or enumeration")
151 Err(CopyImplementationError::HasDestructor) => {
152 struct_span_err!(tcx.sess,
155 "the trait `Copy` may not be implemented for this type; the \
156 type has a destructor")
157 .span_label(span, "Copy not allowed on types with destructors")
163 fn visit_implementation_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
166 debug!("visit_implementation_of_coerce_unsized: impl_did={:?}",
169 // Just compute this for the side-effects, in particular reporting
170 // errors; other parts of the code may demand it for the info of
172 if impl_did.is_local() {
173 let span = tcx.def_span(impl_did);
174 tcx.at(span).coerce_unsized_info(impl_did);
178 pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
180 -> CoerceUnsizedInfo {
181 debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
182 let coerce_unsized_trait = tcx.lang_items.coerce_unsized_trait().unwrap();
184 let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
187 tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
191 // this provider should only get invoked for local def-ids
192 let impl_node_id = tcx.hir.as_local_node_id(impl_did).unwrap_or_else(|| {
193 bug!("coerce_unsized_info: invoked for non-local def-id {:?}", impl_did)
196 let source = tcx.type_of(impl_did);
197 let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
198 assert_eq!(trait_ref.def_id, coerce_unsized_trait);
199 let target = trait_ref.substs.type_at(1);
200 debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)",
204 let span = tcx.hir.span(impl_node_id);
205 let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
206 let source = source.subst(tcx, ¶m_env.free_substs);
207 let target = target.subst(tcx, ¶m_env.free_substs);
208 assert!(!source.has_escaping_regions());
210 let err_info = CoerceUnsizedInfo { custom_kind: None };
212 debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
216 tcx.infer_ctxt(param_env, Reveal::UserFacing).enter(|infcx| {
217 let cause = ObligationCause::misc(span, impl_node_id);
218 let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
219 mt_b: ty::TypeAndMut<'tcx>,
220 mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
221 if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
222 infcx.report_mismatched_types(&cause,
225 ty::error::TypeError::Mutability)
228 (mt_a.ty, mt_b.ty, unsize_trait, None)
230 let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
231 (&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
232 infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
233 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
236 (&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
237 (&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
238 check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
241 (&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b)) if def_a.is_struct() &&
242 def_b.is_struct() => {
244 let source_path = tcx.item_path_str(def_a.did);
245 let target_path = tcx.item_path_str(def_b.did);
249 "the trait `CoerceUnsized` may only be implemented \
250 for a coercion between structures with the same \
251 definition; expected {}, found {}",
257 let fields = &def_a.struct_variant().fields;
258 let diff_fields = fields.iter()
260 .filter_map(|(i, f)| {
261 let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
263 if tcx.type_of(f.did).is_phantom_data() {
264 // Ignore PhantomData fields
268 // Ignore fields that aren't significantly changed
269 if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
270 if ok.obligations.is_empty() {
275 // Collect up all fields that were significantly changed
276 // i.e. those that contain T in coerce_unsized T -> U
279 .collect::<Vec<_>>();
281 if diff_fields.is_empty() {
285 "the trait `CoerceUnsized` may only be implemented \
286 for a coercion between structures with one field \
287 being coerced, none found");
289 } else if diff_fields.len() > 1 {
290 let item = tcx.hir.expect_item(impl_node_id);
291 let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
294 tcx.hir.span(impl_node_id)
297 let mut err = struct_span_err!(tcx.sess,
300 "implementing the trait \
301 `CoerceUnsized` requires multiple \
303 err.note("`CoerceUnsized` may only be implemented for \
304 a coercion between structures with one field being coerced");
305 err.note(&format!("currently, {} fields need coercions: {}",
309 format!("{} ({} to {})", fields[i].name, a, b)
313 err.span_label(span, "requires multiple coercions");
318 let (i, a, b) = diff_fields[0];
319 let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
320 (a, b, coerce_unsized_trait, Some(kind))
327 "the trait `CoerceUnsized` may only be implemented \
328 for a coercion between structures");
333 let mut fulfill_cx = traits::FulfillmentContext::new();
335 // Register an obligation for `A: Trait<B>`.
336 let cause = traits::ObligationCause::misc(span, impl_node_id);
337 let predicate = tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
338 fulfill_cx.register_predicate_obligation(&infcx, predicate);
340 // Check that all transitive obligations are satisfied.
341 if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
342 infcx.report_fulfillment_errors(&errors);
345 // Finally, resolve all regions.
346 let region_maps = RegionMaps::new();
347 let mut free_regions = FreeRegionMap::new();
348 free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
350 infcx.resolve_regions_and_report_errors(impl_did, ®ion_maps, &free_regions);