chalk_solve/infer/
invert.rs

1use chalk_derive::FallibleTypeFolder;
2use chalk_ir::fold::shift::Shift;
3use chalk_ir::fold::{TypeFoldable, TypeFolder};
4use chalk_ir::interner::HasInterner;
5use chalk_ir::interner::Interner;
6use chalk_ir::*;
7use rustc_hash::FxHashMap;
8
9use super::canonicalize::Canonicalized;
10use super::{EnaVariable, InferenceTable};
11
12impl<I: Interner> InferenceTable<I> {
13    /// Converts `value` into a "negation" value -- meaning one that,
14    /// if we can find any answer to it, then the negation fails. For
15    /// goals that do not contain any free variables, then this is a
16    /// no-op operation.
17    ///
18    /// If `value` contains any existential variables that have not
19    /// yet been assigned a value, then this function will return
20    /// `None`, indicating that we cannot prove negation for this goal
21    /// yet.  This follows the approach in Clark's original
22    /// [negation-as-failure paper][1], where negative goals are only
23    /// permitted if they contain no free (existential) variables.
24    ///
25    /// [1]: https://www.doc.ic.ac.uk/~klc/NegAsFailure.pdf
26    ///
27    /// Restricting free existential variables is done because the
28    /// semantics of such queries is not what you expect: it basically
29    /// treats the existential as a universal. For example, consider:
30    ///
31    /// ```rust,ignore
32    /// struct Vec<T> {}
33    /// struct i32 {}
34    /// struct u32 {}
35    /// trait Foo {}
36    /// impl Foo for Vec<u32> {}
37    /// ```
38    ///
39    /// If we ask `exists<T> { not { Vec<T>: Foo } }`, what should happen?
40    /// If we allow negative queries to be definitively answered even when
41    /// they contain free variables, we will get a definitive *no* to the
42    /// entire goal! From a logical perspective, that's just wrong: there
43    /// does exists a `T` such that `not { Vec<T>: Foo }`, namely `i32`. The
44    /// problem is that the proof search procedure is actually trying to
45    /// prove something stronger, that there is *no* such `T`.
46    ///
47    /// An additional complication arises around free universal
48    /// variables.  Consider a query like `not { !0 = !1 }`, where
49    /// `!0` and `!1` are placeholders for universally quantified
50    /// types (i.e., `TyKind::Placeholder`). If we just tried to
51    /// prove `!0 = !1`, we would get false, because those types
52    /// cannot be unified -- this would then allow us to conclude that
53    /// `not { !0 = !1 }`, i.e., `forall<X, Y> { not { X = Y } }`, but
54    /// this is clearly not true -- what if X were to be equal to Y?
55    ///
56    /// Interestingly, the semantics of existential variables turns
57    /// out to be exactly what we want here. So, in addition to
58    /// forbidding existential variables in the original query, the
59    /// `negated` query also converts all universals *into*
60    /// existentials. Hence `negated` applies to `!0 = !1` would yield
61    /// `exists<X,Y> { X = Y }` (note that a canonical, i.e. closed,
62    /// result is returned). Naturally this has a solution, and hence
63    /// `not { !0 = !1 }` fails, as we expect.
64    ///
65    /// (One could imagine converting free existentials into
66    /// universals, rather than forbidding them altogether. This would
67    /// be conceivable, but overly strict. For example, the goal
68    /// `exists<T> { not { ?T: Clone }, ?T = Vec<i32> }` would come
69    /// back as false, when clearly this is true. This is because we
70    /// would wind up proving that `?T: Clone` can *never* be
71    /// satisfied (which is false), when we only really care about
72    /// `?T: Clone` in the case where `?T = Vec<i32>`. The current
73    /// version would delay processing the negative goal (i.e., return
74    /// `None`) until the second unification has occurred.)
75    pub fn invert<T>(&mut self, interner: I, value: T) -> Option<T>
76    where
77        T: TypeFoldable<I> + HasInterner<Interner = I>,
78    {
79        let Canonicalized {
80            free_vars,
81            quantified,
82            ..
83        } = self.canonicalize(interner, value);
84
85        // If the original contains free existential variables, give up.
86        if !free_vars.is_empty() {
87            return None;
88        }
89
90        // If this contains free universal variables, replace them with existentials.
91        assert!(quantified.binders.is_empty(interner));
92        let inverted = quantified
93            .value
94            .try_fold_with(&mut Inverter::new(interner, self), DebruijnIndex::INNERMOST)
95            .unwrap();
96        Some(inverted)
97    }
98
99    /// As `negated_instantiated`, but canonicalizes before
100    /// returning. Just a convenience function.
101    pub fn invert_then_canonicalize<T>(&mut self, interner: I, value: T) -> Option<Canonical<T>>
102    where
103        T: TypeFoldable<I> + HasInterner<Interner = I>,
104    {
105        let snapshot = self.snapshot();
106        let result = self.invert(interner, value);
107        let result = result.map(|r| self.canonicalize(interner, r).quantified);
108        self.rollback_to(snapshot);
109        result
110    }
111}
112
113#[derive(FallibleTypeFolder)]
114struct Inverter<'q, I: Interner> {
115    table: &'q mut InferenceTable<I>,
116    inverted_ty: FxHashMap<PlaceholderIndex, EnaVariable<I>>,
117    inverted_lifetime: FxHashMap<PlaceholderIndex, EnaVariable<I>>,
118    interner: I,
119}
120
121impl<'q, I: Interner> Inverter<'q, I> {
122    fn new(interner: I, table: &'q mut InferenceTable<I>) -> Self {
123        Inverter {
124            table,
125            inverted_ty: FxHashMap::default(),
126            inverted_lifetime: FxHashMap::default(),
127            interner,
128        }
129    }
130}
131
132impl<'i, I: Interner> TypeFolder<I> for Inverter<'i, I> {
133    fn as_dyn(&mut self) -> &mut dyn TypeFolder<I> {
134        self
135    }
136
137    fn fold_free_placeholder_ty(
138        &mut self,
139        universe: PlaceholderIndex,
140        _outer_binder: DebruijnIndex,
141    ) -> Ty<I> {
142        let table = &mut self.table;
143        self.inverted_ty
144            .entry(universe)
145            .or_insert_with(|| table.new_variable(universe.ui))
146            .to_ty(TypeFolder::interner(self))
147            .shifted_in(TypeFolder::interner(self))
148    }
149
150    fn fold_free_placeholder_lifetime(
151        &mut self,
152        universe: PlaceholderIndex,
153        _outer_binder: DebruijnIndex,
154    ) -> Lifetime<I> {
155        let table = &mut self.table;
156        self.inverted_lifetime
157            .entry(universe)
158            .or_insert_with(|| table.new_variable(universe.ui))
159            .to_lifetime(TypeFolder::interner(self))
160            .shifted_in(TypeFolder::interner(self))
161    }
162
163    fn forbid_free_vars(&self) -> bool {
164        true
165    }
166
167    fn forbid_inference_vars(&self) -> bool {
168        true
169    }
170
171    fn interner(&self) -> I {
172        self.interner
173    }
174}