From 99b26382d8adbf89991d0248091a38d193fa4bbc Mon Sep 17 00:00:00 2001 From: Noah Bright Date: Sat, 28 Sep 2024 06:17:22 -0400 Subject: Update handlers.rs to handlers/requests.rs --- src/tools/rust-analyzer/docs/dev/architecture.md | 2 +- src/tools/rust-analyzer/editors/code/src/debug.ts | 2 ++ src/tools/rust-analyzer/editors/code/src/run.ts | 2 +- src/tools/rust-analyzer/editors/code/src/toolchain.ts | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md index 4f8723a9368..6aa57b2f9be 100644 --- a/src/tools/rust-analyzer/docs/dev/architecture.md +++ b/src/tools/rust-analyzer/docs/dev/architecture.md @@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c `crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP. This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it. -`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. +`crates/rust-analyzer/src/handlers/requests.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. `Analysis` and `AnalysisHost` types define the main API for consumers of IDE services. diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index b3f1b056a8b..fb7e340e517 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -173,6 +173,8 @@ async function getDebugConfiguration( if (debugConfig.name === "run binary") { // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs, // fn to_lsp_runnable(...) with RunnableKind::Bin + // FIXME: Neither crates\rust-analyzer\src\main_loop\handlers.rs + // nor to_lsp_runnable exist anymore debugConfig.name = `run ${path.basename(executable)}`; } diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index dd0da6b62c8..8a82a5a58cf 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -36,7 +36,7 @@ export async function selectRunnable( if (runnables.length === 0) { // it is the debug case, run always has at least 'cargo check ...' - // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables + // see crates\rust-analyzer\src\handlers\request.rs, handle_runnables await vscode.window.showErrorMessage("There's no debug target!"); quickPick.dispose(); return; diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index 850a6a55616..e8bab9c3d84 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -29,7 +29,7 @@ export class Cargo { static artifactSpec(cargoArgs: string[], executableArgs?: string[]): ArtifactSpec { cargoArgs = [...cargoArgs, "--message-format=json"]; // arguments for a runnable from the quick pick should be updated. - // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens + // see crates\rust-analyzer\src\handlers\request.rs, handle_code_lens switch (cargoArgs[0]) { case "run": cargoArgs[0] = "build"; -- cgit 1.4.1-3-g733a5 From 7b757b3e17ee950e29beb391c77e1dfebdd19d23 Mon Sep 17 00:00:00 2001 From: Noah Bright Date: Sat, 28 Sep 2024 07:01:32 -0400 Subject: Rename object_safety to dyn_compatibility Up to a trait implemented by another package, linking to $CARGO_HOME/registry/cache/index.crates.io-6f17d22bba15001f/ --- .../rust-analyzer/crates/hir-ty/src/chalk_db.rs | 3 +- src/tools/rust-analyzer/crates/hir-ty/src/db.rs | 10 +- .../crates/hir-ty/src/dyn_compatibility.rs | 615 +++++++++++++++++++++ .../crates/hir-ty/src/dyn_compatibility/tests.rs | 395 +++++++++++++ src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../crates/hir-ty/src/object_safety.rs | 612 -------------------- .../crates/hir-ty/src/object_safety/tests.rs | 393 ------------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +- .../crates/ide-db/src/generated/lints.rs | 4 +- .../rust-analyzer/crates/ide/src/hover/render.rs | 38 +- .../rust-analyzer/crates/ide/src/hover/tests.rs | 12 +- src/tools/rust-analyzer/crates/syntax/src/ast.rs | 2 +- src/tools/rust-analyzer/crates/vfs/src/loader.rs | 4 +- src/tools/rust-analyzer/docs/dev/syntax.md | 2 +- 14 files changed, 1052 insertions(+), 1046 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index e74e3d78988..f7bacbd49b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -382,8 +382,9 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { } fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { + // FIXME: When cargo is updated, change to dyn_compatibility let trait_ = from_chalk_trait_id(trait_id); - crate::object_safety::object_safety(self.db, trait_).is_none() + crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none() } fn closure_kind( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index ce5a821ea2b..5620d80adb5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -20,11 +20,11 @@ use triomphe::Arc; use crate::{ chalk_db, consteval::ConstEvalError, + dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, lower::{GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - object_safety::ObjectSafetyViolation, Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; @@ -108,8 +108,8 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Result, Arc>; - #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)] - fn object_safety_of_trait(&self, trait_: TraitId) -> Option; + #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] + fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; #[salsa::invoke(crate::lower::ty_query)] #[salsa::cycle(crate::lower::ty_recover)] @@ -280,8 +280,8 @@ pub trait HirDatabase: DefDatabase + Upcast { } #[test] -fn hir_database_is_object_safe() { - fn _assert_object_safe(_: &dyn HirDatabase) {} +fn hir_database_is_dyn_compatible() { + fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 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 new file mode 100644 index 00000000000..e0d1758210e --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -0,0 +1,615 @@ +//! Compute the dyn-compatibility of a trait + +use std::ops::ControlFlow; + +use chalk_ir::{ + cast::Cast, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, +}; +use chalk_solve::rust_ir::InlineBound; +use hir_def::{ + lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, +}; +use rustc_hash::FxHashSet; +use smallvec::SmallVec; + +use crate::{ + all_super_traits, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, + generics::{generics, trait_self_param_idx}, + lower::callable_item_sig, + to_assoc_type_id, to_chalk_trait_id, + utils::elaborate_clause_supertraits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, + ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum DynCompatibilityViolation { + SizedSelf, + SelfReferential, + Method(FunctionId, MethodViolationCode), + AssocConst(ConstId), + GAT(TypeAliasId), + // This doesn't exist in rustc, but added for better visualization + HasNonCompatibleSuperTrait(TraitId), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MethodViolationCode { + StaticMethod, + ReferencesSelfInput, + ReferencesSelfOutput, + ReferencesImplTraitInTrait, + AsyncFn, + WhereClauseReferencesSelf, + Generic, + UndispatchableReceiver, +} + +pub fn dyn_compatibility( + db: &dyn HirDatabase, + trait_: TraitId, +) -> Option { + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.dyn_compatibility_of_trait(super_trait).is_some() { + return Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)); + } + } + + db.dyn_compatibility_of_trait(trait_) +} + +pub fn dyn_compatibility_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, +{ + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.dyn_compatibility_of_trait(super_trait).is_some() { + cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?; + } + } + + dyn_compatibility_of_trait_with_callback(db, trait_, cb) +} + +pub fn dyn_compatibility_of_trait_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, +{ + // Check whether this has a `Sized` bound + if generics_require_sized_self(db, trait_.into()) { + cb(DynCompatibilityViolation::SizedSelf)?; + } + + // Check if there exist bounds that referencing self + if predicates_reference_self(db, trait_) { + cb(DynCompatibilityViolation::SelfReferential)?; + } + if bounds_reference_self(db, trait_) { + cb(DynCompatibilityViolation::SelfReferential)?; + } + + // rustc checks for non-lifetime binders here, but we don't support HRTB yet + + let trait_data = db.trait_data(trait_); + for (_, assoc_item) in &trait_data.items { + dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + } + + ControlFlow::Continue(()) +} + +pub fn dyn_compatibility_of_trait_query( + db: &dyn HirDatabase, + trait_: TraitId, +) -> Option { + let mut res = None; + dyn_compatibility_of_trait_with_callback(db, trait_, &mut |osv| { + res = Some(osv); + ControlFlow::Break(()) + }); + + res +} + +fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool { + let krate = def.module(db.upcast()).krate(); + let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else { + return false; + }; + + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + + let predicates = &*db.generic_predicates(def); + let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); + elaborate_clause_supertraits(db, predicates).any(|pred| match pred { + WhereClause::Implemented(trait_ref) => { + if from_chalk_trait_id(trait_ref.trait_id) == sized { + if let TyKind::BoundVar(it) = + *trait_ref.self_type_parameter(Interner).kind(Interner) + { + // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of + // self-parameter is `1` + return it + .index_if_bound_at(DebruijnIndex::ONE) + .is_some_and(|idx| idx == trait_self_param_idx); + } + } + false + } + _ => false, + }) +} + +// rustc gathers all the spans that references `Self` for error rendering, +// 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()) + .iter() + .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) +} + +// Same as the above, `predicates_reference_self` +fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + let trait_data = db.trait_data(trait_); + trait_data + .items + .iter() + .filter_map(|(_, it)| match *it { + AssocItemId::TypeAliasId(id) => { + let assoc_ty_id = to_assoc_type_id(id); + let assoc_ty_data = db.associated_ty_data(assoc_ty_id); + Some(assoc_ty_data) + } + _ => 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, + ) + }), + } + }) + }) +} + +#[derive(Clone, Copy)] +enum AllowSelfProjection { + Yes, + No, +} + +fn predicate_references_self( + db: &dyn HirDatabase, + trait_: TraitId, + predicate: &Binders>, + 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, + ) + }) + } + _ => 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.upcast(), 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<'a> TypeVisitor for IllegalSelfTypeVisitor<'a> { + 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.upcast(), 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 dyn_compatibility_violation_for_assoc_item( + db: &dyn HirDatabase, + trait_: TraitId, + item: AssocItemId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, +{ + // Any item that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if generics_require_sized_self(db, item.into()) { + return ControlFlow::Continue(()); + } + + match item { + AssocItemId::ConstId(it) => cb(DynCompatibilityViolation::AssocConst(it)), + AssocItemId::FunctionId(it) => { + virtual_call_violations_for_method(db, trait_, it, &mut |mvc| { + cb(DynCompatibilityViolation::Method(it, mvc)) + }) + } + AssocItemId::TypeAliasId(it) => { + let def_map = db.crate_def_map(trait_.krate(db.upcast())); + if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + ControlFlow::Continue(()) + } else { + let generic_params = db.generic_params(item.into()); + if !generic_params.is_empty() { + cb(DynCompatibilityViolation::GAT(it)) + } else { + ControlFlow::Continue(()) + } + } + } + } +} + +fn virtual_call_violations_for_method( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(MethodViolationCode) -> ControlFlow<()>, +{ + let func_data = db.function_data(func); + if !func_data.has_self_param() { + cb(MethodViolationCode::StaticMethod)?; + } + + if func_data.is_async() { + cb(MethodViolationCode::AsyncFn)?; + } + + let sig = callable_item_sig(db, 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, + ) + }) { + cb(MethodViolationCode::ReferencesSelfInput)?; + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + sig.skip_binders().ret(), + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::ReferencesSelfOutput)?; + } + + if !func_data.is_async() { + if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { + cb(mvc)?; + } + } + + let generic_params = db.generic_params(func.into()); + if generic_params.len_type_or_consts() > 0 { + cb(MethodViolationCode::Generic)?; + } + + if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) { + cb(MethodViolationCode::UndispatchableReceiver)?; + } + + let predicates = &*db.generic_predicates_without_parent(func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); + for pred in predicates { + let pred = pred.skip_binders().skip_binders(); + + if matches!(pred, WhereClause::TypeOutlives(_)) { + continue; + } + + // Allow `impl AutoTrait` predicates + if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { + let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); + if trait_data.is_auto + && substitution + .as_slice(Interner) + .first() + .and_then(|arg| arg.ty(Interner)) + .and_then(|ty| ty.bound_var(Interner)) + .is_some_and(|b| { + b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx + }) + { + continue; + } + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + pred, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::WhereClauseReferencesSelf)?; + break; + } + } + + ControlFlow::Continue(()) +} + +fn receiver_is_dispatchable( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + sig: &Binders, +) -> bool { + let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else { + return false; + }; + + // `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 } + }) + { + return true; + } + + let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db); + + let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); + let Some(receiver_ty) = substituted_sig.params().first() else { + return false; + }; + + let krate = func.module(db.upcast()).krate(); + let traits = ( + db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()), + db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()), + ); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { + 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 { + 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 trait_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(trait_), + substitution: Substitution::from_iter( + Interner, + std::iter::once(unsized_self_ty.clone().cast(Interner)) + .chain(placeholder_subst.iter(Interner).skip(1).cloned()), + ), + }); + + let generic_predicates = &*db.generic_predicates(func.into()); + + let clauses = std::iter::once(unsized_predicate) + .chain(std::iter::once(trait_predicate)) + .chain(generic_predicates.iter().map(|pred| { + pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 + })) + .map(|pred| { + pred.cast::>(Interner).into_from_env_clause(Interner) + }); + let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + + 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); + let solution = db.trait_solve(krate, None, canonicalized.quantified); + + matches!(solution, Some(Solution::Unique(_))) +} + +fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { + let generics = generics(db.upcast(), func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), 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() + } + }), + ); + let sig = callable_item_sig(db, func.into()); + let sig = sig.substitute(Interner, &subst); + sig.params_and_return.first().cloned() +} + +fn contains_illegal_impl_trait_in_trait( + db: &dyn HirDatabase, + sig: &Binders, +) -> Option { + struct OpaqueTypeCollector(FxHashSet); + + impl TypeVisitor for OpaqueTypeCollector { + 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 { + if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { + self.0.insert(*opaque_ty_id); + } + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let ret = sig.skip_binders().ret(); + let mut visitor = OpaqueTypeCollector(FxHashSet::default()); + ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + + // 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()); + if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } + } + + None +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs new file mode 100644 index 00000000000..3f3e68eeb1c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -0,0 +1,395 @@ +use std::ops::ControlFlow; + +use hir_def::db::DefDatabase; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::ToSmolStr; +use test_fixture::WithFixture; + +use crate::{dyn_compatibility::dyn_compatibility_with_callback, test_db::TestDB}; + +use super::{ + DynCompatibilityViolation, + MethodViolationCode::{self, *}, +}; + +use DynCompatibilityViolationKind::*; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum DynCompatibilityViolationKind { + SizedSelf, + SelfReferential, + Method(MethodViolationCode), + AssocConst, + GAT, + HasNonCompatibleSuperTrait, +} + +fn check_dyn_compatibility<'a>( + ra_fixture: &str, + expected: impl IntoIterator)>, +) { + let mut expected: FxHashMap<_, _> = + expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + scope + .declarations() + .filter_map(|def| { + if let hir_def::ModuleDefId::TraitId(trait_id) = def { + let name = + db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr(); + Some((trait_id, name)) + } else { + None + } + }) + .collect::>() + }) { + let Some(expected) = expected.remove(name.as_str()) else { + continue; + }; + let mut osvs = FxHashSet::default(); + dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { + osvs.insert(match osv { + DynCompatibilityViolation::SizedSelf => SizedSelf, + DynCompatibilityViolation::SelfReferential => SelfReferential, + DynCompatibilityViolation::Method(_, mvc) => Method(mvc), + DynCompatibilityViolation::AssocConst(_) => AssocConst, + DynCompatibilityViolation::GAT(_) => GAT, + DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { + HasNonCompatibleSuperTrait + } + }); + ControlFlow::Continue(()) + }); + assert_eq!(osvs, expected, "Dyn Compatibility violations for `{name}` do not match;"); + } + + let remains: Vec<_> = expected.keys().collect(); + assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}"); +} + +#[test] +fn item_bounds_can_reference_self() { + check_dyn_compatibility( + r#" +//- minicore: eq +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} +"#, + [("Foo", vec![])], + ); +} + +#[test] +fn associated_consts() { + check_dyn_compatibility( + r#" +trait Bar { + const X: usize; +} +"#, + [("Bar", vec![AssocConst])], + ); +} + +#[test] +fn bounds_reference_self() { + check_dyn_compatibility( + r#" +//- minicore: eq +trait X { + type U: PartialEq; +} +"#, + [("X", vec![SelfReferential])], + ); +} + +#[test] +fn by_value_self() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(self); +} + +trait Baz { + fn baz(self: Self); +} + +trait Quux { + // Legal because of the where clause: + fn baz(self: Self) where Self : Sized; +} +"#, + [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])], + ); +} + +#[test] +fn generic_methods() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, t: T); +} + +trait Quux { + fn bar(&self, t: T) + where Self : Sized; +} + +trait Qax { + fn bar<'a>(&self, t: &'a ()); +} +"#, + [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])], + ); +} + +#[test] +fn mentions_self() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &Self); +} + +trait Baz { + fn baz(&self) -> Self; +} + +trait Quux { + fn quux(&self, s: &Self) -> Self where Self : Sized; +} +"#, + [ + ("Bar", vec![Method(ReferencesSelfInput)]), + ("Baz", vec![Method(ReferencesSelfOutput)]), + ("Quux", vec![]), + ], + ); +} + +#[test] +fn no_static() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo() {} +} +"#, + [("Foo", vec![Method(StaticMethod)])], + ); +} + +#[test] +fn sized_self() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar: Sized { + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); + + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar + where Self : Sized +{ + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); +} + +#[test] +fn supertrait_gat() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait GatTrait { + type Gat; +} + +trait SuperTrait: GatTrait {} +"#, + [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonCompatibleSuperTrait])], + ); +} + +#[test] +fn supertrait_mentions_self() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &T); +} + +trait Baz : Bar { +} +"#, + [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + ); +} + +#[test] +fn rustc_issue_19538() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo(&self, val: T); +} + +trait Bar: Foo {} +"#, + [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonCompatibleSuperTrait])], + ); +} + +#[test] +fn rustc_issue_22040() { + check_dyn_compatibility( + r#" +//- minicore: fmt, eq, dispatch_from_dyn +use core::fmt::Debug; + +trait Expr: Debug + PartialEq { + fn print_element_count(&self); +} +"#, + [("Expr", vec![SelfReferential])], + ); +} + +#[test] +fn rustc_issue_102762() { + check_dyn_compatibility( + r#" +//- minicore: future, send, sync, dispatch_from_dyn, deref +use core::pin::Pin; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +struct Vec {} + +pub trait Fetcher: Send + Sync { + fn get<'a>(self: &'a Box) -> Pin> + 'a>> + where + Self: Sync, + { + loop {} + } +} +"#, + [("Fetcher", vec![Method(UndispatchableReceiver)])], + ); +} + +#[test] +fn rustc_issue_102933() { + check_dyn_compatibility( + r#" +//- minicore: future, dispatch_from_dyn, deref +use core::future::Future; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +pub trait Service { + type Response; + type Future: Future; +} + +pub trait A1: Service {} + +pub trait A2: Service>> + A1 { + fn foo(&self) {} +} + +pub trait B1: Service>> {} + +pub trait B2: Service + B1 { + fn foo(&self) {} +} + "#, + [("A2", vec![]), ("B2", vec![])], + ); +} + +#[test] +fn rustc_issue_106247() { + check_dyn_compatibility( + r#" +//- minicore: sync, dispatch_from_dyn +pub trait Trait { + fn method(&self) where Self: Sync; +} +"#, + [("Trait", vec![])], + ); +} + +#[test] +fn std_error_is_dyn_compatible() { + check_dyn_compatibility( + r#" +//- minicore: fmt, dispatch_from_dyn +trait Erased<'a>: 'a {} + +pub struct Request<'a>(dyn Erased<'a> + 'a); + +pub trait Error: core::fmt::Debug + core::fmt::Display { + fn provide<'a>(&'a self, request: &mut Request<'a>); +} +"#, + [("Error", vec![])], + ); +} + +#[test] +fn lifetime_gat_is_dyn_incompatible() { + check_dyn_compatibility( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + type Bar<'a>; +} +"#, + [("Foo", vec![DynCompatibilityViolationKind::GAT])], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 5ed41b99ba3..ef570a20556 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -38,11 +38,11 @@ pub mod consteval; pub mod db; pub mod diagnostics; pub mod display; +pub mod dyn_compatibility; pub mod lang_items; pub mod layout; pub mod method_resolution; pub mod mir; -pub mod object_safety; pub mod primitive; pub mod traits; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs deleted file mode 100644 index a4c66268555..00000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs +++ /dev/null @@ -1,612 +0,0 @@ -//! Compute the object-safety of a trait - -use std::ops::ControlFlow; - -use chalk_ir::{ - cast::Cast, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, - DebruijnIndex, -}; -use chalk_solve::rust_ir::InlineBound; -use hir_def::{ - lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, - TypeAliasId, -}; -use rustc_hash::FxHashSet; -use smallvec::SmallVec; - -use crate::{ - all_super_traits, - db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, - generics::{generics, trait_self_param_idx}, - lower::callable_item_sig, - to_assoc_type_id, to_chalk_trait_id, - utils::elaborate_clause_supertraits, - AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, - ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, -}; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum ObjectSafetyViolation { - SizedSelf, - SelfReferential, - Method(FunctionId, MethodViolationCode), - AssocConst(ConstId), - GAT(TypeAliasId), - // This doesn't exist in rustc, but added for better visualization - HasNonSafeSuperTrait(TraitId), -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum MethodViolationCode { - StaticMethod, - ReferencesSelfInput, - ReferencesSelfOutput, - ReferencesImplTraitInTrait, - AsyncFn, - WhereClauseReferencesSelf, - Generic, - UndispatchableReceiver, -} - -pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option { - for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { - if db.object_safety_of_trait(super_trait).is_some() { - return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait)); - } - } - - db.object_safety_of_trait(trait_) -} - -pub fn object_safety_with_callback( - db: &dyn HirDatabase, - trait_: TraitId, - cb: &mut F, -) -> ControlFlow<()> -where - F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, -{ - for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { - if db.object_safety_of_trait(super_trait).is_some() { - cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?; - } - } - - object_safety_of_trait_with_callback(db, trait_, cb) -} - -pub fn object_safety_of_trait_with_callback( - db: &dyn HirDatabase, - trait_: TraitId, - cb: &mut F, -) -> ControlFlow<()> -where - F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, -{ - // Check whether this has a `Sized` bound - if generics_require_sized_self(db, trait_.into()) { - cb(ObjectSafetyViolation::SizedSelf)?; - } - - // Check if there exist bounds that referencing self - if predicates_reference_self(db, trait_) { - cb(ObjectSafetyViolation::SelfReferential)?; - } - if bounds_reference_self(db, trait_) { - cb(ObjectSafetyViolation::SelfReferential)?; - } - - // rustc checks for non-lifetime binders here, but we don't support HRTB yet - - let trait_data = db.trait_data(trait_); - for (_, assoc_item) in &trait_data.items { - object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; - } - - ControlFlow::Continue(()) -} - -pub fn object_safety_of_trait_query( - db: &dyn HirDatabase, - trait_: TraitId, -) -> Option { - let mut res = None; - object_safety_of_trait_with_callback(db, trait_, &mut |osv| { - res = Some(osv); - ControlFlow::Break(()) - }); - - res -} - -fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool { - let krate = def.module(db.upcast()).krate(); - let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else { - return false; - }; - - let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { - return false; - }; - - let predicates = &*db.generic_predicates(def); - let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); - elaborate_clause_supertraits(db, predicates).any(|pred| match pred { - WhereClause::Implemented(trait_ref) => { - if from_chalk_trait_id(trait_ref.trait_id) == sized { - if let TyKind::BoundVar(it) = - *trait_ref.self_type_parameter(Interner).kind(Interner) - { - // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of - // self-parameter is `1` - return it - .index_if_bound_at(DebruijnIndex::ONE) - .is_some_and(|idx| idx == trait_self_param_idx); - } - } - false - } - _ => false, - }) -} - -// rustc gathers all the spans that references `Self` for error rendering, -// 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()) - .iter() - .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) -} - -// Same as the above, `predicates_reference_self` -fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - let trait_data = db.trait_data(trait_); - trait_data - .items - .iter() - .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => { - let assoc_ty_id = to_assoc_type_id(id); - let assoc_ty_data = db.associated_ty_data(assoc_ty_id); - Some(assoc_ty_data) - } - _ => 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, - ) - }), - } - }) - }) -} - -#[derive(Clone, Copy)] -enum AllowSelfProjection { - Yes, - No, -} - -fn predicate_references_self( - db: &dyn HirDatabase, - trait_: TraitId, - predicate: &Binders>, - 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, - ) - }) - } - _ => 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.upcast(), 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<'a> TypeVisitor for IllegalSelfTypeVisitor<'a> { - 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.upcast(), 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 object_safety_violation_for_assoc_item( - db: &dyn HirDatabase, - trait_: TraitId, - item: AssocItemId, - cb: &mut F, -) -> ControlFlow<()> -where - F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, -{ - // Any item that has a `Self : Sized` requisite is otherwise - // exempt from the regulations. - if generics_require_sized_self(db, item.into()) { - return ControlFlow::Continue(()); - } - - match item { - AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)), - AssocItemId::FunctionId(it) => { - virtual_call_violations_for_method(db, trait_, it, &mut |mvc| { - cb(ObjectSafetyViolation::Method(it, mvc)) - }) - } - AssocItemId::TypeAliasId(it) => { - let def_map = db.crate_def_map(trait_.krate(db.upcast())); - if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { - ControlFlow::Continue(()) - } else { - let generic_params = db.generic_params(item.into()); - if !generic_params.is_empty() { - cb(ObjectSafetyViolation::GAT(it)) - } else { - ControlFlow::Continue(()) - } - } - } - } -} - -fn virtual_call_violations_for_method( - db: &dyn HirDatabase, - trait_: TraitId, - func: FunctionId, - cb: &mut F, -) -> ControlFlow<()> -where - F: FnMut(MethodViolationCode) -> ControlFlow<()>, -{ - let func_data = db.function_data(func); - if !func_data.has_self_param() { - cb(MethodViolationCode::StaticMethod)?; - } - - if func_data.is_async() { - cb(MethodViolationCode::AsyncFn)?; - } - - let sig = callable_item_sig(db, 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, - ) - }) { - cb(MethodViolationCode::ReferencesSelfInput)?; - } - - if contains_illegal_self_type_reference( - db, - func.into(), - trait_, - sig.skip_binders().ret(), - DebruijnIndex::INNERMOST, - AllowSelfProjection::Yes, - ) { - cb(MethodViolationCode::ReferencesSelfOutput)?; - } - - if !func_data.is_async() { - if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { - cb(mvc)?; - } - } - - let generic_params = db.generic_params(func.into()); - if generic_params.len_type_or_consts() > 0 { - cb(MethodViolationCode::Generic)?; - } - - if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) { - cb(MethodViolationCode::UndispatchableReceiver)?; - } - - let predicates = &*db.generic_predicates_without_parent(func.into()); - let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); - for pred in predicates { - let pred = pred.skip_binders().skip_binders(); - - if matches!(pred, WhereClause::TypeOutlives(_)) { - continue; - } - - // Allow `impl AutoTrait` predicates - if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { - let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); - if trait_data.is_auto - && substitution - .as_slice(Interner) - .first() - .and_then(|arg| arg.ty(Interner)) - .and_then(|ty| ty.bound_var(Interner)) - .is_some_and(|b| { - b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx - }) - { - continue; - } - } - - if contains_illegal_self_type_reference( - db, - func.into(), - trait_, - pred, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) { - cb(MethodViolationCode::WhereClauseReferencesSelf)?; - break; - } - } - - ControlFlow::Continue(()) -} - -fn receiver_is_dispatchable( - db: &dyn HirDatabase, - trait_: TraitId, - func: FunctionId, - sig: &Binders, -) -> bool { - let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else { - return false; - }; - - // `self: Self` can't be dispatched on, but this is already considered object safe. - // 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 } - }) - { - return true; - } - - let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db); - - let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); - let Some(receiver_ty) = substituted_sig.params().first() else { - return false; - }; - - let krate = func.module(db.upcast()).krate(); - let traits = ( - db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()), - db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()), - ); - let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { - 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 { - 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 trait_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: Substitution::from_iter( - Interner, - std::iter::once(unsized_self_ty.clone().cast(Interner)) - .chain(placeholder_subst.iter(Interner).skip(1).cloned()), - ), - }); - - let generic_predicates = &*db.generic_predicates(func.into()); - - let clauses = std::iter::once(unsized_predicate) - .chain(std::iter::once(trait_predicate)) - .chain(generic_predicates.iter().map(|pred| { - pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 - })) - .map(|pred| { - pred.cast::>(Interner).into_from_env_clause(Interner) - }); - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - - 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); - let solution = db.trait_solve(krate, None, canonicalized.quantified); - - matches!(solution, Some(Solution::Unique(_))) -} - -fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { - let generics = generics(db.upcast(), func.into()); - let trait_self_idx = trait_self_param_idx(db.upcast(), 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() - } - }), - ); - let sig = callable_item_sig(db, func.into()); - let sig = sig.substitute(Interner, &subst); - sig.params_and_return.first().cloned() -} - -fn contains_illegal_impl_trait_in_trait( - db: &dyn HirDatabase, - sig: &Binders, -) -> Option { - struct OpaqueTypeCollector(FxHashSet); - - impl TypeVisitor for OpaqueTypeCollector { - 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 { - if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { - self.0.insert(*opaque_ty_id); - } - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - - let ret = sig.skip_binders().ret(); - let mut visitor = OpaqueTypeCollector(FxHashSet::default()); - ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); - - // 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()); - if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { - return Some(MethodViolationCode::ReferencesImplTraitInTrait); - } - } - - None -} - -#[cfg(test)] -mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs deleted file mode 100644 index c2a9117c5be..00000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs +++ /dev/null @@ -1,393 +0,0 @@ -use std::ops::ControlFlow; - -use hir_def::db::DefDatabase; -use rustc_hash::{FxHashMap, FxHashSet}; -use syntax::ToSmolStr; -use test_fixture::WithFixture; - -use crate::{object_safety::object_safety_with_callback, test_db::TestDB}; - -use super::{ - MethodViolationCode::{self, *}, - ObjectSafetyViolation, -}; - -use ObjectSafetyViolationKind::*; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum ObjectSafetyViolationKind { - SizedSelf, - SelfReferential, - Method(MethodViolationCode), - AssocConst, - GAT, - HasNonSafeSuperTrait, -} - -fn check_object_safety<'a>( - ra_fixture: &str, - expected: impl IntoIterator)>, -) { - let mut expected: FxHashMap<_, _> = - expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect(); - let (db, file_ids) = TestDB::with_many_files(ra_fixture); - for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| { - let module_id = db.module_for_file(file_id); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id.local_id].scope; - scope - .declarations() - .filter_map(|def| { - if let hir_def::ModuleDefId::TraitId(trait_id) = def { - let name = - db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr(); - Some((trait_id, name)) - } else { - None - } - }) - .collect::>() - }) { - let Some(expected) = expected.remove(name.as_str()) else { - continue; - }; - let mut osvs = FxHashSet::default(); - object_safety_with_callback(&db, trait_id, &mut |osv| { - osvs.insert(match osv { - ObjectSafetyViolation::SizedSelf => SizedSelf, - ObjectSafetyViolation::SelfReferential => SelfReferential, - ObjectSafetyViolation::Method(_, mvc) => Method(mvc), - ObjectSafetyViolation::AssocConst(_) => AssocConst, - ObjectSafetyViolation::GAT(_) => GAT, - ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait, - }); - ControlFlow::Continue(()) - }); - assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;"); - } - - let remains: Vec<_> = expected.keys().collect(); - assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}"); -} - -#[test] -fn item_bounds_can_reference_self() { - check_object_safety( - r#" -//- minicore: eq -pub trait Foo { - type X: PartialEq; - type Y: PartialEq; - type Z: PartialEq; -} -"#, - [("Foo", vec![])], - ); -} - -#[test] -fn associated_consts() { - check_object_safety( - r#" -trait Bar { - const X: usize; -} -"#, - [("Bar", vec![AssocConst])], - ); -} - -#[test] -fn bounds_reference_self() { - check_object_safety( - r#" -//- minicore: eq -trait X { - type U: PartialEq; -} -"#, - [("X", vec![SelfReferential])], - ); -} - -#[test] -fn by_value_self() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar { - fn bar(self); -} - -trait Baz { - fn baz(self: Self); -} - -trait Quux { - // Legal because of the where clause: - fn baz(self: Self) where Self : Sized; -} -"#, - [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])], - ); -} - -#[test] -fn generic_methods() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar { - fn bar(&self, t: T); -} - -trait Quux { - fn bar(&self, t: T) - where Self : Sized; -} - -trait Qax { - fn bar<'a>(&self, t: &'a ()); -} -"#, - [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])], - ); -} - -#[test] -fn mentions_self() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar { - fn bar(&self, x: &Self); -} - -trait Baz { - fn baz(&self) -> Self; -} - -trait Quux { - fn quux(&self, s: &Self) -> Self where Self : Sized; -} -"#, - [ - ("Bar", vec![Method(ReferencesSelfInput)]), - ("Baz", vec![Method(ReferencesSelfOutput)]), - ("Quux", vec![]), - ], - ); -} - -#[test] -fn no_static() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Foo { - fn foo() {} -} -"#, - [("Foo", vec![Method(StaticMethod)])], - ); -} - -#[test] -fn sized_self() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar: Sized { - fn bar(&self, t: T); -} -"#, - [("Bar", vec![SizedSelf])], - ); - - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar - where Self : Sized -{ - fn bar(&self, t: T); -} -"#, - [("Bar", vec![SizedSelf])], - ); -} - -#[test] -fn supertrait_gat() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait GatTrait { - type Gat; -} - -trait SuperTrait: GatTrait {} -"#, - [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])], - ); -} - -#[test] -fn supertrait_mentions_self() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Bar { - fn bar(&self, x: &T); -} - -trait Baz : Bar { -} -"#, - [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], - ); -} - -#[test] -fn rustc_issue_19538() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Foo { - fn foo(&self, val: T); -} - -trait Bar: Foo {} -"#, - [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])], - ); -} - -#[test] -fn rustc_issue_22040() { - check_object_safety( - r#" -//- minicore: fmt, eq, dispatch_from_dyn -use core::fmt::Debug; - -trait Expr: Debug + PartialEq { - fn print_element_count(&self); -} -"#, - [("Expr", vec![SelfReferential])], - ); -} - -#[test] -fn rustc_issue_102762() { - check_object_safety( - r#" -//- minicore: future, send, sync, dispatch_from_dyn, deref -use core::pin::Pin; - -struct Box {} -impl core::ops::Deref for Box { - type Target = T; - - fn deref(&self) -> &Self::Target { - loop {} - } -} -impl, U: ?Sized> DispatchFromDyn> for Box {} - -struct Vec {} - -pub trait Fetcher: Send + Sync { - fn get<'a>(self: &'a Box) -> Pin> + 'a>> - where - Self: Sync, - { - loop {} - } -} -"#, - [("Fetcher", vec![Method(UndispatchableReceiver)])], - ); -} - -#[test] -fn rustc_issue_102933() { - check_object_safety( - r#" -//- minicore: future, dispatch_from_dyn, deref -use core::future::Future; - -struct Box {} -impl core::ops::Deref for Box { - type Target = T; - - fn deref(&self) -> &Self::Target { - loop {} - } -} -impl, U: ?Sized> DispatchFromDyn> for Box {} - -pub trait Service { - type Response; - type Future: Future; -} - -pub trait A1: Service {} - -pub trait A2: Service>> + A1 { - fn foo(&self) {} -} - -pub trait B1: Service>> {} - -pub trait B2: Service + B1 { - fn foo(&self) {} -} - "#, - [("A2", vec![]), ("B2", vec![])], - ); -} - -#[test] -fn rustc_issue_106247() { - check_object_safety( - r#" -//- minicore: sync, dispatch_from_dyn -pub trait Trait { - fn method(&self) where Self: Sync; -} -"#, - [("Trait", vec![])], - ); -} - -#[test] -fn std_error_is_object_safe() { - check_object_safety( - r#" -//- minicore: fmt, dispatch_from_dyn -trait Erased<'a>: 'a {} - -pub struct Request<'a>(dyn Erased<'a> + 'a); - -pub trait Error: core::fmt::Debug + core::fmt::Display { - fn provide<'a>(&'a self, request: &mut Request<'a>); -} -"#, - [("Error", vec![])], - ); -} - -#[test] -fn lifetime_gat_is_object_unsafe() { - check_object_safety( - r#" -//- minicore: dispatch_from_dyn -trait Foo { - type Bar<'a>; -} -"#, - [("Foo", vec![ObjectSafetyViolationKind::GAT])], - ); -} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8f5db32f957..e2cf5b07897 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -144,9 +144,9 @@ pub use { hir_ty::{ consteval::ConstEvalError, display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, - object_safety::{MethodViolationCode, ObjectSafetyViolation}, CastError, FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir @@ -2690,8 +2690,8 @@ impl Trait { .count() } - pub fn object_safety(&self, db: &dyn HirDatabase) -> Option { - hir_ty::object_safety::object_safety(db, self.id) + pub fn dyn_compatibility(&self, db: &dyn HirDatabase) -> Option { + hir_ty::dyn_compatibility::dyn_compatibility(db, self.id) } fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index abf4438a71f..266109765ab 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -7081,8 +7081,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp "##, }, Lint { - label: "object_safe_for_dispatch", - description: r##"# `object_safe_for_dispatch` + label: "dyn_compatible_for_dispatch", + description: r##"# `dyn_compatible_for_dispatch` The tracking issue for this feature is: [#43561] diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 83adf6548a8..5a8c9a9f11f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,9 +3,9 @@ use std::{mem, ops::Not}; use either::Either; use hir::{ - db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, - HirDisplay, Layout, LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, - Trait, Type, TypeInfo, + db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, + DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError, + MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -529,10 +529,10 @@ pub(super) fn definition( _ => None, }; - let object_safety_info = if let Definition::Trait(it) = def { - let mut object_safety_info = String::new(); - render_object_safety(db, &mut object_safety_info, it.object_safety(db)); - Some(object_safety_info) + let dyn_compatibility_info = if let Definition::Trait(it) = def { + let mut dyn_compatibility_info = String::new(); + render_dyn_compatibility(db, &mut dyn_compatibility_info, it.dyn_compatibility(db)); + Some(dyn_compatibility_info) } else { None }; @@ -546,8 +546,8 @@ pub(super) fn definition( desc.push_str(&layout_info); desc.push('\n'); } - if let Some(object_safety_info) = object_safety_info { - desc.push_str(&object_safety_info); + if let Some(dyn_compatibility_info) = dyn_compatibility_info { + desc.push_str(&dyn_compatibility_info); desc.push('\n'); } desc.push_str(&label); @@ -980,24 +980,24 @@ fn keyword_hints( } } -fn render_object_safety( +fn render_dyn_compatibility( db: &RootDatabase, buf: &mut String, - safety: Option, + safety: Option, ) { let Some(osv) = safety else { - buf.push_str("// Object Safety: Yes"); + buf.push_str("// Dyn Compatible: Yes"); return; }; - buf.push_str("// Object Safety: No\n// - Reason: "); + buf.push_str("// Dyn Compatible: No\n// - Reason: "); match osv { - ObjectSafetyViolation::SizedSelf => { + DynCompatibilityViolation::SizedSelf => { buf.push_str("has a `Self: Sized` bound"); } - ObjectSafetyViolation::SelfReferential => { + DynCompatibilityViolation::SelfReferential => { buf.push_str("has a bound that references `Self`"); } - ObjectSafetyViolation::Method(func, mvc) => { + DynCompatibilityViolation::Method(func, mvc) => { let name = hir::Function::from(func).name(db); format_to!( buf, @@ -1020,7 +1020,7 @@ fn render_object_safety( }; buf.push_str(desc); } - ObjectSafetyViolation::AssocConst(const_) => { + DynCompatibilityViolation::AssocConst(const_) => { let name = hir::Const::from(const_).name(db); if let Some(name) = name { format_to!(buf, "has an associated constant `{}`", name.as_str()); @@ -1028,11 +1028,11 @@ fn render_object_safety( buf.push_str("has an associated constant"); } } - ObjectSafetyViolation::GAT(alias) => { + DynCompatibilityViolation::GAT(alias) => { let name = hir::TypeAlias::from(alias).name(db); format_to!(buf, "has a generic associated type `{}`", name.as_str()); } - ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => { + DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait) => { let name = hir::Trait::from(super_trait).name(db); format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index cca62d2181f..db2b98090cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -7175,7 +7175,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: Yes + // Dyn Compatible: Yes trait T {} ``` "#]], @@ -7195,7 +7195,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: Yes + // Dyn Compatible: Yes trait T {} ``` "#]], @@ -7219,7 +7219,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: No + // Dyn Compatible: No // - Reason: has a method `func` that is non dispatchable because of: // - missing a receiver trait T { /* … */ } @@ -7245,7 +7245,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: No + // Dyn Compatible: No // - Reason: has a method `func` that is non dispatchable because of: // - missing a receiver trait T { @@ -7275,7 +7275,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: No + // Dyn Compatible: No // - Reason: has a method `func` that is non dispatchable because of: // - missing a receiver trait T { @@ -7305,7 +7305,7 @@ impl T$0 for () {} ``` ```rust - // Object Safety: No + // Dyn Compatible: No // - Reason: has a method `func` that is non dispatchable because of: // - missing a receiver trait T { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 3ce9afa1dec..32b1f5f7544 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -167,7 +167,7 @@ mod support { } #[test] -fn assert_ast_is_object_safe() { +fn assert_ast_is_dyn_compatible() { fn _f(_: &dyn AstNode, _: &dyn HasName) {} } diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs index f24354cb493..c49e4c4322d 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs @@ -1,4 +1,4 @@ -//! Object safe interface for file watching and reading. +//! Dynamically compatible interface for file watching and reading. use std::fmt; use paths::{AbsPath, AbsPathBuf}; @@ -232,6 +232,6 @@ impl fmt::Debug for Message { } #[test] -fn handle_is_object_safe() { +fn handle_is_dyn_compatible() { fn _assert(_: &dyn Handle) {} } diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md index 6c4daecc58f..3dcd430cea5 100644 --- a/src/tools/rust-analyzer/docs/dev/syntax.md +++ b/src/tools/rust-analyzer/docs/dev/syntax.md @@ -378,7 +378,7 @@ impl AstNode for AssocItem { } ``` -Shared AST substructures are modeled via (object safe) traits: +Shared AST substructures are modeled via (dynamically compatible) traits: ```rust trait HasVisibility: AstNode { -- cgit 1.4.1-3-g733a5