Skip to main content

hir_ty/
variance.rs

1//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide]
2//! chapter for more info.
3//!
4//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html
5//!
6//! The implementation here differs from rustc. Rustc does a crate wide fixpoint resolution
7//! as the algorithm for determining variance is a fixpoint computation with potential cycles that
8//! need to be resolved. rust-analyzer does not want a crate-wide analysis though as that would hurt
9//! incrementality too much and as such our query is based on a per item basis.
10//!
11//! This does unfortunately run into the issue that we can run into query cycles which salsa
12//! currently does not allow to be resolved via a fixpoint computation. This will likely be resolved
13//! by the next salsa version. If not, we will likely have to adapt and go with the rustc approach
14//! while installing firewall per item queries to prevent invalidation issues.
15
16use hir_def::{
17    AdtId, GenericDefId, GenericParamId, VariantId,
18    signatures::{StructFlags, StructSignature},
19};
20use rustc_ast_ir::Mutability;
21use rustc_type_ir::{Variance, inherent::IntoKind};
22use stdx::never;
23
24use crate::{
25    db::HirDatabase,
26    generics::{Generics, generics},
27    next_solver::{
28        Const, ConstKind, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, PatternKind,
29        Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf,
30    },
31};
32
33pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> VariancesOf<'_> {
34    variances_of_query(db, def).as_ref()
35}
36
37#[salsa::tracked(
38    returns(ref),
39    cycle_fn = crate::variance::variances_of_cycle_fn,
40    cycle_initial = crate::variance::variances_of_cycle_initial,
41)]
42fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariancesOf {
43    tracing::debug!("variances_of(def={:?})", def);
44    match def {
45        GenericDefId::FunctionId(_) => (),
46        GenericDefId::AdtId(adt) => {
47            if let AdtId::StructId(id) = adt {
48                let flags = &StructSignature::of(db, id).flags;
49                let types = || crate::next_solver::default_types();
50                if flags.contains(StructFlags::IS_UNSAFE_CELL) {
51                    return types().one_invariant.store();
52                } else if flags.intersects(
53                    StructFlags::IS_PHANTOM_DATA | StructFlags::IS_COVARIANT_UNSAFE_CELL,
54                ) {
55                    return types().one_covariant.store();
56                }
57            }
58        }
59        _ => return VariancesOf::empty().store(),
60    }
61
62    let generics = generics(db, def);
63    let count = generics.len(true);
64    if count == 0 {
65        return VariancesOf::empty().store();
66    }
67    let variances =
68        Context { generics, variances: vec![Variance::Bivariant; count].into_boxed_slice(), db }
69            .solve();
70
71    VariancesOf::new_from_slice(&variances).store()
72}
73
74pub(crate) fn variances_of_cycle_fn(
75    _db: &dyn HirDatabase,
76    _: &salsa::Cycle<'_>,
77    _last_provisional_value: &StoredVariancesOf,
78    value: StoredVariancesOf,
79    _def: GenericDefId,
80) -> StoredVariancesOf {
81    value
82}
83
84fn glb(v1: Variance, v2: Variance) -> Variance {
85    // Greatest lower bound of the variance lattice as defined in The Paper:
86    //
87    //       *
88    //    -     +
89    //       o
90    match (v1, v2) {
91        (Variance::Invariant, _) | (_, Variance::Invariant) => Variance::Invariant,
92
93        (Variance::Covariant, Variance::Contravariant) => Variance::Invariant,
94        (Variance::Contravariant, Variance::Covariant) => Variance::Invariant,
95
96        (Variance::Covariant, Variance::Covariant) => Variance::Covariant,
97
98        (Variance::Contravariant, Variance::Contravariant) => Variance::Contravariant,
99
100        (x, Variance::Bivariant) | (Variance::Bivariant, x) => x,
101    }
102}
103
104pub(crate) fn variances_of_cycle_initial(
105    db: &dyn HirDatabase,
106    _: salsa::Id,
107    def: GenericDefId,
108) -> StoredVariancesOf {
109    let generics = generics(db, def);
110    let count = generics.len(true);
111
112    VariancesOf::new_from_iter(std::iter::repeat_n(Variance::Bivariant, count)).store()
113}
114
115struct Context<'db> {
116    db: &'db dyn HirDatabase,
117    generics: Generics<'db>,
118    variances: Box<[Variance]>,
119}
120
121impl<'db> Context<'db> {
122    fn solve(mut self) -> Box<[Variance]> {
123        tracing::debug!("solve(generics={:?})", self.generics);
124        match self.generics.def() {
125            GenericDefId::AdtId(adt) => {
126                let db = self.db;
127                let mut add_constraints_from_variant = |variant| {
128                    for (_, field) in db.field_types(variant).iter() {
129                        self.add_constraints_from_ty(
130                            field.ty().instantiate_identity().skip_norm_wip(),
131                            Variance::Covariant,
132                        );
133                    }
134                };
135                match adt {
136                    AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)),
137                    AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)),
138                    AdtId::EnumId(e) => {
139                        e.enum_variants(db).variants.values().for_each(|&(variant, _)| {
140                            add_constraints_from_variant(VariantId::EnumVariantId(variant))
141                        });
142                    }
143                }
144            }
145            GenericDefId::FunctionId(f) => {
146                let sig =
147                    self.db.callable_item_signature(f.into()).instantiate_identity().skip_binder();
148                self.add_constraints_from_sig(sig.inputs_and_output.iter(), Variance::Covariant);
149            }
150            _ => {}
151        }
152        let mut variances = self.variances;
153
154        // Const parameters are always invariant.
155        // Make all const parameters invariant.
156        for (idx, param) in self.generics.iter_id(false).enumerate() {
157            if let GenericParamId::ConstParamId(_) = param {
158                variances[idx] = Variance::Invariant;
159            }
160        }
161
162        // Functions are permitted to have unused generic parameters: make those invariant.
163        if let GenericDefId::FunctionId(_) = self.generics.def() {
164            variances
165                .iter_mut()
166                .filter(|&&mut v| v == Variance::Bivariant)
167                .for_each(|v| *v = Variance::Invariant);
168        }
169
170        variances
171    }
172
173    /// Adds constraints appropriate for an instance of `ty` appearing
174    /// in a context with the generics defined in `generics` and
175    /// ambient variance `variance`
176    fn add_constraints_from_ty(&mut self, ty: Ty<'db>, variance: Variance) {
177        tracing::debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance);
178        match ty.kind() {
179            TyKind::Int(_)
180            | TyKind::Uint(_)
181            | TyKind::Float(_)
182            | TyKind::Char
183            | TyKind::Bool
184            | TyKind::Never
185            | TyKind::Str
186            | TyKind::Foreign(..) => {
187                // leaf type -- noop
188            }
189            TyKind::FnDef(..)
190            | TyKind::Coroutine(..)
191            | TyKind::CoroutineClosure(..)
192            | TyKind::Closure(..) => {
193                never!("Unexpected unnameable type in variance computation: {:?}", ty);
194            }
195            TyKind::Ref(lifetime, ty, mutbl) => {
196                self.add_constraints_from_region(lifetime, variance);
197                self.add_constraints_from_mt(ty, mutbl, variance);
198            }
199            TyKind::Array(typ, len) => {
200                self.add_constraints_from_const(len);
201                self.add_constraints_from_ty(typ, variance);
202            }
203            TyKind::Slice(typ) => {
204                self.add_constraints_from_ty(typ, variance);
205            }
206            TyKind::RawPtr(ty, mutbl) => {
207                self.add_constraints_from_mt(ty, mutbl, variance);
208            }
209            TyKind::Tuple(subtys) => {
210                for subty in subtys {
211                    self.add_constraints_from_ty(subty, variance);
212                }
213            }
214            TyKind::Adt(def, args) => {
215                self.add_constraints_from_args(def.def_id().into(), args, variance);
216            }
217            TyKind::Alias(alias) => {
218                // FIXME: Probably not correct wrt. opaques.
219                self.add_constraints_from_invariant_args(alias.args);
220            }
221            TyKind::Dynamic(bounds, region) => {
222                // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`:
223                self.add_constraints_from_region(region, variance);
224
225                for bound in bounds {
226                    match bound.skip_binder() {
227                        ExistentialPredicate::Trait(trait_ref) => {
228                            self.add_constraints_from_invariant_args(trait_ref.args)
229                        }
230                        ExistentialPredicate::Projection(projection) => {
231                            self.add_constraints_from_invariant_args(projection.args);
232                            match projection.term.kind() {
233                                TermKind::Ty(ty) => {
234                                    self.add_constraints_from_ty(ty, Variance::Invariant)
235                                }
236                                TermKind::Const(konst) => self.add_constraints_from_const(konst),
237                            }
238                        }
239                        ExistentialPredicate::AutoTrait(_) => {}
240                    }
241                }
242            }
243
244            // Chalk has no params, so use placeholders for now?
245            TyKind::Param(param) => self.constrain(param.index as usize, variance),
246            TyKind::FnPtr(sig, _) => {
247                self.add_constraints_from_sig(sig.skip_binder().inputs_and_output.iter(), variance);
248            }
249            TyKind::Error(_) => {
250                // we encounter this when walking the trait references for object
251                // types, where we use Error as the Self type
252            }
253            TyKind::Pat(typ, pat) => {
254                self.add_constraints_from_pat(pat);
255                self.add_constraints_from_ty(typ, variance);
256            }
257            TyKind::Bound(..) => {}
258            TyKind::CoroutineWitness(..)
259            | TyKind::Placeholder(..)
260            | TyKind::Infer(..)
261            | TyKind::UnsafeBinder(..) => {
262                never!("unexpected type encountered in variance inference: {:?}", ty)
263            }
264        }
265    }
266
267    fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) {
268        match pat.kind() {
269            PatternKind::Range { start, end } => {
270                self.add_constraints_from_const(start);
271                self.add_constraints_from_const(end);
272            }
273            PatternKind::NotNull => {}
274            PatternKind::Or(patterns) => {
275                for pat in patterns {
276                    self.add_constraints_from_pat(pat)
277                }
278            }
279        }
280    }
281
282    fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) {
283        for k in args.iter() {
284            match k.kind() {
285                GenericArgKind::Lifetime(lt) => {
286                    self.add_constraints_from_region(lt, Variance::Invariant)
287                }
288                GenericArgKind::Type(ty) => self.add_constraints_from_ty(ty, Variance::Invariant),
289                GenericArgKind::Const(val) => self.add_constraints_from_const(val),
290            }
291        }
292    }
293
294    /// Adds constraints appropriate for a nominal type (enum, struct,
295    /// object, etc) appearing in a context with ambient variance `variance`
296    fn add_constraints_from_args(
297        &mut self,
298        def_id: GenericDefId,
299        args: GenericArgs<'db>,
300        variance: Variance,
301    ) {
302        if args.is_empty() {
303            return;
304        }
305        let variances = self.db.variances_of(def_id);
306
307        for (k, v) in args.iter().zip(variances) {
308            match k.kind() {
309                GenericArgKind::Lifetime(lt) => {
310                    self.add_constraints_from_region(lt, variance.xform(v))
311                }
312                GenericArgKind::Type(ty) => self.add_constraints_from_ty(ty, variance.xform(v)),
313                GenericArgKind::Const(val) => self.add_constraints_from_const(val),
314            }
315        }
316    }
317
318    /// Adds constraints appropriate for a const expression `val`
319    /// in a context with ambient variance `variance`
320    fn add_constraints_from_const(&mut self, c: Const<'db>) {
321        match c.kind() {
322            ConstKind::Unevaluated(c) => self.add_constraints_from_invariant_args(c.args),
323            _ => {}
324        }
325    }
326
327    /// Adds constraints appropriate for a function with signature
328    /// `sig` appearing in a context with ambient variance `variance`
329    fn add_constraints_from_sig(
330        &mut self,
331        mut sig_tys: impl DoubleEndedIterator<Item = Ty<'db>>,
332        variance: Variance,
333    ) {
334        let contra = variance.xform(Variance::Contravariant);
335        let Some(output) = sig_tys.next_back() else {
336            return never!("function signature has no return type");
337        };
338        self.add_constraints_from_ty(output, variance);
339        for input in sig_tys {
340            self.add_constraints_from_ty(input, contra);
341        }
342    }
343
344    /// Adds constraints appropriate for a region appearing in a
345    /// context with ambient variance `variance`
346    fn add_constraints_from_region(&mut self, region: Region<'db>, variance: Variance) {
347        tracing::debug!(
348            "add_constraints_from_region(region={:?}, variance={:?})",
349            region,
350            variance
351        );
352        match region.kind() {
353            RegionKind::ReEarlyParam(param) => self.constrain(param.index as usize, variance),
354            RegionKind::ReStatic => {}
355            RegionKind::ReBound(..) => {
356                // Either a higher-ranked region inside of a type or a
357                // late-bound function parameter.
358                //
359                // We do not compute constraints for either of these.
360            }
361            RegionKind::ReError(_) => {}
362            RegionKind::ReLateParam(..)
363            | RegionKind::RePlaceholder(..)
364            | RegionKind::ReVar(..)
365            | RegionKind::ReErased => {
366                // We don't expect to see anything but 'static or bound
367                // regions when visiting member types or method types.
368                never!(
369                    "unexpected region encountered in variance \
370                      inference: {:?}",
371                    region
372                );
373            }
374        }
375    }
376
377    /// Adds constraints appropriate for a mutability-type pair
378    /// appearing in a context with ambient variance `variance`
379    fn add_constraints_from_mt(&mut self, ty: Ty<'db>, mt: Mutability, variance: Variance) {
380        self.add_constraints_from_ty(
381            ty,
382            match mt {
383                Mutability::Mut => Variance::Invariant,
384                Mutability::Not => variance,
385            },
386        );
387    }
388
389    fn constrain(&mut self, index: usize, variance: Variance) {
390        tracing::debug!(
391            "constrain(index={:?}, variance={:?}, to={:?})",
392            index,
393            self.variances[index],
394            variance
395        );
396        self.variances[index] = glb(self.variances[index], variance);
397    }
398}