1 //! Check properties that are required by built-in traits and set
2 //! up data structures required by type-checking/codegen.
4 use rustc_errors
::struct_span_err
;
6 use rustc_hir
::def_id
::DefId
;
7 use rustc_hir
::lang_items
::UnsizeTraitLangItem
;
8 use rustc_hir
::ItemKind
;
9 use rustc_infer
::infer
;
10 use rustc_infer
::infer
::outlives
::env
::OutlivesEnvironment
;
11 use rustc_infer
::infer
::{RegionckMode, TyCtxtInferExt}
;
12 use rustc_middle
::middle
::region
;
13 use rustc_middle
::ty
::adjustment
::CoerceUnsizedInfo
;
14 use rustc_middle
::ty
::TypeFoldable
;
15 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
16 use rustc_trait_selection
::traits
::error_reporting
::InferCtxtExt
;
17 use rustc_trait_selection
::traits
::misc
::{can_type_implement_copy, CopyImplementationError}
;
18 use rustc_trait_selection
::traits
::predicate_for_trait_def
;
19 use rustc_trait_selection
::traits
::{self, ObligationCause, TraitEngine, TraitEngineExt}
;
21 pub fn check_trait(tcx
: TyCtxt
<'_
>, trait_def_id
: DefId
) {
22 let lang_items
= tcx
.lang_items();
23 Checker { tcx, trait_def_id }
24 .check(lang_items
.drop_trait(), visit_implementation_of_drop
)
25 .check(lang_items
.copy_trait(), visit_implementation_of_copy
)
26 .check(lang_items
.coerce_unsized_trait(), visit_implementation_of_coerce_unsized
)
27 .check(lang_items
.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn
);
30 struct Checker
<'tcx
> {
35 impl<'tcx
> Checker
<'tcx
> {
36 fn check
<F
>(&self, trait_def_id
: Option
<DefId
>, mut f
: F
) -> &Self
38 F
: FnMut(TyCtxt
<'tcx
>, DefId
),
40 if Some(self.trait_def_id
) == trait_def_id
{
41 for &impl_id
in self.tcx
.hir().trait_impls(self.trait_def_id
) {
42 let impl_def_id
= self.tcx
.hir().local_def_id(impl_id
);
43 f(self.tcx
, impl_def_id
);
50 fn visit_implementation_of_drop(tcx
: TyCtxt
<'_
>, impl_did
: DefId
) {
51 // Destructors only work on nominal types.
52 if let ty
::Adt(..) | ty
::Error
= tcx
.type_of(impl_did
).kind
{
56 let impl_hir_id
= tcx
.hir().as_local_hir_id(impl_did
).expect("foreign Drop impl on non-ADT");
57 let sp
= match tcx
.hir().expect_item(impl_hir_id
).kind
{
58 ItemKind
::Impl { self_ty, .. }
=> self_ty
.span
,
59 _
=> bug
!("expected Drop impl item"),
66 "the `Drop` trait may only be implemented for structs, enums, and unions",
68 .span_label(sp
, "must be a struct, enum, or union")
72 fn visit_implementation_of_copy(tcx
: TyCtxt
<'_
>, impl_did
: DefId
) {
73 debug
!("visit_implementation_of_copy: impl_did={:?}", impl_did
);
75 let impl_hir_id
= if let Some(n
) = tcx
.hir().as_local_hir_id(impl_did
) {
78 debug
!("visit_implementation_of_copy(): impl not in this crate");
82 let self_type
= tcx
.type_of(impl_did
);
83 debug
!("visit_implementation_of_copy: self_type={:?} (bound)", self_type
);
85 let span
= tcx
.hir().span(impl_hir_id
);
86 let param_env
= tcx
.param_env(impl_did
);
87 assert
!(!self_type
.has_escaping_bound_vars());
89 debug
!("visit_implementation_of_copy: self_type={:?} (free)", self_type
);
91 match can_type_implement_copy(tcx
, param_env
, self_type
) {
93 Err(CopyImplementationError
::InfrigingFields(fields
)) => {
94 let item
= tcx
.hir().expect_item(impl_hir_id
);
95 let span
= if let ItemKind
::Impl { of_trait: Some(ref tr), .. }
= item
.kind
{
101 let mut err
= struct_span_err
!(
105 "the trait `Copy` may not be implemented for this type"
107 for span
in fields
.iter().map(|f
| tcx
.def_span(f
.did
)) {
108 err
.span_label(span
, "this field does not implement `Copy`");
112 Err(CopyImplementationError
::NotAnAdt
) => {
113 let item
= tcx
.hir().expect_item(impl_hir_id
);
115 if let ItemKind
::Impl { self_ty, .. }
= item
.kind { self_ty.span }
else { span }
;
121 "the trait `Copy` may not be implemented for this type"
123 .span_label(span
, "type is not a structure or enumeration")
126 Err(CopyImplementationError
::HasDestructor
) => {
131 "the trait `Copy` may not be implemented for this type; the \
132 type has a destructor"
134 .span_label(span
, "Copy not allowed on types with destructors")
140 fn visit_implementation_of_coerce_unsized(tcx
: TyCtxt
<'tcx
>, impl_did
: DefId
) {
141 debug
!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did
);
143 // Just compute this for the side-effects, in particular reporting
144 // errors; other parts of the code may demand it for the info of
146 if impl_did
.is_local() {
147 let span
= tcx
.def_span(impl_did
);
148 tcx
.at(span
).coerce_unsized_info(impl_did
);
152 fn visit_implementation_of_dispatch_from_dyn(tcx
: TyCtxt
<'_
>, impl_did
: DefId
) {
153 debug
!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did
);
154 if impl_did
.is_local() {
155 let dispatch_from_dyn_trait
= tcx
.lang_items().dispatch_from_dyn_trait().unwrap();
157 let impl_hir_id
= tcx
.hir().as_local_hir_id(impl_did
).unwrap();
158 let span
= tcx
.hir().span(impl_hir_id
);
160 let source
= tcx
.type_of(impl_did
);
161 assert
!(!source
.has_escaping_bound_vars());
163 let trait_ref
= tcx
.impl_trait_ref(impl_did
).unwrap();
164 assert_eq
!(trait_ref
.def_id
, dispatch_from_dyn_trait
);
166 trait_ref
.substs
.type_at(1)
169 debug
!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source
, target
);
171 let param_env
= tcx
.param_env(impl_did
);
173 let create_err
= |msg
: &str| struct_span_err
!(tcx
.sess
, span
, E0378
, "{}", msg
);
175 tcx
.infer_ctxt().enter(|infcx
| {
176 let cause
= ObligationCause
::misc(span
, impl_hir_id
);
179 match (&source
.kind
, &target
.kind
) {
180 (&Ref(r_a
, _
, mutbl_a
), Ref(r_b
, _
, mutbl_b
))
181 if infcx
.at(&cause
, param_env
).eq(r_a
, r_b
).is_ok() && mutbl_a
== *mutbl_b
=> {}
182 (&RawPtr(tm_a
), &RawPtr(tm_b
)) if tm_a
.mutbl
== tm_b
.mutbl
=> (),
183 (&Adt(def_a
, substs_a
), &Adt(def_b
, substs_b
))
184 if def_a
.is_struct() && def_b
.is_struct() =>
187 let source_path
= tcx
.def_path_str(def_a
.did
);
188 let target_path
= tcx
.def_path_str(def_b
.did
);
191 "the trait `DispatchFromDyn` may only be implemented \
192 for a coercion between structures with the same \
193 definition; expected `{}`, found `{}`",
194 source_path
, target_path
,
201 if def_a
.repr
.c() || def_a
.repr
.packed() {
203 "structs implementing `DispatchFromDyn` may not have \
204 `#[repr(packed)]` or `#[repr(C)]`",
209 let fields
= &def_a
.non_enum_variant().fields
;
211 let coerced_fields
= fields
213 .filter_map(|field
| {
214 let ty_a
= field
.ty(tcx
, substs_a
);
215 let ty_b
= field
.ty(tcx
, substs_b
);
217 if let Ok(layout
) = tcx
.layout_of(param_env
.and(ty_a
)) {
218 if layout
.is_zst() && layout
.align
.abi
.bytes() == 1 {
219 // ignore ZST fields with alignment of 1 byte
224 if let Ok(ok
) = infcx
.at(&cause
, param_env
).eq(ty_a
, ty_b
) {
225 if ok
.obligations
.is_empty() {
227 "the trait `DispatchFromDyn` may only be implemented \
228 for structs containing the field being coerced, \
229 ZST fields with 1 byte alignment, and nothing else",
232 "extra field `{}` of type `{}` is not allowed",
243 .collect
::<Vec
<_
>>();
245 if coerced_fields
.is_empty() {
247 "the trait `DispatchFromDyn` may only be implemented \
248 for a coercion between structures with a single field \
249 being coerced, none found",
252 } else if coerced_fields
.len() > 1 {
254 "implementing the `DispatchFromDyn` trait requires multiple coercions",
257 "the trait `DispatchFromDyn` may only be implemented \
258 for a coercion between structures with a single field \
262 "currently, {} fields need coercions: {}",
263 coerced_fields
.len(),
268 "`{}` (`{}` to `{}`)",
270 field
.ty(tcx
, substs_a
),
271 field
.ty(tcx
, substs_b
),
279 let mut fulfill_cx
= TraitEngine
::new(infcx
.tcx
);
281 for field
in coerced_fields
{
282 let predicate
= predicate_for_trait_def(
286 dispatch_from_dyn_trait
,
288 field
.ty(tcx
, substs_a
),
289 &[field
.ty(tcx
, substs_b
).into()],
292 fulfill_cx
.register_predicate_obligation(&infcx
, predicate
);
295 // Check that all transitive obligations are satisfied.
296 if let Err(errors
) = fulfill_cx
.select_all_or_error(&infcx
) {
297 infcx
.report_fulfillment_errors(&errors
, None
, false);
300 // Finally, resolve all regions.
301 let region_scope_tree
= region
::ScopeTree
::default();
302 let outlives_env
= OutlivesEnvironment
::new(param_env
);
303 infcx
.resolve_regions_and_report_errors(
307 RegionckMode
::default(),
313 "the trait `DispatchFromDyn` may only be implemented \
314 for a coercion between structures",
323 pub fn coerce_unsized_info(tcx
: TyCtxt
<'tcx
>, impl_did
: DefId
) -> CoerceUnsizedInfo
{
324 debug
!("compute_coerce_unsized_info(impl_did={:?})", impl_did
);
325 let coerce_unsized_trait
= tcx
.lang_items().coerce_unsized_trait().unwrap();
327 let unsize_trait
= tcx
.lang_items().require(UnsizeTraitLangItem
).unwrap_or_else(|err
| {
328 tcx
.sess
.fatal(&format
!("`CoerceUnsized` implementation {}", err
));
331 // this provider should only get invoked for local def-ids
332 let impl_hir_id
= tcx
.hir().as_local_hir_id(impl_did
).unwrap_or_else(|| {
333 bug
!("coerce_unsized_info: invoked for non-local def-id {:?}", impl_did
)
336 let source
= tcx
.type_of(impl_did
);
337 let trait_ref
= tcx
.impl_trait_ref(impl_did
).unwrap();
338 assert_eq
!(trait_ref
.def_id
, coerce_unsized_trait
);
339 let target
= trait_ref
.substs
.type_at(1);
340 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source
, target
);
342 let span
= tcx
.hir().span(impl_hir_id
);
343 let param_env
= tcx
.param_env(impl_did
);
344 assert
!(!source
.has_escaping_bound_vars());
346 let err_info
= CoerceUnsizedInfo { custom_kind: None }
;
348 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source
, target
);
350 tcx
.infer_ctxt().enter(|infcx
| {
351 let cause
= ObligationCause
::misc(span
, impl_hir_id
);
352 let check_mutbl
= |mt_a
: ty
::TypeAndMut
<'tcx
>,
353 mt_b
: ty
::TypeAndMut
<'tcx
>,
354 mk_ptr
: &dyn Fn(Ty
<'tcx
>) -> Ty
<'tcx
>| {
355 if (mt_a
.mutbl
, mt_b
.mutbl
) == (hir
::Mutability
::Not
, hir
::Mutability
::Mut
) {
357 .report_mismatched_types(
361 ty
::error
::TypeError
::Mutability
,
365 (mt_a
.ty
, mt_b
.ty
, unsize_trait
, None
)
367 let (source
, target
, trait_def_id
, kind
) = match (&source
.kind
, &target
.kind
) {
368 (&ty
::Ref(r_a
, ty_a
, mutbl_a
), &ty
::Ref(r_b
, ty_b
, mutbl_b
)) => {
369 infcx
.sub_regions(infer
::RelateObjectBound(span
), r_b
, r_a
);
370 let mt_a
= ty
::TypeAndMut { ty: ty_a, mutbl: mutbl_a }
;
371 let mt_b
= ty
::TypeAndMut { ty: ty_b, mutbl: mutbl_b }
;
372 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ref(r_b
, ty
))
375 (&ty
::Ref(_
, ty_a
, mutbl_a
), &ty
::RawPtr(mt_b
)) => {
376 let mt_a
= ty
::TypeAndMut { ty: ty_a, mutbl: mutbl_a }
;
377 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ptr(ty
))
380 (&ty
::RawPtr(mt_a
), &ty
::RawPtr(mt_b
)) => {
381 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ptr(ty
))
384 (&ty
::Adt(def_a
, substs_a
), &ty
::Adt(def_b
, substs_b
))
385 if def_a
.is_struct() && def_b
.is_struct() =>
388 let source_path
= tcx
.def_path_str(def_a
.did
);
389 let target_path
= tcx
.def_path_str(def_b
.did
);
394 "the trait `CoerceUnsized` may only be implemented \
395 for a coercion between structures with the same \
396 definition; expected `{}`, found `{}`",
404 // Here we are considering a case of converting
405 // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
406 // which acts like a pointer to `U`, but carries along some extra data of type `T`:
408 // struct Foo<T, U> {
413 // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
414 // to `Foo<T, [i32]>`. That impl would look like:
416 // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
418 // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
419 // when this coercion occurs, we would be changing the
420 // field `ptr` from a thin pointer of type `*mut [i32;
421 // 3]` to a fat pointer of type `*mut [i32]` (with
422 // extra data `3`). **The purpose of this check is to
423 // make sure that we know how to do this conversion.**
425 // To check if this impl is legal, we would walk down
426 // the fields of `Foo` and consider their types with
427 // both substitutes. We are looking to find that
428 // exactly one (non-phantom) field has changed its
429 // type, which we will expect to be the pointer that
430 // is becoming fat (we could probably generalize this
431 // to multiple thin pointers of the same type becoming
432 // fat, but we don't). In this case:
434 // - `extra` has type `T` before and type `T` after
435 // - `ptr` has type `*mut U` before and type `*mut V` after
437 // Since just one field changed, we would then check
438 // that `*mut U: CoerceUnsized<*mut V>` is implemented
439 // (in other words, that we know how to do this
440 // conversion). This will work out because `U:
441 // Unsize<V>`, and we have a builtin rule that `*mut
442 // U` can be coerced to `*mut V` if `U: Unsize<V>`.
443 let fields
= &def_a
.non_enum_variant().fields
;
444 let diff_fields
= fields
447 .filter_map(|(i
, f
)| {
448 let (a
, b
) = (f
.ty(tcx
, substs_a
), f
.ty(tcx
, substs_b
));
450 if tcx
.type_of(f
.did
).is_phantom_data() {
451 // Ignore PhantomData fields
455 // Ignore fields that aren't changed; it may
456 // be that we could get away with subtyping or
457 // something more accepting, but we use
458 // equality because we want to be able to
459 // perform this check without computing
460 // variance where possible. (This is because
461 // we may have to evaluate constraint
462 // expressions in the course of execution.)
464 if let Ok(ok
) = infcx
.at(&cause
, param_env
).eq(a
, b
) {
465 if ok
.obligations
.is_empty() {
470 // Collect up all fields that were significantly changed
471 // i.e., those that contain T in coerce_unsized T -> U
474 .collect
::<Vec
<_
>>();
476 if diff_fields
.is_empty() {
481 "the trait `CoerceUnsized` may only be implemented \
482 for a coercion between structures with one field \
483 being coerced, none found"
487 } else if diff_fields
.len() > 1 {
488 let item
= tcx
.hir().expect_item(impl_hir_id
);
489 let span
= if let ItemKind
::Impl { of_trait: Some(ref t), .. }
= item
.kind
{
492 tcx
.hir().span(impl_hir_id
)
499 "implementing the trait \
500 `CoerceUnsized` requires multiple \
504 "`CoerceUnsized` may only be implemented for \
505 a coercion between structures with one field being coerced",
508 "currently, {} fields need coercions: {}",
513 format
!("`{}` (`{}` to `{}`)", fields
[i
].ident
, a
, b
)
518 .span_label(span
, "requires multiple coercions")
523 let (i
, a
, b
) = diff_fields
[0];
524 let kind
= ty
::adjustment
::CustomCoerceUnsized
::Struct(i
);
525 (a
, b
, coerce_unsized_trait
, Some(kind
))
533 "the trait `CoerceUnsized` may only be implemented \
534 for a coercion between structures"
541 let mut fulfill_cx
= TraitEngine
::new(infcx
.tcx
);
543 // Register an obligation for `A: Trait<B>`.
544 let cause
= traits
::ObligationCause
::misc(span
, impl_hir_id
);
545 let predicate
= predicate_for_trait_def(
554 fulfill_cx
.register_predicate_obligation(&infcx
, predicate
);
556 // Check that all transitive obligations are satisfied.
557 if let Err(errors
) = fulfill_cx
.select_all_or_error(&infcx
) {
558 infcx
.report_fulfillment_errors(&errors
, None
, false);
561 // Finally, resolve all regions.
562 let region_scope_tree
= region
::ScopeTree
::default();
563 let outlives_env
= OutlivesEnvironment
::new(param_env
);
564 infcx
.resolve_regions_and_report_errors(
568 RegionckMode
::default(),
571 CoerceUnsizedInfo { custom_kind: kind }