Skip to main content

rustc_hir_analysis/check/
always_applicable.rs

1//! This module contains methods that assist in checking that impls are general
2//! enough, i.e. that they always apply to every valid instantaiton of the ADT
3//! they're implemented for.
4//!
5//! This is necessary for `Drop` and negative impls to be well-formed.
6
7use rustc_data_structures::fx::FxHashSet;
8use rustc_errors::codes::*;
9use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
10use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
11use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
12use rustc_middle::span_bug;
13use rustc_middle::ty::util::CheckRegions;
14use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
15use rustc_trait_selection::regions::InferCtxtRegionExt;
16use rustc_trait_selection::traits::{self, ObligationCtxt};
17
18use crate::errors;
19use crate::hir::def_id::{DefId, LocalDefId};
20
21/// This function confirms that the `Drop` implementation identified by
22/// `drop_impl_did` is not any more specialized than the type it is
23/// attached to (Issue #8142).
24///
25/// This means:
26///
27/// 1. The self type must be nominal (this is already checked during
28///    coherence),
29///
30/// 2. The generic region/type parameters of the impl's self type must
31///    all be parameters of the Drop impl itself (i.e., no
32///    specialization like `impl Drop for Foo<i32>`), and,
33///
34/// 3. Any bounds on the generic parameters must be reflected in the
35///    struct/enum definition for the nominal type itself (i.e.
36///    cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
37pub(crate) fn check_drop_impl(
38    tcx: TyCtxt<'_>,
39    drop_impl_did: LocalDefId,
40) -> Result<(), ErrorGuaranteed> {
41    match tcx.impl_polarity(drop_impl_did) {
42        ty::ImplPolarity::Positive => {}
43        ty::ImplPolarity::Negative => {
44            return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Negative {
45                span: tcx.def_span(drop_impl_did),
46            }));
47        }
48        ty::ImplPolarity::Reservation => {
49            return Err(tcx.dcx().emit_err(errors::DropImplPolarity::Reservation {
50                span: tcx.def_span(drop_impl_did),
51            }));
52        }
53    }
54
55    tcx.ensure_result().orphan_check_impl(drop_impl_did)?;
56
57    let self_ty = tcx.type_of(drop_impl_did).instantiate_identity().skip_norm_wip();
58
59    match self_ty.kind() {
60        ty::Adt(adt_def, adt_to_impl_args) => {
61            ensure_impl_params_and_item_params_correspond(
62                tcx,
63                drop_impl_did,
64                adt_def.did(),
65                adt_to_impl_args,
66            )?;
67
68            ensure_all_fields_are_const_destruct(tcx, drop_impl_did, adt_def.did())?;
69
70            ensure_impl_predicates_are_implied_by_item_defn(
71                tcx,
72                drop_impl_did,
73                adt_def.did(),
74                adt_to_impl_args,
75            )
76        }
77        _ => {
78            ::rustc_middle::util::bug::span_bug_fmt(tcx.def_span(drop_impl_did),
    format_args!("incoherent impl of Drop"));span_bug!(tcx.def_span(drop_impl_did), "incoherent impl of Drop");
79        }
80    }
81}
82
83pub(crate) fn check_negative_auto_trait_impl<'tcx>(
84    tcx: TyCtxt<'tcx>,
85    impl_def_id: LocalDefId,
86    impl_trait_ref: ty::TraitRef<'tcx>,
87    polarity: ty::ImplPolarity,
88) -> Result<(), ErrorGuaranteed> {
89    let ty::ImplPolarity::Negative = polarity else {
90        return Ok(());
91    };
92
93    if !tcx.trait_is_auto(impl_trait_ref.def_id) {
94        return Ok(());
95    }
96
97    if tcx.defaultness(impl_def_id).is_default() {
98        tcx.dcx().span_delayed_bug(tcx.def_span(impl_def_id), "default impl cannot be negative");
99    }
100
101    tcx.ensure_result().orphan_check_impl(impl_def_id)?;
102
103    match impl_trait_ref.self_ty().kind() {
104        ty::Adt(adt_def, adt_to_impl_args) => {
105            ensure_impl_params_and_item_params_correspond(
106                tcx,
107                impl_def_id,
108                adt_def.did(),
109                adt_to_impl_args,
110            )?;
111
112            ensure_impl_predicates_are_implied_by_item_defn(
113                tcx,
114                impl_def_id,
115                adt_def.did(),
116                adt_to_impl_args,
117            )
118        }
119        _ => {
120            if tcx.features().auto_traits() {
121                // NOTE: We ignore the applicability check for negative auto impls
122                // defined in libcore. In the (almost impossible) future where we
123                // stabilize auto impls, then the proper applicability check MUST
124                // be implemented here to handle non-ADT rigid types.
125                Ok(())
126            } else {
127                Err(tcx.dcx().span_delayed_bug(
128                    tcx.def_span(impl_def_id),
129                    "incoherent impl of negative auto trait",
130                ))
131            }
132        }
133    }
134}
135
136fn ensure_impl_params_and_item_params_correspond<'tcx>(
137    tcx: TyCtxt<'tcx>,
138    impl_def_id: LocalDefId,
139    adt_def_id: DefId,
140    adt_to_impl_args: GenericArgsRef<'tcx>,
141) -> Result<(), ErrorGuaranteed> {
142    let Err(arg) = tcx.uses_unique_generic_params(adt_to_impl_args, CheckRegions::OnlyParam) else {
143        return Ok(());
144    };
145
146    let impl_span = tcx.def_span(impl_def_id);
147    let item_span = tcx.def_span(adt_def_id);
148    let self_descr = tcx.def_descr(adt_def_id);
149    let polarity = match tcx.impl_polarity(impl_def_id) {
150        ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
151        ty::ImplPolarity::Negative => "!",
152    };
153    let trait_name = tcx.item_name(tcx.impl_trait_id(impl_def_id.to_def_id()));
154    let mut err = {
    tcx.dcx().struct_span_err(impl_span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}{1}` impls cannot be specialized",
                            polarity, trait_name))
                })).with_code(E0366)
}struct_span_code_err!(
155        tcx.dcx(),
156        impl_span,
157        E0366,
158        "`{polarity}{trait_name}` impls cannot be specialized",
159    );
160    match arg {
161        ty::util::NotUniqueParam::DuplicateParam(arg) => {
162            err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is mentioned multiple times",
                arg))
    })format!("`{arg}` is mentioned multiple times"))
163        }
164        ty::util::NotUniqueParam::NotParam(arg) => {
165            err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is not a generic parameter",
                arg))
    })format!("`{arg}` is not a generic parameter"))
166        }
167    };
168    err.span_note(
169        item_span,
170        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use the same sequence of generic lifetime, type and const parameters as the {0} definition",
                self_descr))
    })format!(
171            "use the same sequence of generic lifetime, type and const parameters \
172                     as the {self_descr} definition",
173        ),
174    );
175    Err(err.emit())
176}
177
178fn ensure_all_fields_are_const_destruct<'tcx>(
179    tcx: TyCtxt<'tcx>,
180    impl_def_id: LocalDefId,
181    adt_def_id: DefId,
182) -> Result<(), ErrorGuaranteed> {
183    if !tcx.is_conditionally_const(impl_def_id) {
184        return Ok(());
185    }
186    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
187    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
188
189    let impl_span = tcx.def_span(impl_def_id.to_def_id());
190    let env =
191        ty::EarlyBinder::bind(tcx.param_env(impl_def_id)).instantiate_identity().skip_norm_wip();
192    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
193    let destruct_trait = tcx.lang_items().destruct_trait().unwrap();
194    for field in tcx.adt_def(adt_def_id).all_fields() {
195        let field_ty = field.ty(tcx, args);
196        let cause = traits::ObligationCause::new(
197            tcx.def_span(field.did),
198            impl_def_id,
199            ObligationCauseCode::Misc,
200        );
201        ocx.register_obligation(traits::Obligation::new(
202            tcx,
203            cause,
204            env,
205            ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
206                trait_ref: ty::TraitRef::new(tcx, destruct_trait, [field_ty]),
207                constness: ty::BoundConstness::Maybe,
208            }),
209        ));
210    }
211    ocx.evaluate_obligations_error_on_ambiguity()
212        .into_iter()
213        .map(|error| {
214            let ty::ClauseKind::HostEffect(eff) =
215                error.root_obligation.predicate.expect_clause().kind().no_bound_vars().unwrap()
216            else {
217                ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
218            };
219            let field_ty = eff.trait_ref.self_ty();
220            let diag = {
    tcx.dcx().struct_span_err(error.root_obligation.cause.span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}` does not implement `[const] Destruct`",
                            field_ty))
                })).with_code(E0367)
}struct_span_code_err!(
221                tcx.dcx(),
222                error.root_obligation.cause.span,
223                E0367,
224                "`{field_ty}` does not implement `[const] Destruct`",
225            )
226            .with_span_note(impl_span, "required for this `Drop` impl");
227            if field_ty.has_param() {
228                // FIXME: suggest adding `[const] Destruct` by teaching
229                // `suggest_restricting_param_bound` about const traits.
230            }
231            Err(diag.emit())
232        })
233        .collect()
234}
235
236/// Confirms that all predicates defined on the `Drop` impl (`drop_impl_def_id`) are able to be
237/// proven from within `adt_def_id`'s environment. I.e. all the predicates on the impl are
238/// implied by the ADT being well formed.
239fn ensure_impl_predicates_are_implied_by_item_defn<'tcx>(
240    tcx: TyCtxt<'tcx>,
241    impl_def_id: LocalDefId,
242    adt_def_id: DefId,
243    adt_to_impl_args: GenericArgsRef<'tcx>,
244) -> Result<(), ErrorGuaranteed> {
245    let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
246    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
247
248    let impl_span = tcx.def_span(impl_def_id.to_def_id());
249    let trait_name = tcx.item_name(tcx.impl_trait_id(impl_def_id.to_def_id()));
250    let polarity = match tcx.impl_polarity(impl_def_id) {
251        ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
252        ty::ImplPolarity::Negative => "!",
253    };
254    // Take the param-env of the adt and instantiate the args that show up in
255    // the implementation's self type. This gives us the assumptions that the
256    // self ty of the implementation is allowed to know just from it being a
257    // well-formed adt, since that's all we're allowed to assume while proving
258    // the Drop implementation is not specialized.
259    //
260    // We don't need to normalize this param-env or anything, since we're only
261    // instantiating it with free params, so no additional param-env normalization
262    // can occur on top of what has been done in the param_env query itself.
263    //
264    // Note: Ideally instead of instantiating the `ParamEnv` with the arguments from the impl ty we
265    // could instead use identity args for the adt. Unfortunately this would cause any errors to
266    // reference the params from the ADT instead of from the impl which is bad UX. To resolve
267    // this we "rename" the ADT's params to be the impl's params which should not affect behaviour.
268    let impl_adt_ty = Ty::new_adt(tcx, tcx.adt_def(adt_def_id), adt_to_impl_args);
269    let adt_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id))
270        .instantiate(tcx, adt_to_impl_args)
271        .skip_norm_wip();
272
273    let fresh_impl_args = infcx.fresh_args_for_item(impl_span, impl_def_id.to_def_id());
274    let fresh_adt_ty =
275        tcx.impl_trait_ref(impl_def_id).instantiate(tcx, fresh_impl_args).skip_norm_wip().self_ty();
276
277    ocx.eq(&ObligationCause::dummy_with_span(impl_span), adt_env, fresh_adt_ty, impl_adt_ty)
278        .expect("equating fully generic trait ref should never fail");
279
280    for (clause, span) in tcx.predicates_of(impl_def_id).instantiate(tcx, fresh_impl_args) {
281        let normalize_cause = traits::ObligationCause::misc(span, impl_def_id);
282        let pred = ocx.normalize(&normalize_cause, adt_env, clause);
283        let cause = traits::ObligationCause::new(
284            span,
285            impl_def_id,
286            ObligationCauseCode::AlwaysApplicableImpl,
287        );
288        ocx.register_obligation(traits::Obligation::new(tcx, cause, adt_env, pred));
289    }
290
291    // All of the custom error reporting logic is to preserve parity with the old
292    // error messages.
293    //
294    // They can probably get removed with better treatment of the new `DropImpl`
295    // obligation cause code, and perhaps some custom logic in `report_region_errors`.
296
297    let errors = ocx.evaluate_obligations_error_on_ambiguity();
298    if !errors.is_empty() {
299        let mut guar = None;
300        let mut root_predicates = FxHashSet::default();
301        for error in errors {
302            let root_predicate = error.root_obligation.predicate;
303            if root_predicates.insert(root_predicate) {
304                let item_span = tcx.def_span(adt_def_id);
305                let self_descr = tcx.def_descr(adt_def_id);
306                guar = Some(
307                    {
    tcx.dcx().struct_span_err(error.root_obligation.cause.span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}{1}` impl requires `{2}` but the {3} it is implemented for does not",
                            polarity, trait_name, root_predicate, self_descr))
                })).with_code(E0367)
}struct_span_code_err!(
308                        tcx.dcx(),
309                        error.root_obligation.cause.span,
310                        E0367,
311                        "`{polarity}{trait_name}` impl requires `{root_predicate}` \
312                        but the {self_descr} it is implemented for does not",
313                    )
314                    .with_span_note(item_span, "the implementor must specify the same requirement")
315                    .emit(),
316                );
317            }
318        }
319        return Err(guar.unwrap());
320    }
321
322    let errors = ocx.infcx.resolve_regions(impl_def_id, adt_env, []);
323    if !errors.is_empty() {
324        let mut guar = None;
325        for error in errors {
326            let item_span = tcx.def_span(adt_def_id);
327            let self_descr = tcx.def_descr(adt_def_id);
328            let outlives = match error {
329                RegionResolutionError::ConcreteFailure(_, a, b) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}: {1}", b, a))
    })format!("{b}: {a}"),
330                RegionResolutionError::GenericBoundFailure(_, generic, r) => {
331                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}: {1}", generic, r))
    })format!("{generic}: {r}")
332                }
333                RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}: {1}", b, a))
    })format!("{b}: {a}"),
334                RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
335                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{1}: {0}",
                ty::Region::new_var(tcx, a), b))
    })format!("{b}: {a}", a = ty::Region::new_var(tcx, a))
336                }
337                RegionResolutionError::CannotNormalize(..) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
338            };
339            guar = Some(
340                {
    tcx.dcx().struct_span_err(error.origin().span(),
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("`{0}{1}` impl requires `{2}` but the {3} it is implemented for does not",
                            polarity, trait_name, outlives, self_descr))
                })).with_code(E0367)
}struct_span_code_err!(
341                    tcx.dcx(),
342                    error.origin().span(),
343                    E0367,
344                    "`{polarity}{trait_name}` impl requires `{outlives}` \
345                    but the {self_descr} it is implemented for does not",
346                )
347                .with_span_note(item_span, "the implementor must specify the same requirement")
348                .emit(),
349            );
350        }
351        return Err(guar.unwrap());
352    }
353
354    Ok(())
355}