Skip to content

Commit

Permalink
Auto merge of rust-lang#18093 - ShoyuVanilla:skip-dyn-trait-cast-chec…
Browse files Browse the repository at this point in the history
…k, r=Veykril

Skip checks for cast to dyn traits

It seems that chalk fails to solve some obvious goals when there are some recursiveness in trait environments.
And it doesn't support trait upcasting yet. rust-lang/chalk#796

This PR just skips for casting into types containing `dyn Trait` to prevent false positive diagnostics like rust-lang#18047 and rust-lang#18083
  • Loading branch information
bors committed Sep 11, 2024
2 parents 14a4f07 + a169a5b commit 7d8c5ad
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ impl CastCheck {
});
}

// Chalk doesn't support trait upcasting and fails to solve some obvious goals
// when the trait environment contains some recursive traits (See issue #18047)
// We skip cast checks for such cases for now, until the next-gen solver.
if contains_dyn_trait(&self.cast_ty) {
return Ok(());
}

if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) {
apply_adjustments(self.source_expr, adj);
set_coercion_cast(self.source_expr);
Expand Down Expand Up @@ -410,3 +417,35 @@ fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<Pointe
}
}
}

fn contains_dyn_trait(ty: &Ty) -> bool {
use std::ops::ControlFlow;

use chalk_ir::{
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex,
};

struct DynTraitVisitor;

impl TypeVisitor<Interner> for DynTraitVisitor {
type BreakTy = ();

fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
self
}

fn interner(&self) -> Interner {
Interner
}

fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow<Self::BreakTy> {
match ty.kind(Interner) {
TyKind::Dyn(_) => ControlFlow::Break(()),
_ => ty.super_visit_with(self.as_dyn(), outer_binder),
}
}
}

ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break()
}
4 changes: 4 additions & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,10 @@ impl<'a> InferenceTable<'a> {

/// Check if given type is `Sized` or not
pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
// Early return for some obvious types
if matches!(ty.kind(Interner), TyKind::Scalar(..) | TyKind::Ref(..) | TyKind::Raw(..)) {
return true;
}
if let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
let struct_data = self.db.struct_data(id);
if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) {
);
}

#[ignore = "issue #18047"]
#[test]
fn ptr_to_trait_obj_wrap_upcast() {
check_diagnostics(
Expand Down Expand Up @@ -1004,4 +1005,99 @@ fn _slice(bar: &[i32]) -> bool {
&["E0308"],
);
}

#[test]
fn trait_upcasting() {
check_diagnostics(
r#"
//- minicore: coerce_unsized, dispatch_from_dyn
#![feature(trait_upcasting)]
trait Foo {}
trait Bar: Foo {}
impl dyn Bar {
fn bar(&self) {
_ = self as &dyn Foo;
}
}
"#,
);
}

#[test]
fn issue_18047() {
check_diagnostics(
r#"
//- minicore: coerce_unsized, dispatch_from_dyn
trait LocalFrom<T> {
fn from(_: T) -> Self;
}
trait LocalInto<T> {
fn into(self) -> T;
}
impl<T, U> LocalInto<U> for T
where
U: LocalFrom<T>,
{
fn into(self) -> U {
U::from(self)
}
}
impl<T> LocalFrom<T> for T {
fn from(t: T) -> T {
t
}
}
trait Foo {
type ErrorType;
type Assoc;
}
trait Bar {
type ErrorType;
}
struct ErrorLike;
impl<E> LocalFrom<E> for ErrorLike
where
E: Trait + 'static,
{
fn from(_: E) -> Self {
loop {}
}
}
trait Baz {
type Assoc: Bar;
type Error: LocalInto<ErrorLike>;
}
impl<T, U> Baz for T
where
T: Foo<Assoc = U>,
T::ErrorType: LocalInto<ErrorLike>,
U: Bar,
<U as Bar>::ErrorType: LocalInto<ErrorLike>,
{
type Assoc = U;
type Error = T::ErrorType;
}
struct S;
trait Trait {}
impl Trait for S {}
fn test<T>()
where
T: Baz,
T::Assoc: 'static,
{
let _ = &S as &dyn Trait;
}
"#,
);
}
}

0 comments on commit 7d8c5ad

Please sign in to comment.