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
;
16 use rustc
::middle
::lang_items
::UnsizeTraitLangItem
;
18 use rustc
::traits
::{self, ObligationCause}
;
19 use rustc
::ty
::{self, Ty, TyCtxt}
;
20 use rustc
::ty
::TypeFoldable
;
21 use rustc
::ty
::adjustment
::CoerceUnsizedInfo
;
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
.type_of(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
, "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
.type_of(impl_did
);
104 debug
!("visit_implementation_of_copy: self_type={:?} (bound)",
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());
111 debug
!("visit_implementation_of_copy: self_type={:?} (free)",
114 match param_env
.can_type_implement_copy(tcx
, self_type
, span
) {
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
{
124 struct_span_err
!(tcx
.sess
,
127 "the trait `Copy` may not be implemented for this type")
129 tcx
.def_span(field
.did
),
130 "this field does not implement `Copy`")
133 Err(CopyImplementationError
::NotAnAdt
) => {
134 let item
= tcx
.hir
.expect_item(impl_node_id
);
135 let span
= if let ItemImpl(.., ref ty
, _
) = item
.node
{
141 struct_span_err
!(tcx
.sess
,
144 "the trait `Copy` may not be implemented for this type")
145 .span_label(span
, "type is not a structure or enumeration")
148 Err(CopyImplementationError
::HasDestructor
) => {
149 struct_span_err
!(tcx
.sess
,
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")
160 fn visit_implementation_of_coerce_unsized
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
163 debug
!("visit_implementation_of_coerce_unsized: impl_did={:?}",
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
169 if impl_did
.is_local() {
170 let span
= tcx
.def_span(impl_did
);
171 tcx
.at(span
).coerce_unsized_info(impl_did
);
175 pub fn coerce_unsized_info
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
177 -> CoerceUnsizedInfo
{
178 debug
!("compute_coerce_unsized_info(impl_did={:?})", impl_did
);
179 let coerce_unsized_trait
= tcx
.lang_items().coerce_unsized_trait().unwrap();
181 let unsize_trait
= match tcx
.lang_items().require(UnsizeTraitLangItem
) {
184 tcx
.sess
.fatal(&format
!("`CoerceUnsized` implementation {}", err
));
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
)
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)",
201 let span
= tcx
.hir
.span(impl_node_id
);
202 let param_env
= tcx
.param_env(impl_did
);
203 assert
!(!source
.has_escaping_regions());
205 let err_info
= CoerceUnsizedInfo { custom_kind: None }
;
207 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)",
211 tcx
.infer_ctxt().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
,
220 ty
::error
::TypeError
::Mutability
)
223 (mt_a
.ty
, mt_b
.ty
, unsize_trait
, None
)
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
))
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
))
236 (&ty
::TyAdt(def_a
, substs_a
), &ty
::TyAdt(def_b
, substs_b
)) if def_a
.is_struct() &&
237 def_b
.is_struct() => {
239 let source_path
= tcx
.item_path_str(def_a
.did
);
240 let target_path
= tcx
.item_path_str(def_b
.did
);
244 "the trait `CoerceUnsized` may only be implemented \
245 for a coercion between structures with the same \
246 definition; expected {}, found {}",
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`:
256 // struct Foo<T, U> {
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:
264 // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
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.**
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:
282 // - `extra` has type `T` before and type `T` after
283 // - `ptr` has type `*mut U` before and type `*mut V` after
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()
294 .filter_map(|(i
, f
)| {
295 let (a
, b
) = (f
.ty(tcx
, substs_a
), f
.ty(tcx
, substs_b
));
297 if tcx
.type_of(f
.did
).is_phantom_data() {
298 // Ignore PhantomData fields
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.)
311 if let Ok(ok
) = infcx
.at(&cause
, param_env
).eq(a
, b
) {
312 if ok
.obligations
.is_empty() {
317 // Collect up all fields that were significantly changed
318 // i.e. those that contain T in coerce_unsized T -> U
321 .collect
::<Vec
<_
>>();
323 if diff_fields
.is_empty() {
327 "the trait `CoerceUnsized` may only be implemented \
328 for a coercion between structures with one field \
329 being coerced, none found");
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
{
336 tcx
.hir
.span(impl_node_id
)
339 let mut err
= struct_span_err
!(tcx
.sess
,
342 "implementing the trait \
343 `CoerceUnsized` requires multiple \
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: {}",
351 format
!("{} ({} to {})", fields
[i
].name
, a
, b
)
355 err
.span_label(span
, "requires multiple coercions");
360 let (i
, a
, b
) = diff_fields
[0];
361 let kind
= ty
::adjustment
::CustomCoerceUnsized
::Struct(i
);
362 (a
, b
, coerce_unsized_trait
, Some(kind
))
369 "the trait `CoerceUnsized` may only be implemented \
370 for a coercion between structures");
375 let mut fulfill_cx
= traits
::FulfillmentContext
::new();
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(param_env
,
385 fulfill_cx
.register_predicate_obligation(&infcx
, predicate
);
387 // Check that all transitive obligations are satisfied.
388 if let Err(errors
) = fulfill_cx
.select_all_or_error(&infcx
) {
389 infcx
.report_fulfillment_errors(&errors
, None
);
392 // Finally, resolve all regions.
393 let region_scope_tree
= region
::ScopeTree
::default();
394 let mut free_regions
= FreeRegionMap
::new();
395 free_regions
.relate_free_regions_from_predicates(¶m_env
.caller_bounds
);
396 infcx
.resolve_regions_and_report_errors(impl_did
, ®ion_scope_tree
, &free_regions
);