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