rustc_trait_selection/traits/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
//! Trait Resolution. See the [rustc dev guide] for more information on how this works.
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html

pub mod auto_trait;
pub(crate) mod coherence;
pub mod const_evaluatable;
mod dyn_compatibility;
pub mod effects;
mod engine;
mod fulfill;
pub mod misc;
pub mod normalize;
pub mod outlives_bounds;
pub mod project;
pub mod query;
#[allow(hidden_glob_reexports)]
mod select;
mod specialize;
mod structural_normalize;
#[allow(hidden_glob_reexports)]
mod util;
pub mod vtable;
pub mod wf;

use std::fmt::Debug;
use std::ops::ControlFlow;

use rustc_errors::ErrorGuaranteed;
pub use rustc_infer::traits::*;
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{
    self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, TypingMode,
    Upcast,
};
use rustc_span::Span;
use rustc_span::def_id::DefId;
use tracing::{debug, instrument};

pub use self::coherence::{
    InCrate, IsFirstInputType, OrphanCheckErr, OrphanCheckMode, OverlapResult, UncoveredTyParams,
    add_placeholder_note, orphan_check_trait_ref, overlapping_impls,
};
pub use self::dyn_compatibility::{
    DynCompatibilityViolation, dyn_compatibility_violations_for_assoc_item,
    hir_ty_lowering_dyn_compatibility_violations, is_vtable_safe_method,
};
pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
pub use self::normalize::NormalizeExt;
pub use self::project::{normalize_inherent_projection, normalize_projection_ty};
pub use self::select::{
    EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache,
    SelectionContext,
};
pub use self::specialize::specialization_graph::{
    FutureCompatOverlapError, FutureCompatOverlapErrorKind,
};
pub use self::specialize::{
    OverlapError, specialization_graph, translate_args, translate_args_with_cause,
};
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::{
    BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, elaborate,
    expand_trait_aliases, impl_item_is_final, supertraits,
    transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars,
};
use crate::error_reporting::InferCtxtErrorExt;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::regions::InferCtxtRegionExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;

pub struct FulfillmentError<'tcx> {
    pub obligation: PredicateObligation<'tcx>,
    pub code: FulfillmentErrorCode<'tcx>,
    /// Diagnostics only: the 'root' obligation which resulted in
    /// the failure to process `obligation`. This is the obligation
    /// that was initially passed to `register_predicate_obligation`
    pub root_obligation: PredicateObligation<'tcx>,
}

impl<'tcx> FulfillmentError<'tcx> {
    pub fn new(
        obligation: PredicateObligation<'tcx>,
        code: FulfillmentErrorCode<'tcx>,
        root_obligation: PredicateObligation<'tcx>,
    ) -> FulfillmentError<'tcx> {
        FulfillmentError { obligation, code, root_obligation }
    }

    pub fn is_true_error(&self) -> bool {
        match self.code {
            FulfillmentErrorCode::Select(_)
            | FulfillmentErrorCode::Project(_)
            | FulfillmentErrorCode::Subtype(_, _)
            | FulfillmentErrorCode::ConstEquate(_, _) => true,
            FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
                false
            }
        }
    }
}

impl<'tcx> Debug for FulfillmentError<'tcx> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
    }
}

#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
    /// Inherently impossible to fulfill; this trait is implemented if and only
    /// if it is already implemented.
    Cycle(PredicateObligations<'tcx>),
    Select(SelectionError<'tcx>),
    Project(MismatchedProjectionTypes<'tcx>),
    Subtype(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
    ConstEquate(ExpectedFound<ty::Const<'tcx>>, TypeError<'tcx>),
    Ambiguity {
        /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
        /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
        /// emitting a fatal error instead.
        overflow: Option<bool>,
    },
}

impl<'tcx> Debug for FulfillmentErrorCode<'tcx> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match *self {
            FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
            FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
            FulfillmentErrorCode::Subtype(ref a, ref b) => {
                write!(f, "CodeSubtypeError({a:?}, {b:?})")
            }
            FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
                write!(f, "CodeConstEquateError({a:?}, {b:?})")
            }
            FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
                write!(f, "Overflow({suggest_increasing_limit})")
            }
            FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
        }
    }
}

/// Whether to skip the leak check, as part of a future compatibility warning step.
///
/// The "default" for skip-leak-check corresponds to the current
/// behavior (do not skip the leak check) -- not the behavior we are
/// transitioning into.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub enum SkipLeakCheck {
    Yes,
    #[default]
    No,
}

impl SkipLeakCheck {
    fn is_yes(self) -> bool {
        self == SkipLeakCheck::Yes
    }
}

/// The mode that trait queries run in.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TraitQueryMode {
    /// Standard/un-canonicalized queries get accurate
    /// spans etc. passed in and hence can do reasonable
    /// error reporting on their own.
    Standard,
    /// Canonical queries get dummy spans and hence
    /// must generally propagate errors to
    /// pre-canonicalization callsites.
    Canonical,
}

/// Creates predicate obligations from the generic bounds.
#[instrument(level = "debug", skip(cause, param_env))]
pub fn predicates_for_generics<'tcx>(
    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
    generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation {
        cause: cause(idx, span),
        recursion_depth: 0,
        param_env,
        predicate: clause.as_predicate(),
    })
}

/// Determines whether the type `ty` is known to meet `bound` and
/// returns true if so. Returns false if `ty` either does not meet
/// `bound` or is not known to meet bound (note that this is
/// conservative towards *no impl*, which is the opposite of the
/// `evaluate` methods).
pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
    infcx: &InferCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    ty: Ty<'tcx>,
    def_id: DefId,
) -> bool {
    let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
    pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
}

/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
///
/// Ping me on zulip if you want to use this method and need help with finding
/// an appropriate replacement.
#[instrument(level = "debug", skip(infcx, param_env, pred), ret)]
fn pred_known_to_hold_modulo_regions<'tcx>(
    infcx: &InferCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    pred: impl Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
) -> bool {
    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);

    let result = infcx.evaluate_obligation_no_overflow(&obligation);
    debug!(?result);

    if result.must_apply_modulo_regions() {
        true
    } else if result.may_apply() {
        // Sometimes obligations are ambiguous because the recursive evaluator
        // is not smart enough, so we fall back to fulfillment when we're not certain
        // that an obligation holds or not. Even still, we must make sure that
        // the we do no inference in the process of checking this obligation.
        let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
        infcx.probe(|_| {
            let ocx = ObligationCtxt::new(infcx);
            ocx.register_obligation(obligation);

            let errors = ocx.select_all_or_error();
            match errors.as_slice() {
                // Only known to hold if we did no inference.
                [] => infcx.resolve_vars_if_possible(goal) == goal,

                errors => {
                    debug!(?errors);
                    false
                }
            }
        })
    } else {
        false
    }
}

#[instrument(level = "debug", skip(tcx, elaborated_env))]
fn do_normalize_predicates<'tcx>(
    tcx: TyCtxt<'tcx>,
    cause: ObligationCause<'tcx>,
    elaborated_env: ty::ParamEnv<'tcx>,
    predicates: Vec<ty::Clause<'tcx>>,
) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> {
    let span = cause.span;

    // FIXME. We should really... do something with these region
    // obligations. But this call just continues the older
    // behavior (i.e., doesn't cause any new bugs), and it would
    // take some further refactoring to actually solve them. In
    // particular, we would have to handle implied bounds
    // properly, and that code is currently largely confined to
    // regionck (though I made some efforts to extract it
    // out). -nmatsakis
    //
    // @arielby: In any case, these obligations are checked
    // by wfcheck anyway, so I'm not sure we have to check
    // them here too, and we will remove this function when
    // we move over to lazy normalization *anyway*.
    let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
    let predicates = ocx.normalize(&cause, elaborated_env, predicates);

    let errors = ocx.select_all_or_error();
    if !errors.is_empty() {
        let reported = infcx.err_ctxt().report_fulfillment_errors(errors);
        return Err(reported);
    }

    debug!("do_normalize_predicates: normalized predicates = {:?}", predicates);

    // We can use the `elaborated_env` here; the region code only
    // cares about declarations like `'a: 'b`.
    let outlives_env = OutlivesEnvironment::new(elaborated_env);

    // FIXME: It's very weird that we ignore region obligations but apparently
    // still need to use `resolve_regions` as we need the resolved regions in
    // the normalized predicates.
    let errors = infcx.resolve_regions(&outlives_env);
    if !errors.is_empty() {
        tcx.dcx().span_delayed_bug(
            span,
            format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"),
        );
    }

    match infcx.fully_resolve(predicates) {
        Ok(predicates) => Ok(predicates),
        Err(fixup_err) => {
            // If we encounter a fixup error, it means that some type
            // variable wound up unconstrained. I actually don't know
            // if this can happen, and I certainly don't expect it to
            // happen often, but if it did happen it probably
            // represents a legitimate failure due to some kind of
            // unconstrained variable.
            //
            // @lcnr: Let's still ICE here for now. I want a test case
            // for that.
            span_bug!(
                span,
                "inference variables in normalized parameter environment: {}",
                fixup_err
            );
        }
    }
}

// FIXME: this is gonna need to be removed ...
/// Normalizes the parameter environment, reporting errors if they occur.
#[instrument(level = "debug", skip(tcx))]
pub fn normalize_param_env_or_error<'tcx>(
    tcx: TyCtxt<'tcx>,
    unnormalized_env: ty::ParamEnv<'tcx>,
    cause: ObligationCause<'tcx>,
) -> ty::ParamEnv<'tcx> {
    // I'm not wild about reporting errors here; I'd prefer to
    // have the errors get reported at a defined place (e.g.,
    // during typeck). Instead I have all parameter
    // environments, in effect, going through this function
    // and hence potentially reporting errors. This ensures of
    // course that we never forget to normalize (the
    // alternative seemed like it would involve a lot of
    // manual invocations of this fn -- and then we'd have to
    // deal with the errors at each of those sites).
    //
    // In any case, in practice, typeck constructs all the
    // parameter environments once for every fn as it goes,
    // and errors will get reported then; so outside of type inference we
    // can be sure that no errors should occur.
    let mut predicates: Vec<_> = util::elaborate(
        tcx,
        unnormalized_env.caller_bounds().into_iter().map(|predicate| {
            if tcx.features().generic_const_exprs() {
                return predicate;
            }

            struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);

            impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
                fn cx(&self) -> TyCtxt<'tcx> {
                    self.0
                }

                fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
                    // FIXME(return_type_notation): track binders in this normalizer, as
                    // `ty::Const::normalize` can only work with properly preserved binders.

                    if c.has_escaping_bound_vars() {
                        return ty::Const::new_misc_error(self.0);
                    }
                    // While it is pretty sus to be evaluating things with an empty param env, it
                    // should actually be okay since without `feature(generic_const_exprs)` the only
                    // const arguments that have a non-empty param env are array repeat counts. These
                    // do not appear in the type system though.
                    c.normalize_internal(self.0, ty::ParamEnv::empty())
                }
            }

            // This whole normalization step is a hack to work around the fact that
            // `normalize_param_env_or_error` is fundamentally broken from using an
            // unnormalized param env with a trait solver that expects the param env
            // to be normalized.
            //
            // When normalizing the param env we can end up evaluating obligations
            // that have been normalized but can only be proven via a where clause
            // which is still in its unnormalized form. example:
            //
            // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
            // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
            // we first normalize obligations before proving them so we end up proving
            // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
            // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
            // we cannot prove `T: Trait<u8>`.
            //
            // The same thing is true for const generics- attempting to prove
            // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
            // will fail. After normalization we may be attempting to prove `T: Trait<4>` with
            // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
            // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
            // but as we do not have lazy norm implemented, equating the two consts fails outright.
            //
            // Ideally we would not normalize consts here at all but it is required for backwards
            // compatibility. Eventually when lazy norm is implemented this can just be removed.
            // We do not normalize types here as there is no backwards compatibility requirement
            // for us to do so.
            //
            // FIXME(-Znext-solver): remove this hack since we have deferred projection equality
            predicate.fold_with(&mut ConstNormalizer(tcx))
        }),
    )
    .collect();

    debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);

    let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal());
    if !normalize::needs_normalization(&elaborated_env, unnormalized_env.reveal()) {
        return elaborated_env;
    }

    // HACK: we are trying to normalize the param-env inside *itself*. The problem is that
    // normalization expects its param-env to be already normalized, which means we have
    // a circularity.
    //
    // The way we handle this is by normalizing the param-env inside an unnormalized version
    // of the param-env, which means that if the param-env contains unnormalized projections,
    // we'll have some normalization failures. This is unfortunate.
    //
    // Lazy normalization would basically handle this by treating just the
    // normalizing-a-trait-ref-requires-itself cycles as evaluation failures.
    //
    // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated
    // types, so to make the situation less bad, we normalize all the predicates *but*
    // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
    // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
    //
    // This works fairly well because trait matching does not actually care about param-env
    // TypeOutlives predicates - these are normally used by regionck.
    let outlives_predicates: Vec<_> = predicates
        .extract_if(|predicate| {
            matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
        })
        .collect();

    debug!(
        "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})",
        predicates, outlives_predicates
    );
    let Ok(non_outlives_predicates) =
        do_normalize_predicates(tcx, cause.clone(), elaborated_env, predicates)
    else {
        // An unnormalized env is better than nothing.
        debug!("normalize_param_env_or_error: errored resolving non-outlives predicates");
        return elaborated_env;
    };

    debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates);

    // Not sure whether it is better to include the unnormalized TypeOutlives predicates
    // here. I believe they should not matter, because we are ignoring TypeOutlives param-env
    // predicates here anyway. Keeping them here anyway because it seems safer.
    let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
    let outlives_env =
        ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal());
    let Ok(outlives_predicates) =
        do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates)
    else {
        // An unnormalized env is better than nothing.
        debug!("normalize_param_env_or_error: errored resolving outlives predicates");
        return elaborated_env;
    };
    debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates);

    let mut predicates = non_outlives_predicates;
    predicates.extend(outlives_predicates);
    debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
    ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal())
}

/// Normalizes the predicates and checks whether they hold in an empty environment. If this
/// returns true, then either normalize encountered an error or one of the predicates did not
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
/// used during analysis.
pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool {
    debug!("impossible_predicates(predicates={:?})", predicates);
    let infcx = tcx.infer_ctxt().build(TypingMode::PostAnalysis);
    let param_env = ty::ParamEnv::reveal_all();
    let ocx = ObligationCtxt::new(&infcx);
    let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates);
    for predicate in predicates {
        let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
        ocx.register_obligation(obligation);
    }
    let errors = ocx.select_all_or_error();

    let result = !errors.is_empty();
    debug!("impossible_predicates = {:?}", result);
    result
}

fn instantiate_and_check_impossible_predicates<'tcx>(
    tcx: TyCtxt<'tcx>,
    key: (DefId, GenericArgsRef<'tcx>),
) -> bool {
    debug!("instantiate_and_check_impossible_predicates(key={:?})", key);

    let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;

    // Specifically check trait fulfillment to avoid an error when trying to resolve
    // associated items.
    if let Some(trait_def_id) = tcx.trait_of_item(key.0) {
        let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1);
        predicates.push(trait_ref.upcast(tcx));
    }

    predicates.retain(|predicate| !predicate.has_param());
    let result = impossible_predicates(tcx, predicates);

    debug!("instantiate_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
    result
}

/// Checks whether a trait's associated item is impossible to reference on a given impl.
///
/// This only considers predicates that reference the impl's generics, and not
/// those that reference the method's generics.
fn is_impossible_associated_item(
    tcx: TyCtxt<'_>,
    (impl_def_id, trait_item_def_id): (DefId, DefId),
) -> bool {
    struct ReferencesOnlyParentGenerics<'tcx> {
        tcx: TyCtxt<'tcx>,
        generics: &'tcx ty::Generics,
        trait_item_def_id: DefId,
    }
    impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ReferencesOnlyParentGenerics<'tcx> {
        type Result = ControlFlow<()>;
        fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
            // If this is a parameter from the trait item's own generics, then bail
            if let ty::Param(param) = *t.kind()
                && let param_def_id = self.generics.type_param(param, self.tcx).def_id
                && self.tcx.parent(param_def_id) == self.trait_item_def_id
            {
                return ControlFlow::Break(());
            }
            t.super_visit_with(self)
        }
        fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
            if let ty::ReEarlyParam(param) = r.kind()
                && let param_def_id = self.generics.region_param(param, self.tcx).def_id
                && self.tcx.parent(param_def_id) == self.trait_item_def_id
            {
                return ControlFlow::Break(());
            }
            ControlFlow::Continue(())
        }
        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
            if let ty::ConstKind::Param(param) = ct.kind()
                && let param_def_id = self.generics.const_param(param, self.tcx).def_id
                && self.tcx.parent(param_def_id) == self.trait_item_def_id
            {
                return ControlFlow::Break(());
            }
            ct.super_visit_with(self)
        }
    }

    let generics = tcx.generics_of(trait_item_def_id);
    let predicates = tcx.predicates_of(trait_item_def_id);

    // Be conservative in cases where we have `W<T: ?Sized>` and a method like `Self: Sized`,
    // since that method *may* have some substitutions where the predicates hold.
    //
    // This replicates the logic we use in coherence.
    let infcx = tcx
        .infer_ctxt()
        .ignoring_regions()
        .with_next_trait_solver(true)
        .build(TypingMode::Coherence);
    let param_env = ty::ParamEnv::empty();
    let fresh_args = infcx.fresh_args_for_item(tcx.def_span(impl_def_id), impl_def_id);

    let impl_trait_ref = tcx
        .impl_trait_ref(impl_def_id)
        .expect("expected impl to correspond to trait")
        .instantiate(tcx, fresh_args);

    let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
    let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| {
        pred.visit_with(&mut visitor).is_continue().then(|| {
            Obligation::new(
                tcx,
                ObligationCause::dummy_with_span(*span),
                param_env,
                ty::EarlyBinder::bind(*pred).instantiate(tcx, impl_trait_ref.args),
            )
        })
    });

    let ocx = ObligationCtxt::new(&infcx);
    ocx.register_obligations(predicates_for_trait);
    !ocx.select_where_possible().is_empty()
}

pub fn provide(providers: &mut Providers) {
    dyn_compatibility::provide(providers);
    vtable::provide(providers);
    *providers = Providers {
        specialization_graph_of: specialize::specialization_graph_provider,
        specializes: specialize::specializes,
        specialization_enabled_in: specialize::specialization_enabled_in,
        instantiate_and_check_impossible_predicates,
        is_impossible_associated_item,
        ..*providers
    };
}