Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/traits.rs')
| -rw-r--r-- | crates/hir-ty/src/traits.rs | 281 |
1 files changed, 23 insertions, 258 deletions
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index cd125f3af8..7f6d4ff17f 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -3,35 +3,26 @@ use core::fmt; use std::hash::Hash; -use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; - use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ - InferCtxtLike, TypingMode, - inherent::{IntoKind, SliceLike, Span as _, Ty as _}, + TypingMode, + inherent::{IntoKind, Span as _}, solve::Certainty, }; -use span::Edition; -use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - from_assoc_type_id, next_solver::{ - DbInterner, GenericArg, ParamEnv, Predicate, SolverContext, Span, + Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, Ty, + TyKind, infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, NextSolverToChalk, convert_canonical_args_for_result}, obligation_ctxt::ObligationCtxt, - util::mini_canonicalize, }, - utils::UnevaluatedConstEvaluatorFolder, }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -44,7 +35,7 @@ pub struct TraitEnvironment<'db> { pub krate: Crate, pub block: Option<BlockId>, // FIXME make this a BTreeMap - traits_from_clauses: Box<[(crate::next_solver::Ty<'db>, TraitId)]>, + traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, pub env: ParamEnv<'db>, } @@ -61,7 +52,7 @@ impl<'db> TraitEnvironment<'db> { pub fn new( krate: Crate, block: Option<BlockId>, - traits_from_clauses: Box<[(crate::next_solver::Ty<'db>, TraitId)]>, + traits_from_clauses: Box<[(Ty<'db>, TraitId)]>, env: ParamEnv<'db>, ) -> Arc<Self> { Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) @@ -72,10 +63,7 @@ impl<'db> TraitEnvironment<'db> { Arc::make_mut(this).block = Some(block); } - pub fn traits_in_scope_from_clauses( - &self, - ty: crate::next_solver::Ty<'db>, - ) -> impl Iterator<Item = TraitId> + '_ { + pub fn traits_in_scope_from_clauses(&self, ty: Ty<'db>) -> impl Iterator<Item = TraitId> + '_ { self.traits_from_clauses .iter() .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then_some(*trait_id)) @@ -85,172 +73,19 @@ impl<'db> TraitEnvironment<'db> { /// This should be used in `hir` only. pub fn structurally_normalize_ty<'db>( infcx: &InferCtxt<'db>, - ty: crate::next_solver::Ty<'db>, + ty: Ty<'db>, env: Arc<TraitEnvironment<'db>>, -) -> crate::next_solver::Ty<'db> { - let crate::next_solver::TyKind::Alias(..) = ty.kind() else { return ty }; +) -> Ty<'db> { + let TyKind::Alias(..) = ty.kind() else { return ty }; let mut ocx = ObligationCtxt::new(infcx); let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env.env, ty).unwrap_or(ty); ty.replace_infer_with_error(infcx.interner) } -pub(crate) fn normalize_projection_query<'db>( - db: &'db dyn HirDatabase, - projection: ProjectionTy, - env: Arc<TraitEnvironment<'db>>, -) -> Ty { - if projection.substitution.iter(Interner).any(|arg| { - arg.ty(Interner) - .is_some_and(|ty| ty.data(Interner).flags.intersects(TypeFlags::HAS_TY_INFER)) - }) { - never!( - "Invoking `normalize_projection_query` with a projection type containing inference var" - ); - return TyKind::Error.intern(Interner); - } - - let interner = DbInterner::new_with(db, Some(env.krate), env.block); - // FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things), - // but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT - // and async blocks. - let infcx = interner.infer_ctxt().build(TypingMode::Analysis { - defining_opaque_types_and_generators: crate::next_solver::SolverDefIds::new_from_iter( - interner, - [], - ), - }); - let alias_ty = crate::next_solver::Ty::new_alias( - interner, - rustc_type_ir::AliasTyKind::Projection, - crate::next_solver::AliasTy::new( - interner, - from_assoc_type_id(projection.associated_ty_id).into(), - <crate::Substitution as ChalkToNextSolver<crate::next_solver::GenericArgs<'_>>>::to_nextsolver(&projection.substitution, interner), - ), - ); - let mut ctxt = crate::next_solver::obligation_ctxt::ObligationCtxt::new(&infcx); - let normalized = ctxt - .structurally_normalize_ty(&ObligationCause::dummy(), env.env, alias_ty) - .unwrap_or(alias_ty); - normalized.replace_infer_with_error(interner).to_chalk(interner) -} - -fn identity_subst( - binders: chalk_ir::CanonicalVarKinds<Interner>, -) -> chalk_ir::Canonical<chalk_ir::Substitution<Interner>> { - let identity_subst = chalk_ir::Substitution::from_iter( - Interner, - binders.iter(Interner).enumerate().map(|(index, c)| { - let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index); - match &c.kind { - chalk_ir::VariableKind::Ty(_) => { - chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner)) - .intern(Interner) - } - chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime( - chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner), - ) - .intern(Interner), - chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const( - chalk_ir::ConstData { - ty: ty.clone(), - value: chalk_ir::ConstValue::BoundVar(index_db), - } - .intern(Interner), - ) - .intern(Interner), - } - }), - ); - chalk_ir::Canonical { binders, value: identity_subst } -} - -/// Solve a trait goal using next trait solver. -pub(crate) fn trait_solve_query( - db: &dyn HirDatabase, - krate: Crate, - block: Option<BlockId>, - goal: Canonical<InEnvironment<Goal>>, -) -> NextTraitSolveResult { - let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db - .trait_signature(it.hir_trait_id()) - .name - .display(db, Edition::LATEST) - .to_string(), - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), - _ => "??".to_owned(), - }) - .entered(); - - if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - .. - }))) = &goal.value.goal.data(Interner) - && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) - { - // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); - } - - // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So - // we should get rid of it when talking to chalk. - let goal = goal - .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) - .unwrap(); - - // We currently don't deal with universes (I think / hope they're not yet - // relevant for our use cases?) - next_trait_solve(db, krate, block, goal) -} - -fn solve_nextsolver<'db>( - db: &'db dyn HirDatabase, - krate: Crate, - block: Option<BlockId>, - goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>, -) -> Result< - (HasChanged, Certainty, rustc_type_ir::Canonical<DbInterner<'db>, Vec<GenericArg<'db>>>), - rustc_type_ir::solve::NoSolution, -> { - // FIXME: should use analysis_in_body, but that needs GenericDefId::Block - let context = SolverContext( - DbInterner::new_with(db, Some(krate), block) - .infer_ctxt() - .build(TypingMode::non_body_analysis()), - ); - - match goal.canonical.value.goal.data(Interner) { - // FIXME: args here should be...what? not empty - GoalData::All(goals) if goals.is_empty(Interner) => { - return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![]))); - } - _ => {} - } - - let goal = goal.canonical.to_nextsolver(context.cx()); - tracing::info!(?goal); - - let (goal, var_values) = context.instantiate_canonical(&goal); - tracing::info!(?var_values); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let vars = - var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); - let canonical_var_values = mini_canonicalize(context, vars); - - let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - res -} - #[derive(Clone, Debug, PartialEq)] pub enum NextTraitSolveResult { - Certain(chalk_ir::Canonical<chalk_ir::ConstrainedSubst<Interner>>), - Uncertain(chalk_ir::Canonical<chalk_ir::Substitution<Interner>>), + Certain, + Uncertain, NoSolution, } @@ -260,75 +95,17 @@ impl NextTraitSolveResult { } pub fn certain(&self) -> bool { - matches!(self, NextTraitSolveResult::Certain(..)) + matches!(self, NextTraitSolveResult::Certain) } pub fn uncertain(&self) -> bool { - matches!(self, NextTraitSolveResult::Uncertain(..)) - } -} - -pub fn next_trait_solve( - db: &dyn HirDatabase, - krate: Crate, - block: Option<BlockId>, - goal: Canonical<InEnvironment<Goal>>, -) -> NextTraitSolveResult { - let detail = match &goal.value.goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { - db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string() - } - GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), - _ => "??".to_owned(), - }; - let _p = tracing::info_span!("next_trait_solve", ?detail).entered(); - tracing::info!("next_trait_solve({:?})", goal.value.goal); - - if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - .. - }))) = &goal.value.goal.data(Interner) - && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) - { - // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - // FIXME - return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); - } - - // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So - // we should get rid of it when talking to chalk. - let goal = goal - .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) - .unwrap(); - - // We currently don't deal with universes (I think / hope they're not yet - // relevant for our use cases?) - let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - tracing::info!(?u_canonical); - - let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical); - - match next_solver_res { - Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), - ), - Ok((_, Certainty::Maybe { .. }, args)) => { - let subst = convert_canonical_args_for_result( - DbInterner::new_with(db, Some(krate), block), - args, - ); - NextTraitSolveResult::Uncertain(chalk_ir::Canonical { - binders: subst.binders, - value: subst.value.subst, - }) - } + matches!(self, NextTraitSolveResult::Uncertain) } } pub fn next_trait_solve_canonical_in_ctxt<'db>( infer_ctxt: &InferCtxt<'db>, - goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, + goal: Canonical<'db, Goal<'db, Predicate<'db>>>, ) -> NextTraitSolveResult { let context = SolverContext(infer_ctxt.clone()); @@ -339,33 +116,21 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( let res = context.evaluate_root_goal(goal, Span::dummy(), None); - let vars = - var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); - let canonical_var_values = mini_canonicalize(context, vars); - - let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + let res = res.map(|r| (r.has_changed, r.certainty)); tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); match res { Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(infer_ctxt.interner, args), - ), - Ok((_, Certainty::Maybe { .. }, args)) => { - let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); - NextTraitSolveResult::Uncertain(chalk_ir::Canonical { - binders: subst.binders, - value: subst.value.subst, - }) - } + Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain, + Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain, } } /// Solve a trait goal using next trait solver. pub fn next_trait_solve_in_ctxt<'db, 'a>( infer_ctxt: &'a InferCtxt<'db>, - goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, + goal: Goal<'db, Predicate<'db>>, ) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { tracing::info!(?goal); @@ -459,7 +224,7 @@ impl FnTrait { /// This should not be used in `hir-ty`, only in `hir`. pub fn implements_trait_unique<'db>( - ty: crate::next_solver::Ty<'db>, + ty: Ty<'db>, db: &'db dyn HirDatabase, env: Arc<TraitEnvironment<'db>>, trait_: TraitId, @@ -474,7 +239,7 @@ pub fn implements_trait_unique_with_args<'db>( db: &'db dyn HirDatabase, env: Arc<TraitEnvironment<'db>>, trait_: TraitId, - args: crate::next_solver::GenericArgs<'db>, + args: GenericArgs<'db>, ) -> bool { implements_trait_unique_impl(db, env, trait_, &mut |_| args) } @@ -483,7 +248,7 @@ fn implements_trait_unique_impl<'db>( db: &'db dyn HirDatabase, env: Arc<TraitEnvironment<'db>>, trait_: TraitId, - create_args: &mut dyn FnMut(&InferCtxt<'db>) -> crate::next_solver::GenericArgs<'db>, + create_args: &mut dyn FnMut(&InferCtxt<'db>) -> GenericArgs<'db>, ) -> bool { let interner = DbInterner::new_with(db, Some(env.krate), env.block); // FIXME(next-solver): I believe this should be `PostAnalysis`. @@ -491,7 +256,7 @@ fn implements_trait_unique_impl<'db>( let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); - let goal = crate::next_solver::Goal::new(interner, env.env, trait_ref); + let goal = Goal::new(interner, env.env, trait_ref); let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal); matches!(result, Ok((_, Certainty::Yes))) |