From b2f107cb8bd77d4d7853e436bee7761a061b8fd0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Aug 2025 21:10:47 +0000 Subject: [PATCH] Convert more of dyn_compatibility to next-solver --- crates/hir-ty/src/dyn_compatibility.rs | 251 ++++++++++++------------- 1 file changed, 122 insertions(+), 129 deletions(-) diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index f571b64464..237cc34d70 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; use chalk_ir::{ DebruijnIndex, - cast::Cast, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use chalk_solve::rust_ir::InlineBound; @@ -12,20 +11,26 @@ use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; +use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, inherent::IntoKind, + AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, + Upcast, + inherent::{IntoKind, SliceLike}, }; use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, - OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, - db::HirDatabase, + AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, + WhereClause, all_super_traits, + db::{HirDatabase, InternedOpaqueTyId}, from_assoc_type_id, from_chalk_trait_id, - generics::{generics, trait_self_param_idx}, - next_solver::{DbInterner, SolverDefId, TraitPredicate}, - to_chalk_trait_id, + generics::trait_self_param_idx, + next_solver::{ + Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, + infer::DbInternerInferExt, mk_param, + }, + traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, }; @@ -434,26 +439,17 @@ where cb(MethodViolationCode::AsyncFn)?; } - let sig = db.callable_item_signature(func.into()); - if sig.skip_binders().params().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference( - db, - func.into(), - trait_, - ty, - DebruijnIndex::INNERMOST, - AllowSelfProjection::Yes, - ) + let sig = db.callable_item_signature_ns(func.into()); + if sig.skip_binder().inputs().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference_ns(db, trait_, &ty, AllowSelfProjection::Yes) }) { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference( + if contains_illegal_self_type_reference_ns( db, - func.into(), trait_, - sig.skip_binders().ret(), - DebruijnIndex::INNERMOST, + &sig.skip_binder().output(), AllowSelfProjection::Yes, ) { cb(MethodViolationCode::ReferencesSelfOutput)?; @@ -483,7 +479,7 @@ where } // Allow `impl AutoTrait` predicates - let interner = DbInterner::new_with(db, None, None); + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); if let ClauseKind::Trait(TraitPredicate { trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, @@ -509,34 +505,30 @@ where ControlFlow::Continue(()) } -fn receiver_is_dispatchable( +fn receiver_is_dispatchable<'db>( db: &dyn HirDatabase, trait_: TraitId, func: FunctionId, - sig: &Binders, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> bool { - let Some(trait_self_idx) = trait_self_param_idx(db, func.into()) else { - return false; - }; + let sig = sig.instantiate_identity(); + + let interner: DbInterner<'_> = DbInterner::new_with(db, Some(trait_.krate(db)), None); + let self_param_ty = crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + ); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 - if sig - .skip_binders() - .params() - .first() - .and_then(|receiver| receiver.bound_var(Interner)) - .is_some_and(|b| { - b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } - }) - { + if sig.inputs().iter().next().is_some_and(|p| p.skip_binder() == self_param_ty) { return true; } - let placeholder_subst = generics(db, func.into()).placeholder_subst(db); - - let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); - let Some(receiver_ty) = substituted_sig.params().first() else { + let Some(&receiver_ty) = sig.inputs().skip_binder().as_slice().first() else { return false; }; @@ -549,118 +541,119 @@ fn receiver_is_dispatchable( return false; }; - // Type `U` - let unsized_self_ty = - TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); - // `Receiver[Self => U]` - let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + let meta_sized_did = LangItem::MetaSized.resolve_trait(db, krate); + let Some(meta_sized_did) = meta_sized_did else { return false; }; - let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); - let unsized_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(unsize_did), - substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), - }); - let unsized_predicate = - Binders::empty(Interner, unsized_predicate.cast::(Interner)); - let trait_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: Substitution::from_iter( - Interner, - std::iter::once(unsized_self_ty.cast(Interner)) - .chain(placeholder_subst.iter(Interner).skip(1).cloned()), - ), - }); - let trait_predicate = Binders::empty(Interner, trait_predicate.cast::(Interner)); + // Type `U` + let unsized_self_ty = crate::next_solver::Ty::new_param(interner, u32::MAX, Symbol::empty()); + // `Receiver[Self => U]` + let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); - let generic_predicates = &*db.generic_predicates(func.into()); + let param_env = { + let generic_predicates = &*db.generic_predicates_ns(func.into()); - let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( - generic_predicates.iter().map(|pred| { - pred.clone() - .substitute(Interner, &placeholder_subst) - .map(|g| g.cast::(Interner)) - }), + // Self: Unsize + let unsize_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(unsize_did), + [self_param_ty, unsized_self_ty], + ); + + // U: Trait + let trait_def_id = SolverDefId::TraitId(trait_); + let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { + if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) } + }); + let trait_predicate = + crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); + + let meta_sized_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(meta_sized_did), + [unsized_self_ty], + ); + + ParamEnv { + clauses: Clauses::new_from_iter( + interner, + generic_predicates.iter().copied().chain([ + unsize_predicate.upcast(interner), + trait_predicate.upcast(interner), + meta_sized_predicate.upcast(interner), + ]), + ), + } + }; + + // Receiver: DispatchFromDyn U]> + let predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(dispatch_from_dyn_did), + [receiver_ty, unsized_receiver_ty], ); - let clauses = chalk_ir::ProgramClauses::from_iter( - Interner, - goals.into_iter().map(|g| { - chalk_ir::ProgramClause::new( - Interner, - chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { - consequence: g, - conditions: chalk_ir::Goals::empty(Interner), - constraints: chalk_ir::Constraints::empty(Interner), - priority: chalk_ir::ClausePriority::High, - })), - ) - }), - ); - let env: chalk_ir::Environment = chalk_ir::Environment { clauses }; + let goal = crate::next_solver::Goal::new(interner, param_env, predicate); - let obligation = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(dispatch_from_dyn_did), - substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), - }); - let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); - - let in_env = chalk_ir::InEnvironment::new(&env, goal); - - let mut table = chalk_solve::infer::InferenceTable::::new(); - let canonicalized = table.canonicalize(Interner, in_env); - - db.trait_solve(krate, None, canonicalized.quantified).certain() + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + // the receiver is dispatchable iff the obligation holds + let res = next_trait_solve_in_ctxt(&infcx, goal); + res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) } -fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { - let generics = generics(db, func.into()); - let trait_self_idx = trait_self_param_idx(db, func.into())?; - let subst = generics.placeholder_subst(db); - let subst = Substitution::from_iter( - Interner, - subst.iter(Interner).enumerate().map(|(idx, arg)| { - if idx == trait_self_idx { ty.clone().cast(Interner) } else { arg.clone() } - }), +fn receiver_for_self_ty<'db>( + interner: DbInterner<'db>, + func: FunctionId, + receiver_ty: crate::next_solver::Ty<'db>, + self_ty: crate::next_solver::Ty<'db>, +) -> crate::next_solver::Ty<'db> { + let args = crate::next_solver::GenericArgs::for_item( + interner, + SolverDefId::FunctionId(func), + |name, index, kind, _| { + if index == 0 { self_ty.into() } else { mk_param(index, name, kind) } + }, ); - let sig = db.callable_item_signature(func.into()); - let sig = sig.substitute(Interner, &subst); - sig.params_and_return.first().cloned() + + + crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) } -fn contains_illegal_impl_trait_in_trait( - db: &dyn HirDatabase, - sig: &Binders, +fn contains_illegal_impl_trait_in_trait<'db>( + db: &'db dyn HirDatabase, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> Option { - struct OpaqueTypeCollector(FxHashSet); + struct OpaqueTypeCollector(FxHashSet); - impl TypeVisitor for OpaqueTypeCollector { - type BreakTy = (); + impl<'db> rustc_type_ir::TypeVisitor> for OpaqueTypeCollector { + type Result = ControlFlow<()>; - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { - self.0.insert(*opaque_ty_id); + fn visit_ty( + &mut self, + ty: as rustc_type_ir::Interner>::Ty, + ) -> Self::Result { + if let rustc_type_ir::TyKind::Alias(AliasTyKind::Opaque, op) = ty.kind() { + let id = match op.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + self.0.insert(id); } - ty.super_visit_with(self.as_dyn(), outer_binder) + ty.super_visit_with(self) } } - let ret = sig.skip_binders().ret(); + let ret = sig.skip_binder().output(); let mut visitor = OpaqueTypeCollector(FxHashSet::default()); - _ = ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + _ = ret.visit_with(&mut visitor); // Since we haven't implemented RPITIT in proper way like rustc yet, // just check whether `ret` contains RPIT for now for opaque_ty in visitor.0 { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty); if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { return Some(MethodViolationCode::ReferencesImplTraitInTrait); }