rustc_hir_analysis/check/
dropck.rs

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