Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/variance.rs')
| -rw-r--r-- | crates/hir-ty/src/variance.rs | 623 |
1 files changed, 255 insertions, 368 deletions
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index 08a215fecf..b57bf03f24 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs @@ -13,45 +13,65 @@ //! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach //! while installing firewall per item queries to prevent invalidation issues. -use crate::db::HirDatabase; -use crate::generics::{Generics, generics}; -use crate::{ - AliasTy, Const, ConstScalar, DynTyExt, GenericArg, GenericArgData, Interner, Lifetime, - LifetimeData, Ty, TyKind, +use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId, signatures::StructFlags}; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + Variance, + inherent::{AdtDef, IntoKind, SliceLike}, }; -use chalk_ir::Mutability; -use hir_def::signatures::StructFlags; -use hir_def::{AdtId, GenericDefId, GenericParamId, VariantId}; -use std::fmt; -use std::ops::Not; use stdx::never; -use triomphe::Arc; -pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Arc<[Variance]>> { +use crate::{ + db::HirDatabase, + generics::{Generics, generics}, + next_solver::{ + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArg, GenericArgs, Region, + RegionKind, Term, Ty, TyKind, VariancesOf, + }, +}; + +pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> VariancesOf<'_> { tracing::debug!("variances_of(def={:?})", def); + let interner = DbInterner::new_with(db, None, None); match def { GenericDefId::FunctionId(_) => (), GenericDefId::AdtId(adt) => { if let AdtId::StructId(id) = adt { let flags = &db.struct_signature(id).flags; if flags.contains(StructFlags::IS_UNSAFE_CELL) { - return Some(Arc::from_iter(vec![Variance::Invariant; 1])); + return VariancesOf::new_from_iter(interner, [Variance::Invariant]); } else if flags.contains(StructFlags::IS_PHANTOM_DATA) { - return Some(Arc::from_iter(vec![Variance::Covariant; 1])); + return VariancesOf::new_from_iter(interner, [Variance::Covariant]); } } } - _ => return None, + _ => return VariancesOf::new_from_iter(interner, []), } let generics = generics(db, def); let count = generics.len(); if count == 0 { - return None; + return VariancesOf::new_from_iter(interner, []); + } + let mut variances = + Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); + + // FIXME(next-solver): This is *not* the correct behavior. I don't know if it has an actual effect, + // since bivariance is prohibited in Rust, but rustc definitely does not fallback bivariance. + // So why do we do this? Because, with the new solver, the effects of bivariance are catastrophic: + // it leads to not relating types properly, and to very, very hard to debug bugs (speaking from experience). + // Furthermore, our variance infra is known to not handle cycles properly. Therefore, at least until we fix + // cycles, and perhaps forever at least for out tests, not allowing bivariance makes sense. + // Why specifically invariance? I don't have a strong reason, mainly that invariance is a stronger relationship + // (therefore, less room for mistakes) and that IMO incorrect covariance can be more problematic that incorrect + // bivariance, at least while we don't handle lifetimes anyway. + for variance in &mut variances { + if *variance == Variance::Bivariant { + *variance = Variance::Invariant; + } } - let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); - variances.is_empty().not().then(|| Arc::from_iter(variances)) + VariancesOf::new_from_iter(interner, variances) } // pub(crate) fn variances_of_cycle_fn( @@ -63,129 +83,36 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar // salsa::CycleRecoveryAction::Iterate // } -pub(crate) fn variances_of_cycle_initial( - db: &dyn HirDatabase, - def: GenericDefId, -) -> Option<Arc<[Variance]>> { - let generics = generics(db, def); - let count = generics.len(); +fn glb(v1: Variance, v2: Variance) -> Variance { + // Greatest lower bound of the variance lattice as defined in The Paper: + // + // * + // - + + // o + match (v1, v2) { + (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, - if count == 0 { - return None; - } - Some(Arc::from(vec![Variance::Bivariant; count])) -} + (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Variance { - Covariant, // T<A> <: T<B> iff A <: B -- e.g., function return type - Invariant, // T<A> <: T<B> iff B == A -- e.g., type of mutable cell - Contravariant, // T<A> <: T<B> iff B <: A -- e.g., function param type - Bivariant, // T<A> <: T<B> -- e.g., unused type parameter -} + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, -impl fmt::Display for Variance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Variance::Covariant => write!(f, "covariant"), - Variance::Invariant => write!(f, "invariant"), - Variance::Contravariant => write!(f, "contravariant"), - Variance::Bivariant => write!(f, "bivariant"), - } - } -} + (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, -impl Variance { - /// `a.xform(b)` combines the variance of a context with the - /// variance of a type with the following meaning. If we are in a - /// context with variance `a`, and we encounter a type argument in - /// a position with variance `b`, then `a.xform(b)` is the new - /// variance with which the argument appears. - /// - /// Example 1: - /// ```ignore (illustrative) - /// *mut Vec<i32> - /// ``` - /// Here, the "ambient" variance starts as covariant. `*mut T` is - /// invariant with respect to `T`, so the variance in which the - /// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which - /// yields `Invariant`. Now, the type `Vec<T>` is covariant with - /// respect to its type argument `T`, and hence the variance of - /// the `i32` here is `Invariant.xform(Covariant)`, which results - /// (again) in `Invariant`. - /// - /// Example 2: - /// ```ignore (illustrative) - /// fn(*const Vec<i32>, *mut Vec<i32) - /// ``` - /// The ambient variance is covariant. A `fn` type is - /// contravariant with respect to its parameters, so the variance - /// within which both pointer types appear is - /// `Covariant.xform(Contravariant)`, or `Contravariant`. `*const - /// T` is covariant with respect to `T`, so the variance within - /// which the first `Vec<i32>` appears is - /// `Contravariant.xform(Covariant)` or `Contravariant`. The same - /// is true for its `i32` argument. In the `*mut T` case, the - /// variance of `Vec<i32>` is `Contravariant.xform(Invariant)`, - /// and hence the outermost type is `Invariant` with respect to - /// `Vec<i32>` (and its `i32` argument). - /// - /// Source: Figure 1 of "Taming the Wildcards: - /// Combining Definition- and Use-Site Variance" published in PLDI'11. - fn xform(self, v: Variance) -> Variance { - match (self, v) { - // Figure 1, column 1. - (Variance::Covariant, Variance::Covariant) => Variance::Covariant, - (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, - (Variance::Covariant, Variance::Invariant) => Variance::Invariant, - (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, - - // Figure 1, column 2. - (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, - (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, - (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, - (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, - - // Figure 1, column 3. - (Variance::Invariant, _) => Variance::Invariant, - - // Figure 1, column 4. - (Variance::Bivariant, _) => Variance::Bivariant, - } - } - - fn glb(self, v: Variance) -> Variance { - // Greatest lower bound of the variance lattice as - // defined in The Paper: - // - // * - // - + - // o - match (self, v) { - (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant, - - (Variance::Covariant, Variance::Contravariant) => Variance::Invariant, - (Variance::Contravariant, Variance::Covariant) => Variance::Invariant, - - (Variance::Covariant, Variance::Covariant) => Variance::Covariant, - - (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant, - - (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, - } - } - - pub fn invariant(self) -> Self { - self.xform(Variance::Invariant) + (x, Variance::Bivariant) | (Variance::Bivariant, x) => x, } +} - pub fn covariant(self) -> Self { - self.xform(Variance::Covariant) - } +pub(crate) fn variances_of_cycle_initial( + db: &dyn HirDatabase, + def: GenericDefId, +) -> VariancesOf<'_> { + let interner = DbInterner::new_with(db, None, None); + let generics = generics(db, def); + let count = generics.len(); - pub fn contravariant(self) -> Self { - self.xform(Variance::Contravariant) - } + // FIXME(next-solver): Returns `Invariance` and not `Bivariance` here, see the comment in the main query. + VariancesOf::new_from_iter(interner, std::iter::repeat_n(Variance::Invariant, count)) } struct Context<'db> { @@ -194,17 +121,16 @@ struct Context<'db> { variances: Vec<Variance>, } -impl Context<'_> { +impl<'db> Context<'db> { fn solve(mut self) -> Vec<Variance> { tracing::debug!("solve(generics={:?})", self.generics); match self.generics.def() { GenericDefId::AdtId(adt) => { let db = self.db; let mut add_constraints_from_variant = |variant| { - let subst = self.generics.placeholder_subst(db); for (_, field) in db.field_types(variant).iter() { self.add_constraints_from_ty( - &field.clone().substitute(Interner, &subst), + field.instantiate_identity(), Variance::Covariant, ); } @@ -220,15 +146,9 @@ impl Context<'_> { } } GenericDefId::FunctionId(f) => { - let subst = self.generics.placeholder_subst(self.db); - self.add_constraints_from_sig( - self.db - .callable_item_signature(f.into()) - .substitute(Interner, &subst) - .params_and_return - .iter(), - Variance::Covariant, - ); + let sig = + self.db.callable_item_signature(f.into()).instantiate_identity().skip_binder(); + self.add_constraints_from_sig(sig.inputs_and_output.iter(), Variance::Covariant); } _ => {} } @@ -256,122 +176,102 @@ impl Context<'_> { /// Adds constraints appropriate for an instance of `ty` appearing /// in a context with the generics defined in `generics` and /// ambient variance `variance` - fn add_constraints_from_ty(&mut self, ty: &Ty, variance: Variance) { + fn add_constraints_from_ty(&mut self, ty: Ty<'db>, variance: Variance) { tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - match ty.kind(Interner) { - TyKind::Scalar(_) | TyKind::Never | TyKind::Str | TyKind::Foreign(..) => { + match ty.kind() { + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Char + | TyKind::Bool + | TyKind::Never + | TyKind::Str + | TyKind::Foreign(..) => { // leaf type -- noop } - TyKind::FnDef(..) | TyKind::Coroutine(..) | TyKind::Closure(..) => { + TyKind::FnDef(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineClosure(..) + | TyKind::Closure(..) => { never!("Unexpected unnameable type in variance computation: {:?}", ty); } - TyKind::Ref(mutbl, lifetime, ty) => { + TyKind::Ref(lifetime, ty, mutbl) => { self.add_constraints_from_region(lifetime, variance); - self.add_constraints_from_mt(ty, *mutbl, variance); + self.add_constraints_from_mt(ty, mutbl, variance); } TyKind::Array(typ, len) => { - self.add_constraints_from_const(len, variance); + self.add_constraints_from_const(len); self.add_constraints_from_ty(typ, variance); } TyKind::Slice(typ) => { self.add_constraints_from_ty(typ, variance); } - TyKind::Raw(mutbl, ty) => { - self.add_constraints_from_mt(ty, *mutbl, variance); + TyKind::RawPtr(ty, mutbl) => { + self.add_constraints_from_mt(ty, mutbl, variance); } - TyKind::Tuple(_, subtys) => { - for subty in subtys.type_parameters(Interner) { - self.add_constraints_from_ty(&subty, variance); + TyKind::Tuple(subtys) => { + for subty in subtys { + self.add_constraints_from_ty(subty, variance); } } TyKind::Adt(def, args) => { - self.add_constraints_from_args(def.0.into(), args.as_slice(Interner), variance); - } - TyKind::Alias(AliasTy::Opaque(opaque)) => { - self.add_constraints_from_invariant_args( - opaque.substitution.as_slice(Interner), - variance, - ); - } - TyKind::Alias(AliasTy::Projection(proj)) => { - self.add_constraints_from_invariant_args( - proj.substitution.as_slice(Interner), - variance, - ); + self.add_constraints_from_args(def.def_id().0.into(), args, variance); } - // FIXME: check this - TyKind::AssociatedType(_, subst) => { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); + TyKind::Alias(_, alias) => { + // FIXME: Probably not correct wrt. opaques. + self.add_constraints_from_invariant_args(alias.args); } - // FIXME: check this - TyKind::OpaqueType(_, subst) => { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); - } - TyKind::Dyn(it) => { + TyKind::Dynamic(bounds, region) => { // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`: - self.add_constraints_from_region(&it.lifetime, variance); - - if let Some(trait_ref) = it.principal() { - // Trait are always invariant so we can take advantage of that. - self.add_constraints_from_invariant_args( - trait_ref - .map(|it| it.map(|it| it.substitution.clone())) - .substitute( - Interner, - &[GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(TyKind::Error.intern(Interner)), - )], - ) - .skip_binders() - .as_slice(Interner), - variance, - ); - } + self.add_constraints_from_region(region, variance); - // FIXME - // for projection in data.projection_bounds() { - // match projection.skip_binder().term.unpack() { - // TyKind::TermKind::Ty(ty) => { - // self.add_constraints_from_ty( ty, self.invariant); - // } - // TyKind::TermKind::Const(c) => { - // self.add_constraints_from_const( c, self.invariant) - // } - // } - // } + for bound in bounds { + match bound.skip_binder() { + ExistentialPredicate::Trait(trait_ref) => { + self.add_constraints_from_invariant_args(trait_ref.args) + } + ExistentialPredicate::Projection(projection) => { + self.add_constraints_from_invariant_args(projection.args); + match projection.term { + Term::Ty(ty) => { + self.add_constraints_from_ty(ty, Variance::Invariant) + } + Term::Const(konst) => self.add_constraints_from_const(konst), + } + } + ExistentialPredicate::AutoTrait(_) => {} + } + } } // Chalk has no params, so use placeholders for now? - TyKind::Placeholder(index) => { - let idx = crate::from_placeholder_idx(self.db, *index); - let index = self.generics.type_or_const_param_idx(idx).unwrap(); - self.constrain(index, variance); + TyKind::Param(param) => self.constrain(param.index as usize, variance), + TyKind::FnPtr(sig, _) => { + self.add_constraints_from_sig(sig.skip_binder().inputs_and_output.iter(), variance); } - TyKind::Function(f) => { - self.add_constraints_from_sig( - f.substitution.0.iter(Interner).filter_map(move |p| p.ty(Interner)), - variance, - ); - } - TyKind::Error => { + TyKind::Error(_) => { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } - TyKind::CoroutineWitness(..) | TyKind::BoundVar(..) | TyKind::InferenceVar(..) => { + TyKind::Bound(..) => {} + TyKind::CoroutineWitness(..) + | TyKind::Placeholder(..) + | TyKind::Infer(..) + | TyKind::UnsafeBinder(..) + | TyKind::Pat(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } - fn add_constraints_from_invariant_args(&mut self, args: &[GenericArg], variance: Variance) { - let variance_i = variance.invariant(); - - for k in args { - match k.data(Interner) { - GenericArgData::Lifetime(lt) => self.add_constraints_from_region(lt, variance_i), - GenericArgData::Ty(ty) => self.add_constraints_from_ty(ty, variance_i), - GenericArgData::Const(val) => self.add_constraints_from_const(val, variance_i), + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { + for k in args.iter() { + match k { + GenericArg::Lifetime(lt) => { + self.add_constraints_from_region(lt, Variance::Invariant) + } + GenericArg::Ty(ty) => self.add_constraints_from_ty(ty, Variance::Invariant), + GenericArg::Const(val) => self.add_constraints_from_const(val), } } } @@ -381,51 +281,40 @@ impl Context<'_> { fn add_constraints_from_args( &mut self, def_id: GenericDefId, - args: &[GenericArg], + args: GenericArgs<'db>, variance: Variance, ) { - // We don't record `inferred_starts` entries for empty generics. if args.is_empty() { return; } - let Some(variances) = self.db.variances_of(def_id) else { - return; - }; + let variances = self.db.variances_of(def_id); - for (i, k) in args.iter().enumerate() { - match k.data(Interner) { - GenericArgData::Lifetime(lt) => { - self.add_constraints_from_region(lt, variance.xform(variances[i])) - } - GenericArgData::Ty(ty) => { - self.add_constraints_from_ty(ty, variance.xform(variances[i])) - } - GenericArgData::Const(val) => self.add_constraints_from_const(val, variance), + for (k, v) in args.iter().zip(variances) { + match k { + GenericArg::Lifetime(lt) => self.add_constraints_from_region(lt, variance.xform(v)), + GenericArg::Ty(ty) => self.add_constraints_from_ty(ty, variance.xform(v)), + GenericArg::Const(val) => self.add_constraints_from_const(val), } } } /// Adds constraints appropriate for a const expression `val` /// in a context with ambient variance `variance` - fn add_constraints_from_const(&mut self, c: &Const, variance: Variance) { - match &c.data(Interner).value { - chalk_ir::ConstValue::Concrete(c) => { - if let ConstScalar::UnevaluatedConst(_, subst) = &c.interned { - self.add_constraints_from_invariant_args(subst.as_slice(Interner), variance); - } - } + fn add_constraints_from_const(&mut self, c: Const<'db>) { + match c.kind() { + ConstKind::Unevaluated(c) => self.add_constraints_from_invariant_args(c.args), _ => {} } } /// Adds constraints appropriate for a function with signature /// `sig` appearing in a context with ambient variance `variance` - fn add_constraints_from_sig<'a>( + fn add_constraints_from_sig( &mut self, - mut sig_tys: impl DoubleEndedIterator<Item = &'a Ty>, + mut sig_tys: impl DoubleEndedIterator<Item = Ty<'db>>, variance: Variance, ) { - let contra = variance.contravariant(); + let contra = variance.xform(Variance::Contravariant); let Some(output) = sig_tys.next_back() else { return never!("function signature has no return type"); }; @@ -437,27 +326,26 @@ impl Context<'_> { /// Adds constraints appropriate for a region appearing in a /// context with ambient variance `variance` - fn add_constraints_from_region(&mut self, region: &Lifetime, variance: Variance) { + fn add_constraints_from_region(&mut self, region: Region<'db>, variance: Variance) { tracing::debug!( "add_constraints_from_region(region={:?}, variance={:?})", region, variance ); - match region.data(Interner) { - LifetimeData::Placeholder(index) => { - let idx = crate::lt_from_placeholder_idx(self.db, *index); - let inferred = self.generics.lifetime_idx(idx).unwrap(); - self.constrain(inferred, variance); - } - LifetimeData::Static => {} - LifetimeData::BoundVar(..) => { + match region.kind() { + RegionKind::ReEarlyParam(param) => self.constrain(param.index as usize, variance), + RegionKind::ReStatic => {} + RegionKind::ReBound(..) => { // Either a higher-ranked region inside of a type or a // late-bound function parameter. // // We do not compute constraints for either of these. } - LifetimeData::Error => {} - LifetimeData::Phantom(..) | LifetimeData::InferenceVar(..) | LifetimeData::Erased => { + RegionKind::ReError(_) => {} + RegionKind::ReLateParam(..) + | RegionKind::RePlaceholder(..) + | RegionKind::ReVar(..) + | RegionKind::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. never!( @@ -471,11 +359,11 @@ impl Context<'_> { /// Adds constraints appropriate for a mutability-type pair /// appearing in a context with ambient variance `variance` - fn add_constraints_from_mt(&mut self, ty: &Ty, mt: Mutability, variance: Variance) { + fn add_constraints_from_mt(&mut self, ty: Ty<'db>, mt: Mutability, variance: Variance) { self.add_constraints_from_ty( ty, match mt { - Mutability::Mut => variance.invariant(), + Mutability::Mut => Variance::Invariant, Mutability::Not => variance, }, ); @@ -488,7 +376,7 @@ impl Context<'_> { self.variances[index], variance ); - self.variances[index] = self.variances[index].glb(variance); + self.variances[index] = glb(self.variances[index], variance); } } @@ -499,6 +387,7 @@ mod tests { AdtId, GenericDefId, ModuleDefId, hir::generics::GenericParamDataRef, src::HasSource, }; use itertools::Itertools; + use rustc_type_ir::{Variance, inherent::SliceLike}; use stdx::format_to; use syntax::{AstNode, ast::HasName}; use test_fixture::WithFixture; @@ -581,8 +470,8 @@ struct Other<'a> { } "#, expect![[r#" - Hello['a: bivariant] - Other['a: bivariant] + Hello['a: invariant] + Other['a: invariant] "#]], ); } @@ -601,7 +490,7 @@ struct Foo<T: Trait> { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: bivariant] + Foo[T: invariant] "#]], ); } @@ -683,9 +572,9 @@ struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR [U: *, T: +] get[Self: contravariant, T: covariant] get[Self: contravariant, T: contravariant] TestStruct[U: covariant, T: covariant] - TestEnum[U: bivariant, T: covariant] - TestContraStruct[U: bivariant, T: covariant] - TestBox[U: bivariant, T: covariant] + TestEnum[U: invariant, T: covariant] + TestContraStruct[U: invariant, T: covariant] + TestBox[U: invariant, T: covariant] "#]], ); } @@ -805,8 +694,8 @@ enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used trait SomeTrait<'a> { fn foo(&self); } // OK on traits. "#, expect![[r#" - SomeStruct['a: bivariant] - SomeEnum['a: bivariant] + SomeStruct['a: invariant] + SomeEnum['a: invariant] foo[Self: contravariant, 'a: invariant] "#]], ); @@ -834,14 +723,14 @@ struct DoubleNothing<T> { "#, expect![[r#" - SomeStruct[A: bivariant] - SomeEnum[A: bivariant] - ListCell[T: bivariant] - SelfTyAlias[T: bivariant] - WithBounds[T: bivariant] - WithWhereBounds[T: bivariant] - WithOutlivesBounds[T: bivariant] - DoubleNothing[T: bivariant] + SomeStruct[A: invariant] + SomeEnum[A: invariant] + ListCell[T: invariant] + SelfTyAlias[T: invariant] + WithBounds[T: invariant] + WithWhereBounds[T: invariant] + WithOutlivesBounds[T: invariant] + DoubleNothing[T: invariant] "#]], ); } @@ -952,7 +841,7 @@ struct S3<T>(S<T, T>); "#, expect![[r#" S[T: covariant] - S2[T: bivariant] + S2[T: invariant] S3[T: covariant] "#]], ); @@ -965,7 +854,7 @@ struct S3<T>(S<T, T>); struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V); "#, expect![[r#" - FixedPoint[T: bivariant, U: bivariant, V: bivariant] + FixedPoint[T: invariant, U: invariant, V: invariant] "#]], ); } @@ -981,89 +870,87 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V); // )); let (db, file_id) = TestDB::with_single_file(ra_fixture); - let mut defs: Vec<GenericDefId> = Vec::new(); - let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); - let def_map = module.def_map(&db); - crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::AdtId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::TraitId(it) => it.into(), - ModuleDefId::TraitAliasId(it) => it.into(), - ModuleDefId::TypeAliasId(it) => it.into(), - _ => return, - }) - }); - let defs = defs - .into_iter() - .filter_map(|def| { - Some(( - def, - match def { - GenericDefId::FunctionId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::EnumId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::StructId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::AdtId(AdtId::UnionId(it)) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TraitId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TraitAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::TypeAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } - GenericDefId::ImplId(_) => return None, - GenericDefId::ConstId(_) => return None, - GenericDefId::StaticId(_) => return None, - }, - )) - }) - .sorted_by_key(|(_, n)| n.syntax().text_range().start()); - let mut res = String::new(); - for (def, name) in defs { - let Some(variances) = db.variances_of(def) else { - continue; - }; - format_to!( - res, - "{name}[{}]\n", - generics(&db, def) - .iter() - .map(|(_, param)| match param { - GenericParamDataRef::TypeParamData(type_param_data) => { - type_param_data.name.as_ref().unwrap() - } - GenericParamDataRef::ConstParamData(const_param_data) => - &const_param_data.name, - GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { - &lifetime_param_data.name - } - }) - .zip_eq(&*variances) - .format_with(", ", |(name, var), f| f(&format_args!( - "{}: {var}", - name.as_str() - ))) - ); - } + crate::attach_db(&db, || { + let mut defs: Vec<GenericDefId> = Vec::new(); + let module = db.module_for_file_opt(file_id.file_id(&db)).unwrap(); + let def_map = module.def_map(&db); + crate::tests::visit_module(&db, def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::AdtId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::TraitId(it) => it.into(), + ModuleDefId::TypeAliasId(it) => it.into(), + _ => return, + }) + }); + let defs = defs + .into_iter() + .filter_map(|def| { + Some(( + def, + match def { + GenericDefId::FunctionId(it) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::EnumId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::StructId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::AdtId(AdtId::UnionId(it)) => { + let loc = it.lookup(&db); + loc.source(&db).value.name().unwrap() + } + GenericDefId::TraitId(_) + | GenericDefId::TypeAliasId(_) + | GenericDefId::ImplId(_) + | GenericDefId::ConstId(_) + | GenericDefId::StaticId(_) => return None, + }, + )) + }) + .sorted_by_key(|(_, n)| n.syntax().text_range().start()); + let mut res = String::new(); + for (def, name) in defs { + let variances = db.variances_of(def); + if variances.is_empty() { + continue; + } + format_to!( + res, + "{name}[{}]\n", + generics(&db, def) + .iter() + .map(|(_, param)| match param { + GenericParamDataRef::TypeParamData(type_param_data) => { + type_param_data.name.as_ref().unwrap() + } + GenericParamDataRef::ConstParamData(const_param_data) => + &const_param_data.name, + GenericParamDataRef::LifetimeParamData(lifetime_param_data) => { + &lifetime_param_data.name + } + }) + .zip_eq(variances) + .format_with(", ", |(name, var), f| f(&format_args!( + "{}: {}", + name.as_str(), + match var { + Variance::Covariant => "covariant", + Variance::Invariant => "invariant", + Variance::Contravariant => "contravariant", + Variance::Bivariant => "bivariant", + }, + ))) + ); + } - expected.assert_eq(&res); + expected.assert_eq(&res); + }) } } |