Skip to main content

hir_ty/next_solver/
solver.rs

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