1 //! Check properties that are required by built-in traits and set
2 //! up data structures required by type-checking/codegen.
4 use crate::errors
::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}
;
5 use rustc_errors
::struct_span_err
;
7 use rustc_hir
::def_id
::{DefId, LocalDefId}
;
8 use rustc_hir
::lang_items
::LangItem
;
9 use rustc_hir
::ItemKind
;
10 use rustc_infer
::infer
;
11 use rustc_infer
::infer
::outlives
::env
::OutlivesEnvironment
;
12 use rustc_infer
::infer
::{RegionckMode, TyCtxtInferExt}
;
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
>, LocalDefId
),
40 if Some(self.trait_def_id
) == trait_def_id
{
41 for &impl_def_id
in self.tcx
.hir().trait_impls(self.trait_def_id
) {
42 f(self.tcx
, impl_def_id
);
49 fn visit_implementation_of_drop(tcx
: TyCtxt
<'_
>, impl_did
: LocalDefId
) {
50 // Destructors only work on nominal types.
51 if let ty
::Adt(..) | ty
::Error(_
) = tcx
.type_of(impl_did
).kind() {
55 let impl_hir_id
= tcx
.hir().local_def_id_to_hir_id(impl_did
);
56 let sp
= match tcx
.hir().expect_item(impl_hir_id
).kind
{
57 ItemKind
::Impl(ref impl_
) => impl_
.self_ty
.span
,
58 _
=> bug
!("expected Drop impl item"),
61 tcx
.sess
.emit_err(DropImplOnWrongItem { span: sp }
);
64 fn visit_implementation_of_copy(tcx
: TyCtxt
<'_
>, impl_did
: LocalDefId
) {
65 debug
!("visit_implementation_of_copy: impl_did={:?}", impl_did
);
67 let impl_hir_id
= tcx
.hir().local_def_id_to_hir_id(impl_did
);
69 let self_type
= tcx
.type_of(impl_did
);
70 debug
!("visit_implementation_of_copy: self_type={:?} (bound)", self_type
);
72 let span
= tcx
.hir().span(impl_hir_id
);
73 let param_env
= tcx
.param_env(impl_did
);
74 assert
!(!self_type
.has_escaping_bound_vars());
76 debug
!("visit_implementation_of_copy: self_type={:?} (free)", self_type
);
78 match can_type_implement_copy(tcx
, param_env
, self_type
) {
80 Err(CopyImplementationError
::InfrigingFields(fields
)) => {
81 let item
= tcx
.hir().expect_item(impl_hir_id
);
82 let span
= if let ItemKind
::Impl(hir
::Impl { of_trait: Some(ref tr), .. }
) = item
.kind
{
88 let mut err
= struct_span_err
!(
92 "the trait `Copy` may not be implemented for this type"
94 for span
in fields
.iter().map(|f
| tcx
.def_span(f
.did
)) {
95 err
.span_label(span
, "this field does not implement `Copy`");
99 Err(CopyImplementationError
::NotAnAdt
) => {
100 let item
= tcx
.hir().expect_item(impl_hir_id
);
102 if let ItemKind
::Impl(ref impl_
) = item
.kind { impl_.self_ty.span }
else { span }
;
104 tcx
.sess
.emit_err(CopyImplOnNonAdt { span }
);
106 Err(CopyImplementationError
::HasDestructor
) => {
107 tcx
.sess
.emit_err(CopyImplOnTypeWithDtor { span }
);
112 fn visit_implementation_of_coerce_unsized(tcx
: TyCtxt
<'tcx
>, impl_did
: LocalDefId
) {
113 debug
!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did
);
115 // Just compute this for the side-effects, in particular reporting
116 // errors; other parts of the code may demand it for the info of
118 let span
= tcx
.def_span(impl_did
);
119 tcx
.at(span
).coerce_unsized_info(impl_did
);
122 fn visit_implementation_of_dispatch_from_dyn(tcx
: TyCtxt
<'_
>, impl_did
: LocalDefId
) {
123 debug
!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did
);
125 let impl_hir_id
= tcx
.hir().local_def_id_to_hir_id(impl_did
);
126 let span
= tcx
.hir().span(impl_hir_id
);
128 let dispatch_from_dyn_trait
= tcx
.require_lang_item(LangItem
::DispatchFromDyn
, Some(span
));
130 let source
= tcx
.type_of(impl_did
);
131 assert
!(!source
.has_escaping_bound_vars());
133 let trait_ref
= tcx
.impl_trait_ref(impl_did
).unwrap();
134 assert_eq
!(trait_ref
.def_id
, dispatch_from_dyn_trait
);
136 trait_ref
.substs
.type_at(1)
139 debug
!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source
, target
);
141 let param_env
= tcx
.param_env(impl_did
);
143 let create_err
= |msg
: &str| struct_span_err
!(tcx
.sess
, span
, E0378
, "{}", msg
);
145 tcx
.infer_ctxt().enter(|infcx
| {
146 let cause
= ObligationCause
::misc(span
, impl_hir_id
);
149 match (source
.kind(), target
.kind()) {
150 (&Ref(r_a
, _
, mutbl_a
), Ref(r_b
, _
, mutbl_b
))
151 if infcx
.at(&cause
, param_env
).eq(r_a
, r_b
).is_ok() && mutbl_a
== *mutbl_b
=> {}
152 (&RawPtr(tm_a
), &RawPtr(tm_b
)) if tm_a
.mutbl
== tm_b
.mutbl
=> (),
153 (&Adt(def_a
, substs_a
), &Adt(def_b
, substs_b
))
154 if def_a
.is_struct() && def_b
.is_struct() =>
157 let source_path
= tcx
.def_path_str(def_a
.did
);
158 let target_path
= tcx
.def_path_str(def_b
.did
);
161 "the trait `DispatchFromDyn` may only be implemented \
162 for a coercion between structures with the same \
163 definition; expected `{}`, found `{}`",
164 source_path
, target_path
,
171 if def_a
.repr
.c() || def_a
.repr
.packed() {
173 "structs implementing `DispatchFromDyn` may not have \
174 `#[repr(packed)]` or `#[repr(C)]`",
179 let fields
= &def_a
.non_enum_variant().fields
;
181 let coerced_fields
= fields
183 .filter_map(|field
| {
184 let ty_a
= field
.ty(tcx
, substs_a
);
185 let ty_b
= field
.ty(tcx
, substs_b
);
187 if let Ok(layout
) = tcx
.layout_of(param_env
.and(ty_a
)) {
188 if layout
.is_zst() && layout
.align
.abi
.bytes() == 1 {
189 // ignore ZST fields with alignment of 1 byte
194 if let Ok(ok
) = infcx
.at(&cause
, param_env
).eq(ty_a
, ty_b
) {
195 if ok
.obligations
.is_empty() {
197 "the trait `DispatchFromDyn` may only be implemented \
198 for structs containing the field being coerced, \
199 ZST fields with 1 byte alignment, and nothing else",
202 "extra field `{}` of type `{}` is not allowed",
213 .collect
::<Vec
<_
>>();
215 if coerced_fields
.is_empty() {
217 "the trait `DispatchFromDyn` may only be implemented \
218 for a coercion between structures with a single field \
219 being coerced, none found",
222 } else if coerced_fields
.len() > 1 {
224 "implementing the `DispatchFromDyn` trait requires multiple coercions",
227 "the trait `DispatchFromDyn` may only be implemented \
228 for a coercion between structures with a single field \
232 "currently, {} fields need coercions: {}",
233 coerced_fields
.len(),
238 "`{}` (`{}` to `{}`)",
240 field
.ty(tcx
, substs_a
),
241 field
.ty(tcx
, substs_b
),
249 let mut fulfill_cx
= <dyn TraitEngine
<'_
>>::new(infcx
.tcx
);
251 for field
in coerced_fields
{
252 let predicate
= predicate_for_trait_def(
256 dispatch_from_dyn_trait
,
258 field
.ty(tcx
, substs_a
),
259 &[field
.ty(tcx
, substs_b
).into()],
262 fulfill_cx
.register_predicate_obligation(&infcx
, predicate
);
265 // Check that all transitive obligations are satisfied.
266 if let Err(errors
) = fulfill_cx
.select_all_or_error(&infcx
) {
267 infcx
.report_fulfillment_errors(&errors
, None
, false);
270 // Finally, resolve all regions.
271 let outlives_env
= OutlivesEnvironment
::new(param_env
);
272 infcx
.resolve_regions_and_report_errors(
273 impl_did
.to_def_id(),
275 RegionckMode
::default(),
281 "the trait `DispatchFromDyn` may only be implemented \
282 for a coercion between structures",
290 pub fn coerce_unsized_info(tcx
: TyCtxt
<'tcx
>, impl_did
: DefId
) -> CoerceUnsizedInfo
{
291 debug
!("compute_coerce_unsized_info(impl_did={:?})", impl_did
);
293 // this provider should only get invoked for local def-ids
294 let impl_hir_id
= tcx
.hir().local_def_id_to_hir_id(impl_did
.expect_local());
295 let span
= tcx
.hir().span(impl_hir_id
);
297 let coerce_unsized_trait
= tcx
.require_lang_item(LangItem
::CoerceUnsized
, Some(span
));
299 let unsize_trait
= tcx
.lang_items().require(LangItem
::Unsize
).unwrap_or_else(|err
| {
300 tcx
.sess
.fatal(&format
!("`CoerceUnsized` implementation {}", err
));
303 let source
= tcx
.type_of(impl_did
);
304 let trait_ref
= tcx
.impl_trait_ref(impl_did
).unwrap();
305 assert_eq
!(trait_ref
.def_id
, coerce_unsized_trait
);
306 let target
= trait_ref
.substs
.type_at(1);
307 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source
, target
);
309 let param_env
= tcx
.param_env(impl_did
);
310 assert
!(!source
.has_escaping_bound_vars());
312 let err_info
= CoerceUnsizedInfo { custom_kind: None }
;
314 debug
!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source
, target
);
316 tcx
.infer_ctxt().enter(|infcx
| {
317 let cause
= ObligationCause
::misc(span
, impl_hir_id
);
318 let check_mutbl
= |mt_a
: ty
::TypeAndMut
<'tcx
>,
319 mt_b
: ty
::TypeAndMut
<'tcx
>,
320 mk_ptr
: &dyn Fn(Ty
<'tcx
>) -> Ty
<'tcx
>| {
321 if (mt_a
.mutbl
, mt_b
.mutbl
) == (hir
::Mutability
::Not
, hir
::Mutability
::Mut
) {
323 .report_mismatched_types(
327 ty
::error
::TypeError
::Mutability
,
331 (mt_a
.ty
, mt_b
.ty
, unsize_trait
, None
)
333 let (source
, target
, trait_def_id
, kind
) = match (source
.kind(), target
.kind()) {
334 (&ty
::Ref(r_a
, ty_a
, mutbl_a
), &ty
::Ref(r_b
, ty_b
, mutbl_b
)) => {
335 infcx
.sub_regions(infer
::RelateObjectBound(span
), r_b
, r_a
);
336 let mt_a
= ty
::TypeAndMut { ty: ty_a, mutbl: mutbl_a }
;
337 let mt_b
= ty
::TypeAndMut { ty: ty_b, mutbl: mutbl_b }
;
338 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ref(r_b
, ty
))
341 (&ty
::Ref(_
, ty_a
, mutbl_a
), &ty
::RawPtr(mt_b
)) => {
342 let mt_a
= ty
::TypeAndMut { ty: ty_a, mutbl: mutbl_a }
;
343 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ptr(ty
))
346 (&ty
::RawPtr(mt_a
), &ty
::RawPtr(mt_b
)) => {
347 check_mutbl(mt_a
, mt_b
, &|ty
| tcx
.mk_imm_ptr(ty
))
350 (&ty
::Adt(def_a
, substs_a
), &ty
::Adt(def_b
, substs_b
))
351 if def_a
.is_struct() && def_b
.is_struct() =>
354 let source_path
= tcx
.def_path_str(def_a
.did
);
355 let target_path
= tcx
.def_path_str(def_b
.did
);
360 "the trait `CoerceUnsized` may only be implemented \
361 for a coercion between structures with the same \
362 definition; expected `{}`, found `{}`",
370 // Here we are considering a case of converting
371 // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
372 // which acts like a pointer to `U`, but carries along some extra data of type `T`:
374 // struct Foo<T, U> {
379 // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
380 // to `Foo<T, [i32]>`. That impl would look like:
382 // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
384 // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
385 // when this coercion occurs, we would be changing the
386 // field `ptr` from a thin pointer of type `*mut [i32;
387 // 3]` to a fat pointer of type `*mut [i32]` (with
388 // extra data `3`). **The purpose of this check is to
389 // make sure that we know how to do this conversion.**
391 // To check if this impl is legal, we would walk down
392 // the fields of `Foo` and consider their types with
393 // both substitutes. We are looking to find that
394 // exactly one (non-phantom) field has changed its
395 // type, which we will expect to be the pointer that
396 // is becoming fat (we could probably generalize this
397 // to multiple thin pointers of the same type becoming
398 // fat, but we don't). In this case:
400 // - `extra` has type `T` before and type `T` after
401 // - `ptr` has type `*mut U` before and type `*mut V` after
403 // Since just one field changed, we would then check
404 // that `*mut U: CoerceUnsized<*mut V>` is implemented
405 // (in other words, that we know how to do this
406 // conversion). This will work out because `U:
407 // Unsize<V>`, and we have a builtin rule that `*mut
408 // U` can be coerced to `*mut V` if `U: Unsize<V>`.
409 let fields
= &def_a
.non_enum_variant().fields
;
410 let diff_fields
= fields
413 .filter_map(|(i
, f
)| {
414 let (a
, b
) = (f
.ty(tcx
, substs_a
), f
.ty(tcx
, substs_b
));
416 if tcx
.type_of(f
.did
).is_phantom_data() {
417 // Ignore PhantomData fields
421 // Ignore fields that aren't changed; it may
422 // be that we could get away with subtyping or
423 // something more accepting, but we use
424 // equality because we want to be able to
425 // perform this check without computing
426 // variance where possible. (This is because
427 // we may have to evaluate constraint
428 // expressions in the course of execution.)
430 if let Ok(ok
) = infcx
.at(&cause
, param_env
).eq(a
, b
) {
431 if ok
.obligations
.is_empty() {
436 // Collect up all fields that were significantly changed
437 // i.e., those that contain T in coerce_unsized T -> U
440 .collect
::<Vec
<_
>>();
442 if diff_fields
.is_empty() {
447 "the trait `CoerceUnsized` may only be implemented \
448 for a coercion between structures with one field \
449 being coerced, none found"
453 } else if diff_fields
.len() > 1 {
454 let item
= tcx
.hir().expect_item(impl_hir_id
);
455 let span
= if let ItemKind
::Impl(hir
::Impl { of_trait: Some(ref t), .. }
) =
460 tcx
.hir().span(impl_hir_id
)
467 "implementing the trait \
468 `CoerceUnsized` requires multiple \
472 "`CoerceUnsized` may only be implemented for \
473 a coercion between structures with one field being coerced",
476 "currently, {} fields need coercions: {}",
481 format
!("`{}` (`{}` to `{}`)", fields
[i
].ident
, a
, b
)
486 .span_label(span
, "requires multiple coercions")
491 let (i
, a
, b
) = diff_fields
[0];
492 let kind
= ty
::adjustment
::CustomCoerceUnsized
::Struct(i
);
493 (a
, b
, coerce_unsized_trait
, Some(kind
))
501 "the trait `CoerceUnsized` may only be implemented \
502 for a coercion between structures"
509 let mut fulfill_cx
= <dyn TraitEngine
<'_
>>::new(infcx
.tcx
);
511 // Register an obligation for `A: Trait<B>`.
512 let cause
= traits
::ObligationCause
::misc(span
, impl_hir_id
);
513 let predicate
= predicate_for_trait_def(
522 fulfill_cx
.register_predicate_obligation(&infcx
, predicate
);
524 // Check that all transitive obligations are satisfied.
525 if let Err(errors
) = fulfill_cx
.select_all_or_error(&infcx
) {
526 infcx
.report_fulfillment_errors(&errors
, None
, false);
529 // Finally, resolve all regions.
530 let outlives_env
= OutlivesEnvironment
::new(param_env
);
531 infcx
.resolve_regions_and_report_errors(impl_did
, &outlives_env
, RegionckMode
::default());
533 CoerceUnsizedInfo { custom_kind: kind }