| use std::cell::LazyCell; | |
| use std::debug_assert_matches; | |
| use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; | |
| use rustc_data_structures::unord::UnordSet; | |
| use rustc_errors::{Diagnostic, Subdiagnostic, msg}; | |
| use rustc_hir as hir; | |
| use rustc_hir::def::DefKind; | |
| use rustc_hir::def_id::{DefId, LocalDefId}; | |
| use rustc_infer::infer::TyCtxtInferExt; | |
| use rustc_infer::infer::outlives::env::OutlivesEnvironment; | |
| use rustc_macros::Diagnostic; | |
| use rustc_middle::middle::resolve_bound_vars::ResolvedArg; | |
| use rustc_middle::ty::relate::{ | |
| Relate, RelateResult, TypeRelation, relate_args_with_variances, structurally_relate_consts, | |
| structurally_relate_tys, | |
| }; | |
| use rustc_middle::ty::{ | |
| self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, | |
| }; | |
| use rustc_middle::{bug, span_bug}; | |
| use rustc_session::lint::fcw; | |
| use rustc_session::{declare_lint, declare_lint_pass}; | |
| use rustc_span::{Span, Symbol}; | |
| use rustc_trait_selection::errors::{ | |
| AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion, | |
| }; | |
| use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; | |
| use rustc_trait_selection::traits::ObligationCtxt; | |
| use crate::{LateContext, LateLintPass}; | |
| declare_lint! { | |
| /// The `impl_trait_overcaptures` lint warns against cases where lifetime | |
| /// capture behavior will differ in edition 2024. | |
| /// | |
| /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope, | |
| /// rather than just the lifetimes that are mentioned in the bounds of the type. | |
| /// Often these sets are equal, but if not, it means that the `impl Trait` may | |
| /// cause erroneous borrow-checker errors. | |
| /// | |
| /// ### Example | |
| /// | |
| /// ```rust,compile_fail,edition2021 | |
| /// # #![deny(impl_trait_overcaptures)] | |
| /// # use std::fmt::Display; | |
| /// let mut x = vec![]; | |
| /// x.push(1); | |
| /// | |
| /// fn test(x: &Vec<i32>) -> impl Display { | |
| /// x[0] | |
| /// } | |
| /// | |
| /// let element = test(&x); | |
| /// x.push(2); | |
| /// println!("{element}"); | |
| /// ``` | |
| /// | |
| /// {{produces}} | |
| /// | |
| /// ### Explanation | |
| /// | |
| /// In edition < 2024, the returned `impl Display` doesn't capture the | |
| /// lifetime from the `&Vec<i32>`, so the vector can be mutably borrowed | |
| /// while the `impl Display` is live. | |
| /// | |
| /// To fix this, we can explicitly state that the `impl Display` doesn't | |
| /// capture any lifetimes, using `impl Display + use<>`. | |
| pub IMPL_TRAIT_OVERCAPTURES, | |
| Allow, | |
| "`impl Trait` will capture more lifetimes than possibly intended in edition 2024", | |
| @future_incompatible = FutureIncompatibleInfo { | |
| reason: fcw!(EditionSemanticsChange 2024 "rpit-lifetime-capture"), | |
| }; | |
| } | |
| declare_lint! { | |
| /// The `impl_trait_redundant_captures` lint warns against cases where use of the | |
| /// precise capturing `use<...>` syntax is not needed. | |
| /// | |
| /// In the 2024 edition, `impl Trait`s will capture all lifetimes in scope. | |
| /// If precise-capturing `use<...>` syntax is used, and the set of parameters | |
| /// that are captures are *equal* to the set of parameters in scope, then | |
| /// the syntax is redundant, and can be removed. | |
| /// | |
| /// ### Example | |
| /// | |
| /// ```rust,edition2024,compile_fail | |
| /// # #![deny(impl_trait_redundant_captures)] | |
| /// fn test<'a>(x: &'a i32) -> impl Sized + use<'a> { x } | |
| /// ``` | |
| /// | |
| /// {{produces}} | |
| /// | |
| /// ### Explanation | |
| /// | |
| /// To fix this, remove the `use<'a>`, since the lifetime is already captured | |
| /// since it is in scope. | |
| pub IMPL_TRAIT_REDUNDANT_CAPTURES, | |
| Allow, | |
| "redundant precise-capturing `use<...>` syntax on an `impl Trait`", | |
| } | |
| declare_lint_pass!( | |
| /// Lint for opaque types that will begin capturing in-scope but unmentioned lifetimes | |
| /// in edition 2024. | |
| ImplTraitOvercaptures => [IMPL_TRAIT_OVERCAPTURES, IMPL_TRAIT_REDUNDANT_CAPTURES] | |
| ); | |
| impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures { | |
| fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) { | |
| match &it.kind { | |
| hir::ItemKind::Fn { .. } => check_fn(cx.tcx, it.owner_id.def_id), | |
| _ => {} | |
| } | |
| } | |
| fn check_impl_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::ImplItem<'tcx>) { | |
| match &it.kind { | |
| hir::ImplItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id), | |
| _ => {} | |
| } | |
| } | |
| fn check_trait_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::TraitItem<'tcx>) { | |
| match &it.kind { | |
| hir::TraitItemKind::Fn(_, _) => check_fn(cx.tcx, it.owner_id.def_id), | |
| _ => {} | |
| } | |
| } | |
| } | |
| #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] | |
| enum ParamKind { | |
| // Early-bound var. | |
| Early(Symbol, u32), | |
| // Late-bound var on function, not within a binder. We can capture these. | |
| Free(DefId), | |
| // Late-bound var in a binder. We can't capture these yet. | |
| Late, | |
| } | |
| fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { | |
| let sig = tcx.fn_sig(parent_def_id).instantiate_identity(); | |
| let mut in_scope_parameters = FxIndexMap::default(); | |
| // Populate the in_scope_parameters list first with all of the generics in scope | |
| let mut current_def_id = Some(parent_def_id.to_def_id()); | |
| while let Some(def_id) = current_def_id { | |
| let generics = tcx.generics_of(def_id); | |
| for param in &generics.own_params { | |
| in_scope_parameters.insert(param.def_id, ParamKind::Early(param.name, param.index)); | |
| } | |
| current_def_id = generics.parent; | |
| } | |
| for bound_var in sig.bound_vars() { | |
| let ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) = bound_var else { | |
| span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig"); | |
| }; | |
| in_scope_parameters.insert(def_id, ParamKind::Free(def_id)); | |
| } | |
| let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig); | |
| // Then visit the signature to walk through all the binders (incl. the late-bound | |
| // vars on the function itself, which we need to count too). | |
| sig.visit_with(&mut VisitOpaqueTypes { | |
| tcx, | |
| parent_def_id, | |
| in_scope_parameters, | |
| seen: Default::default(), | |
| // Lazily compute these two, since they're likely a bit expensive. | |
| variances: LazyCell::new(|| { | |
| let mut functional_variances = FunctionalVariances { | |
| tcx, | |
| variances: FxHashMap::default(), | |
| ambient_variance: ty::Covariant, | |
| generics: tcx.generics_of(parent_def_id), | |
| }; | |
| functional_variances.relate(sig, sig).unwrap(); | |
| functional_variances.variances | |
| }), | |
| outlives_env: LazyCell::new(|| { | |
| let typing_env = ty::TypingEnv::non_body_analysis(tcx, parent_def_id); | |
| let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); | |
| let ocx = ObligationCtxt::new(&infcx); | |
| let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); | |
| OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys) | |
| }), | |
| }); | |
| } | |
| struct VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> { | |
| tcx: TyCtxt<'tcx>, | |
| parent_def_id: LocalDefId, | |
| in_scope_parameters: FxIndexMap<DefId, ParamKind>, | |
| variances: LazyCell<FxHashMap<DefId, ty::Variance>, VarFn>, | |
| outlives_env: LazyCell<OutlivesEnvironment<'tcx>, OutlivesFn>, | |
| seen: FxIndexSet<LocalDefId>, | |
| } | |
| impl<'tcx, VarFn, OutlivesFn> TypeVisitor<TyCtxt<'tcx>> | |
| for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> | |
| where | |
| VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>, | |
| OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, | |
| { | |
| fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) { | |
| // When we get into a binder, we need to add its own bound vars to the scope. | |
| let mut added = vec![]; | |
| for arg in t.bound_vars() { | |
| let arg: ty::BoundVariableKind<'tcx> = arg; | |
| match arg { | |
| ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) | |
| | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => { | |
| added.push(def_id); | |
| let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late); | |
| assert_eq!(unique, None); | |
| } | |
| _ => { | |
| self.tcx.dcx().span_delayed_bug( | |
| self.tcx.def_span(self.parent_def_id), | |
| format!("unsupported bound variable kind: {arg:?}"), | |
| ); | |
| } | |
| } | |
| } | |
| t.super_visit_with(self); | |
| // And remove them. The `shift_remove` should be `O(1)` since we're popping | |
| // them off from the end. | |
| for arg in added.into_iter().rev() { | |
| self.in_scope_parameters.shift_remove(&arg); | |
| } | |
| } | |
| fn visit_ty(&mut self, t: Ty<'tcx>) { | |
| if !t.has_aliases() { | |
| return; | |
| } | |
| if let ty::Alias(ty::Projection, opaque_ty) = *t.kind() | |
| && self.tcx.is_impl_trait_in_trait(opaque_ty.def_id) | |
| { | |
| // visit the opaque of the RPITIT | |
| self.tcx | |
| .type_of(opaque_ty.def_id) | |
| .instantiate(self.tcx, opaque_ty.args) | |
| .visit_with(self) | |
| } else if let ty::Alias(ty::Opaque, opaque_ty) = *t.kind() | |
| && let Some(opaque_def_id) = opaque_ty.def_id.as_local() | |
| // Don't recurse infinitely on an opaque | |
| && self.seen.insert(opaque_def_id) | |
| // If it's owned by this function | |
| && let opaque = | |
| self.tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty() | |
| // We want to recurse into RPITs and async fns, even though the latter | |
| // doesn't overcapture on its own, it may mention additional RPITs | |
| // in its bounds. | |
| && let hir::OpaqueTyOrigin::FnReturn { parent, .. } | |
| | hir::OpaqueTyOrigin::AsyncFn { parent, .. } = opaque.origin | |
| && parent == self.parent_def_id | |
| { | |
| let opaque_span = self.tcx.def_span(opaque_def_id); | |
| let new_capture_rules = opaque_span.at_least_rust_2024(); | |
| if !new_capture_rules | |
| && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) | |
| { | |
| // Compute the set of args that are captured by the opaque... | |
| let mut captured = FxIndexSet::default(); | |
| let mut captured_regions = FxIndexSet::default(); | |
| let variances = self.tcx.variances_of(opaque_def_id); | |
| let mut current_def_id = Some(opaque_def_id.to_def_id()); | |
| while let Some(def_id) = current_def_id { | |
| let generics = self.tcx.generics_of(def_id); | |
| for param in &generics.own_params { | |
| // A param is captured if it's invariant. | |
| if variances[param.index as usize] != ty::Invariant { | |
| continue; | |
| } | |
| let arg = opaque_ty.args[param.index as usize]; | |
| // We need to turn all `ty::Param`/`ConstKind::Param` and | |
| // `ReEarlyParam`/`ReBound` into def ids. | |
| captured.insert(extract_def_id_from_arg(self.tcx, generics, arg)); | |
| captured_regions.extend(arg.as_region()); | |
| } | |
| current_def_id = generics.parent; | |
| } | |
| // Compute the set of in scope params that are not captured. | |
| let mut uncaptured_args: FxIndexSet<_> = self | |
| .in_scope_parameters | |
| .iter() | |
| .filter(|&(def_id, _)| !captured.contains(def_id)) | |
| .collect(); | |
| // Remove the set of lifetimes that are in-scope that outlive some other captured | |
| // lifetime and are contravariant (i.e. covariant in argument position). | |
| uncaptured_args.retain(|&(def_id, kind)| { | |
| let Some(ty::Bivariant | ty::Contravariant) = self.variances.get(def_id) else { | |
| // Keep all covariant/invariant args. Also if variance is `None`, | |
| // then that means it's either not a lifetime, or it didn't show up | |
| // anywhere in the signature. | |
| return true; | |
| }; | |
| // We only computed variance of lifetimes... | |
| debug_assert_matches!(self.tcx.def_kind(*def_id), DefKind::LifetimeParam); | |
| let uncaptured = match *kind { | |
| ParamKind::Early(name, index) => ty::Region::new_early_param( | |
| self.tcx, | |
| ty::EarlyParamRegion { name, index }, | |
| ), | |
| ParamKind::Free(def_id) => ty::Region::new_late_param( | |
| self.tcx, | |
| self.parent_def_id.to_def_id(), | |
| ty::LateParamRegionKind::Named(def_id), | |
| ), | |
| // Totally ignore late bound args from binders. | |
| ParamKind::Late => return true, | |
| }; | |
| // Does this region outlive any captured region? | |
| !captured_regions.iter().any(|r| { | |
| self.outlives_env | |
| .free_region_map() | |
| .sub_free_regions(self.tcx, *r, uncaptured) | |
| }) | |
| }); | |
| // If we have uncaptured args, and if the opaque doesn't already have | |
| // `use<>` syntax on it, and we're < edition 2024, then warn the user. | |
| if !uncaptured_args.is_empty() { | |
| let suggestion = impl_trait_overcapture_suggestion( | |
| self.tcx, | |
| opaque_def_id, | |
| self.parent_def_id, | |
| captured, | |
| ); | |
| let uncaptured_spans: Vec<_> = uncaptured_args | |
| .into_iter() | |
| .map(|(&def_id, _)| self.tcx.def_span(def_id)) | |
| .collect(); | |
| self.tcx.emit_node_span_lint( | |
| IMPL_TRAIT_OVERCAPTURES, | |
| self.tcx.local_def_id_to_hir_id(opaque_def_id), | |
| opaque_span, | |
| ImplTraitOvercapturesLint { | |
| self_ty: t, | |
| num_captured: uncaptured_spans.len(), | |
| uncaptured_spans, | |
| suggestion, | |
| }, | |
| ); | |
| } | |
| } | |
| // Otherwise, if we are edition 2024, have `use<>` syntax, and | |
| // have no uncaptured args, then we should warn to the user that | |
| // it's redundant to capture all args explicitly. | |
| if new_capture_rules | |
| && let Some((captured_args, capturing_span)) = | |
| opaque.bounds.iter().find_map(|bound| match *bound { | |
| hir::GenericBound::Use(a, s) => Some((a, s)), | |
| _ => None, | |
| }) | |
| { | |
| let mut explicitly_captured = UnordSet::default(); | |
| for arg in captured_args { | |
| match self.tcx.named_bound_var(arg.hir_id()) { | |
| Some( | |
| ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id), | |
| ) => { | |
| if self.tcx.def_kind(self.tcx.local_parent(def_id)) == DefKind::OpaqueTy | |
| { | |
| let def_id = self | |
| .tcx | |
| .map_opaque_lifetime_to_parent_lifetime(def_id) | |
| .opt_param_def_id(self.tcx, self.parent_def_id.to_def_id()) | |
| .expect("variable should have been duplicated from parent"); | |
| explicitly_captured.insert(def_id); | |
| } else { | |
| explicitly_captured.insert(def_id.to_def_id()); | |
| } | |
| } | |
| _ => { | |
| self.tcx.dcx().span_delayed_bug( | |
| self.tcx.hir_span(arg.hir_id()), | |
| "no valid for captured arg", | |
| ); | |
| } | |
| } | |
| } | |
| if self | |
| .in_scope_parameters | |
| .iter() | |
| .all(|(def_id, _)| explicitly_captured.contains(def_id)) | |
| { | |
| self.tcx.emit_node_span_lint( | |
| IMPL_TRAIT_REDUNDANT_CAPTURES, | |
| self.tcx.local_def_id_to_hir_id(opaque_def_id), | |
| opaque_span, | |
| ImplTraitRedundantCapturesLint { capturing_span }, | |
| ); | |
| } | |
| } | |
| // Walk into the bounds of the opaque, too, since we want to get nested opaques | |
| // in this lint as well. Interestingly, one place that I expect this lint to fire | |
| // is for `impl for<'a> Bound<Out = impl Other>`, since `impl Other` will begin | |
| // to capture `'a` in e2024 (even though late-bound vars in opaques are not allowed). | |
| for clause in | |
| self.tcx.item_bounds(opaque_ty.def_id).iter_instantiated(self.tcx, opaque_ty.args) | |
| { | |
| clause.visit_with(self) | |
| } | |
| } | |
| t.super_visit_with(self); | |
| } | |
| } | |
| struct ImplTraitOvercapturesLint<'tcx> { | |
| uncaptured_spans: Vec<Span>, | |
| self_ty: Ty<'tcx>, | |
| num_captured: usize, | |
| suggestion: Option<AddPreciseCapturingForOvercapture>, | |
| } | |
| impl<'a> Diagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> { | |
| fn into_diag( | |
| self, | |
| dcx: rustc_errors::DiagCtxtHandle<'a>, | |
| level: rustc_errors::Level, | |
| ) -> rustc_errors::Diag<'a, ()> { | |
| let mut diag = rustc_errors::Diag::new( | |
| dcx, | |
| level, | |
| msg!("`{$self_ty}` will capture more lifetimes than possibly intended in edition 2024"), | |
| ); | |
| diag.arg("self_ty", self.self_ty.to_string()) | |
| .arg("num_captured", self.num_captured) | |
| .span_note( | |
| self.uncaptured_spans, | |
| msg!( | |
| "specifically, {$num_captured -> | |
| [one] this lifetime is | |
| *[other] these lifetimes are | |
| } in scope but not mentioned in the type's bounds" | |
| ), | |
| ) | |
| .note(msg!("all lifetimes in scope will be captured by `impl Trait`s in edition 2024")); | |
| if let Some(suggestion) = self.suggestion { | |
| suggestion.add_to_diag(&mut diag); | |
| } | |
| diag | |
| } | |
| } | |
| #[derive(Diagnostic)] | |
| #[diag("all possible in-scope parameters are already captured, so `use<...>` syntax is redundant")] | |
| struct ImplTraitRedundantCapturesLint { | |
| #[suggestion("remove the `use<...>` syntax", code = "", applicability = "machine-applicable")] | |
| capturing_span: Span, | |
| } | |
| fn extract_def_id_from_arg<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| generics: &'tcx ty::Generics, | |
| arg: ty::GenericArg<'tcx>, | |
| ) -> DefId { | |
| match arg.kind() { | |
| ty::GenericArgKind::Lifetime(re) => match re.kind() { | |
| ty::ReEarlyParam(ebr) => generics.region_param(ebr, tcx).def_id, | |
| ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. }) | |
| | ty::ReLateParam(ty::LateParamRegion { | |
| scope: _, | |
| kind: ty::LateParamRegionKind::Named(def_id), | |
| }) => def_id, | |
| _ => unreachable!(), | |
| }, | |
| ty::GenericArgKind::Type(ty) => { | |
| let ty::Param(param_ty) = *ty.kind() else { | |
| bug!(); | |
| }; | |
| generics.type_param(param_ty, tcx).def_id | |
| } | |
| ty::GenericArgKind::Const(ct) => { | |
| let ty::ConstKind::Param(param_ct) = ct.kind() else { | |
| bug!(); | |
| }; | |
| generics.const_param(param_ct, tcx).def_id | |
| } | |
| } | |
| } | |
| /// Computes the variances of regions that appear in the type, but considering | |
| /// late-bound regions too, which don't have their variance computed usually. | |
| /// | |
| /// Like generalization, this is a unary operation implemented on top of the binary | |
| /// relation infrastructure, mostly because it's much easier to have the relation | |
| /// track the variance for you, rather than having to do it yourself. | |
| struct FunctionalVariances<'tcx> { | |
| tcx: TyCtxt<'tcx>, | |
| variances: FxHashMap<DefId, ty::Variance>, | |
| ambient_variance: ty::Variance, | |
| generics: &'tcx ty::Generics, | |
| } | |
| impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> { | |
| fn cx(&self) -> TyCtxt<'tcx> { | |
| self.tcx | |
| } | |
| fn relate_ty_args( | |
| &mut self, | |
| a_ty: Ty<'tcx>, | |
| _: Ty<'tcx>, | |
| def_id: DefId, | |
| a_args: ty::GenericArgsRef<'tcx>, | |
| b_args: ty::GenericArgsRef<'tcx>, | |
| _: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>, | |
| ) -> RelateResult<'tcx, Ty<'tcx>> { | |
| let variances = self.cx().variances_of(def_id); | |
| relate_args_with_variances(self, variances, a_args, b_args)?; | |
| Ok(a_ty) | |
| } | |
| fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>( | |
| &mut self, | |
| variance: ty::Variance, | |
| _: ty::VarianceDiagInfo<TyCtxt<'tcx>>, | |
| a: T, | |
| b: T, | |
| ) -> RelateResult<'tcx, T> { | |
| let old_variance = self.ambient_variance; | |
| self.ambient_variance = self.ambient_variance.xform(variance); | |
| self.relate(a, b).unwrap(); | |
| self.ambient_variance = old_variance; | |
| Ok(a) | |
| } | |
| fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { | |
| structurally_relate_tys(self, a, b).unwrap(); | |
| Ok(a) | |
| } | |
| fn regions( | |
| &mut self, | |
| a: ty::Region<'tcx>, | |
| _: ty::Region<'tcx>, | |
| ) -> RelateResult<'tcx, ty::Region<'tcx>> { | |
| let def_id = match a.kind() { | |
| ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id, | |
| ty::ReBound(_, ty::BoundRegion { kind: ty::BoundRegionKind::Named(def_id), .. }) | |
| | ty::ReLateParam(ty::LateParamRegion { | |
| scope: _, | |
| kind: ty::LateParamRegionKind::Named(def_id), | |
| }) => def_id, | |
| _ => { | |
| return Ok(a); | |
| } | |
| }; | |
| if let Some(variance) = self.variances.get_mut(&def_id) { | |
| *variance = unify(*variance, self.ambient_variance); | |
| } else { | |
| self.variances.insert(def_id, self.ambient_variance); | |
| } | |
| Ok(a) | |
| } | |
| fn consts( | |
| &mut self, | |
| a: ty::Const<'tcx>, | |
| b: ty::Const<'tcx>, | |
| ) -> RelateResult<'tcx, ty::Const<'tcx>> { | |
| structurally_relate_consts(self, a, b).unwrap(); | |
| Ok(a) | |
| } | |
| fn binders<T>( | |
| &mut self, | |
| a: ty::Binder<'tcx, T>, | |
| b: ty::Binder<'tcx, T>, | |
| ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> | |
| where | |
| T: Relate<TyCtxt<'tcx>>, | |
| { | |
| self.relate(a.skip_binder(), b.skip_binder()).unwrap(); | |
| Ok(a) | |
| } | |
| } | |
| /// What is the variance that satisfies the two variances? | |
| fn unify(a: ty::Variance, b: ty::Variance) -> ty::Variance { | |
| match (a, b) { | |
| // Bivariance is lattice bottom. | |
| (ty::Bivariant, other) | (other, ty::Bivariant) => other, | |
| // Invariant is lattice top. | |
| (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, | |
| // If type is required to be covariant and contravariant, then it's invariant. | |
| (ty::Contravariant, ty::Covariant) | (ty::Covariant, ty::Contravariant) => ty::Invariant, | |
| // Otherwise, co + co = co, contra + contra = contra. | |
| (ty::Contravariant, ty::Contravariant) => ty::Contravariant, | |
| (ty::Covariant, ty::Covariant) => ty::Covariant, | |
| } | |
| } |