• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
2 //
3 // We don't do any drop checking during hir typeck.
4 use rustc_data_structures::fx::FxHashSet;
5 use rustc_errors::{struct_span_err, ErrorGuaranteed};
6 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
7 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
8 use rustc_middle::ty::subst::SubstsRef;
9 use rustc_middle::ty::util::CheckRegions;
10 use rustc_middle::ty::{self, TyCtxt};
11 use rustc_trait_selection::traits::{self, ObligationCtxt};
12 
13 use crate::errors;
14 use crate::hir::def_id::{DefId, LocalDefId};
15 
16 /// This function confirms that the `Drop` implementation identified by
17 /// `drop_impl_did` is not any more specialized than the type it is
18 /// attached to (Issue #8142).
19 ///
20 /// This means:
21 ///
22 /// 1. The self type must be nominal (this is already checked during
23 ///    coherence),
24 ///
25 /// 2. The generic region/type parameters of the impl's self type must
26 ///    all be parameters of the Drop impl itself (i.e., no
27 ///    specialization like `impl Drop for Foo<i32>`), and,
28 ///
29 /// 3. Any bounds on the generic parameters must be reflected in the
30 ///    struct/enum definition for the nominal type itself (i.e.
31 ///    cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
32 ///
check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed>33 pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> {
34     match tcx.impl_polarity(drop_impl_did) {
35         ty::ImplPolarity::Positive => {}
36         ty::ImplPolarity::Negative => {
37             return Err(tcx.sess.emit_err(errors::DropImplPolarity::Negative {
38                 span: tcx.def_span(drop_impl_did),
39             }));
40         }
41         ty::ImplPolarity::Reservation => {
42             return Err(tcx.sess.emit_err(errors::DropImplPolarity::Reservation {
43                 span: tcx.def_span(drop_impl_did),
44             }));
45         }
46     }
47     let dtor_self_type = tcx.type_of(drop_impl_did).subst_identity();
48     match dtor_self_type.kind() {
49         ty::Adt(adt_def, adt_to_impl_substs) => {
50             ensure_drop_params_and_item_params_correspond(
51                 tcx,
52                 drop_impl_did.expect_local(),
53                 adt_def.did(),
54                 adt_to_impl_substs,
55             )?;
56 
57             ensure_drop_predicates_are_implied_by_item_defn(
58                 tcx,
59                 drop_impl_did.expect_local(),
60                 adt_def.did().expect_local(),
61                 adt_to_impl_substs,
62             )
63         }
64         _ => {
65             // Destructors only work on nominal types. This was
66             // already checked by coherence, but compilation may
67             // not have been terminated.
68             let span = tcx.def_span(drop_impl_did);
69             let reported = tcx.sess.delay_span_bug(
70                 span,
71                 format!("should have been rejected by coherence check: {dtor_self_type}"),
72             );
73             Err(reported)
74         }
75     }
76 }
77 
ensure_drop_params_and_item_params_correspond<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_did: LocalDefId, self_type_did: DefId, adt_to_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorGuaranteed>78 fn ensure_drop_params_and_item_params_correspond<'tcx>(
79     tcx: TyCtxt<'tcx>,
80     drop_impl_did: LocalDefId,
81     self_type_did: DefId,
82     adt_to_impl_substs: SubstsRef<'tcx>,
83 ) -> Result<(), ErrorGuaranteed> {
84     let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_substs, CheckRegions::OnlyEarlyBound) else {
85         return Ok(())
86     };
87 
88     let drop_impl_span = tcx.def_span(drop_impl_did);
89     let item_span = tcx.def_span(self_type_did);
90     let self_descr = tcx.def_descr(self_type_did);
91     let mut err =
92         struct_span_err!(tcx.sess, drop_impl_span, E0366, "`Drop` impls cannot be specialized");
93     match arg {
94         ty::util::NotUniqueParam::DuplicateParam(arg) => {
95             err.note(format!("`{arg}` is mentioned multiple times"))
96         }
97         ty::util::NotUniqueParam::NotParam(arg) => {
98             err.note(format!("`{arg}` is not a generic parameter"))
99         }
100     };
101     err.span_note(
102         item_span,
103         format!(
104             "use the same sequence of generic lifetime, type and const parameters \
105                      as the {self_descr} definition",
106         ),
107     );
108     Err(err.emit())
109 }
110 
111 /// Confirms that every predicate imposed by dtor_predicates is
112 /// implied by assuming the predicates attached to self_type_did.
ensure_drop_predicates_are_implied_by_item_defn<'tcx>( tcx: TyCtxt<'tcx>, drop_impl_def_id: LocalDefId, adt_def_id: LocalDefId, adt_to_impl_substs: SubstsRef<'tcx>, ) -> Result<(), ErrorGuaranteed>113 fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
114     tcx: TyCtxt<'tcx>,
115     drop_impl_def_id: LocalDefId,
116     adt_def_id: LocalDefId,
117     adt_to_impl_substs: SubstsRef<'tcx>,
118 ) -> Result<(), ErrorGuaranteed> {
119     let infcx = tcx.infer_ctxt().build();
120     let ocx = ObligationCtxt::new(&infcx);
121 
122     // Take the param-env of the adt and substitute the substs that show up in
123     // the implementation's self type. This gives us the assumptions that the
124     // self ty of the implementation is allowed to know just from it being a
125     // well-formed adt, since that's all we're allowed to assume while proving
126     // the Drop implementation is not specialized.
127     //
128     // We don't need to normalize this param-env or anything, since we're only
129     // substituting it with free params, so no additional param-env normalization
130     // can occur on top of what has been done in the param_env query itself.
131     let param_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id))
132         .subst(tcx, adt_to_impl_substs)
133         .with_constness(tcx.constness(drop_impl_def_id));
134 
135     for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) {
136         let normalize_cause = traits::ObligationCause::misc(span, adt_def_id);
137         let pred = ocx.normalize(&normalize_cause, param_env, pred);
138         let cause = traits::ObligationCause::new(span, adt_def_id, traits::DropImpl);
139         ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, pred));
140     }
141 
142     // All of the custom error reporting logic is to preserve parity with the old
143     // error messages.
144     //
145     // They can probably get removed with better treatment of the new `DropImpl`
146     // obligation cause code, and perhaps some custom logic in `report_region_errors`.
147 
148     let errors = ocx.select_all_or_error();
149     if !errors.is_empty() {
150         let mut guar = None;
151         let mut root_predicates = FxHashSet::default();
152         for error in errors {
153             let root_predicate = error.root_obligation.predicate;
154             if root_predicates.insert(root_predicate) {
155                 let item_span = tcx.def_span(adt_def_id);
156                 let self_descr = tcx.def_descr(adt_def_id.to_def_id());
157                 guar = Some(
158                     struct_span_err!(
159                         tcx.sess,
160                         error.root_obligation.cause.span,
161                         E0367,
162                         "`Drop` impl requires `{root_predicate}` \
163                         but the {self_descr} it is implemented for does not",
164                     )
165                     .span_note(item_span, "the implementor must specify the same requirement")
166                     .emit(),
167                 );
168             }
169         }
170         return Err(guar.unwrap());
171     }
172 
173     let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(param_env));
174     if !errors.is_empty() {
175         let mut guar = None;
176         for error in errors {
177             let item_span = tcx.def_span(adt_def_id);
178             let self_descr = tcx.def_descr(adt_def_id.to_def_id());
179             let outlives = match error {
180                 RegionResolutionError::ConcreteFailure(_, a, b) => format!("{b}: {a}"),
181                 RegionResolutionError::GenericBoundFailure(_, generic, r) => {
182                     format!("{generic}: {r}")
183                 }
184                 RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"),
185                 RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
186                     format!("{b}: {a}", a = ty::Region::new_var(tcx, a))
187                 }
188             };
189             guar = Some(
190                 struct_span_err!(
191                     tcx.sess,
192                     error.origin().span(),
193                     E0367,
194                     "`Drop` impl requires `{outlives}` \
195                     but the {self_descr} it is implemented for does not",
196                 )
197                 .span_note(item_span, "the implementor must specify the same requirement")
198                 .emit(),
199             );
200         }
201         return Err(guar.unwrap());
202     }
203 
204     Ok(())
205 }
206