Description
This only affects RPIT(IT), not TAIT or ATPIT.
Fixing this issue would be a breaking change.
This exploits the issue #132212 (syntactically rejecting impl-Trait inside non-final path segments & inside fn ptr types is futile).
All subsequent snippets assume the following definitions:
type DiscardT<T> = <T as Discard>::Output;
trait Discard { type Output; }
impl<T: ?Sized> Discard for T { type Output = Point; } // you can substitute `Point` with `()` everywhere, this is just for clarity
struct Point;
trait NobodyImplsThis {}
RPIT check-pass reproducer (playground):
fn demo0() -> DiscardT<impl ?Sized> { Point }
RPIT check-fail reproducer (playground):
fn demo1() -> DiscardT<impl NobodyImplsThis + ?Sized> { Point }
//~^ ERROR the trait bound `(): NobodyImplsThis` is not satisfied
I expected both reproducers to get rejected by the compiler due to uninferable types or unconstrained opaque types (with an error roughly of the form item does not constrain demoN::{opaque#0}
, but has it in its signature (mirroring TAIT/ATPIT)). The opaque types should be unconstrained because DiscardT<{opaque}>
normalizes to Point
which does not contain any opaque types.
However, in both cases the hidden type gets inferred to be ()
. See also the compiler log for the first reproducer:
rustc_hir_analysis::check::check::check_opaque_meets_bounds def_id=DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), span=oi.rs:6:24: 6:35 (#4), origin=FnReturn { parent: DefId(0:12 ~ oi[08b9]::demo0), in_trait_or_impl: None }
rustc_middle::ty::sty::new_opaque def_id=DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args=[]
rustc_infer::infer::opaque_types::register_hidden_type opaque_type_key=OpaqueTypeKey { def_id: DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args: [] }, span=oi.rs:6:24: 6:35 (#4), param_env=ParamEnv { caller_bounds: [], reveal: UserFacing }, hidden_ty=()
rustc_infer::infer::opaque_types::table::register key=OpaqueTypeKey { def_id: DefId(0:13 ~ oi[08b9]::demo0::{opaque#0}), args: [] }, hidden_type=OpaqueHiddenType { span: oi.rs:6:24: 6:35 (#4), ty: () }
It's odd that some sort of type inference fallback occurs (in case you're wondering: In Rust 2024, it also falls back to ()
, not !
(since we don't have anything diverging here)).
Related: The fixed issue #96460 (PhantomData<{opaque}>
).
Ranking this P-low because it's pretty artificial and not unsound.
For completeness, RPITIT reproducers
trait Demo2 { fn f() -> DiscardT<impl ?Sized> { Point } }
trait Demo3 { fn f() -> DiscardT<impl NobodyImplsThis + ?Sized> { Point } }
//~^ ERROR the trait bound `(): NobodyImplsThis` is not satisfied
For comparison, the TAIT and ATPIT analogues result in an error (though I'm not sure if that's an interesting comparison to draw).
TAIT/ATPIT
#![feature(type_alias_impl_trait, impl_trait_in_assoc_type)]
type Weak0 = impl ?Sized;
fn demo4() -> DiscardT<Weak0> { Point }
//~^ ERROR item does not constrain `Weak0::{opaque#0}`, but has it in its signature
type Weak1 = DiscardT<impl ?Sized>;
fn demo5() -> Weak1 { Point }
//~^ ERROR item does not constrain `Weak1::{opaque#0}`, but has it in its signature
trait Demo6 {
type Proj: ?Sized;
fn f() -> DiscardT<Self::Proj>;
}
impl Demo6 for i32 {
type Proj = impl ?Sized;
fn f() -> DiscardT<Self::Proj> { Point }
//~^ ERROR item does not constrain `<i32 as Demo6>::Proj::{opaque#0}`, but has it in its signature
}
trait Demo7 {
type Proj;
fn f() -> Self::Proj;
}
impl Demo7 for i32 {
type Proj = DiscardT<impl ?Sized>;
fn f() -> Self::Proj { Point }
//~^ ERROR item does not constrain `<i32 as Demo7>::Proj::{opaque#0}`, but has it in its signature
}