rustc_lint/
impl_trait_overcaptures.rs

1use std::assert_matches::debug_assert_matches;
2use std::cell::LazyCell;
3
4use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
5use rustc_data_structures::unord::UnordSet;
6use rustc_errors::{LintDiagnostic, Subdiagnostic};
7use rustc_hir as hir;
8use rustc_hir::def::DefKind;
9use rustc_hir::def_id::{DefId, LocalDefId};
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_infer::infer::outlives::env::OutlivesEnvironment;
12use rustc_macros::LintDiagnostic;
13use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
14use rustc_middle::ty::relate::{
15    Relate, RelateResult, TypeRelation, relate_args_with_variances, structurally_relate_consts,
16    structurally_relate_tys,
17};
18use rustc_middle::ty::{
19    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
20};
21use rustc_middle::{bug, span_bug};
22use rustc_session::lint::fcw;
23use rustc_session::{declare_lint, declare_lint_pass};
24use rustc_span::{Span, Symbol};
25use rustc_trait_selection::errors::{
26    AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion,
27};
28use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
29use rustc_trait_selection::traits::ObligationCtxt;
30
31use crate::{LateContext, LateLintPass, fluent_generated as fluent};
32
33declare_lint! {
34    /// The `impl_trait_overcaptures` lint warns against cases where lifetime
35    /// capture behavior will differ in edition 2024.
36    ///
37    /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope,
38    /// rather than just the lifetimes that are mentioned in the bounds of the type.
39    /// Often these sets are equal, but if not, it means that the `impl Trait` may
40    /// cause erroneous borrow-checker errors.
41    ///
42    /// ### Example
43    ///
44    /// ```rust,compile_fail,edition2021
45    /// # #![deny(impl_trait_overcaptures)]
46    /// # use std::fmt::Display;
47    /// let mut x = vec![];
48    /// x.push(1);
49    ///
50    /// fn test(x: &Vec<i32>) -> impl Display {
51    ///     x[0]
52    /// }
53    ///
54    /// let element = test(&x);
55    /// x.push(2);
56    /// println!("{element}");
57    /// ```
58    ///
59    /// {{produces}}
60    ///
61    /// ### Explanation
62    ///
63    /// In edition < 2024, the returned `impl Display` doesn't capture the
64    /// lifetime from the `&Vec<i32>`, so the vector can be mutably borrowed
65    /// while the `impl Display` is live.
66    ///
67    /// To fix this, we can explicitly state that the `impl Display` doesn't
68    /// capture any lifetimes, using `impl Display + use<>`.
69    pub IMPL_TRAIT_OVERCAPTURES,
70    Allow,
71    "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
72    @future_incompatible = FutureIncompatibleInfo {
73        reason: fcw!(EditionSemanticsChange 2024 "rpit-lifetime-capture"),
74    };
75}
76
77declare_lint! {
78    /// The `impl_trait_redundant_captures` lint warns against cases where use of the
79    /// precise capturing `use<...>` syntax is not needed.
80    ///
81    /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope.
82    /// If precise-capturing `use<...>` syntax is used, and the set of parameters
83    /// that are captures are *equal* to the set of parameters in scope, then
84    /// the syntax is redundant, and can be removed.
85    ///
86    /// ### Example
87    ///
88    /// ```rust,edition2024,compile_fail
89    /// # #![deny(impl_trait_redundant_captures)]
90    /// fn test<'a>(x: &'a i32) -> impl Sized + use<'a> { x }
91    /// ```
92    ///
93    /// {{produces}}
94    ///
95    /// ### Explanation
96    ///
97    /// To fix this, remove the `use<'a>`, since the lifetime is already captured
98    /// since it is in scope.
99    pub IMPL_TRAIT_REDUNDANT_CAPTURES,
100    Allow,
101    "redundant precise-capturing `use<...>` syntax on an `impl Trait`",
102}
103
104declare_lint_pass!(
105    /// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes
106    /// in edition 2024.
107    ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES]
108);
109
110impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
111    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) {
112        match &it.kind {
113            hir::ItemKind::Fn { .. } => check_fn(cx.tcx, it.owner_id.def_id),
114            _ => {}
115        }
116    }
117
118    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::ImplItem<'tcx>) {
119        match &it.kind {
120            hir::ImplItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
121            _ => {}
122        }
123    }
124
125    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::TraitItem<'tcx>) {
126        match &it.kind {
127            hir::TraitItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id),
128            _ => {}
129        }
130    }
131}
132
133#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
134enum ParamKind {
135    // Early-bound var.
136    Early(Symbol, u32),
137    // Late-bound var on function, not within a binder. We can capture these.
138    Free(DefId),
139    // Late-bound var in a binder. We can't capture these yet.
140    Late,
141}
142
143fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
144    let sig = tcx.fn_sig(parent_def_id).instantiate_identity();
145
146    let mut in_scope_parameters = FxIndexMap::default();
147    // Populate the in_scope_parameters list first with all of the generics in scope
148    let mut current_def_id = Some(parent_def_id.to_def_id());
149    while let Some(def_id) = current_def_id {
150        let generics = tcx.generics_of(def_id);
151        for param in &generics.own_params {
152            in_scope_parameters.insert(param.def_id, ParamKind::Early(param.name, param.index));
153        }
154        current_def_id = generics.parent;
155    }
156
157    for bound_var in sig.bound_vars() {
158        let ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) = bound_var else {
159            span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig");
160        };
161
162        in_scope_parameters.insert(def_id, ParamKind::Free(def_id));
163    }
164
165    let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig);
166
167    // Then visit the signature to walk through all the binders (incl. the late-bound
168    // vars on the function itself, which we need to count too).
169    sig.visit_with(&mut VisitOpaqueTypes {
170        tcx,
171        parent_def_id,
172        in_scope_parameters,
173        seen: Default::default(),
174        // Lazily compute these two, since they're likely a bit expensive.
175        variances: LazyCell::new(|| {
176            let mut functional_variances = FunctionalVariances {
177                tcx,
178                variances: FxHashMap::default(),
179                ambient_variance: ty::Covariant,
180                generics: tcx.generics_of(parent_def_id),
181            };
182            functional_variances.relate(sig, sig).unwrap();
183            functional_variances.variances
184        }),
185        outlives_env: LazyCell::new(|| {
186            let typing_env = ty::TypingEnv::non_body_analysis(tcx, parent_def_id);
187            let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
188            let ocx = ObligationCtxt::new(&infcx);
189            let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default();
190            OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys)
191        }),
192    });
193}
194
195struct VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> {
196    tcx: TyCtxt<'tcx>,
197    parent_def_id: LocalDefId,
198    in_scope_parameters: FxIndexMap<DefId, ParamKind>,
199    variances: LazyCell<FxHashMap<DefId, ty::Variance>, VarFn>,
200    outlives_env: LazyCell<OutlivesEnvironment<'tcx>, OutlivesFn>,
201    seen: FxIndexSet<LocalDefId>,
202}
203
204impl<'tcx, VarFn, OutlivesFn> TypeVisitor<TyCtxt<'tcx>>
205    for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn>
206where
207    VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
208    OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
209{
210    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
211        // When we get into a binder, we need to add its own bound vars to the scope.
212        let mut added = vec![];
213        for arg in t.bound_vars() {
214            let arg: ty::BoundVariableKind = arg;
215            match arg {
216                ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
217                | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => {
218                    added.push(def_id);
219                    let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late);
220                    assert_eq!(unique, None);
221                }
222                _ => {
223                    self.tcx.dcx().span_delayed_bug(
224                        self.tcx.def_span(self.parent_def_id),
225                        format!("unsupported bound variable kind: {arg:?}"),
226                    );
227                }
228            }
229        }
230
231        t.super_visit_with(self);
232
233        // And remove them. The `shift_remove` should be `O(1)` since we're popping
234        // them off from the end.
235        for arg in added.into_iter().rev() {
236            self.in_scope_parameters.shift_remove(&arg);
237        }
238    }
239
240    fn visit_ty(&mut self, t: Ty<'tcx>) {
241        if !t.has_aliases() {
242            return;
243        }
244
245        if let ty::Alias(ty::Projection, opaque_ty) = *t.kind()
246            && self.tcx.is_impl_trait_in_trait(opaque_ty.def_id)
247        {
248            // visit the opaque of the RPITIT
249            self.tcx
250                .type_of(opaque_ty.def_id)
251                .instantiate(self.tcx, opaque_ty.args)
252                .visit_with(self)
253        } else if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind()
254            && let Some(opaque_def_id) = opaque_ty.def_id.as_local()
255            // Don't recurse infinitely on an opaque
256            && self.seen.insert(opaque_def_id)
257            // If it's owned by this function
258            && let opaque =
259                self.tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty()
260            // We want to recurse into RPITs and async fns, even though the latter
261            // doesn't overcapture on its own, it may mention additional RPITs
262            // in its bounds.
263            && let hir::OpaqueTyOrigin::FnReturn { parent, .. }
264                | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = opaque.origin
265            && parent == self.parent_def_id
266        {
267            let opaque_span = self.tcx.def_span(opaque_def_id);
268            let new_capture_rules = opaque_span.at_least_rust_2024();
269            if !new_capture_rules
270                && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
271            {
272                // Compute the set of args that are captured by the opaque...
273                let mut captured = FxIndexSet::default();
274                let mut captured_regions = FxIndexSet::default();
275                let variances = self.tcx.variances_of(opaque_def_id);
276                let mut current_def_id = Some(opaque_def_id.to_def_id());
277                while let Some(def_id) = current_def_id {
278                    let generics = self.tcx.generics_of(def_id);
279                    for param in &generics.own_params {
280                        // A param is captured if it's invariant.
281                        if variances[param.index as usize] != ty::Invariant {
282                            continue;
283                        }
284
285                        let arg = opaque_ty.args[param.index as usize];
286                        // We need to turn all `ty::Param`/`ConstKind::Param` and
287                        // `ReEarlyParam`/`ReBound` into def ids.
288                        captured.insert(extract_def_id_from_arg(self.tcx, generics, arg));
289
290                        captured_regions.extend(arg.as_region());
291                    }
292                    current_def_id = generics.parent;
293                }
294
295                // Compute the set of in scope params that are not captured.
296                let mut uncaptured_args: FxIndexSet<_> = self
297                    .in_scope_parameters
298                    .iter()
299                    .filter(|&(def_id, _)| !captured.contains(def_id))
300                    .collect();
301                // Remove the set of lifetimes that are in-scope that outlive some other captured
302                // lifetime and are contravariant (i.e. covariant in argument position).
303                uncaptured_args.retain(|&(def_id, kind)| {
304                    let Some(ty::Bivariant | ty::Contravariant) = self.variances.get(def_id) else {
305                        // Keep all covariant/invariant args. Also if variance is `None`,
306                        // then that means it's either not a lifetime, or it didn't show up
307                        // anywhere in the signature.
308                        return true;
309                    };
310                    // We only computed variance of lifetimes...
311                    debug_assert_matches!(self.tcx.def_kind(def_id), DefKind::LifetimeParam);
312                    let uncaptured = match *kind {
313                        ParamKind::Early(name, index) => ty::Region::new_early_param(
314                            self.tcx,
315                            ty::EarlyParamRegion { name, index },
316                        ),
317                        ParamKind::Free(def_id) => ty::Region::new_late_param(
318                            self.tcx,
319                            self.parent_def_id.to_def_id(),
320                            ty::LateParamRegionKind::Named(def_id),
321                        ),
322                        // Totally ignore late bound args from binders.
323                        ParamKind::Late => return true,
324                    };
325                    // Does this region outlive any captured region?
326                    !captured_regions.iter().any(|r| {
327                        self.outlives_env
328                            .free_region_map()
329                            .sub_free_regions(self.tcx, *r, uncaptured)
330                    })
331                });
332
333                // If we have uncaptured args, and if the opaque doesn't already have
334                // `use<>` syntax on it, and we're < edition 2024, then warn the user.
335                if !uncaptured_args.is_empty() {
336                    let suggestion = impl_trait_overcapture_suggestion(
337                        self.tcx,
338                        opaque_def_id,
339                        self.parent_def_id,
340                        captured,
341                    );
342
343                    let uncaptured_spans: Vec<_> = uncaptured_args
344                        .into_iter()
345                        .map(|(def_id, _)| self.tcx.def_span(def_id))
346                        .collect();
347
348                    self.tcx.emit_node_span_lint(
349                        IMPL_TRAIT_OVERCAPTURES,
350                        self.tcx.local_def_id_to_hir_id(opaque_def_id),
351                        opaque_span,
352                        ImplTraitOvercapturesLint {
353                            self_ty: t,
354                            num_captured: uncaptured_spans.len(),
355                            uncaptured_spans,
356                            suggestion,
357                        },
358                    );
359                }
360            }
361
362            // Otherwise, if we are edition 2024, have `use<>` syntax, and
363            // have no uncaptured args, then we should warn to the user that
364            // it's redundant to capture all args explicitly.
365            if new_capture_rules
366                && let Some((captured_args, capturing_span)) =
367                    opaque.bounds.iter().find_map(|bound| match *bound {
368                        hir::GenericBound::Use(a, s) => Some((a, s)),
369                        _ => None,
370                    })
371            {
372                let mut explicitly_captured = UnordSet::default();
373                for arg in captured_args {
374                    match self.tcx.named_bound_var(arg.hir_id()) {
375                        Some(
376                            ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
377                        ) => {
378                            if self.tcx.def_kind(self.tcx.local_parent(def_id)) == DefKind::OpaqueTy
379                            {
380                                let def_id = self
381                                    .tcx
382                                    .map_opaque_lifetime_to_parent_lifetime(def_id)
383                                    .opt_param_def_id(self.tcx, self.parent_def_id.to_def_id())
384                                    .expect("variable should have been duplicated from parent");
385
386                                explicitly_captured.insert(def_id);
387                            } else {
388                                explicitly_captured.insert(def_id.to_def_id());
389                            }
390                        }
391                        _ => {
392                            self.tcx.dcx().span_delayed_bug(
393                                self.tcx.hir_span(arg.hir_id()),
394                                "no valid for captured arg",
395                            );
396                        }
397                    }
398                }
399
400                if self
401                    .in_scope_parameters
402                    .iter()
403                    .all(|(def_id, _)| explicitly_captured.contains(def_id))
404                {
405                    self.tcx.emit_node_span_lint(
406                        IMPL_TRAIT_REDUNDANT_CAPTURES,
407                        self.tcx.local_def_id_to_hir_id(opaque_def_id),
408                        opaque_span,
409                        ImplTraitRedundantCapturesLint { capturing_span },
410                    );
411                }
412            }
413
414            // Walk into the bounds of the opaque, too, since we want to get nested opaques
415            // in this lint as well. Interestingly, one place that I expect this lint to fire
416            // is for `impl for<'a> Bound<Out = impl Other>`, since `impl Other` will begin
417            // to capture `'a` in e2024 (even though late-bound vars in opaques are not allowed).
418            for clause in
419                self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args)
420            {
421                clause.visit_with(self)
422            }
423        }
424
425        t.super_visit_with(self);
426    }
427}
428
429struct ImplTraitOvercapturesLint<'tcx> {
430    uncaptured_spans: Vec<Span>,
431    self_ty: Ty<'tcx>,
432    num_captured: usize,
433    suggestion: Option<AddPreciseCapturingForOvercapture>,
434}
435
436impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
437    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
438        diag.primary_message(fluent::lint_impl_trait_overcaptures);
439        diag.arg("self_ty", self.self_ty.to_string())
440            .arg("num_captured", self.num_captured)
441            .span_note(self.uncaptured_spans, fluent::lint_note)
442            .note(fluent::lint_note2);
443        if let Some(suggestion) = self.suggestion {
444            suggestion.add_to_diag(diag);
445        }
446    }
447}
448
449#[derive(LintDiagnostic)]
450#[diag(lint_impl_trait_redundant_captures)]
451struct ImplTraitRedundantCapturesLint {
452    #[suggestion(lint_suggestion, code = "", applicability = "machine-applicable")]
453    capturing_span: Span,
454}
455
456fn extract_def_id_from_arg<'tcx>(
457    tcx: TyCtxt<'tcx>,
458    generics: &'tcx ty::Generics,
459    arg: ty::GenericArg<'tcx>,
460) -> DefId {
461    match arg.kind() {
462        ty::GenericArgKind::Lifetime(re) => match re.kind() {
463            ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id,
464            ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
465            | ty::ReLateParam(ty::LateParamRegion {
466                scope: _,
467                kind: ty::LateParamRegionKind::Named(def_id),
468            }) => def_id,
469            _ => unreachable!(),
470        },
471        ty::GenericArgKind::Type(ty) => {
472            let ty::Param(param_ty) = *ty.kind() else {
473                bug!();
474            };
475            generics.type_param(param_ty, tcx).def_id
476        }
477        ty::GenericArgKind::Const(ct) => {
478            let ty::ConstKind::Param(param_ct) = ct.kind() else {
479                bug!();
480            };
481            generics.const_param(param_ct, tcx).def_id
482        }
483    }
484}
485
486/// Computes the variances of regions that appear in the type, but considering
487/// late-bound regions too, which don't have their variance computed usually.
488///
489/// Like generalization, this is a unary operation implemented on top of the binary
490/// relation infrastructure, mostly because it's much easier to have the relation
491/// track the variance for you, rather than having to do it yourself.
492struct FunctionalVariances<'tcx> {
493    tcx: TyCtxt<'tcx>,
494    variances: FxHashMap<DefId, ty::Variance>,
495    ambient_variance: ty::Variance,
496    generics: &'tcx ty::Generics,
497}
498
499impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
500    fn cx(&self) -> TyCtxt<'tcx> {
501        self.tcx
502    }
503
504    fn relate_ty_args(
505        &mut self,
506        a_ty: Ty<'tcx>,
507        _: Ty<'tcx>,
508        def_id: DefId,
509        a_args: ty::GenericArgsRef<'tcx>,
510        b_args: ty::GenericArgsRef<'tcx>,
511        _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,
512    ) -> RelateResult<'tcx, Ty<'tcx>> {
513        let variances = self.cx().variances_of(def_id);
514        relate_args_with_variances(self, variances, a_args, b_args)?;
515        Ok(a_ty)
516    }
517
518    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
519        &mut self,
520        variance: ty::Variance,
521        _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
522        a: T,
523        b: T,
524    ) -> RelateResult<'tcx, T> {
525        let old_variance = self.ambient_variance;
526        self.ambient_variance = self.ambient_variance.xform(variance);
527        self.relate(a, b).unwrap();
528        self.ambient_variance = old_variance;
529        Ok(a)
530    }
531
532    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
533        structurally_relate_tys(self, a, b).unwrap();
534        Ok(a)
535    }
536
537    fn regions(
538        &mut self,
539        a: ty::Region<'tcx>,
540        _: ty::Region<'tcx>,
541    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
542        let def_id = match a.kind() {
543            ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id,
544            ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. })
545            | ty::ReLateParam(ty::LateParamRegion {
546                scope: _,
547                kind: ty::LateParamRegionKind::Named(def_id),
548            }) => def_id,
549            _ => {
550                return Ok(a);
551            }
552        };
553
554        if let Some(variance) = self.variances.get_mut(&def_id) {
555            *variance = unify(*variance, self.ambient_variance);
556        } else {
557            self.variances.insert(def_id, self.ambient_variance);
558        }
559
560        Ok(a)
561    }
562
563    fn consts(
564        &mut self,
565        a: ty::Const<'tcx>,
566        b: ty::Const<'tcx>,
567    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
568        structurally_relate_consts(self, a, b).unwrap();
569        Ok(a)
570    }
571
572    fn binders<T>(
573        &mut self,
574        a: ty::Binder<'tcx, T>,
575        b: ty::Binder<'tcx, T>,
576    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
577    where
578        T: Relate<TyCtxt<'tcx>>,
579    {
580        self.relate(a.skip_binder(), b.skip_binder()).unwrap();
581        Ok(a)
582    }
583}
584
585/// What is the variance that satisfies the two variances?
586fn unify(a: ty::Variance, b: ty::Variance) -> ty::Variance {
587    match (a, b) {
588        // Bivariance is lattice bottom.
589        (ty::Bivariant, other) | (other, ty::Bivariant) => other,
590        // Invariant is lattice top.
591        (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant,
592        // If type is required to be covariant and contravariant, then it's invariant.
593        (ty::Contravariant, ty::Covariant) | (ty::Covariant, ty::Contravariant) => ty::Invariant,
594        // Otherwise, co + co = co, contra + contra = contra.
595        (ty::Contravariant, ty::Contravariant) => ty::Contravariant,
596        (ty::Covariant, ty::Covariant) => ty::Covariant,
597    }
598}