Auto merge of #18093 - ShoyuVanilla:skip-dyn-trait-cast-check, 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 #18047 and #18083
This commit is contained in:
bors 2024-09-11 07:14:58 +00:00
commit 7d8c5ad5e6
3 changed files with 139 additions and 0 deletions

View File

@ -120,6 +120,13 @@ pub(super) fn check<F, G>(
});
}
// 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);
@ -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()
}

View File

@ -902,6 +902,10 @@ pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
/// 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() {

View File

@ -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(
@ -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;
}
"#,
);
}
}