hir_ty/next_solver/
solver.rs

1//! Defining `SolverContext` for next-trait-solver.
2
3use hir_def::{AssocItemId, GeneralConstId};
4use rustc_next_trait_solver::delegate::SolverDelegate;
5use rustc_type_ir::{
6    AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags,
7    TypeVisitableExt,
8    inherent::{IntoKind, SliceLike, Term as _, Ty as _},
9    lang_items::SolverTraitLangItem,
10    solve::{Certainty, NoSolution},
11};
12use tracing::debug;
13
14use crate::next_solver::{
15    AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
16    ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
17    util::sizedness_fast_path,
18};
19
20use super::{
21    DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span,
22    infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt},
23};
24
25pub type Goal<'db, P> = rustc_type_ir::solve::Goal<DbInterner<'db>, P>;
26
27#[repr(transparent)]
28pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>);
29
30impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> {
31    fn from(infcx: &'a InferCtxt<'db>) -> Self {
32        // SAFETY: `repr(transparent)`
33        unsafe { std::mem::transmute(infcx) }
34    }
35}
36
37impl<'db> std::ops::Deref for SolverContext<'db> {
38    type Target = InferCtxt<'db>;
39
40    fn deref(&self) -> &Self::Target {
41        &self.0
42    }
43}
44
45impl<'db> SolverDelegate for SolverContext<'db> {
46    type Interner = DbInterner<'db>;
47    type Infcx = InferCtxt<'db>;
48
49    fn cx(&self) -> Self::Interner {
50        self.0.interner
51    }
52
53    fn build_with_canonical<V>(
54        cx: Self::Interner,
55        canonical: &rustc_type_ir::CanonicalQueryInput<Self::Interner, V>,
56    ) -> (Self, V, rustc_type_ir::CanonicalVarValues<Self::Interner>)
57    where
58        V: rustc_type_ir::TypeFoldable<Self::Interner>,
59    {
60        let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical);
61        (SolverContext(infcx), value, vars)
62    }
63
64    fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, _span: Span) -> GenericArg<'db> {
65        match arg.kind() {
66            GenericArgKind::Lifetime(_) => self.next_region_var().into(),
67            GenericArgKind::Type(_) => self.next_ty_var().into(),
68            GenericArgKind::Const(_) => self.next_const_var().into(),
69        }
70    }
71
72    fn leak_check(
73        &self,
74        _max_input_universe: rustc_type_ir::UniverseIndex,
75    ) -> Result<(), NoSolution> {
76        Ok(())
77    }
78
79    fn well_formed_goals(
80        &self,
81        _param_env: ParamEnv<'db>,
82        _arg: <Self::Interner as rustc_type_ir::Interner>::Term,
83    ) -> Option<
84        Vec<
85            rustc_type_ir::solve::Goal<
86                Self::Interner,
87                <Self::Interner as rustc_type_ir::Interner>::Predicate,
88            >,
89        >,
90    > {
91        // FIXME(next-solver):
92        None
93    }
94
95    fn make_deduplicated_outlives_constraints(
96        &self,
97    ) -> Vec<
98        rustc_type_ir::OutlivesPredicate<
99            Self::Interner,
100            <Self::Interner as rustc_type_ir::Interner>::GenericArg,
101        >,
102    > {
103        // FIXME: add if we care about regions
104        vec![]
105    }
106
107    fn instantiate_canonical<V>(
108        &self,
109        canonical: rustc_type_ir::Canonical<Self::Interner, V>,
110        values: rustc_type_ir::CanonicalVarValues<Self::Interner>,
111    ) -> V
112    where
113        V: rustc_type_ir::TypeFoldable<Self::Interner>,
114    {
115        canonical.instantiate(self.cx(), &values)
116    }
117
118    fn instantiate_canonical_var(
119        &self,
120        kind: CanonicalVarKind<'db>,
121        _span: <Self::Interner as Interner>::Span,
122        var_values: &[GenericArg<'db>],
123        universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex,
124    ) -> GenericArg<'db> {
125        self.0.instantiate_canonical_var(kind, var_values, universe_map)
126    }
127
128    fn add_item_bounds_for_hidden_type(
129        &self,
130        def_id: SolverDefId,
131        args: GenericArgs<'db>,
132        param_env: ParamEnv<'db>,
133        hidden_ty: Ty<'db>,
134        goals: &mut Vec<Goal<'db, Predicate<'db>>>,
135    ) {
136        let interner = self.interner;
137        let opaque_id = def_id.expect_opaque_ty();
138        // Require that the hidden type is well-formed. We have to
139        // make sure we wf-check the hidden type to fix #114728.
140        //
141        // However, we don't check that all types are well-formed.
142        // We only do so for types provided by the user or if they are
143        // "used", e.g. for method selection.
144        //
145        // This means we never check the wf requirements of the hidden
146        // type during MIR borrowck, causing us to infer the wrong
147        // lifetime for its member constraints which then results in
148        // unexpected region errors.
149        goals.push(Goal::new(interner, param_env, ClauseKind::WellFormed(hidden_ty.into())));
150
151        let replace_opaques_in = |clause: Clause<'db>| {
152            fold_tys(interner, clause, |ty| match ty.kind() {
153                // Replace all other mentions of the same opaque type with the hidden type,
154                // as the bounds must hold on the hidden type after all.
155                TyKind::Alias(
156                    AliasTyKind::Opaque,
157                    AliasTy { def_id: def_id2, args: args2, .. },
158                ) if def_id == def_id2 && args == args2 => hidden_ty,
159                _ => ty,
160            })
161        };
162
163        let item_bounds = opaque_id.predicates(interner.db);
164        for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) {
165            let predicate = replace_opaques_in(predicate);
166
167            // Require that the predicate holds for the concrete type.
168            debug!(?predicate);
169            goals.push(Goal::new(interner, param_env, predicate));
170        }
171    }
172
173    fn fetch_eligible_assoc_item(
174        &self,
175        _goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
176        trait_assoc_def_id: SolverDefId,
177        impl_id: ImplIdWrapper,
178    ) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
179        let impl_items = impl_id.0.impl_items(self.0.interner.db());
180        let id =
181            match trait_assoc_def_id {
182                SolverDefId::TypeAliasId(trait_assoc_id) => {
183                    let trait_assoc_data = self.0.interner.db.type_alias_signature(trait_assoc_id);
184                    impl_items
185                        .items
186                        .iter()
187                        .find_map(|(impl_assoc_name, impl_assoc_id)| {
188                            if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id
189                                && *impl_assoc_name == trait_assoc_data.name
190                            {
191                                Some(impl_assoc_id)
192                            } else {
193                                None
194                            }
195                        })
196                        .or_else(|| {
197                            if trait_assoc_data.ty.is_some() { Some(trait_assoc_id) } else { None }
198                        })
199                        .map(SolverDefId::TypeAliasId)
200                }
201                SolverDefId::ConstId(trait_assoc_id) => {
202                    let trait_assoc_data = self.0.interner.db.const_signature(trait_assoc_id);
203                    let trait_assoc_name = trait_assoc_data
204                        .name
205                        .as_ref()
206                        .expect("unnamed consts should not get passed to the solver");
207                    impl_items
208                        .items
209                        .iter()
210                        .find_map(|(impl_assoc_name, impl_assoc_id)| {
211                            if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id
212                                && impl_assoc_name == trait_assoc_name
213                            {
214                                Some(impl_assoc_id)
215                            } else {
216                                None
217                            }
218                        })
219                        .or_else(|| {
220                            if trait_assoc_data.has_body() { Some(trait_assoc_id) } else { None }
221                        })
222                        .map(SolverDefId::ConstId)
223                }
224                _ => panic!("Unexpected SolverDefId"),
225            };
226        Ok(id)
227    }
228
229    fn is_transmutable(
230        &self,
231        _dst: Ty<'db>,
232        _src: Ty<'db>,
233        _assume: <Self::Interner as rustc_type_ir::Interner>::Const,
234    ) -> Result<Certainty, NoSolution> {
235        // It's better to return some value while not fully implement
236        // then panic in the mean time
237        Ok(Certainty::Yes)
238    }
239
240    fn evaluate_const(
241        &self,
242        _param_env: ParamEnv<'db>,
243        uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
244    ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
245        match uv.def.0 {
246            GeneralConstId::ConstId(c) => {
247                let subst = uv.args;
248                let ec = self.cx().db.const_eval(c, subst, None).ok()?;
249                Some(ec)
250            }
251            GeneralConstId::StaticId(c) => {
252                let ec = self.cx().db.const_eval_static(c).ok()?;
253                Some(ec)
254            }
255        }
256    }
257
258    fn compute_goal_fast_path(
259        &self,
260        goal: rustc_type_ir::solve::Goal<
261            Self::Interner,
262            <Self::Interner as rustc_type_ir::Interner>::Predicate,
263        >,
264        _span: <Self::Interner as rustc_type_ir::Interner>::Span,
265    ) -> Option<Certainty> {
266        if let Some(trait_pred) = goal.predicate.as_trait_clause() {
267            if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var()
268                // We don't do this fast path when opaques are defined since we may
269                // eventually use opaques to incompletely guide inference via ty var
270                // self types.
271                // FIXME: Properly consider opaques here.
272                && self.inner.borrow_mut().opaque_types().is_empty()
273            {
274                return Some(Certainty::AMBIGUOUS);
275            }
276
277            if trait_pred.polarity() == PredicatePolarity::Positive {
278                match self.0.cx().as_trait_lang_item(trait_pred.def_id()) {
279                    Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => {
280                        let predicate = self.resolve_vars_if_possible(goal.predicate);
281                        if sizedness_fast_path(self.cx(), predicate, goal.param_env) {
282                            return Some(Certainty::Yes);
283                        }
284                    }
285                    Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => {
286                        let self_ty =
287                            self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
288                        // Unlike `Sized` traits, which always prefer the built-in impl,
289                        // `Copy`/`Clone` may be shadowed by a param-env candidate which
290                        // could force a lifetime error or guide inference. While that's
291                        // not generally desirable, it is observable, so for now let's
292                        // ignore this fast path for types that have regions or infer.
293                        if !self_ty
294                            .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
295                            && self_ty.is_trivially_pure_clone_copy()
296                        {
297                            return Some(Certainty::Yes);
298                        }
299                    }
300                    _ => {}
301                }
302            }
303        }
304
305        let pred = goal.predicate.kind();
306        match pred.no_bound_vars()? {
307            PredicateKind::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes),
308            PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => Some(Certainty::Yes),
309            PredicateKind::Subtype(SubtypePredicate { a, b, .. })
310            | PredicateKind::Coerce(CoercePredicate { a, b }) => {
311                if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() {
312                    // FIXME: We also need to register a subtype relation between these vars
313                    // when those are added, and if they aren't in the same sub root then
314                    // we should mark this goal as `has_changed`.
315                    Some(Certainty::AMBIGUOUS)
316                } else {
317                    None
318                }
319            }
320            PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => {
321                if self.shallow_resolve_const(ct).is_ct_infer() {
322                    Some(Certainty::AMBIGUOUS)
323                } else {
324                    None
325                }
326            }
327            PredicateKind::Clause(ClauseKind::WellFormed(arg)) => {
328                if arg.is_trivially_wf(self.interner) {
329                    Some(Certainty::Yes)
330                } else if arg.is_infer() {
331                    Some(Certainty::AMBIGUOUS)
332                } else {
333                    None
334                }
335            }
336            _ => None,
337        }
338    }
339}