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
::lang_items
::UnsizeTraitLangItem
;
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
;
25 use rustc
::hir
::def_id
::DefId
;
26 use rustc
::hir
::map
as hir_map
;
27 use rustc
::hir
::{self, ItemImpl}
;
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
);
37 struct Checker
<'a
, 'tcx
: 'a
> {
38 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
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
)
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
);
56 fn visit_implementation_of_drop
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
59 match tcx
.item_type(impl_did
).sty
{
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
,
70 struct_span_err
!(tcx
.sess
,
73 "the Drop trait may only be implemented on \
75 .span_label(span
, &format
!("implementing Drop requires a struct"))
79 bug
!("didn't find impl in ast map");
83 bug
!("found external impl of Drop trait on \
84 something other than a struct");
90 fn visit_implementation_of_copy
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
93 debug
!("visit_implementation_of_copy: impl_did={:?}", impl_did
);
95 let impl_node_id
= if let Some(n
) = tcx
.hir
.as_local_node_id(impl_did
) {
98 debug
!("visit_implementation_of_copy(): impl not in this \
103 let self_type
= tcx
.item_type(impl_did
);
104 debug
!("visit_implementation_of_copy: self_type={:?} (bound)",
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
, ¶m_env
.free_substs
);
110 assert
!(!self_type
.has_escaping_regions());
112 debug
!("visit_implementation_of_copy: self_type={:?} (free)",
115 match param_env
.can_type_implement_copy(tcx
, self_type
, span
) {
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
{
125 struct_span_err
!(tcx
.sess
,
128 "the trait `Copy` may not be implemented for this type")
130 tcx
.def_span(field
.did
),
131 &"this field does not implement `Copy`")
134 Err(CopyImplementationError
::NotAnAdt
) => {
135 let item
= tcx
.hir
.expect_item(impl_node_id
);
136 let span
= if let ItemImpl(.., ref ty
, _
) = item
.node
{
142 struct_span_err
!(tcx
.sess
,
145 "the trait `Copy` may not be implemented for this type")
146 .span_label(span
, &format
!("type is not a structure or enumeration"))
149 Err(CopyImplementationError
::HasDestructor
) => {
150 struct_span_err
!(tcx
.sess
,
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"))
161 fn visit_implementation_of_coerce_unsized
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
162 coerce_unsized_trait
: DefId
,
164 debug
!("visit_implementation_of_coerce_unsized: impl_did={:?}",
167 let unsize_trait
= match tcx
.lang_items
.require(UnsizeTraitLangItem
) {
170 tcx
.sess
.fatal(&format
!("`CoerceUnsized` implementation {}", err
));
174 let impl_node_id
= if let Some(n
) = tcx
.hir
.as_local_node_id(impl_did
) {
177 debug
!("visit_implementation_of_coerce_unsized(): impl not \
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)",
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
, ¶m_env
.free_substs
);
192 let target
= target
.subst(tcx
, ¶m_env
.free_substs
);
193 assert
!(!source
.has_escaping_regions());
195 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
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
,
208 ty
::error
::TypeError
::Mutability
)
211 (mt_a
.ty
, mt_b
.ty
, unsize_trait
, None
)
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
))
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
))
224 (&ty
::TyAdt(def_a
, substs_a
), &ty
::TyAdt(def_b
, substs_b
)) if def_a
.is_struct() &&
225 def_b
.is_struct() => {
227 let source_path
= tcx
.item_path_str(def_a
.did
);
228 let target_path
= tcx
.item_path_str(def_b
.did
);
232 "the trait `CoerceUnsized` may only be implemented \
233 for a coercion between structures with the same \
234 definition; expected {}, found {}",
240 let fields
= &def_a
.struct_variant().fields
;
241 let diff_fields
= fields
.iter()
243 .filter_map(|(i
, f
)| {
244 let (a
, b
) = (f
.ty(tcx
, substs_a
), f
.ty(tcx
, substs_b
));
246 if tcx
.item_type(f
.did
).is_phantom_data() {
247 // Ignore PhantomData fields
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() {
258 // Collect up all fields that were significantly changed
259 // i.e. those that contain T in coerce_unsized T -> U
262 .collect
::<Vec
<_
>>();
264 if diff_fields
.is_empty() {
268 "the trait `CoerceUnsized` may only be implemented \
269 for a coercion between structures with one field \
270 being coerced, none found");
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
{
277 tcx
.hir
.span(impl_node_id
)
280 let mut err
= struct_span_err
!(tcx
.sess
,
283 "implementing the trait \
284 `CoerceUnsized` requires multiple \
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: {}",
292 format
!("{} ({} to {})", fields
[i
].name
, a
, b
)
296 err
.span_label(span
, &format
!("requires multiple coercions"));
301 let (i
, a
, b
) = diff_fields
[0];
302 let kind
= ty
::adjustment
::CustomCoerceUnsized
::Struct(i
);
303 (a
, b
, coerce_unsized_trait
, Some(kind
))
310 "the trait `CoerceUnsized` may only be implemented \
311 for a coercion between structures");
316 let mut fulfill_cx
= traits
::FulfillmentContext
::new();
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
);
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
);
328 // Finally, resolve all regions.
329 let mut free_regions
= FreeRegionMap
::new();
330 free_regions
.relate_free_regions_from_predicates(&infcx
.parameter_environment
332 infcx
.resolve_regions_and_report_errors(&free_regions
, impl_node_id
);
334 if let Some(kind
) = kind
{
335 tcx
.maps
.custom_coerce_unsized_kind
.borrow_mut().insert(impl_did
, kind
);