]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_analysis/src/coherence/builtin.rs
28c04087868a7d35e4677488297a5ebe865eed6f
[rust.git] / compiler / rustc_hir_analysis / src / coherence / builtin.rs
1 //! Check properties that are required by built-in traits and set
2 //! up data structures required by type-checking/codegen.
3
4 use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
5 use rustc_errors::{struct_span_err, MultiSpan};
6 use rustc_hir as hir;
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::outlives::env::OutlivesEnvironment;
11 use rustc_infer::infer::TyCtxtInferExt;
12 use rustc_infer::infer::{self, RegionResolutionError};
13 use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
14 use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
15 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
16 use rustc_trait_selection::traits::misc::{
17     type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason,
18 };
19 use rustc_trait_selection::traits::predicate_for_trait_def;
20 use rustc_trait_selection::traits::{self, ObligationCause};
21 use std::collections::BTreeMap;
22
23 pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
24     let lang_items = tcx.lang_items();
25     Checker { tcx, trait_def_id }
26         .check(lang_items.drop_trait(), visit_implementation_of_drop)
27         .check(lang_items.copy_trait(), visit_implementation_of_copy)
28         .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
29         .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
30 }
31
32 struct Checker<'tcx> {
33     tcx: TyCtxt<'tcx>,
34     trait_def_id: DefId,
35 }
36
37 impl<'tcx> Checker<'tcx> {
38     fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
39     where
40         F: FnMut(TyCtxt<'tcx>, LocalDefId),
41     {
42         if Some(self.trait_def_id) == trait_def_id {
43             for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) {
44                 f(self.tcx, impl_def_id);
45             }
46         }
47         self
48     }
49 }
50
51 fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
52     // Destructors only work on local ADT types.
53     match tcx.type_of(impl_did).kind() {
54         ty::Adt(def, _) if def.did().is_local() => return,
55         ty::Error(_) => return,
56         _ => {}
57     }
58
59     let ItemKind::Impl(impl_) = tcx.hir().expect_item(impl_did).kind else { bug!("expected Drop impl item") };
60
61     tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span });
62 }
63
64 fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
65     debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
66
67     let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
68
69     let self_type = tcx.type_of(impl_did);
70     debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
71
72     let param_env = tcx.param_env(impl_did);
73     assert!(!self_type.has_escaping_bound_vars());
74
75     debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
76
77     let span = match tcx.hir().expect_item(impl_did).kind {
78         ItemKind::Impl(hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. }) => return,
79         ItemKind::Impl(impl_) => impl_.self_ty.span,
80         _ => bug!("expected Copy impl item"),
81     };
82
83     let cause = traits::ObligationCause::misc(span, impl_hir_id);
84     match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
85         Ok(()) => {}
86         Err(CopyImplementationError::InfrigingFields(fields)) => {
87             let mut err = struct_span_err!(
88                 tcx.sess,
89                 span,
90                 E0204,
91                 "the trait `Copy` may not be implemented for this type"
92             );
93
94             // We'll try to suggest constraining type parameters to fulfill the requirements of
95             // their `Copy` implementation.
96             let mut errors: BTreeMap<_, Vec<_>> = Default::default();
97             let mut bounds = vec![];
98
99             for (field, ty, reason) in fields {
100                 let field_span = tcx.def_span(field.did);
101                 err.span_label(field_span, "this field does not implement `Copy`");
102
103                 match reason {
104                     InfringingFieldsReason::Fulfill(fulfillment_errors) => {
105                         for error in fulfillment_errors {
106                             let error_predicate = error.obligation.predicate;
107                             // Only note if it's not the root obligation, otherwise it's trivial and
108                             // should be self-explanatory (i.e. a field literally doesn't implement Copy).
109
110                             // FIXME: This error could be more descriptive, especially if the error_predicate
111                             // contains a foreign type or if it's a deeply nested type...
112                             if error_predicate != error.root_obligation.predicate {
113                                 errors
114                                     .entry((ty.to_string(), error_predicate.to_string()))
115                                     .or_default()
116                                     .push(error.obligation.cause.span);
117                             }
118                             if let ty::PredicateKind::Clause(ty::Clause::Trait(
119                                 ty::TraitPredicate {
120                                     trait_ref,
121                                     polarity: ty::ImplPolarity::Positive,
122                                     ..
123                                 },
124                             )) = error_predicate.kind().skip_binder()
125                             {
126                                 let ty = trait_ref.self_ty();
127                                 if let ty::Param(_) = ty.kind() {
128                                     bounds.push((
129                                         format!("{ty}"),
130                                         trait_ref.print_only_trait_path().to_string(),
131                                         Some(trait_ref.def_id),
132                                     ));
133                                 }
134                             }
135                         }
136                     }
137                     InfringingFieldsReason::Regions(region_errors) => {
138                         for error in region_errors {
139                             let ty = ty.to_string();
140                             match error {
141                                 RegionResolutionError::ConcreteFailure(origin, a, b) => {
142                                     let predicate = format!("{b}: {a}");
143                                     errors
144                                         .entry((ty.clone(), predicate.clone()))
145                                         .or_default()
146                                         .push(origin.span());
147                                     if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() {
148                                         bounds.push((b.to_string(), a.to_string(), None));
149                                     }
150                                 }
151                                 RegionResolutionError::GenericBoundFailure(origin, a, b) => {
152                                     let predicate = format!("{a}: {b}");
153                                     errors
154                                         .entry((ty.clone(), predicate.clone()))
155                                         .or_default()
156                                         .push(origin.span());
157                                     if let infer::region_constraints::GenericKind::Param(_) = a {
158                                         bounds.push((a.to_string(), b.to_string(), None));
159                                     }
160                                 }
161                                 _ => continue,
162                             }
163                         }
164                     }
165                 }
166             }
167             for ((ty, error_predicate), spans) in errors {
168                 let span: MultiSpan = spans.into();
169                 err.span_note(
170                     span,
171                     &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate),
172                 );
173             }
174             suggest_constraining_type_params(
175                 tcx,
176                 tcx.hir().get_generics(impl_did).expect("impls always have generics"),
177                 &mut err,
178                 bounds.iter().map(|(param, constraint, def_id)| {
179                     (param.as_str(), constraint.as_str(), *def_id)
180                 }),
181             );
182             err.emit();
183         }
184         Err(CopyImplementationError::NotAnAdt) => {
185             tcx.sess.emit_err(CopyImplOnNonAdt { span });
186         }
187         Err(CopyImplementationError::HasDestructor) => {
188             tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
189         }
190     }
191 }
192
193 fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
194     debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
195
196     // Just compute this for the side-effects, in particular reporting
197     // errors; other parts of the code may demand it for the info of
198     // course.
199     let span = tcx.def_span(impl_did);
200     tcx.at(span).coerce_unsized_info(impl_did);
201 }
202
203 fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
204     debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
205
206     let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
207     let span = tcx.hir().span(impl_hir_id);
208
209     let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
210
211     let source = tcx.type_of(impl_did);
212     assert!(!source.has_escaping_bound_vars());
213     let target = {
214         let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
215         assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
216
217         trait_ref.substs.type_at(1)
218     };
219
220     debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
221
222     let param_env = tcx.param_env(impl_did);
223
224     let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
225
226     let infcx = tcx.infer_ctxt().build();
227     let cause = ObligationCause::misc(span, impl_hir_id);
228
229     use rustc_type_ir::sty::TyKind::*;
230     match (source.kind(), target.kind()) {
231         (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
232             if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
233         (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
234         (&Adt(def_a, substs_a), &Adt(def_b, substs_b))
235             if def_a.is_struct() && def_b.is_struct() =>
236         {
237             if def_a != def_b {
238                 let source_path = tcx.def_path_str(def_a.did());
239                 let target_path = tcx.def_path_str(def_b.did());
240
241                 create_err(&format!(
242                     "the trait `DispatchFromDyn` may only be implemented \
243                             for a coercion between structures with the same \
244                             definition; expected `{}`, found `{}`",
245                     source_path, target_path,
246                 ))
247                 .emit();
248
249                 return;
250             }
251
252             if def_a.repr().c() || def_a.repr().packed() {
253                 create_err(
254                     "structs implementing `DispatchFromDyn` may not have \
255                          `#[repr(packed)]` or `#[repr(C)]`",
256                 )
257                 .emit();
258             }
259
260             let fields = &def_a.non_enum_variant().fields;
261
262             let coerced_fields = fields
263                 .iter()
264                 .filter(|field| {
265                     let ty_a = field.ty(tcx, substs_a);
266                     let ty_b = field.ty(tcx, substs_b);
267
268                     if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
269                         if layout.is_zst() && layout.align.abi.bytes() == 1 {
270                             // ignore ZST fields with alignment of 1 byte
271                             return false;
272                         }
273                     }
274
275                     if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
276                         if ok.obligations.is_empty() {
277                             create_err(
278                                 "the trait `DispatchFromDyn` may only be implemented \
279                                  for structs containing the field being coerced, \
280                                  ZST fields with 1 byte alignment, and nothing else",
281                             )
282                             .note(&format!(
283                                 "extra field `{}` of type `{}` is not allowed",
284                                 field.name, ty_a,
285                             ))
286                             .emit();
287
288                             return false;
289                         }
290                     }
291
292                     return true;
293                 })
294                 .collect::<Vec<_>>();
295
296             if coerced_fields.is_empty() {
297                 create_err(
298                     "the trait `DispatchFromDyn` may only be implemented \
299                         for a coercion between structures with a single field \
300                         being coerced, none found",
301                 )
302                 .emit();
303             } else if coerced_fields.len() > 1 {
304                 create_err("implementing the `DispatchFromDyn` trait requires multiple coercions")
305                     .note(
306                         "the trait `DispatchFromDyn` may only be implemented \
307                             for a coercion between structures with a single field \
308                             being coerced",
309                     )
310                     .note(&format!(
311                         "currently, {} fields need coercions: {}",
312                         coerced_fields.len(),
313                         coerced_fields
314                             .iter()
315                             .map(|field| {
316                                 format!(
317                                     "`{}` (`{}` to `{}`)",
318                                     field.name,
319                                     field.ty(tcx, substs_a),
320                                     field.ty(tcx, substs_b),
321                                 )
322                             })
323                             .collect::<Vec<_>>()
324                             .join(", ")
325                     ))
326                     .emit();
327             } else {
328                 let errors = traits::fully_solve_obligations(
329                     &infcx,
330                     coerced_fields.into_iter().map(|field| {
331                         predicate_for_trait_def(
332                             tcx,
333                             param_env,
334                             cause.clone(),
335                             dispatch_from_dyn_trait,
336                             0,
337                             [field.ty(tcx, substs_a), field.ty(tcx, substs_b)],
338                         )
339                     }),
340                 );
341                 if !errors.is_empty() {
342                     infcx.err_ctxt().report_fulfillment_errors(&errors, None);
343                 }
344
345                 // Finally, resolve all regions.
346                 let outlives_env = OutlivesEnvironment::new(param_env);
347                 let _ = infcx
348                     .err_ctxt()
349                     .check_region_obligations_and_report_errors(impl_did, &outlives_env);
350             }
351         }
352         _ => {
353             create_err(
354                 "the trait `DispatchFromDyn` may only be implemented \
355                     for a coercion between structures",
356             )
357             .emit();
358         }
359     }
360 }
361
362 pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
363     debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
364
365     // this provider should only get invoked for local def-ids
366     let impl_did = impl_did.expect_local();
367     let span = tcx.def_span(impl_did);
368
369     let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
370
371     let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
372         tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string()));
373     });
374
375     let source = tcx.type_of(impl_did);
376     let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
377     assert_eq!(trait_ref.def_id, coerce_unsized_trait);
378     let target = trait_ref.substs.type_at(1);
379     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
380
381     let param_env = tcx.param_env(impl_did);
382     assert!(!source.has_escaping_bound_vars());
383
384     let err_info = CoerceUnsizedInfo { custom_kind: None };
385
386     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
387
388     let infcx = tcx.infer_ctxt().build();
389     let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
390     let cause = ObligationCause::misc(span, impl_hir_id);
391     let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
392                        mt_b: ty::TypeAndMut<'tcx>,
393                        mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
394         if mt_a.mutbl < mt_b.mutbl {
395             infcx
396                 .err_ctxt()
397                 .report_mismatched_types(
398                     &cause,
399                     mk_ptr(mt_b.ty),
400                     target,
401                     ty::error::TypeError::Mutability,
402                 )
403                 .emit();
404         }
405         (mt_a.ty, mt_b.ty, unsize_trait, None)
406     };
407     let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
408         (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
409             infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
410             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
411             let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
412             check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
413         }
414
415         (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
416             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
417             check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
418         }
419
420         (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)),
421
422         (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
423             if def_a.is_struct() && def_b.is_struct() =>
424         {
425             if def_a != def_b {
426                 let source_path = tcx.def_path_str(def_a.did());
427                 let target_path = tcx.def_path_str(def_b.did());
428                 struct_span_err!(
429                     tcx.sess,
430                     span,
431                     E0377,
432                     "the trait `CoerceUnsized` may only be implemented \
433                            for a coercion between structures with the same \
434                            definition; expected `{}`, found `{}`",
435                     source_path,
436                     target_path
437                 )
438                 .emit();
439                 return err_info;
440             }
441
442             // Here we are considering a case of converting
443             // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
444             // which acts like a pointer to `U`, but carries along some extra data of type `T`:
445             //
446             //     struct Foo<T, U> {
447             //         extra: T,
448             //         ptr: *mut U,
449             //     }
450             //
451             // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
452             // to `Foo<T, [i32]>`. That impl would look like:
453             //
454             //   impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
455             //
456             // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
457             // when this coercion occurs, we would be changing the
458             // field `ptr` from a thin pointer of type `*mut [i32;
459             // 3]` to a fat pointer of type `*mut [i32]` (with
460             // extra data `3`). **The purpose of this check is to
461             // make sure that we know how to do this conversion.**
462             //
463             // To check if this impl is legal, we would walk down
464             // the fields of `Foo` and consider their types with
465             // both substitutes. We are looking to find that
466             // exactly one (non-phantom) field has changed its
467             // type, which we will expect to be the pointer that
468             // is becoming fat (we could probably generalize this
469             // to multiple thin pointers of the same type becoming
470             // fat, but we don't). In this case:
471             //
472             // - `extra` has type `T` before and type `T` after
473             // - `ptr` has type `*mut U` before and type `*mut V` after
474             //
475             // Since just one field changed, we would then check
476             // that `*mut U: CoerceUnsized<*mut V>` is implemented
477             // (in other words, that we know how to do this
478             // conversion). This will work out because `U:
479             // Unsize<V>`, and we have a builtin rule that `*mut
480             // U` can be coerced to `*mut V` if `U: Unsize<V>`.
481             let fields = &def_a.non_enum_variant().fields;
482             let diff_fields = fields
483                 .iter()
484                 .enumerate()
485                 .filter_map(|(i, f)| {
486                     let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
487
488                     if tcx.type_of(f.did).is_phantom_data() {
489                         // Ignore PhantomData fields
490                         return None;
491                     }
492
493                     // Ignore fields that aren't changed; it may
494                     // be that we could get away with subtyping or
495                     // something more accepting, but we use
496                     // equality because we want to be able to
497                     // perform this check without computing
498                     // variance where possible. (This is because
499                     // we may have to evaluate constraint
500                     // expressions in the course of execution.)
501                     // See e.g., #41936.
502                     if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
503                         if ok.obligations.is_empty() {
504                             return None;
505                         }
506                     }
507
508                     // Collect up all fields that were significantly changed
509                     // i.e., those that contain T in coerce_unsized T -> U
510                     Some((i, a, b))
511                 })
512                 .collect::<Vec<_>>();
513
514             if diff_fields.is_empty() {
515                 struct_span_err!(
516                     tcx.sess,
517                     span,
518                     E0374,
519                     "the trait `CoerceUnsized` may only be implemented \
520                            for a coercion between structures with one field \
521                            being coerced, none found"
522                 )
523                 .emit();
524                 return err_info;
525             } else if diff_fields.len() > 1 {
526                 let item = tcx.hir().expect_item(impl_did);
527                 let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind {
528                     t.path.span
529                 } else {
530                     tcx.def_span(impl_did)
531                 };
532
533                 struct_span_err!(
534                     tcx.sess,
535                     span,
536                     E0375,
537                     "implementing the trait \
538                                                 `CoerceUnsized` requires multiple \
539                                                 coercions"
540                 )
541                 .note(
542                     "`CoerceUnsized` may only be implemented for \
543                           a coercion between structures with one field being coerced",
544                 )
545                 .note(&format!(
546                     "currently, {} fields need coercions: {}",
547                     diff_fields.len(),
548                     diff_fields
549                         .iter()
550                         .map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) })
551                         .collect::<Vec<_>>()
552                         .join(", ")
553                 ))
554                 .span_label(span, "requires multiple coercions")
555                 .emit();
556                 return err_info;
557             }
558
559             let (i, a, b) = diff_fields[0];
560             let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
561             (a, b, coerce_unsized_trait, Some(kind))
562         }
563
564         _ => {
565             struct_span_err!(
566                 tcx.sess,
567                 span,
568                 E0376,
569                 "the trait `CoerceUnsized` may only be implemented \
570                        for a coercion between structures"
571             )
572             .emit();
573             return err_info;
574         }
575     };
576
577     // Register an obligation for `A: Trait<B>`.
578     let cause = traits::ObligationCause::misc(span, impl_hir_id);
579     let predicate =
580         predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]);
581     let errors = traits::fully_solve_obligation(&infcx, predicate);
582     if !errors.is_empty() {
583         infcx.err_ctxt().report_fulfillment_errors(&errors, None);
584     }
585
586     // Finally, resolve all regions.
587     let outlives_env = OutlivesEnvironment::new(param_env);
588     let _ = infcx.err_ctxt().check_region_obligations_and_report_errors(impl_did, &outlives_env);
589
590     CoerceUnsizedInfo { custom_kind: kind }
591 }