diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 48c0c81b4781..54d78dea3d3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,11 +2,7 @@ use std::ops::ControlFlow; -use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; -use chalk_solve::rust_ir::InlineBound; +use chalk_ir::DebruijnIndex; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, @@ -21,14 +17,14 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, - WhereClause, all_super_traits, + ImplTraitId, Interner, TyKind, WhereClause, all_super_traits, db::{HirDatabase, InternedOpaqueTyId}, - from_assoc_type_id, from_chalk_trait_id, + from_chalk_trait_id, generics::trait_self_param_idx, + lower_nextsolver::associated_ty_item_bounds, next_solver::{ - Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, - infer::DbInternerInferExt, mk_param, + Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, + TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, @@ -165,7 +161,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // but we don't have good way to render such locations. // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - db.generic_predicates(trait_.into()) + db.generic_predicates_ns(trait_.into()) .iter() .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) } @@ -177,37 +173,18 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { .items .iter() .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => { - let assoc_ty_data = db.associated_ty_data(id); - Some(assoc_ty_data) - } + AssocItemId::TypeAliasId(id) => Some(associated_ty_item_bounds(db, id)), _ => None, }) - .any(|assoc_ty_data| { - assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { - let def = from_assoc_type_id(assoc_ty_data.id).into(); - match bound.skip_binders() { - InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - } + .any(|bounds| { + bounds.skip_binder().iter().any(|pred| match pred.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::Projection(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::AutoTrait(_) => false, }) }) } @@ -218,120 +195,26 @@ enum AllowSelfProjection { No, } -fn predicate_references_self( - db: &dyn HirDatabase, +fn predicate_references_self<'db>( + db: &'db dyn HirDatabase, trait_: TraitId, - predicate: &Binders>, + predicate: &Clause<'db>, allow_self_projection: AllowSelfProjection, ) -> bool { - match predicate.skip_binders().skip_binders() { - WhereClause::Implemented(trait_ref) => { - trait_ref.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) - }) - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { - proj.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) + match predicate.kind().skip_binder() { + ClauseKind::Trait(trait_pred) => trait_pred.trait_ref.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) + }), + ClauseKind::Projection(proj_pred) => { + proj_pred.projection_term.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) }) } _ => false, } } -fn contains_illegal_self_type_reference>( - db: &dyn HirDatabase, - def: GenericDefId, - trait_: TraitId, - t: &T, - outer_binder: DebruijnIndex, - allow_self_projection: AllowSelfProjection, -) -> bool { - let Some(trait_self_param_idx) = trait_self_param_idx(db, def) else { - return false; - }; - struct IllegalSelfTypeVisitor<'a> { - db: &'a dyn HirDatabase, - trait_: TraitId, - super_traits: Option>, - trait_self_param_idx: usize, - allow_self_projection: AllowSelfProjection, - } - impl TypeVisitor for IllegalSelfTypeVisitor<'_> { - type BreakTy = (); - - 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 { - match ty.kind(Interner) { - TyKind::BoundVar(BoundVar { debruijn, index }) => { - if *debruijn == outer_binder && *index == self.trait_self_param_idx { - ControlFlow::Break(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { - AllowSelfProjection::Yes => { - let trait_ = proj.trait_(self.db); - if self.super_traits.is_none() { - self.super_traits = Some(all_super_traits(self.db, self.trait_)); - } - if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { - ControlFlow::Continue(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), - }, - _ => ty.super_visit_with(self.as_dyn(), outer_binder), - } - } - - fn visit_const( - &mut self, - constant: &chalk_ir::Const, - outer_binder: DebruijnIndex, - ) -> std::ops::ControlFlow { - constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - - let mut visitor = IllegalSelfTypeVisitor { - db, - trait_, - super_traits: None, - trait_self_param_idx, - allow_self_projection, - }; - t.visit_with(visitor.as_dyn(), outer_binder).is_break() -} - -fn contains_illegal_self_type_reference_ns< - 'db, - T: rustc_type_ir::TypeVisitable>, ->( +fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable>>( db: &'db dyn HirDatabase, trait_: TraitId, t: &T, @@ -440,13 +323,17 @@ where } 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) - }) { + if sig + .skip_binder() + .inputs() + .iter() + .skip(1) + .any(|ty| contains_illegal_self_type_reference(db, trait_, &ty, AllowSelfProjection::Yes)) + { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference_ns( + if contains_illegal_self_type_reference( db, trait_, &sig.skip_binder().output(), @@ -496,7 +383,7 @@ where continue; } - if contains_illegal_self_type_reference_ns(db, trait_, &pred, AllowSelfProjection::Yes) { + if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjection::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 15c675be5819..2777869bd48e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -62,9 +62,10 @@ use crate::{ lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, next_solver::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, - BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, - TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, + EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, + TraitPredicate, TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, + mapping::ChalkToNextSolver, }, }; @@ -1593,6 +1594,95 @@ fn fn_sig_for_enum_variant_constructor<'db>( })) } +pub(crate) fn associated_ty_item_bounds<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + let type_alias_data = db.type_alias_signature(type_alias); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + if let Some(bound) = pred + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ))) + } + } + rustc_type_ir::ClauseKind::Projection(p) => Some( + ExistentialPredicate::Projection(ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + )), + ), + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => None, + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + bounds.push(bound); + } + }); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( + interner, + SolverDefId::TraitId(trait_), + [] as [crate::next_solver::GenericArg<'_>; 0], + ))); + bounds.push(sized_clause); + bounds.shrink_to_fit(); + } + + EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) +} + pub(crate) fn associated_type_by_name_including_super_traits<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>,