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